android中系統(tǒng)不允許在非主線程更新UI。當我們在非主線程做了耗時操作后,需要去更新UI的時候,我們就需要使用handler來執(zhí)行更新操作。
首先在Activity中新建一個handler對象,Handler handler = new Handler();這樣我們的handler會自動綁定到ActivityThread線程中。當我們要更新UI的時候可以使用handler.post(runnable)方法,將要執(zhí)行的操作交給handler執(zhí)行。
如果我們在一個非UI線程中更新UI將會拋出運行時異常:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
下面是一個使用Handler更新UI的demo:
public class MainActivity extends ActionBarActivity {
private Handler handler = new Handler();
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.hello);
new Thread() {
@Override
public void run() {
super.run();
try {
Thread.sleep(3000);
handler.post(new Runnable() {
@Override
public void run() {
textView.setText("update by handler");
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
}
常見的Handler使用方法:
- 在主線程中new 一個對象Handler handler = new Handler()(會自動綁定到主線程)。在需要更新的地方使用handler.post(runnable);發(fā)送一個runnable對象在runnable的run方法中執(zhí)行我們想要的操作。
- 在主線程new 一個handler 在需要更新的地方使用 handler.sendMessage()方法傳遞一個Message對象。可以使用message對象的arg1參數(shù)或者obj參數(shù)或者what參數(shù)攜帶參數(shù)。在handler中的回調handler.handleMessage方法中執(zhí)行想做的方法。
- 在主線程新建一個Runnable對象和Handler對象。在需要子線程中使用handler.post()來更新操作。
下面是一個demo每隔1一秒更新textView;
public class MainActivity extends ActionBarActivity {
private Handler handler = new Handler();
private TextView textView;
private Runnable runnable = new Runnable() {
@Override
public void run() {
textView.setText(System.currentTimeMillis() + "");
try {
Thread.sleep(1000);
handler.post(runnable);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.hello);
new Thread() {
@Override
public void run() {
super.run();
handler.post(runnable);
}
}.start();
}
}
當這個runnable被post出去后會進入無線循環(huán),一直發(fā)送runnable。當我們要取消這個不讓其更新怎么辦呢,需要remove這個runnable。現(xiàn)在新建一個Button讓button點擊后停止更新TextView().
在Button的OnClick時間中加入:
@Override
public void onClick(View v) {
handler.removeCallbacks(runnable);
}
攔截Handler的message;當我們構造一個handler的時候使用Handler handler = new Handler(new Callback(){});
的時候來看看效果。
現(xiàn)在我們讓textView自動改變從1開始遞增加,但是增加10的時候就自動停止不再增加了。
public class MainActivity extends ActionBarActivity {
private int count = 1;
private TextView textView;
private Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
if (count == 10) {
return true; //當我們的Callback對象返回true后就不會再執(zhí)行Handler的handleMessage了,相當于攔截了handler的處理
} else {
return false; //當返回false后不影響
}
}
}) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
textView.setText(count + "");
count++;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.hello);
new Thread() {
@Override
public void run() {
super.run();
for (int i = 0; i < 50; i++) {
try {
Thread.sleep(1000);
handler.sendEmptyMessage(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
小結:
Androi更新UI要設計成單線程模型,主要是為了防止多個線程同時修改界面,導致的界面錯亂,雖然也可以通過對界面資源加鎖來控制只讓某個線程在某一時刻只修改一個資源,但是明顯增加了復雜性。而使用單線程模型,加上我們的handler就可用很好的解決這個問題了。新建handler對象的時候就會自動綁定到我們的主線程,然后當handler收到消息后就是在主線程中執(zhí)行的了。