我们都知道android中,不允许在子线程中更新UI,凡是更新UI的操作必须放到主线程中,否则就会报如下异常:
android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
既然如此,android肯定也会给我们解决办法。
第一种:利用handler
1)private Handler handler = new Handler(){ public void handleMessage(android.os.Message msg){
//这里是主线程 在这更新UI }; };
// 往handler发送一条消息 更改button的text属性
Message message = handler.obtainMessage();//当然也可以直接new 但是不推荐 因为通过obtainMessage实际是从消息池中获取的,可以节省内存
message.what =
1
;
handler.sendMessage(message);
handler.post(new Runnable() { @Override public void run() { } });
在这有一点需要注意 在非UI线程中使用handler时,需要Looper.prepare();
// 创建该线程的Looper对象
handler =
new
Handler(Looper.myLooper()) {
public
void
handleMessage(android.os.Message msg) {
Log.i(
"handleMessage"
,
""
+ msg.what);
};
};
Looper.loop();
package com.personal.xiaoshuai.demo; import android.content.Context; import android.graphics.Bitmap; import android.os.AsyncTask; import android.widget.Toast; class MyAsyncTask extends AsyncTask{ private Context context; public MyAsyncTask(Context context) { this.context = context; } /** * 运行在UI线程中,在调用doInBackground()之前执行 */ @Override protected void onPreExecute() { Toast.makeText(context, "onPreExecute", Toast.LENGTH_SHORT).show(); } /** * 后台运行的方法,可以运行非UI线程,可以执行耗时的方法 */ @Override protected Bitmap doInBackground(String... params) { return null; } /** * 运行在ui线程中,在doInBackground()执行完毕后执行 */ @Override protected void onPostExecute(Bitmap bitmap) { //在这更新UI } /** * 在publishProgress()被调用以后执行,publishProgress()用于更新进度 */ @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); } } 第三种:利用runOnUiThread方法中更新UI
//这个方法是activity的方法 假如当前线程是UI线程会立即执行方法体中的方法,如果当前线程不是UI线程,操作是发布到事件队列的UI线程再执行
runOnUiThread(new Runnable() { @Override public void run() { //在这更新UI } });
顺便给大家看一下acitivity中的源码,其实它内部也是封装的handler
/** * Runs the specified action on the UI thread. If the current thread is the UI * thread, then the action is executed immediately. If the current thread is * not the UI thread, the action is posted to the event queue of the UI thread. * * @param action the action to run on the UI thread */ public final void runOnUiThread(Runnable action) { if (Thread.currentThread() != mUiThread) { mHandler.post(action); } else { action.run(); } }第四种: View调用post方法实现更新UI
textView.post(new Runnable() { @Override public void run() { textView.setText("嘻嘻嘻"); } });
view的post方法又是如何实现从子线程中回到主线程的呢 我们看一下
/** * Causes the Runnable to be added to the message queue. * The runnable will be run on the user interface thread. * * @param action The Runnable that will be executed. * * @return Returns true if the Runnable was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. * * @see #postDelayed * @see #removeCallbacks */ public boolean post(Runnable action) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { return attachInfo.mHandler.post(action); } // Assume that post will succeed later ViewRootImpl.getRunQueue().post(action); return true; }可以看到当attachInfo != null(Android的窗口系统就是用过AttachInfo来判断View的所属窗口的)的时候还是会回到handler 假如为null还会走到下面的代码 大致我们可以猜到是放到某个执行队列中当中,我大致跟了下