参考自:https://blog.csdn.net/ydxlt/article/details/51247822 异步更新UI的五种方式

一、为什么不能在主线程更新UI

  • ViewRootImpl通过 checkThread() 方法检查更新UI操作是否是在主线程当中

    • 原因:Android的UI是线程不安全的,存在并发访问的问题。加锁也不合适:
      • 加锁会让UI访问的逻辑变得复杂
      • 加锁会降低UI访问的效率,因为锁会阻塞某些线程的执行
  • 直接在子线程修改UI

     @Override protected void onResume() {     super.onResume();     btn.setOnClickListener(new View.OnClickListener() {         @Override         public void onClick(View view) {             Log.d(TAG, "onClick: " + Thread.currentThread().getName());             new Thread(new Runnable() {                 @Override                 public void run() {                                          Log.d(TAG, "run: 前" + Thread.currentThread().getName());                     tv.setText("更新后的UI");                     Log.d(TAG, "run: 后" + Thread.currentThread().getName());                 }             }).start();         }     }); }
    • API 27可成功修改

      • 原因未知
    • API 23报错

      • android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
    • 原因分析:

      • ViewRootImpl类中的checkThread()方法需要匹配Native方法中的Thread
      void checkThread() {    if (mThread != Thread.currentThread()) {        throw new CalledFromWrongThreadException(                "Only the original thread that created a view hierarchy can touch its views.");    }}
      • ViewRootImpl在WindowManager类中的addView()方法中实例化,在Activity的onResume中调用,所以绑定的是主线程

二、异步更新UI的五种常用方式

  • eg:在请求网络图片并更新UI(会引发非UI线程更新UI操作的报错)
public void onClick(View v) {     new Thread(new Runnable() {         public void run() {            Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png");            mImageView.setImageBitmap(bitmap);             }     }).start(); }
1、Activity.runOnUiThread(Runnable)
  • 只有在Activity中才可使用此方法
public void onClick(View v) {     new Thread(new Runnable() {         public void run() {            Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png");           runOnUiThread(new Runnable() {                @Override                public void run() {                    mImageView.setImageBitmap(bitmap);                  }           });                      }     }).start(); }
2、View.post(Runnable)
  • View类及其子类提供了一个post(Runable)方法,允许重写其中的run()更新UI
public void onClick(View v) {     new Thread(new Runnable() {         public void run() {            Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png");           imageView.post(new Runnable() {              @Override              public void run() {                  mImageView.setImageBitmap(bitmap);                }           });                     }     }).start(); }
3、View.postDelayed(Runnable, long)
  • 与第二种相同,只是多了一个延迟更新的时间(ms为单位)
public void onClick(View v) {     new Thread(new Runnable() {         public void run() {            Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png");             imageView.postDelayed(new Runnable() {              @Override              public void run() {                  mImageView.setImageBitmap(bitmap);                }           },2000);                  }     }).start(); }
4、使用handler(线程间通讯)(推荐)

参考:https://blog.csdn.net/iispring/article/details/47115879

  • 当在同一个线程中更新多个UI时使用
  • 一定要在主线程中定义接收
    • 每个Hanlder都关联了一个线程,每个线程内部都维护了一个消息队列MessageQueue,这样Handler实际上也就关联了一个消息队列
    • 在执行new Handler()的时候,默认情况下Handler会绑定当前代码执行的线程程
//发送消息new Thread(new Runnable() {     public void run() {         Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png");                 //网络请求任务结束后执行下面的代码发送Message        Message message = mHandler.obtainMessage();        message.what = 1;        //传递对象        message.obj = bitmap;        mHandler.sendMessage(message);            } }).start();
//主线程中定义接收Handler mHandler = new Handler(){    @Override    public void handleMessage(Message msg) {        switch (msg.what){            case 1:                Bitmap bitmap = (Bitmap) msg.obj;                imageView.setImageBitmap(bitmap);                break;            case 2:                // ...                break;            default:                break;        }    }};
5、AsyncTask(推荐)
  • doInBackground
    • 参数类型是AsyncTask.execute方法的参数
    • 后台(子线程)执行耗时操作,返回值将作为onPostExecute方法的参数
  • onPostExecute
    • UI线程执行
    • 参数类型是doInBackground方法的返回值类型,即后台处理耗时操作返回的结果
//定义AsyncTaskAsyncTask asyncTask = new AsyncTask() {    /**     * 即将要执行耗时任务时回调,这里可以做一些初始化操作     */    @Override    protected void onPreExecute() {        super.onPreExecute();    }    /**     * 在后台执行耗时操作,其返回值将作为onPostExecute方法的参数     * @param params     * @return     */    @Override    protected Bitmap doInBackground(String... params) {        Bitmap bitmap = loadImageFromNetwork(params[0]);        return bitmap;    }    /**     * 当这个异步任务执行完成后,也就是doInBackground()方法完成后,     * 其方法的返回结果就是这里的参数     * @param bitmap     */    @Override    protected void onPostExecute(Bitmap bitmap) {        imageView.setImageBitmap(bitmap);    }};
//调用asyncTask.execute("http://example.com/image.png");

更多相关文章

  1. Android线程优先级设置方法技巧
  2. android 学习之---Android之GoogleMap(转)
  3. 探究 Android(安卓)View 绘制流程,Activity 的 View 如何展示到屏
  4. 【Android】 NDK开发基础
  5. android 图形系统加速学习系列 (一)
  6. Android(安卓)App 使用javax.mail.* API发送email&&Eclipse工程
  7. 【读书笔记】【Android(安卓)开发艺术探索】第 7 章 Android(安
  8. UI 初步介绍
  9. Android类加载之PathClassLoader和DexClassLoader

随机推荐

  1. Android中的界面组成
  2. 《Android(安卓)Dev Guide》系列教程1:什
  3. Android(安卓)线性布局详解
  4. Android(安卓)要想美化就用Shape
  5. Android(安卓)NDK 入门
  6. android的Instrumentation详解
  7. Android(安卓)核心分析 之八------Androi
  8. Android中LCD背光驱动
  9. 《Android/OPhone开发完全讲义》连载(4):And
  10. android单元测试