Android Handler機制深一步理解

Android UI是線程不安全的,如果在子線程中嘗試進行UI操作,程序就有可能會崩潰。解決這一問題,即創建一個Message對象,然後藉助Handler發送出去,之後在Handler的handleMessage()方法中獲得剛纔發送的Message對象,然後在這裏進行UI操作就不會再出現崩潰了。這種處理方式被稱爲異步消息處理線程。

一.Handler的基本使用 在主線程定義Handler

public class MainActivity extends AppCompatActivity {

    public static final String TAG = MainActivity.class.getSimpleName();
    private TextView texttitle = null;

    /**
     * 在主線程中定義Handler,並實現對應的handleMessage方法
     */
    public static Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            //接收消息
            if (msg.what == 101) {
                texttitle.setText("接收到handler消息...");
              
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        texttitle = (TextView) findViewById(R.id.texttitle);
        texttitle.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread() {
                    @Override
                    public void run() {
                        // 在子線程中發送異步消息 通知主線程更新UI
                        mHandler.sendEmptyMessage(101);
                    }
                }.start();
            }
        });
    }
}

二.在子線程中定義Handler

主要用於進程之間通訊

2.1 直接在子線程第一handler會報錯誤

 new Thread(new Runnable() {  

            @Override  

            public void run() {  

            Looper.prepare();//每一個線程只能調用一次

            mHandler = new Handler() {

           public void handleMessage(Message msg) {

               Log.i(TAG, "在子線程中定義Handler,並接收到消息。。。");

        super.handleMessage(msg);

        System.out.println("這個消息是從-->>" + msg.obj+"過來的,在第二個子線程當中" );

        }

       }}

Looper.loop();//開始輪循

              }).start();  

在子線程中創建的Handler是會導致程序崩潰的,提示的錯誤信息爲 Can't create handler inside thread that has not called Looper.prepare() 。說是不能在沒有調用Looper.prepare() 的線程中創建Handler,那麼可以理解當我們在new Handler的時候還需要有個Looper,

2.2查看Handler代碼

  1. public Handler() {  
  2.     if (FIND_POTENTIAL_LEAKS) {  
  3.         final Class<? extends Handler> klass = getClass();  
  4.         if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&  
  5.                 (klass.getModifiers() & Modifier.STATIC) == 0) {  
  6.             Log.w(TAG, "The following Handler class should be static or leaks might occur: " +  
  7.                 klass.getCanonicalName());  
  8.         }  
  9.     }  
  10.     mLooper = Looper.myLooper();  //從Looper.myLooper方法中取個looper對象
  11.     if (mLooper == null) {  
  12.         throw new RuntimeException(  
  13.             "Can't create handler inside thread that has not called Looper.prepare()");  //如果爲空則報異常
  14.     }  
  15.     mQueue = mLooper.mQueue;  
  16.     mCallback = null;  
  17. }  

查看Looper.myLooper();  代碼

public static final Looper myLooper() {      return (Looper)sThreadLocal.get();  }  //從sThreadLocal中得到looper並返回與下邊Looper.perpare中sThreadLocal.set方法想吻合

查看perpare方法代碼可看到,自動創建一個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());  

}  

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

可以看到,首先判斷sThreadLocal中是否已經存在Looper了,如果還沒有則創建一個新的Looper設置進去。這樣也就完全解釋了爲什麼我們要先調用Looper.prepare()方法,才能創建Handler對象。同時也可以看出每個線程中最多隻會有一個Looper對象。


 

再創建第二個子線程

new Thread(new Runnable() {

                      @Override
                      public void run() {
                         Looper loop = Looper.myLooper();
               Message msg = childHandler.obtainMessage();
                         msg.obj = "第二個線程信息";
                         mHandler.sendMessage(msg);
                     }
                 }).start();

 

這樣一個線程發送消息,一個線程接收消息 完成線程之間的通訊,但是在主線程中定義Handler會默認在程序啓動的時候,系統已經幫我們自動調用了Looper.prepare()方法;

只有在子線程定義Handler需要prepare

 三.其他異步更新UI方法

1.Activity中的runOnUiThread()方法,代碼如下所示:

     public final void runOnUiThread(Runnable action) {  
  1.     if (Thread.currentThread() != mUiThread) {  
  2.         mHandler.post(action); //不爲主線程時候
  3.     } else {  
  4.         action.run();  
  5.     }  
  6. }  

2.

然後再來看一下View中的post()方法,代碼如下所示:

public boolean post(Runnable action) {  
  1.     Handler handler;  
  2.     if (mAttachInfo != null) {  
  3.         handler = mAttachInfo.mHandler;  
  4.     } else {  
  5.         ViewRoot.getRunQueue().post(action);  
  6.         return true;  
  7.     }  
  8.     return handler.post(action);  //同樣會調用handler.post();
  9. }  

查看Handler中的post()方法代碼:

  1. public final boolean post(Runnable r)  
{  
  1.    return  sendMessageDelayed(getPostMessage(r), 0);  
  2. }  
public final boolean post(Runnable r)
{
   return  sendMessageDelayed(getPostMessage(r), 0);
}

這裏調用了sendMessageDelayed()方法去發送一條消息,並且還使用了getPostMessage()方法將Runnable對象轉換成了一條消息,看下這個方法的源碼:

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

在這個方法中將消息的callback字段的值指定爲傳入的Runnable對象。callback在Handler的dispatchMessage()方法中原來有做一個檢查,

通過追蹤可以知道就是我們定義的Handler對象,然後我們查看一下Handler類的dispatchMessage方法:

/**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

 

如果Message的callback等於null纔會去調用handleMessage()方法,否則就調用handleCallback()方法。那我們快來看下handleCallback()方法中的代碼吧:

private final void handleCallback(Message message) {  

    message.callback.run();  //調用我們run方法

}  

上邊說了getPostMessage(Runnable r)方法中將我們自己的Runnble給了m.callback = r;message.callback相當於我們自己的Runnble 此時調用到了run方法。

 

自己的一些簡單理解,諸位見笑

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章