(一)什么是UI线程?

  Android在启动应用程序的时候,会为应用创建一个Main线程,这个线程负责将事件分派给相应用户接口的widget,其中包括drawing事件。除了事件分派之外,Main线程还负责应用与Android UI组件(例如, android.widget 和android.view 包)进行交互,因此Main 线程有时候也被称为UI线程。

(二)为什么会出现ANR(Application Not Response)?

  Android不会为每个组件实例创建单独的线程。运行于同一进程的所有组件均在 UI 线程(Main)中实例化,并且对每个组件的系统调用均由该线程进行分派。这样一来,响应系统回调的方法(例如,报告用户操作的 onKeyDown() 或生命周期回调方法)始终在进程的 UI 线程中运行。举个例子来说,当用户触摸屏幕上的按钮时,应用的 UI 线程会将触摸事件分派给Widget,而Widget反过来又设置其按下状态,并将无效请求发布到事件队列中。UI 线程从队列中取消该请求并通知Widget进行重新绘制。
  那么,如果应用在响应用户的操作的时候,在UI线程中执行了大量的耗时操作,比方说网络访问或数据库查询。这样做的后果势必会阻塞整个 UI。一旦UI线程被阻塞,将无法分派任何事件,包括绘图事件。
  如果 UI 线程被阻塞超过几秒钟时间(目前大约是 5 秒钟),就会出现我们常说的ANR。

(三)解决在worker线程中访问UI资源的问题?

  你可能会想,既然ANR是由于在UI线程中执行大量耗时的操作引起的,那么我们在主线程中新建一个worker线程问题不就解决了么?事实上,这种方式的确能够解决一些问题,但是对于UI来说就不是那么灵光了。不要忘了,Android UI toolkit并不是线程安全的,这也就意味着你不能在worker线程中来管理UI,也就是我们平常所说的不能在线程中更新UI。
  至此,我们其实可以总结出两条适用于Android单线程模型的规则:

  • 不要阻塞UI线程;
  • 不要在UI线程之外访问Androd UI toolkit。

  针对不能再线程中更新UI的问题,Android提供了三种在线程中更新UI的方式来解决这一问题:

  • Activity.runOnUiThread(Runnable)
  • View.post(Runnable)
  • View.postDelayed(Runnable, long)

  我们以View.post为例

public void onClick(View v) {    new Thread(new Runnable() {        public void run() {            final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png");            //在此处来更新UI            mImageView.post(new Runnable() {                public void run() {                    mImageView.setImageBitmap(bitmap);                }            });        }    }).start();}

  但是随着代码复杂度的提升,再用此种方法来解决问题势必要启动更多的线程,从而使代码难以管理。要通过工作线程处理更复杂的交互,可以考虑在工作线程中使用 Handler 处理来自 UI 线程的消息。当然,最好的解决方案或许是扩展 AsyncTask 类,此类简化了与 UI 进行交互所需执行的工作线程任务。下面我们分别来看一下这两种方式的的具体使用形式:

使用Handler来解决线程中更新UI的问题

private BitMap bitmap;public static final int UPDATE_IMG=1;private Handler handler=new Handler(){    public void handleMessage(Message msg){        switch(msg.what){            case UPDATE_IMG:                //在主线程中更新UI                mImageView.setImageBitmap(bitmap);                break;        }    }};public void onClick(View v) {    new Thread(new Runnable() {        public void run() {            bitmap = loadImageFromNetwork("http://example.com/image.png");            Message message = new Message();            message.what=UPDATE_IMG;            handler.sendMessage(message);//将消息发送至主线程        }    }).start();}

使用AsyncTask来解决在线程中更新UI的问题

  AsyncTask的出现就是为了简化在在线程中更新UI,有了这个类就不必显式的调用handler和Treads了。AsyncTask的使用非常简单主要分为四个主要的步骤:

  1. onPreExecute(), 这个方法会在 UI线程 中调用,因此这个方法也通常用于在Task开启之前准备Task需要的启动信息。例如展示一个progress bar。
  2. doInBackground(Params…), 其中Params是异步任务的参数。在onPreExecute()执行完成之后,这个方法会在后台线程中迅速被调用,主要用于进行后台一些比较耗时的计算。doInBackground(Params…)方法返回的计算结果会在最后一步中调用。此外,在这一步中还可以调用publishProgress(Progress…)来展示其中的过程单元,Progress…参数的值会转发到UI线程中的onProgressUpdate(Progress…) 方法。
  3. onProgressUpdate(Progress…), 在 publishProgress(Progress…)之后在UI线程调用. 即使此时后台的运算仍然在执行,这个方法可以用多种UI形式用户界面上展示后台进度。比如,用一个进度条和文本的形式来显示后台的下载进度。
  4. onPostExecute(Result), 其中Result是后台线程的运行结果。这个方法会在后台线程的计算工作完成之后再UI线程中调用。因此此方法也可进行相关的UI操作。比如,提醒后台的执行结果,关闭对话框。
 private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {     protected Long doInBackground(URL... urls) {         int count = urls.length;         long totalSize = 0;         for (int i = 0; i < count; i++) {             totalSize += Downloader.downloadFile(urls[i]);             publishProgress((int) ((i / (float) count) * 100));             // Escape early if cancel() is called             if (isCancelled()) break;         }         return totalSize;     }     protected void onProgressUpdate(Integer... progress) {         setProgressPercent(progress[0]);     }     protected void onPostExecute(Long result) {         showDialog("Downloaded " + result + " bytes");     } }

更多相关文章

  1. ubuntu 编译 Android(安卓)出现的若干错误及解决方法
  2. android连续调用setVisibility(View.VISIBLE)和setVisibility(Vi
  3. android用jdbc多线程操作sqlite小结
  4. android sqlite SQLiteDatabase 操作大全 不看后悔!必收藏!看后精
  5. Android多线程(三)HandlerThread源码原理解析
  6. [置顶] android ANR
  7. android 数据存储之 SharedPreference
  8. Android单元测试之Robolectric
  9. XposedHook:hook敏感函数

随机推荐

  1. 推荐一些高级Android开发者必备工具
  2. android基于百度云消息推送通知集成
  3. 【Android UI设计与开发】第09期:底部菜单
  4. Android进程绝杀技--forceStop
  5. 基于Ubuntu12.04的Android内核源码下载介
  6. IDC:Android 将在 2012 达到顶峰,未来五年
  7. Android零基础入门第12节:熟悉Android Stu
  8. 关于Android屏幕刷新机制的一些总结
  9. 使用 Xcode 和 Android Studio 管理 iOS
  10. 30天React Native从零到IOS/Android双平