標籤:android style blog http io ar color os 使用
學習android線程時,直接在UI線程中使用子線程來更新TextView顯示的內容,會有如下錯誤:android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
大致意思就是:只有建立這個控制項的線程才能去更新該控制項的內容。
android中,如果要操作UI,都必須在UI線程,即主線程中去做,我們不能直接在UI線程中去建立子線程來實現,即不能通過這種方式來實現:
new Thread( new Runnable() { public void run() { textView.setText("update"); } }).start();
要想實現更新TextView顯示的內容,要利用訊息機制:Handler;
訊息機制的三個方面,也可以是四個方面:
(1)Looper:一個線程可以產生一個Looper對象,由它來管理此線程裡的MessageQueue(訊息佇列);
(2)Handler:使用Handler發送訊息對象到訊息佇列,或者接受Looper從訊息佇列裡取出來的訊息;
(3)MessageQueue:用來存放線程放入的訊息;
(4)線程:UI Thread通常就是main Thread,而Android啟動程式時會替它建立一個MessageQueue;
1.Handler建立訊息:
每一個訊息都需要被指定的Handler處理,通過Handler建立訊息便可完成此功能。
Android訊息機制中引入了訊息池,Handler建立訊息時首先查詢訊息池中是否有訊息存在,如果有直接從訊息池中取得,如果沒有則重新初始化一個訊息執行個體。
使用訊息池的好處是:訊息不被使用時,並不作為記憶體回收,而是放入訊息池,可供下次Handler建立訊息時使用,訊息池提高了訊息對象的複用,減少系統記憶體回收的次數。
訊息的建立流程:
2.Handler發送訊息
UI主線程初始化第一個Handler時會通過ThreadLocal建立一個Looper,該Looper與UI主線程一一對應,使用ThreadLocal的目的是保證每一個線程只建立唯一一個Looper,這點可以查看android的原始碼;
之後其他Handler初始化的時候直接擷取第一個Handler建立的Looper,而Looper初始化的時候會建立一個訊息佇列MessageQueue。
至此,主線程、訊息迴圈、訊息佇列之間的關係是1:1:1;
Handler、Looper、MessageQueue的初始化流程
而Hander持有對UI主線程訊息佇列MessageQueue和訊息迴圈Looper的引用,子線程可以通過Handler將訊息發送到UI線程的訊息佇列MessageQueue中;
3.Handler處理訊息
UI主線程通過Looper迴圈查詢訊息佇列UI_MQ,當發現有訊息存在時會將訊息從訊息佇列中取出;
首先分析訊息,通過訊息的參數判斷該訊息對應的Handler,然後將訊息分發到指定的Handler進行處理。
子線程通過Handler、Looper與UI主線程通訊的流程:
下面我們用一個例子來實現更新UI Thread中TextView的內容;
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.xiaozhang.handler1.MainActivity" > <TextView android:id="@+id/textView" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:text="@string/hello_world" /> <Button android:id="@+id/button" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/textView" android:text="button" /> </RelativeLayout>
MainActivity.java
package com.xiaozhang.handler1;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.TextView;public class MainActivity extends Activity { private Button button; private TextView textView; private Handler handler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); button = (Button) findViewById(R.id.button); textView = (TextView) findViewById(R.id.textView); button.setOnClickListener(new ButtonListener()); handler = new MyHandler(); } class MyHandler extends Handler { @Override public void handleMessage(Message msg) { textView.setText((String) msg.obj); } } class ButtonListener implements OnClickListener { @Override public void onClick(View v) { Thread t = new MyThread(); t.start(); } } class MyThread extends Thread { @Override public void run() { try { Thread.sleep(2 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } String s = "the message from child thread!"; Message msg = handler.obtainMessage(); msg.obj = s; handler.sendMessage(msg); } }}
註:MainActivity中的內部類一般通過匿名內部類來實現;
例如:
package com.xiaozhang.handler1;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.TextView;public class MainActivity extends Activity { private Button button; private TextView textView; private Handler handler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); button = (Button) findViewById(R.id.button); textView = (TextView) findViewById(R.id.textView); button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { new Thread() { @Override public void run() { try { Thread.sleep(2 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } String s = "the message from child thread!"; Message msg = handler.obtainMessage(); msg.obj = s; handler.sendMessage(msg); } }.start(); } }); handler = new Handler() { @Override public void handleMessage(Message msg) { textView.setText((String) msg.obj); } }; }}
我只是隨便寫了下,應該還可以再進行改進;
顯示效果為:
然後點擊button,等待2秒後,再次顯示為:
文章參考及整理自:
http://blog.csdn.net/itachi85/article/details/8035333
Mars視頻;
推薦:
http://www.jb51.net/article/33514.htm
android Handler及訊息處理機制的簡單介紹