Only the original thread that created a view hierarchy can touch its views.

场景:
项目开发中会涉及到一些耗时操作,这个时候就会开启一个子线程,将耗时操作放到子线程中取操作,操作完毕后,往往还要同步更新ui,这个时候如果直接在子线程中更新ui,将会导致程序的闪退,同时还会看到

android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

的错误日志,意思是:只有创建视图层次结构的原始线程才能触及它的视图。说白了,就是子线程中不能更新ui操作,那么为什么子线程中不能更新ui呢?

每个view在ui更新的时候都会去检测线程,最终就会去调用ViewRootImpl里面的checkThread()方法

void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
            "Only the original thread that created a view hierarchy can touch its views.");
    }
}

mThread是在ViewRootImpl的构造方法中实例化的,在调用checkThread()方法的时候就会去判断当前线程是否是主线程,如果不等于主线程,就会抛异常导致程序闪退。如果要在子线程中更新ui,其实是可以的,这里说的可不是直接在子线程中更新ui,而是通过其他的途径在子线程中更新ui;

public class MainActivity extends AppCompatActivity {
    private TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv= (TextView) findViewById(R.id.tv);

        //开启子线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                SystemClock.sleep(3000);
            }
        }).start();
    }
}

开启一个子线程,睡眠3000毫秒后更新ui;

方式一:runOnUiThread

private void updataFist(){
    MainActivity.this.runOnUiThread(new Runnable() {
        @Override
        public void run() {
            tv.setText("更新ui啦  updataFist");
        }
    });
}

runOnUiThread是Activity里面的方法,其实会发现runOnUiThread里面还是进行了线程的判断;

/**
  * @param action the action to run on the UI thread
  */
public final void runOnUiThread(Runnable action) {
    //当前线程是否等于UI线程
    if (Thread.currentThread() != mUiThread) {
        mHandler.post(action);
    } else {
        action.run();
    }
}

如果当前线程是UI线程就直接更新;如果当前线程不是ui线程,就调用Handler里面的post();方法进行更新。

方式二:Handler

private void updataSecond(){
    Message message = handler.obtainMessage();
    message.what=0;
    handler.sendMessage(message);
}

在子线程里面调用updataSecond();利用handler发送一个Message ,然后去接收该Message;

private Handler handler=new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        int what = msg.what;
        if(what==0){
            tv.setText("更新ui啦  updataSecond");
        }
    }
};

在使用Handler的时候,特别是想无限轮播广告图等效果中涉及到使用Handler时,要注意Handler造成的内存泄漏,在该界面不可视时可以调用removeMessages();或者removeCallbacksAndMessages();方法,在界面可视的时候再调用相应方法发送消息。

方式三:view.post();

private void updataThrid(){
    tv.post(new Runnable() {
        @Override
            public void run() {
                tv.setText("更新ui啦  updataThrid");
        }
    });
}

方式四:Handler.post();

private void updataFour(){
    Handler handle=new Handler(Looper.getMainLooper());
    handle.post(new Runnable() {
        @Override
        public void run() {
            tv.setText("更新ui啦  updataFour");
        }
    });
}

方式五:AsyncTask

private class MyAsyncTask extends AsyncTask<String, Integer, String>{
   /**
    * 子线程中执行,执行一些耗时操作,关键方法
    * @param params
    * @return
   */
    @Override
    protected String doInBackground(String... params) {
        Log.e("TAG","doInBackground--->");
        return "更新ui啦  MyAsyncTask";
    }

    /**
      * 主线程中执行
      * 在execute()被调用后首先执行
      * 一般用来在执行后台任务前对UI做一些标记
     */
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        Log.e("TAG","onPreExecute--->");
    }

    /**
     * 主线程中执行
     * 在调用publishProgress(Progress... values)时,此方法被执行,直接将进度信息更新到UI组件中
     * @param values
     */
    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
        Log.e("TAG","onProgressUpdate--->");
    }

    /**
     * 在主线程中,当后台操作结束时,此方法将会被调用
     * 计算结果将做为参数传递到此方法中,直接将结果显示到UI组件上。
     * @param s
     */
    @Override
    protected void onPostExecute(String s) {
        super.onPostExecute(s);
        Log.e("TAG","onPostExecute--->");
        tv.setText(s);

    }

    /**
     * 主线程中执行
     * 当异步任务取消后的,会回调该函数。在该方法内可以更新UI
    */
    @Override
    protected void onCancelled() {
        super.onCancelled();
    }

    @Override
    protected void onCancelled(String s) {
        super.onCancelled(s);
    }
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值