Android Handler 消息机制

1、先来了解下下面的几个重要的组件:

  • Handler:收发消息工具。
  • Message:Hander接收和处理消息对象。
  • Looper:每一个线程拥有一个looper,创建消息队列,循环取队列里的消息进行分发。
  • MessageQueue:消息队列,采用先进先出的方法俩管理Message对象,程序创建Looper对象 时,会在它的构造器中创建MessageQueue对象。
Android Handler 消息机制_第1张图片
image.png

2、在子线程中创建Handler:
方法1:在new Handler()前后加上 Looper.prepare()和 Looper.loop()

//子线程创建handler,需要创建looper
  new Thread(new Runnable() {
     @Override
      public void run() {
          Looper.prepare();
          @SuppressLint("HandlerLeak")
          Handler handler = new Handler() {
           @Override
           public void handleMessage(Message msg) {
             super.handleMessage(msg);
                    }
                };
              Looper.loop();
            }
        }).start();  

方法二:传入主线程的Looper来实现

 //将主线程的looper传进来
new Thread(new Runnable() {
  @Override
  public void run() {
  Handler handler = new Handler(Looper.getMainLooper()) {
  @Override
  public void handleMessage(Message msg) {
    super.handleMessage(msg);
            }
        };
    }
 }).start();

3、handler之内存泄漏:
造成泄露原因:

  • Handler 的生命周期与Activity 不一致:当Android应用启动的时候,会先创建一个UI主线程的Looper对象,Looper实现了一个简单的消息队列,一个一个的处理里面的Message对象。主线程Looper对象在整个应用生命周期中存在。当在主线程中初始化Handler时,该Handler和Looper的消息队列关联(没有关联会报错的)。发送到消息队列的Message会引用发送该消息的Handler对象,这样系统可以调用 Handler#handleMessage(Message) 来分发处理该消息。
  • handler 引用 Activity 阻止了GC对Acivity的回收在Java中,非静态(匿名)内部类会默认隐性引用外部类对象。而静态内部类不会引用外部类对象。如果外部类是Activity,则会引起Activity泄露 。当Activity finish后,延时消息会继续存在主线程消息队列中1分钟,然后处理消息。而该消息引用了Activity的Handler对象,然后这个Handler又引用了这个Activity。这些引用对象会保持到该消息被处理完,这样就导致该Activity对象无法被回收,从而导致了上面说的 Activity泄露。

4、如何避免内存泄露:

  • 通过程序逻辑来进行保护
    在Activity销毁时,将消息从消息队列中移除
    removeMessages(int what)
    removeCallbacks(Runnable r)
    removeCallbacksAndMessages(null);
  • 将Handler声明为静态类 (见如下代码)
 private static class ActivityHandler extends Handler {
        private final WeakReference mActivity;

        public ActivityHandler(AsyncActivity activity) {
            this.mActivity = new WeakReference(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            if (this.mActivity != null) {
                AsyncActivity activity = this.mActivity.get();
                if (activity != null && !activity.isFinishing()) {
                    activity.handleMessage(msg);
                }
            }
        }
    }

    public void handleMessage(Message msg) {
    }

5、Looper能创建多个实例嘛?能自己创建Looper实例嘛?
Looper只能创建一个实例,可以自己调用Looper.prepare()方法创建实例。
先来看Looper的prepare()方法,调用多次prepare方法创建实例,会抛出异常。

  public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

ThreadLocal类的特性:ThreadLocal是线程内部的数据存储类,当使用ThreadLocal维护变量的时候,它会为每个使用该变量的线程提供一个独立的变量副本,这个变量副本是该线程独有,不受其他线程影响。
Looper的主要作用是与当前线程形成一种绑定的关系,同时创建一个MessageQueue,这样保证一个线程只能持有一个Looper和MessageQueue,同时Looper使得MessageQueue循环起来。

6、handler,asyncTask有什么区别?
Android的AsyncTask比Handler更轻量级一些(只是代码上轻量一些,而实际上要比handler更耗资源),适用于简单的异步处理。
AsyncTask的重写方法:

  • doInBackground(Params…) 后台执行,比较耗时的操作都可以放在这里。注意这里不能直接操作UI。此方法在后台线程执行,完成任务的主要工作,通常需要较长的时间。在执行过程中可以调用+ publicProgress(Progress…)来更新任务的进度。
  • onPostExecute(Result) 相当于Handler 处理UI的方式,在这里面可以使用在doInBackground 得到的结果处理操作UI。 此方法在主线程执行,任务执行的结果作为此方法的参数返回。
  • onProgressUpdate(Progress…) 可以使用进度条增加用户体验度。 此方法在主线程执行,用于显示任务执行的进度。
  • onPreExecute() 这里是最终用户调用Excute时的接口,当任务执行之前开始调用此方法,可以在这里显示进度对话框。
  • onCancelled() 用户调用取消时,要做的操作。

使用AsyncTask类,以下是几条必须遵守的准则:

  • Task的实例必须在UI thread中创建;
  • execute方法必须在UI thread中调用;
  • 不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)这几个方法;
  • 该task只能被执行一次,否则多次调用时将会出现异常;

AsyncTask优缺点
优点:简单,快捷, 过程可控。
缺点:在使用多个异步操作和并需要进行Ui变更时,就变得复杂起来,最大并发数不超过5。
Handler优缺点:
优点:结构清晰,功能定义明确,对于多个后台任务时,简单,清晰。
缺点:在单个后台异步处理时,显得代码过多,结构过于复杂(相对性)。

你可能感兴趣的:(Android Handler 消息机制)