[android开发]小说下载软件

前言

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

app效果图

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

源码介绍

下面简单介绍一下源码:

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

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

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

 <cn.karent.downloadtxt.UI.ProgressViewInResult
                android: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;
    }

    @Override
    protected 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> {

        @Override
        protected void onProgressUpdate(Void... values) {
            super.onProgressUpdate(values);
            invalidate();
        }

        @Override
        protected 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() {
        @Override
        public 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());
            }
        }
    };

    @Override
    public 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();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("service", "onStartCommand.....");
        return super.onStartCommand(intent, flags, startId);
    }

    @Nullable
    @Override
    public 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() {
                @Override
                public void run() {
                    //开启事件循环,使线程能够显示UI提示
                    Looper.prepare();
                    //下载小说
                    Http.downloadTxt(url, name, new RefreshListenerImpl());
                    Looper.loop();
                }
            }).start();

        }

    }

    public class RefreshListenerImpl implements RefreshListener {
        @Override
        public void refresh(int length) {
            Message msg = new Message();
            msg.what = 0x123;
            msg.obj = length;
            mHandler.sendMessage(msg);
        }

        @Override
        public 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就行

最后

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

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
如何在Android系统手机上安装软件的三种方法 很多朋友初用安卓手机,可能都会比较陌生。下面就介绍一下如何在安卓系统上安装软件。在Android平台上安装文件的后缀名一般为“.apk”(如果你下载软件是rar或者zip的话请解压之后再安装),和WM平台上的安装文件的后缀名“.exe”或者S60平台上的安装文件的后缀名“.sis或.sisx”相似。所以只要看到“.apk”结尾的文件就肯定是安装文件了。 在安装软件之前我们首先要对手机进行一系列的设置。在手机程序菜单中点“设置”进入,然后点击“应用程序”选项,在打开菜单中将“未知源”选项勾选中。然后进入“开发”子选项,勾选“USB 调试”选项。这样就完成了手机的设置。 完成如上准备工作后就可以进行软件的安装工作了。常用的安装方法一共有三种,下面我依次来为大家讲解。 方法一 使用Android系统的手机最简单的软件安装方法是进入件Market或者也叫电子市场(如果提示需要设置Gmail的话请参看链接)中下载安装。使用非常简单,只需用手指拖出程序菜单,点击Market(或者叫电子市场)图标进入该程序。 然后在搜索框(search)中填写你需要的软件名称搜索即可,或者在软件分类和推荐软件列表中找到你要安装的软件点击下载安装即可,如果是免费软件会显示为“Free”,如果是收费软件的话会提示软件的费用为“$xx”。 利用Market下载和安装都非常方便,但是此方法的缺点是非常浪费流量(就算软件是免费的,但是网络流量还是要收费的哦!),所以此法只推荐在连接Wifi(如果你Wifi不会设置的话,可以参看该链接)的情况下使用。另外如果你想安装收费软件,但是又不想花钱那么你可以按照下边的方法安装你在论坛或网站下载的以“.apk”为后缀名的破解版软件。 方法二 如果你的手机所刷的固件是安卓自制的固件话,那么你的的手机中可能已经集成了“APK安装器”,你只需要把要安装的文件用读卡器拷贝到内存卡中。然后在在手机程序菜单中点“设置”进入,然后点击“应用程序”选项,拖动菜单到下端可以看到“APK安装器”,点击进入。 安装器会自动搜索你内存卡中的安装程序,你只需选择要安装的程序名称点击安装即可。 如果你的手机使用的是原生的或者没有集成程序安装器的ROM那么你需要去Market中下载一款名为App Installer(其实就是软件安装器)的软件。在Market中下载安装完成后,在程序菜单中就可以找到刚安装的App Installer了,使用方法同上。 方法三 直接使用电脑安装软件。使用“APK安装器”可以直接连接PC中将电脑中的软件安装到你的手机中。不过前提是必须先要安装Android手机的USB驱动,否则电脑无法识别所连接的手机,点击下载USB驱动程序,下载完成解压安装即可。 USB驱动安装完成后电脑就可以将你的手机自动识别为移动磁盘了。这是再点击下载“APK安装器”,下载安装完成后将该压缩包解压,然后点击运行该程序。该程序会自动关联电脑中的apk程序,安装软件时只需双击apk安装文件即可。该程序会帮助你自动将软件安装到你的手机里。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值