android中系统不允许在非主线程更新UI。当我们在非主线程做了耗时操作后,需要去更新UI的时候,我们就需要使用handler来执行更新操作。
首先在Activity中新建一个handler对象,Handler handler = new Handler();这样我们的handler会自动绑定到ActivityThread线程中。当我们要更新UI的时候可以使用handler.post(runnable)方法,将要执行的操作交给handler执行。
如果我们在一个非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);发送一个runnable对象在runnable的run方法中执行我们想要的操作。
- 在主线程new 一个handler 在需要更新的地方使用 handler.sendMessage()方法传递一个Message对象。可以使用message对象的arg1参数或者obj参数或者what参数携带参数。在handler中的回调handler.handleMessage方法中执行想做的方法。
- 在主线程新建一个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出去后会进入无线循环,一直发送runnable。当我们要取消这个不让其更新怎么办呢,需要remove这个runnable。现在新建一个Button让button点击后停止更新TextView().
在Button的OnClick时间中加入:
@Override
public void onClick(View v) {
handler.removeCallbacks(runnable);
}
拦截Handler的message;当我们构造一个handler的时候使用Handler handler = new Handler(new Callback(){});
的时候来看看效果。
现在我们让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后就不会再执行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收到消息后就是在主线程中执行的了。
网友评论