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 mAc;        private MyHandler(Activity activity) {            mAc = new WeakReference(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 mActivity;      public MyHandler(MyActivity activity) {          mActivity = new WeakReference(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. Android消息机制Looper与VSync的传播
  2. [Android Studio系列(五)] Android Studio手动配置Gradle的方法
  3. 【Android 界面效果29】研究一下Android滑屏的功能的原理,及scrol
  4. Android中View的绘制过程 onMeasure方法简述 附有自定义View例子
  5. [置顶] Android学习记录(6)—将java中的多线程下载移植到Android中
  6. Android学习记录(6)—将java中的多线程下载移植到Android中(即多线
  7. Android 照相机打开方法

随机推荐

  1. 第二部分 工具库
  2. View去锯齿,在有些机器需要在图层的软件层
  3. android -> 尺寸变化动画类( ScaleAnimati
  4. [android]浅谈android的selector,背景选择
  5. Android 上SuperUser获取ROOT权限原理解
  6. Android(安卓)Editable
  7. RxJava2+retrofit简单使用,基于POST请求封
  8. Android高级界面组件(1)
  9. Xamarin Android(安卓)所见即所得问题
  10. android:padding和android:layout_margin