Android的消息机制其实就是Handler的运行机制,因为Handler是Android消息机制的上层接口,所以在开发过程中只需要和Handler交互就可以。在开发过程中,Handler多用于控制UI线程更新,这其实是Handler的一个具体的作用。由于在Android中UI线程是非线程安全的,由于加锁会阻塞一些线程导致降低UI更新效率,所以Handler就充当了这个切换UI访问的执行线程的工具。
Handler的运行需要底层的Looper(消息循环)和MessageQueue(消息队列)支撑,Looper会无限循环去查找MessageQueue这个单项表有没有新的消息,没有就一直等待。这三个是一个整体,所以Handler需要当前线程的Looper来构造消息系统,这就需要ThreadLocal来获取每个线程中的Looper。线程默认是没有Looper的,所以需要为线程创建Looper,在UI线程中由于ActivityThread创建时候会初始化一个Looper,所以我们就可以在UI线程中使用Handler。Handler的主要任务就是切换到某个指定的线程中去执行,ViewRootImpl的checkThread方法如果发现不是在UI线程中访问UI就会抛出异常。所以我们需要在子线程中做耗时操作通过Handler将UI工作切换到主线程中执行。

Handler工作原理

那么下面我们来看一下Looper和MessageQueue与Handler如何协同的工作

  1. 创建Looper对象
    在自定义的线程中,调用Looper.prepare()方法,在这个prepare()方法中,ThreadLocal< Looper > 对象在当前线程会创建一个Looper,并通过一个Map映射表(Values类)保存当前线程的Looper对象,形成一个线程–Looper的映射表。
    注意一个线程最多可以映射一个looper对象(UI线程已经存在Looper不需手动创建)

  2. 创建Handler对象
    创建handler时,会通过LocalThread的get方法检测Map映射表(Values)中是否包含当前线程的looper对象,如果包含,则将handler内的消息队列指向looper内部的消息队列,即handler.messageQueue = Looper.messageQueue,否则,抛出异常

  3. Handler发送消息
    通过Handler的send或post方式进行消息传输,post方法本身也是在调用send方式(sendMessageAtTime),当Handler的send方法被调用时,会调用MessageQueue的enqueueMessage方法将消息放入消息队列中

  4. Looper接收消息
    Looper在队列中发现新消息就会及时处理,这里的Looper是运行在创建Handler所在的线程中,这样就实现了返回到了创建Handler的线程中来,然后会回调原handler的handlerMessage方法

ThreadLocal的原理

ThreadLocal是一个线程内部数据存贮类,所有线程共享相同的ThreadLocal对象,但是每个线程通过他取value值都是不一样的,不同的值也使得不同线程不相互影响,通过get()方法就可以返回当前线程对应的value值。ThreadLocal在开发过程中应用的比较少,在线程做作用域时就体现出了价值,比如Looper就是这样,每个线程只有一个Looper,Handler要获取当前线程中的Looper,可见Looper的作用域就是线程,那么ThreadLocal就发挥了作用。
为什么不同线程访问同一个ThreadLocal对象,通过get()返回的结果会不一样呢,我们通过内部实现来看一下

set方法

    public void set(T value) {        Thread currentThread = Thread.currentThread();        Values values = values(currentThread);        if (values == null) {            values = initializeValues(currentThread);        }        values.put(this, value);    }

在Thread类中,ThreadLocal.Values类专为存贮线程的ThreadLocal而设计,在Values内部有一个数组Object[] table,ThreadLocal的value值(Looper对象)就存贮在这个table的数组中,这里是通过values方法返回当前线程对应的Values对象,然后通过得到的Values对象的put方法将T(value)对象放入,形成了table[index+1] = value这样的对应形式

get方法

    public T get() {        Thread currentThread = Thread.currentThread();        Values values = values(currentThread);        if (values != null) {            Object[] table = values.table;            int index = hash & values.mask;            if (this.reference == table[index]) {                return (T) table[index + 1];            }        } else {            values = initializeValues(currentThread);        }        return (T) values.getAfterMiss(this);    }

可以看到,他也是取出当前线程的Values对象,如果对象为null,则返回初始值默认情况下为null,不为空时返回当前线程对应的Values对象。
从get和set方法就可以看出,ThreadLocal的读写操作仅限于线程内部,这也就是为什么ThreadLocal可以在多线程中互不干扰的存贮和修改数据。

消息对象


MessageQueue

MessageQueue就是消息队列,它的内部实现其实是一个单列表的数据结构,主要包含两个操作(enqueueMessage)插入和读取(next),读取会伴随着删除操作,他会从消息队列中取出一个消息并且把它从消息队列中删除,同时他是一个无线循环的方法,队列中没有消息时会阻塞。

Looper

他是一个消息循环的角色,他会不停的从MessageQueue中查看是否有新的消息,否则会持续阻塞。Looper有着对应的两个方法,Looper.prepare() 和 Looper.loop() ,分别代表着创建一个Looper和循环开始的作用。
此外Looper还提供了getMainLooper()这个方法,用于在任何地方获得主线程的Looper。同时Looper提供了退出的方法:quit和quitSafely。quit会直接退出Looper,而quitSafely只是设置一个退出标记,会在队列信息处理完后安全的退出Looper,退出后Handler消息发送就会失败。在子线程中如果手动创建了Looper,那么建议在不需要时通过quit方式来终止Looper,否则这个子线程会一直处于等待状态。
Looper的loop()方法是一个死循环,唯一跳出循环的方式就是next为null,上面说的两种退出方式也是将next值返回为空。所以一定要退出Looper否则loop方法会无限循环下去。

Handler

Handler负责消息的发送和接收,发送本质就是post和send的一系列方法,handler 发送Message 给MessageQueue,Looper 来轮询消息,如果有Message,然后再交由Handler处理,Handler 拿到消息就可以在所在的线程执行了。post和send这两种消息都会插在message queue队尾并按先进先出执行。但通过这两种方法发送的消息执行的方式略有不同:
通过sendMessage发送的是一个message对象,会被Handler的handleMessage()函数处理;而通过post方法发送的是一个runnable对象,则会自己执行。
注意handler.post(new Runnable()),这里的Runnable同样是运行在handler所在线程中的,并没有开启一个新线程,如果Handler是主线程的,那么这个post就运行在UI主线程中

   public final boolean post(Runnable r){       return  sendMessageDelayed(getPostMessage(r), 0);   }   public final boolean postAtTime(Runnable r, long uptimeMillis){        return sendMessageAtTime(getPostMessage(r), uptimeMillis);   }   public final boolean postDelayed(Runnable r, long delayMillis)   {        return sendMessageDelayed(getPostMessage(r), delayMillis);   }       public final boolean postAtFrontOfQueue(Runnable r){        return sendMessageAtFrontOfQueue(getPostMessage(r));   }   public boolean sendMessageAtTime(Message msg, long uptimeMillis) {        MessageQueue queue = mQueue;        if (queue == null) {            RuntimeException e = new RuntimeException(                    this + " sendMessageAtTime() called with no mQueue");            Log.w("Looper", e.getMessage(), e);            return false;        }        return enqueueMessage(queue, msg, uptimeMillis);    }    public final boolean sendMessageDelayed(Message msg, long delayMillis){        if (delayMillis < 0) {            delayMillis = 0;        }        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);    }

项目应用

说道Handler无非就是两种情况,UIThread 发送给WorkThread 和WorkThread 发送给UIThread,我们要知道Handler创建到哪个线程就和其绑定在一起,如果需要这个线程处理后续信息,需要用这个线程的Handler来做对应的send操作

UIThread 发送给WorkThread

这种场景就是我们在WorkThread 执行一些耗时的操作(网络请求,文件读写),返回的数据来更新MainThread,我们来看一下

public class MainActivity extends Activity {    private static class MyHandler extends Handler {        private final WeakReference<Activity> mAc;        private MyHandler(Activity activity) {            mAc = new WeakReference<Activity>(activity);        }        @Override        public void handleMessage(Message msg) {            switch (msg.what) {                case 0x22:                    Toast.makeText(mAc.get(), "copy", Toast.LENGTH_SHORT).show();            }        }    }    private MyHandler myHandler = new MyHandler(this);    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        new Thread(new Runnable() {            @Override            public void run() {                try {                    Thread.sleep(5000);                } catch (InterruptedException e) {                    e.printStackTrace();                }                myHandler.sendEmptyMessage(0x22);            }        }).start();    }}


WorkThread 发送给UIThread

另一种就是WorkThread 发送给MainThread

public class MainActivity extends Activity {    private Handler mainHandler = new Handler() {        @Override        public void handleMessage(Message msg) {            if (msg.what == 0x22) {                Bundle data = msg.getData();                Toast.makeText(MainActivity.this, data.getString("key") + "", Toast.LENGTH_SHORT).show();            }        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        final MyThread mm = new MyThread();        mm.start();        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                mm.workHandler.sendEmptyMessage(0x11);            }        });    }    class MyThread extends Thread {        public Handler workHandler;        @Override        public void run() {            super.run();            Looper.prepare();            workHandler = new Handler() {                @Override                public void handleMessage(Message msg) {                    switch (msg.what) {                        case 0x11:                            //模拟一些耗时操作                            try {                                Thread.sleep(5000);                            } catch (InterruptedException e) {                                e.printStackTrace();                            }                            Bundle b = new Bundle();                            b.putString("key", "copy");                            Message mMsg = Message.obtain();                            mMsg.setData(b);                            mMsg.what = 0x22;                            mainHandler.sendMessage(mMsg);                            //关闭该线程中的Looper                            Looper looper = getLooper();                            looper.quitSafely();                        default:                            break;                    }                    super.handleMessage(msg);                }            };            Looper.loop();        }    }}

运行上面代码点击按钮后5s后收到copy信息,但是第二次点击后就无法收到信息了,原因就是我们在第一次消息传递后关闭了子线程中的Looper。


处理Warning提示

有时我们在使用Handler更新UI的时候,Android Lint 会给出如下警告

This Handler class should be static or leaks might occur

意思是说:这个Handler 必须是static的,否则就会引发内存泄露。
这是因为在java中非静态匿名内部类将持有一个对外部类的隐式引用,而静态内部类则不会。 我们知道当Activity被finish()掉,Message 可能将存在于消息队列中很长时间才会被执行到。这个Message持有一个对Handler的引用,Handler也会持有一个对于外部类(Activity)的隐式引用,这些引用在Message被执行前将一直保持,这样会使得Activity的上下文不被垃圾回收机制回收,会有泄露应用程序的资源的风险。

为解决这个问题,下面这段代码中的Handler是一个静态匿名内部类。静态匿名内部类不会持有一个对外部类的隐式引用,因此Activity将不会被泄露。如果你需要在Handler中调用外部Activity的方法,就让Handler持有一个对Activity的WeakReference,这样就不会泄露Activity的上下文了

 private static class MyHandler extends Handler {      private final WeakReference<MyActivity> mActivity;      public MyHandler(MyActivity activity) {          mActivity = new WeakReference<MyActivity>(activity);      }    @Override      public void handleMessage(Message msg) {          MyActivity activity = mActivity.get();          if (activity != null) {              // ...          }      }  }

Handler的变体形式

runOnUiThread和view.post其实与handler.post方式类似,都是一种类似方法的委托,用户传递方法,使用post,postDelayed 添加委托,使用 removeCallbacks移除委托。这个特性我们可以简单看出handler类似一个容器对象,它携带了消息的集合和委托的集合,不光是传递数据并且封装了一些操作行为。java里没有委托delegate的概念,但是可以通过class来持有一个可执行的方法代理,所以具体总结如下

Runnable是一个接口,不是一个线程,这些方法的变形体其实就是将Runnable对象转换为一个Message,然后将其添加到handler所在的线程队列中,然后在适当的时间来委托执行这个Runnable,这样就可以看出这一系列方法都是运行在handler所在线程中的(runOnUiThread默认位于主线程),并没有开启一个新线程,如果这些方法在handler所在线程中会立即执行Runnable,如果在其他线程中会通过传递Message到handler所在队列排队的形式等待该线程执行Runnable,如果位于UI线程中千万别做复杂的计算逻辑,否则会导致ANR发生

runOnUiThread(Runnable action)

利用Activity.runOnUiThread(Runnable)把更新ui的代码创建在Runnable中,然后在需要更新ui时,把这个Runnable对象传给Activity.runOnUiThread(Runnable)。 这样Runnable对像就能在ui程序中被调用。如果当前线程是UI线程,那么行动是立即执行。如果当前线程不是UI线程,操作是发布到事件队列的UI线程

public class MainActivity extends Activity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        runOnUiThread(new Runnable() {            @Override            public void run() {                Toast.makeText(MainActivity.this, "mainThread", Toast.LENGTH_SHORT).show();            }        });        new Thread(new Runnable() {            @Override            public void run() {                try {                    Thread.sleep(5000);                } catch (InterruptedException e) {                    e.printStackTrace();                }                runOnUiThread(new Runnable() {                    @Override                    public void run() {                        Toast.makeText(MainActivity.this, "otherThread", Toast.LENGTH_SHORT).show();                    }                });            }        }).start();    }}


view.post(Runnable action)

我们看一下这里的代码是怎么写的

   public boolean post(Runnable action) {         final AttachInfo attachInfo = mAttachInfo;         if (attachInfo != null) {             return attachInfo.mHandler.post(action);         }         // Assume that post will succeed later         ViewRootImpl.getRunQueue().post(action);         return true;    }

可以看到又回到了handler.post方法中,所以这两个变体的方式本身就是handler.post方法的一种延伸,所以我们应该更深刻的理解一下类似于委托代理方法的本质

以上就是对Handler机制的一点总结

更多相关文章

  1. 浅谈Java中Collections.sort对List排序的两种方法
  2. Python list sort方法的具体使用
  3. python list.sort()根据多个关键字排序的方法实现
  4. Unity3D研究院之与Android相互传递消息(十九)
  5. Android(安卓)内存泄漏相关
  6. 在Android中监控来电和去电
  7. 如何看待 Kotlin 成为 Android(安卓)官方支持开发语言?
  8. Android(安卓)studio怎么创建文件? Android(安卓)studio新建Java
  9. Android进程保活总结

随机推荐

  1. android studio导入类库
  2. android动画初级入门
  3. Android(安卓)面向对象编程 类与对象
  4. 关于Android按键处理
  5. Android中的SurfaceFlinger和Choreograph
  6. Android自定义action与permission!!!
  7. Android日志分类及查看过滤
  8. RoboGuice入门
  9. Android(安卓)触摸屏驱动代码分析(ADC 类
  10. Android(安卓)过滤器机制应用演示