Handler与多线程

1、Handler介绍

在Android开发中,我们常会使用单独的线程来完成某些操作,比如用一个线程来完成从网络上下的图片,然后显示在一个ImageView上,在多线程操作时,Android中必须保证以下两点:

(1)不要阻塞UI线程

(2)不要再UI线程之外访问Android UI工具包

有了以上两点的限制,我们在程序之间的消息如何进行传递呢?

用Handler,消息的处理者。

public class MainActivity extends Activity {

    private TextView tv;

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

        tv = (TextView) findViewById(R.id.tv);
    }

    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 100:
                    tv.setText("下载完成");
                    break;
            }
        }
    };

    public void downloadClick(View view) {
        //使用线程模拟下载操作
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    break;
                }
                handler.sendEmptyMessage(100);
            }
        }).start();
    }
}

 

2、Handler常用API

使用handler可以完成以下两点工作:

(1)消息调度和在将来的某个时间点执行一个Runnable

(2)多个任务加入到一个队列中执行

Handler相关方法:

                //发送一个空消息,即obj为空,标记为100
                handler.sendEmptyMessage(100);

                //获取一个消息对象,返回一个Msg对象
                Message msg = handler.obtainMessage();
                msg.what = 100;
                msg.obj = "要存的信息";//任意类型
                handler.sendMessage(msg);//发送消息

                //在制定时间后发送消息
                handler.sendEmptyMessageAtTime(200, System.currentTimeMillis() + 3000);
                //延迟2s后发送消息
                handler.sendEmptyMessageDelayed(300, 2000);

 

3、Handler内部实现原理

Handler实现机制:

(1)Message对象,表示要传递的一个消息

(2)MessageQueue对象,存放消息对象的消息队列,先进先出原则

(3)Looper对象负责管理当前线程的消息队列(MessageQueue)

(4)Handler对象负责把消息push到消息队列中,以及接收Looper从消息队列中取出的消息

 

Android启动程序时会在UI线程创建一个MessageQueue。

/**
 * Handler机制
 * 1、Message 消息对象,内部使用链表数据结构实现一个消息池,用于重复利用,避免大量创建消息对象,造成内存浪费
 * 2、Handler 消息处理者,通过该对象把消息存入消息队列,并最后通过HandlerMessage方法处理消息
 * 3、MessageQueue 消息队列,用于存储Message对象的数据结构,先进先出
 * 4、Looper 消息队列的处理者,用于循环检查消息队列,从消息队列中一个一个的取出消息对象,传入HandlerMessage方法
 */

 

4、Handler内存泄露问题分析

内存泄漏:当activity退出后,handler依然还占用activity的引用,导致activity没有真正退出,依然占用内存。解决方法如下:

/**
 * Handler的内存泄露问题
 * 1、定义一个内部类时,会默认拥有外部类对象的引用,所以建议使用内部类时,最好定义为一个静态内部类
 * 2、引用的强弱,强引用->软引用 ->弱引用
 */

public class HandlerMemoryActivity extends Activity {

    private MyHandler handler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_memory);
        //使用Handler延迟执行一个Runnable(10分钟)
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                System.out.println("!!!!!!run");
            }
        }, 1000 * 60 * 10);
        //关闭当前的Activity
        finish();
    }

    private static class MyHandler extends Handler {

        WeakReference<HandlerMemoryActivity> weakReference;

        public MyHandler(HandlerMemoryActivity activity) {
            weakReference = new WeakReference<HandlerMemoryActivity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            HandlerMemoryActivity activity = weakReference.get();
            if (activity != null) {
                //做处理
            }
        }
    }
}

 

5、AsyncTask

除了使用Handler实现线程间的通信外,Android提供了一个工具类:AsyncTask,他使创建需要与用户界面交互的长时间运行的任务变得简单,相对来说AsyncTask更清凉已写,适用与简单的异步处理,不需要借助线程和Handler即可实现。

AsyncTask是抽象类,AsyncTask定义了三种泛型类型:Params,Progress和Result

Params启动任务执行的输入参数,比如,Http请求的URL;

Progress后台任务执行的百分比

Result后台执行任务的最中返回结果,比如String

AsyncTask的执行步骤:

AsyncTask的执行分为四个步骤,每一步对应一个回调方法,我们需要的就是实现这些方法。

(1)首先定义一个类继承AsyncTask

(2)实现AsyncTask中定义的下面一个或几个方法

四个执行步骤分别为:

(1)onPreExecute():被UI Thread调用,该方法用来做已写准备工作,如在界面上显示一个进度条

(2)doInBackground(Params..):将在onPreExcute之后执行,运行在后台的线程中。负责执行耗时操作。可以调用publishProgress方法来更新实时任务进度

(3)onProgressUpdate(Progress..):在publishProgress方法被调用后,UI Thread将调用该方法在界面上展示任务的进展情况

(4)onPostExcute(Result):在doInBackground执行完成后,onPostExcute(Result)方法将被UI Thread调用,后台的计算结果将通过该方法传递到UI Thread。

AsyncTask准则:

(1)AsyncTask的实例必须在UI Thread中创建。

(2)excute方法必须在UI Thread中调用

(3)不要手动调用onPreExecute、onPostExecute、doInBackground和onProgressUpdate这借个方法

(4)改Task只能被执行一次,否则多次调用时会出现异常

(5)AsyncTask不能饿完全取代线程,在一些逻辑较为复杂或者后台反复执行的逻辑可能就需要线程来实现了

public class MainActivity extends Activity {

    private TextView tv;
    private ProgressBar progressBar;

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

        tv = (TextView) findViewById(R.id.tv);
        progressBar = (ProgressBar) findViewById(R.id.progressBar);
    }

    public void downloadClick(View view) {
        new MyAsyncTask(MainActivity.this).execute("http://a.hiphotos.baidu.com/image/pic/item/d50735fae6cd7b8926b326c20c2442a7d8330e97.jpg");
    }

    /**
     * 通过AsyncTask实现一个异步任务
     */
    private static class MyAsyncTask extends AsyncTask<String, Integer, Integer> {
        private MainActivity activity;

        public MyAsyncTask(MainActivity activity) {
            this.activity = activity;
        }

        //执行任务之前触发的事件,可以在该方法中做一些初始化动作,例如显示一个dialog
        //这个是在主线程中
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            activity.progressBar.setProgress(0);

        }

        //在子线程中
        //执行后台任务的方法
        @Override
        protected Integer doInBackground(String... params) {
            String s = params[0];
            try {
                URL url = new URL(s);
                HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
                //获取文件的大小
                int size = urlConnection.getContentLength();
                //0是一个标记,表示需要更新的最大进度值,1表示更新当下下载的进度值
                publishProgress(0, size);
                byte[] bytes = new byte[100];
                int len = -1;
                FileInputStream in = (FileInputStream) urlConnection.getInputStream();
                FileOutputStream out = new FileOutputStream("/sdcard/" + System.currentTimeMillis() + ".jpg");
                while ((len = in.read(bytes)) != -1) {
                    out.write(bytes, 0, len);
                    //更新进度
                    publishProgress(1, len);
                    out.flush();
                }
                out.close();
                in.close();

            } catch (Exception e) {
                e.printStackTrace();
            }
            return 200;
        }

        //更新进度
        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            switch (values[0]) {
                case 0:
                    activity.progressBar.setMax(values[1]);
                    break;
                case 1:
                    activity.progressBar.incrementProgressBy(values[1]);
                    break;
            }
        }

        @Override
        protected void onPostExecute(Integer integer) {
            super.onPostExecute(integer);
            if (integer == 200) {
                activity.tv.setText("下载完成");
            }
        }
    }
}

 

posted @ 2015-08-15 11:26  ChHM  阅读(610)  评论(0)    收藏  举报