我们通常总是说Android的UI线程不能处理超过5s的耗时任务,也不能处理网络操作。因为为了性能,设计的时候并没有考虑线程安全(就是多线程操作可能同时更改同一数据从而让结果混乱)。所以限定规则,只能在主线程操作UI组件。那么我们平时开发肯定会涉及后台任务修改UI数据的,那么Handler的消息传递机制就是用其他线程处理事务逻辑,通知主线程修改UI操作的中间人。
Message、Loop、MessageQueue这三个就像是Handler的小弟一样,关系要好,缺一不可。
Handler:其他线程中发送消息、主线程中获取处理消息。Message:Handler发送或处理的具体消息对象。Looper:每个线程只能有一个Looper,不断从MessageQueue读取消息传递到Handler。MessageQueue:消息队列,先进先出的方式管理Message、Looper创建时会绑定与之关联的MessageQueue四者的关系缺一不可
Handler想要正常工作,需要MessageQueue管理Message,而MessageQueue是由Looper管理的,所以Looper也是必要的。因此他们四个是相辅相成,缺一不可的关系。UI线程系统已经初始化过默认Looper所以我们平常使用未指定Looper也用了Handler是这个原因。但我们如果在自己的线程中就要指定Looper对象了。需压注意的是使用Handler前要先调用prepare()方法实例化Looper。
MessageQueue对象由Looper()方法构建:
private Looper(){ mQueue = new MessageQueue(); mRun = true; mThread = Thread.currentThread(); }创建Looper使用它的prepare()方法即可,一个线程只能有一个Looper存在:
public static final void prepare(){ if(sThreadLocal.get() != null){ throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper()); }然后调用Looper.loop()方法运行消息队列。loop()方法执行一个死循环来处理消息队列,如果没有消息进入阻塞状态。
for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger final Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } final long traceTag = me.mTraceTag; if (traceTag != 0 && Trace.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } try { msg.target.dispatchMessage(msg); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // 使用final保证分发消息过程中,线程标识符不会被修改 final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycleUnchecked(); }所以我们在子线程中使用Handler处理事务时,只要注意Looper的实例化和Message的处理即可。
自定义线程处理Handler操作:
调用Looper.loop()实例化looper对象,looper构造时自动创建MessageQueue对象使用Handler实例化,覆写handleMessage(Message msg)处理自己的事务调用Looper.loop()方法启动消息循环机制MainActivity.java
public class MainActivity extends AppCompatActivity { private Button mButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //启动线程监听handler消息 final MyThread myThread = new MyThread(); myThread.start(); mButton = (Button) findViewById(R.id.btn_click); mButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //发送空消息what类型为1,这个无法传递数据,捕获到1既可作相关操作 //myThread.mHandler.sendEmptyMessage(1); //或者发送Message类型的消息,根据需求选择即可Message可携带的数据类型更丰富 Message message = new Message(); message.what = 1; Bundle bundle = new Bundle(); bundle.putString("test","hello"); message.setData(bundle); myThread.mHandler.sendMessage(message); } }); } //自定义线程处理Handler信息 class MyThread extends Thread{ private Handler mHandler; @Override public void run() { //在handler之前调用,保证Looper对象创建好,因为子线程是不会自动帮我们创建Looper对象的 Looper.prepare(); mHandler = new Handler(){ @Override public void handleMessage(Message msg) { //获取到消息的what类型 if (msg.what == 1){ //Message 携带消息获取 Bundle result = msg.getData(); String s = result.getString("test"); Toast.makeText(MainActivity.this,s,Toast.LENGTH_SHORT).show(); } } }; //循环处理开始 Looper.loop(); } } }布局文件也只是一个按钮,不贴了,运行后点击按钮就可以toast显示出hello字样。 这样处理逻辑我们新开了线程,在里边添加Thread.sleep(8888);模拟耗时操作也不会阻塞主线程引发ANR了。
Handler常用的方法:
hasMessages(int what) :检查队列中是否含what属性值的消息hasMessages(int what, Object object):检查队列是否含指定What属性值且object指定对象的消息obtainMessage() :获取消息sendEmptyMessage(int what) :发送只含what属性的空消息sendMessage(Message msg) :立即发送消息sendMessageDelayed(Message msg, long delayMillis):指定多少毫秒后发送消息handleMessage(Message msg) :处理收到的消息,通常覆写该方法实现自己的需求