非UI主线程如何弹出Toast以及Handler.post()流程分析

Android系统中丰富的图形界面都依赖于UI主线程的渲染,如果我在Service里边想修改UI界面呢,比如弹出Toast或者Dialog之类的需求,那就需要Service和UI线程取得通信,通过UI主线程去呈现。

下面讲述一下在Service里边通过Handler和主UI线程交互的解决方案:

public class XXService extends Service {

...

    private Handler mHandler;

...

}


然后在onStartCommand()方法中初始化:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
... 

    mHandler = new Handler();
...

}


以下这段放在XXService的类定义内部即可。

 private class ToastRunnable implements Runnable {
   String mText;

  public ToastRunnable(String text) {
  mText = text;
  }

  @Override
  public void run() {
  Toast.makeText(getApplicationContext(), mText, Toast.LENGTH_SHORT).show();
  }
}

此段独立于Runnable,调用此方法就可以弹出Toast:
 private void showMtpToast() {
   mHandler.post(new ToastRunnable(getBaseContext().getString(R.string.toast_text)));
 }

下面对Handler.post()调用机制做一个流程分析:

 在Handler调用其post()方法时,方法的调用顺序如下:
 Handler的post()方法--->Handler的sendMessageDelayed()方法--->
 Handler的sendMessageAtTime()方法.
 至此又回到了前面熟悉的sendMessageAtTime()

 详细的代码如下:
 public final boolean post(Runnable r){
     return sendMessageDelayed(getPostMessage(r), 0);
 }

 private final Message getPostMessage(Runnable r) {
   Message m = Message.obtain();
     m.callback = r;
  return m;
 }

 public final boolean sendMessageDelayed(Message msg, long delayMillis){
     if (delayMillis < 0) {
         delayMillis = 0;
     }
     return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
 }

 在这个详细的过程中可以看到在post(Runnable r)中利用该方法的输入参数r调用了getPostMessage(r)
 将r封装成了一个Message.
 重点是getPostMessage(r)方法中的 m.callback = r
 将r赋值给了Message的callback!!!!!

 回顾一下出队时调用的dispatchMessage()方法
 下面接着看Handler的dispatchMessage(Message msg)方法:
 public void dispatchMessage(Message msg) {
  //1 message的callback
  if (msg.callback != null) {
  handleCallback(msg);
  } else {
  //2 handler的callback
  if (mCallback != null) {
  if (mCallback.handleMessage(msg)) {
  return;
  }
  }
  //3 Handler的handleMessage()
  handleMessage(msg);
  }
 }

 在1处就判断出该Message的callback不为null,于是调用 handleCallback(msg);
 详细代码如下:
 private static void handleCallback(Message message) {
   message.callback.run();
 }
 该方法比较简单:就是直接调用post(Runnable r)的输入参数(Runnable对象)的run()方法.

 小结:
 1 在子线程中利用post(Runnable r)更新UI,原理和sendMessage()类似.
 2 注意:
 在下面的示例中mHandler是主线程的Handler.
 所以“在子线程中利用post(Runnable r)更新UI”这个说法不是特别准确.
 确切地说还是在子线程中发送了消息到主线程的消息队列从而更新了UI.
 3 调用post(Runnable r)不会开启一个新的线程,UI的更新是在主线程中完成.
 在下面的示例中可在两个地方输出线程ID发现两个值是一样的.
 所以在post方法中勿做耗时操作.


欢迎列位留言切磋~~~大家共同进步!













你可能感兴趣的:(Android)