Android(安卓)异步更新UI的几种方式
16lz
2021-01-24
参考自:https://blog.csdn.net/ydxlt/article/details/51247822 异步更新UI的五种方式
一、为什么不能在主线程更新UI
-
ViewRootImpl通过 checkThread() 方法检查更新UI操作是否是在主线程当中
- 原因:Android的UI是线程不安全的,存在并发访问的问题。加锁也不合适:
- 加锁会让UI访问的逻辑变得复杂
- 加锁会降低UI访问的效率,因为锁会阻塞某些线程的执行
- 原因:Android的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");
更多相关文章
- Android线程优先级设置方法技巧
- android 学习之---Android之GoogleMap(转)
- 探究 Android(安卓)View 绘制流程,Activity 的 View 如何展示到屏
- 【Android】 NDK开发基础
- android 图形系统加速学习系列 (一)
- Android(安卓)App 使用javax.mail.* API发送email&&Eclipse工程
- 【读书笔记】【Android(安卓)开发艺术探索】第 7 章 Android(安
- UI 初步介绍
- Android类加载之PathClassLoader和DexClassLoader