android中在子线程中更新UI的几种方法

我们都知道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);
2)或者是通过handler的post方法回到主线程
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还会走到下面的代码 大致我们可以猜到是放到某个执行队列中当中,我大致跟了下
源码,最终还是回到了handler 。另外AsyncTask内部其实也是对handler进行了封装。
好了,其实没有新知识,建议大家看下handler源码 可以更好的理解android机制。

你可能感兴趣的:(android中在子线程中更新UI的几种方法)