前言

今天闲来无事就将上个月写的一款小的app介绍一下吧,这款app是用来下载小说,小说来源是无限小说网,原理自然是直接扒掉了人家 的html页面,然后从html源码里面寻找出来小说的下载地址,里面用到了三个开源库,一个是设置状态栏变色的(有bug,不太通用),另一个是下拉刷新的开源库,第三个解析xml的库jsoup(源代码在文末

app效果图






源码介绍

下面简单介绍一下源码:

  • 自定义进度条:
    自定义进度条可以参考我前面的代码

http://blog.csdn.net/supervictim/article/details/53907393

我在这个基础上增加了自定义属性,用来设置旋转的进度条图片和文字,使用过程中变成了如下形式:

 <cn.karent.downloadtxt.UI.ProgressViewInResultandroid:id="@+id/result_progress"android:layout_centerInParent="true"android:layout_width="70dp"android:layout_height="70dp"wan:bitmap="@drawable/pic_dlg_loading"wan:text="载"/>

自定义属性需要在values文件夹中创建一个叫做attrs.xml的文件:

<resources><declare-styleable name="ProgressBitmap"><!--显示的是哪个背景图的id--><attr name="bitmap" format="reference"/><!--要绘制在中心的字符串--><attr name="text" format="string"/></declare-styleable></resources>

这样就可以在上面的使用了,下面搬上最终的进度条代码:

public class ProgressViewInResult extends View {private Bitmap mBitmap;private Matrix mMatrix;private int mCurrent = 0;private Progress mProgress;private boolean mContinue = true;private Paint mTextPaint;private String mText;public ProgressViewInResult(Context context) {super(context);init();}public ProgressViewInResult(Context context, AttributeSet attrs) {super(context, attrs);mTextPaint = new Paint();mTextPaint.setTextSize(ScreenUtil.dp2px(18));mTextPaint.setColor(Color.rgb(0xa6, 0xa6, 0xa6));//获取自定义属性TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ProgressBitmap);if( typedArray != null) {int res = typedArray.getResourceId(R.styleable.ProgressBitmap_bitmap, R.drawable.abs__spinner_48_inner_holo);mBitmap = BitmapFactory.decodeResource(context.getResources(), res);mText = typedArray.getString(R.styleable.ProgressBitmap_text);} else {mBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.abs__spinner_48_inner_holo);}init();}private void init() {mMatrix = new Matrix();mProgress = new Progress();//执行操作mProgress.execute();}public Bitmap getmBitmap() {return mBitmap;}public void setmBitmap(Bitmap mBitmap) {this.mBitmap = mBitmap;}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);mMatrix.setRotate( (mCurrent ++) * 6, mBitmap.getWidth() / 2, mBitmap.getHeight() / 2);//旋转之后将图片缩小mMatrix.postScale(0.7f, 0.7f);canvas.drawBitmap(mBitmap, mMatrix, null);//绘制文字if( mText == null)return;Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();float strWidth = mTextPaint.measureText(mText);float y = mBitmap.getHeight() * 0.7f / 2;y += (Math.abs(fontMetrics.ascent) - fontMetrics.descent) / 2;float x = ( mBitmap.getWidth() * 0.7f - strWidth ) / 2;canvas.drawText(mText, x, y, mTextPaint);}public void setContinue(boolean c) {mContinue = c;}class Progress extends AsyncTask<Void, Void, Void> {@Overrideprotected void onProgressUpdate(Void... values) {super.onProgressUpdate(values);invalidate();}@Overrideprotected Void doInBackground(Void... params) {while( mContinue ) {try {Thread.sleep(50);} catch (Exception e) {e.printStackTrace();}publishProgress();}return null;}}
}
  • 下载功能:创建了一个service(service的执行也是在UI线程里面)在后台下载。
    DownloadService.java
public class DownloadService extends Service {private NotificationManager mNotifyManager;private DownloadBinder mBinder = new DownloadBinder();private NotificationCompat.Builder mBuilder;private Integer mLength = 0;private Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {if( msg.what == 0x123) {int length = (Integer)msg.obj;mBuilder.setProgress(100, length, false);mBuilder.setContentText(length + "%");if( length != 100) {mBuilder.setContentInfo("正在下载...");} else {mBuilder.setContentInfo("下载完成!");}mNotifyManager.notify(0, mBuilder.build());}}};@Overridepublic void onCreate() {
Log.d("service", "onCreate....");mNotifyManager = (NotificationManager) getSystemService(Activity.NOTIFICATION_SERVICE);mBuilder = new NotificationCompat.Builder(this);mBuilder.setProgress(100,0,false).setSmallIcon(R.drawable.paper_flight).setContentInfo("下载中...").setContentTitle("正在下载");super.onCreate();}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {
Log.d("service", "onStartCommand.....");return super.onStartCommand(intent, flags, startId);}@Nullable@Overridepublic IBinder onBind(Intent intent) {return mBinder;}public class DownloadBinder extends Binder {public void startDownload(final String url,final String name) {if( url == null) {Toast.makeText(DownloadService.this, "sorry,下载地址没有找到!", Toast.LENGTH_SHORT).show();return;}new Thread(new Runnable() {@Overridepublic void run() {//开启事件循环,使线程能够显示UI提示Looper.prepare();//下载小说Http.downloadTxt(url, name, new RefreshListenerImpl());Looper.loop();}}).start();}}public class RefreshListenerImpl implements RefreshListener {@Overridepublic void refresh(int length) {Message msg = new Message();msg.what = 0x123;msg.obj = length;mHandler.sendMessage(msg);}@Overridepublic void finish() {}}}

Http.downloadTxt():

public static void downloadTxt(String url, String name, DownloadService.RefreshListenerImpl refresh) {String path = "/mnt/sdcard/小说下载/";String filePath = path + name + ".zip";try {File directory = new File(path);if( !directory.exists() )directory.mkdirs();File file = new File(filePath);if( !file.exists())file.createNewFile();FileOutputStream fos = new FileOutputStream(filePath);URL u = new URL(url);HttpURLConnection conn =  (HttpURLConnection) u.openConnection();conn.setRequestMethod("GET");conn.setRequestProperty("Charset", "UTF-8");conn.setDoInput(true);//禁止转发conn.setInstanceFollowRedirects(false);conn.connect();//获取转发后的真正地址String location = conn.getHeaderField("Location");//说明下载地址不存在if( location == null) {Toast.makeText(MyApplication.getContext(), "小说不存在!", Toast.LENGTH_SHORT).show();return;}//小米手机获取的中文地址是以ISO-8859-1为编码的,需要将其转换为utf8,否则找不到真正的下载地址
//            location = new String(location.getBytes("ISO-8859-1"), "utf8");
System.out.println("location:" + location);String url1 = StringUtil.encodeUrl(location);url1 += ".zip";
//System.out.println("url1:" + url1);URL u2 = new URL(url1);HttpURLConnection conn1 =(HttpURLConnection) u2.openConnection();InputStream is = conn1.getInputStream();//获取文件长度int fileLength = conn1.getContentLength();byte[] bytes = new byte[1024];int total = 0;int length = -1;int currentProgress = 0;int downloadPrecent = 0;while( (length = is.read(bytes)) != -1) {
//System.out.println("bytes:" + bytes);total += length;//更新进度//卡顿解决方案,当进度积累到一定量才更新,不要频繁调用更新currentProgress = total * 100 / fileLength;if( currentProgress - downloadPrecent >= 2) {refresh.refresh(currentProgress);downloadPrecent = currentProgress;}fos.write(bytes, 0, length);}is.close();fos.close();
System.out.println("下载完成...");} catch (Exception e) {e.printStackTrace();}}

在真正下载小说的时候我获取到了下载地址,点击之后会发现无限小说网的后台重定向了:


如上图,在获取到了访问地址之后返回的状态码是302,而真正的地址就藏在返回头的Location字段里面,所以下载的代码才需要获取Location字段进而进行真正的下载

          String location = conn.getHeaderField("Location");//说明下载地址不存在if( location == null) {Toast.makeText(MyApplication.getContext(), "小说不存在!", Toast.LENGTH_SHORT).show();return;}//小米手机获取的中文地址是以ISO-8859-1为编码的,需要将其转换为utf8,否则找不到真正的下载地址
//            location = new String(location.getBytes("ISO-8859-1"), "utf8");
System.out.println("location:" + location);String url1 = StringUtil.encodeUrl(location);url1 += ".zip";
//System.out.println("url1:" + url1);URL u2 = new URL(url1);HttpURLConnection conn1 =(HttpURLConnection) u2.openConnection();InputStream is = conn1.getInputStream();

获取Location字段还有一个问题,那就是Location字段里面确实是小说的地址,但是却含有中文,小米、vivo手机必须要将其再用ISO-8859-1才能使用,否则就会找不到地址,其他的华为、模拟器等就直接是utf8就行

最后

上面代码可用,但是不要用于商业用途,出了问题概不负责
源代码:点我下载

[android开发]小说下载软件相关推荐

  1. 老罗android开发视频教程 下载地址

    感觉老罗android开发视频教程讲得挺好挺全面的,适合新手学习.分享 老罗android开发视频教程 下载地址: 电驴:http://www.verycd.com/topics/2929580/ 老 ...

  2. LPC1768处理器开发板下载软件的使用方法

    介绍LPC1768开发板下载软件 文章目录 前言 软件包 二.软件内选择 1.界面 2.读入数据 总结 前言 LPC1768开发板下载过程与单片机下载过程极为相似 软件包 链接:https://pan ...

  3. 基于Android开发的备忘录软件(创建、通知)

    基于Android开发的备忘录软件(创建.通知) 系统概述 系统总体设计 系统设计与实现部分 关键代码及说明 程序运行截图 学习点 系统概述 结合本学期课程所学的关于Android通知的知识以及期中期 ...

  4. 【Android 应用开发】Android 开发环境下载地址 -- 百度网盘 adt-bundle android-studio sdk adt 下载

    . 最新下载 : 2017年 8 月 28 日 : 平台 Android Studio 软件包 大小 SHA-1 校验和 Windows (64 位) android-studio-bundle-16 ...

  5. 【Android 应用开发】Android 开发环境下载地址

    此文章来自于[http://blog.csdn.net/shulianghan/article/details/38023959] 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+ ...

  6. Android开发之在软件内录屏录画面,直接CV就能用

    关于Android开发录屏的资料,真的是很难找到那种拿来就能用的,不是付费专栏就是要积分才能下载源码,起不到任何帮助.我需要的是录制软件画面,找到的几个例子都是录制时返回桌面,然后弹出小窗口开始录制桌 ...

  7. Android开发工具下载及环境配置

    工作一年,家里出了点事,回家发展,找到一份Android开发,大恩大德不嫌弃我菜,而且公司规模在本地来说同行业内是比较大的了,挺好的,今天刚来,发现电脑没安装开发软件,遂着手先把吃饭的工具先下下来,难 ...

  8. Android 开发环境下载地址

    Android 开发环境介绍 : 目前 Android 有两种开发环境, 一种是传统的 Eclipse + ADT, 一种就是目前最新并且得到 Google 推荐的 Android Studio; - ...

  9. 【转载】 Android 开发环境下载地址 -- 百度网盘 adt-bundle android-studio sdk adt 下载

    本文转自http://blog.csdn.net/shulianghan/article/details/38023959 Android Studio 最详细开发环境下载 (特别推荐) : http ...

最新文章

  1. android View.isShown() 和 getVisibility() 的区别
  2. MediaWiki 1.19.2 发布 - 开源 Wiki 系统
  3. [译]Speeding up your PHP scripts
  4. 新功能:阿里云负载均衡SLB支持HTTP访问强制跳转HTTPS
  5. python如何生成随机数_python如何生成随机数
  6. 简单mysql优化_MySQL简单优化
  7. 手机java版怎么换皮肤_手机QQ皮肤更换方法(安卓版)
  8. 史上最全的 struts2 面试题
  9. ILSVRC2016
  10. pimple idiom
  11. CentOS 7 安装rar解压rar
  12. 【Scratch案例实操】Scratch字母排序 scratch编程案例教学 scratch创意编程 少儿编程教案
  13. 【TypeError: Descriptors cannot not be created directly. 】解决方法
  14. 位置在此计算机上运行程序灰色,Win10电脑中定位服务按钮灰色无法开启的2种解决方法...
  15. 51单片机——定时器的原理和使用
  16. 【179期】这些最常用的Linux命令都不会,你怎么敢去面试?
  17. 盘点OLED显示技术的几大优势
  18. php artisan命令表,php artisan 命令列表
  19. 基金知识汇总和实战经验分享
  20. Row Migration和row chained

热门文章

  1. CodeForces 1253C Sweets Eating
  2. 阿帕奇php,阿帕奇如何摆脱index.php
  3. html转换成xsl,xml+xsl转换为html
  4. Dos下怎么返回上一级目录(DOS命令大全)
  5. 【小强推歌】---邓丽君演绎古词专集《淡淡幽情》
  6. 眼镜猴--Tarsius syrichta ( Philippine tarsier )
  7. metro 风格 php源码,Win8 Metro UI风格Web素材样式资源合集
  8. 圣邦微电子FAE_信号链笔试记录
  9. 【转】FAE及其发展前景
  10. 网络攻防实验:aircrack-ng实验