Handler实现线程之间的通信-下载文件动态更新进度条

1. 原理

每一个线程对应一个消息队列MessageQueue,实现线程之间的通信,可通过Handler对象将数据装进Message中,再将消息加入消息队列,而后线程会依次处理消息队列中的消息。

2. Message

初始化:一般使用Message.obtain()方法获取一个消息对象,该方法会检查Message对象池中是否存在可重复利用的对象,若无,才会new一个新对象。

what:相当于Message的标识符,区别于其它消息。

arg1、arg2:int类型,可传递整数。

obj:object类型,可传递任意对象。

3. 发送消息

在子线程中可调用主线程的handler.sendMessage(msg)进行发送消息,经过一系列方法调用,会触发handler的handleMessage方法,从而进行消息处理。

发送消息的主要方法:

handler.sendMessage(Message msg);
handler.sendMessageAtTime(Message msg, int time);
handler.sendMessageDelayed(Message msg, int time);

sendMessageAtTime()和sendMessageDelayed()区别在于前者是在指定时间发送消息,可配合SystemClock.uptimeMillis()使用;而后者则是延时发送消息。

除了SendMessage()方法以外,还可以通过post()方法发送消息:

handler.post(Runnable r);
handler.postDelayed(Runnable r, int time);

sendMessage()与post()的区别:http://blog.csdn.net/u013168615/article/details/47024073

4. 内存泄漏

http://www.cnblogs.com/xujian2014/p/5025650.html

5. 通过Handler对象实现下载文件动态更新进度条

AndroidManifest加入权限声明:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="10dp"
    tools:context="com.studying.network.DownloadActivity">

    <ProgressBar
        android:id="@+id/progress_bar"
        style="?android:progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="100" />

    <Button
        android:id="@+id/download"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="@string/download" />

</LinearLayout>

Activity:

public class DownloadActivity extends Activity {

    private static final int DOWNLOAD_MESSAGE_CODE = 100001;
    private static final int DOWNLOAD_MESSAGE_FAIL_CODE = 100002;
    private static final String APP_URL = "http://clfile.imooc.com/class/assist/119/1328281/Android%20Studio%20教辅%20.pdf";
    private MyHandler mHandler;
    private ProgressBar mProgressBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_download);

        findViewById(R.id.download).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //开启子线程
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        download(APP_URL);
                    }
                }).start();
            }
        });
        mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
        mHandler = new MyHandler(this);
    }

    private void download(String appUrl) {
        try {
            URL url = new URL(appUrl);
            URLConnection conn = url.openConnection();

            InputStream in = conn.getInputStream();
            int contentLength = conn.getContentLength();//获取文件总大小

            String downloadPath = Environment.getExternalStorageDirectory() + File.separator + "imooc" + File.separator;
            File file = new File(downloadPath);
            if (!file.exists()) {
                file.mkdir();
            }

            String fileName = downloadPath + "test.pdf";
            File apkFile = new File(fileName);
            if (apkFile.exists()) {
                apkFile.delete();
            }

            int downloadSize = 0;//记录已经下载的大小
            byte[] bytes = new byte[1024];
            int length = 0;

            OutputStream out = new FileOutputStream(fileName);
            while ((length = in.read(bytes)) != -1) {
                out.write(bytes, 0, length);
                downloadSize += length;

                Message msg = Message.obtain();
                msg.obj = downloadSize / contentLength * 100;//progress的值为0到100,因此得到的百分数要乘以100
                msg.what = DOWNLOAD_MESSAGE_CODE;
                mHandler.sendMessage(msg);
            }

            in.close();
            out.close();

        } catch (IOException e) {
            notifyDownloadFailed();
            e.printStackTrace();
        }
    }

    private void notifyDownloadFailed() {
        Message msg = Message.obtain();
        msg.what = DOWNLOAD_MESSAGE_FAIL_CODE;
        mHandler.sendMessage(msg);
    }

    private static class MyHandler extends Handler{

        private WeakReference<DownloadActivity> weakReference;

        MyHandler(DownloadActivity activity) {
            this.weakReference = new WeakReference<>(activity);//以弱引用的形式传递Activity,避免内存泄漏
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            DownloadActivity activity = weakReference.get();
            //消息处理
            switch (msg.what) {
                case DOWNLOAD_MESSAGE_CODE:
                    activity.mProgressBar.setProgress((Integer) msg.obj);
                    break;
                case DOWNLOAD_MESSAGE_FAIL_CODE:
                    Toast.makeText(activity, "下载失败!", Toast.LENGTH_SHORT).show();
                    break;
            }
        }
    }
}
posted @ 2017-08-07 21:42  jyau  阅读(1404)  评论(0编辑  收藏  举报