转载请标明出处: 

http://blog.csdn.net/yujun411522/article/details/46041637
本文出自:【yujun411522的博客】

这里说的交互方式应该指的是如何在非UI线程中修改UI线程中的组件。

     一般来说有三种方式:
     1.Activity.unOnUiThread(Runnable)
     
   如果当前线程是UI Thread,立马执行action.run方法;否则将Runnable发送到UI Thread的event 队列中。
    2. view.post(Runnable)
     
将action加入到UI thread 的message queue。
     3.view.postDelayed(Runnable,long)同2一样,经过long时间之后将runnable发送给message queue。
     5.1UI线程
     当一个app启动时,系统为该app创建一个线程,成为主线程。这个主线程管理ui中的组件,包括分发事件重绘view等。因此也叫做UI Thread。我们还知道当默认情况下,系统并不为四大组件创建新的线程,都是运行在这个UI Thread之中,这种方式也称为单线程模型。所以一旦app中出现了耗时的工作(访问网络,写文件,读写数据库等等)有可能导致thread阻塞,如果这时系统无法及时分发事件,对于用户来说最直观的感受就是卡住了,而这时用户又不停的点击屏幕很有可能出现ANR。在设计app时要尽力避免出现ANR的出现,关于ANR请看5.2
     而且UI toolkit不是线程安全的,所以一定不能从子线程中操作ui,只能在ui thread中操作ui。对于android中的这种单线程模型有两条规则
     1.不要阻塞UI thread     
     2.不要在非UI Thread中操作UI
     如果在子线程中访问ui线程中的view,会报错:android.view.ViewRootImpl$CalledFromWrongThreadException:Onlythe original thread that created a view hierarchy can touch its views.
     但是现实中需要进行耗时操作的场景又非常多,如果需要耗时操作,然后更新UI怎么做呢(下载图片然后更新imageview)?我们先来按照它的规则来指定自己的方案:
     先按照规则1:不在UI Thread中做耗时操作,那么我们开启一个子线程来处理耗时操作,然后再更新view
     newThread(new Runnable(){
               public void run(){
                          //模拟耗时操作,比如上网下载图片
                         Bitmap b = loadBitmapFormWeb();      
                         //不能直接访问UI Thread        
                        
imageview.setBitmap(b);//在子线程中如果直接访问UI线程,会报错,不允许这么做。
//这里可以操作runOnUiThread,imageView.post(Runnable),imageView.postDelay();
                         imageview.post(new Runnable(){
                               imageview.setImageBitmap(b);
                         });
               }          
     }).start();
     但是这又不符合规则2,不能在非UI Thread中操作UI。这就很矛盾了,android系统在设计时已经考虑过这个问题,所以在android系统提供了几种方式在非UI Thread中访问UI线程:                
     Activity.runOnUiThread,View.post(),View.postDelayed()。
     这样的话,耗时操作在子线程中执行,UI 更新在UI Thread中执行,两者互不干扰。 
    如果子线程过多时,代码就会显得很臃肿、可读性不好。所以android 系统中集成了一个AsyncTask工具类用来包装Thread+Handler,这个类的主要在子线程运行耗时操作,然后在UI线程更新UI。同时使得代码的可读性好多了。关于AsyncTask有专门介绍。
     
     5.2 ANR
     在使用android系统的时候或多或少可能遇到过ANR(Application Not Responding), 类似的这种ANR对话框应该都见过。
         
为什么会出现:android中的所有组件默认都是在UI线程中完成的,其中有ActivityManagerService(AMS)和WindowsManagerService(WMS)会监控一个动作的响应时间,如果在一定范围内对用户交互没有处理完毕就会出现ANR。比如说分派事件,假如某一段时间内UI 线程做了耗时操作,线程阻塞了,那么用户的一个触摸事件就可能来不得及处理,超过一定的时间就会导致ANR。当然这只是ANR其中的一种出现原因。对于用于来说一旦开始交互事件没有响应,就有可能不停的触摸。这更增加了ANR的几率,对于开发者来说开发过程中一定尽量避免ANR出现的可能。
     ANR出现有原因的,要同时满足几个条件:
     1.主线程也就是必须在UI线程中相应超时。所以我们可以将耗时操作放在子线程中执行来避免这个条件出现。
     2.超时,必须超过一定的时间限制,不同的ANR的时间限制不一样,下面有介绍。
     3.交互时间或者特定操作。不同的ANR触发的时间不一样。
     主要有三种ANR类型
     1.KeyDispatchTimeOut(5s),主线程对用户交互事件5s中没有处理完毕(这种情况最常见):当app获得用户交互事件时(按键,触摸),系统先获得这个交互事件然后分派到这个app,分派到app之后再分派给对应的View(比如button)。如果在5s内,该交互事件没有处理完成就会通过一系列的回调函数,最终到AMS中处理该处理keyDispatchTimeOut超时。     
     一定要注意:出现这种ANR前提是有用户交互事件,如果没有交互事件,就算是主线程阻塞主线程也不会ANR,因为这个时候就没有事件派发。可以测试,如果仅仅是Thread.sleep(60000),不做任何交互都不会出现ANR(看具体的设备和系统,我的荣耀3c畅玩版就不会出现,HTC的就出现)。
2.BroadcastTimeOut(10s)BroadcastReceiver 中的onReceiver方法执行超过10s。          
3.ServiceTimeout(20s),service生命周期方法中执行事件超过20s。
2和3中不会产生对话框形式(和设备有关。我的华为荣耀3c畅玩版、htc都出现了)。这三种anr的出现影响最大的就是第一个,但是三者之间并不是单一出现的,有可能receiver中执行时阻塞,用户交互,系统没法及时处理该交互,出现第一种;如果receiver超时后又出现第二种。不管怎样,避免这三种操作就能最大程度的避免ANR的出现。
出现ANR时,有两个输出文件要重视:一个就是系统输出的log tag,一个就是/data/anr/traces.txt,这两个文件使我们定位ANR的利器
先看系统的log:
     //ANR 在哪个进程发生了ANR
05-14 15:24:10.519: E/ActivityManager(499): ANR in com.example.servicedemo, time=1037238
//reson指明原因

05-14 15:24:10.519: E/ActivityManager(499):  Reason: Broadcast of Intent { act=com.service.ACTION flg=0x10 cmp=com.example.servicedemo/.MyReceiver }

//ago 指明ANR之前cup使用情况     
05-14 15:24:10.519: E/ActivityManager(499): CPU usage from 4973ms to -1016msago:
//later指明ANR之后cpu使用情况
     05-14 15:24:10.519: E/ActivityManager(499): CPU usage from 472ms to 993mslater:
 再来看traces.txt内容:(目录:/data/anr/traces.txt)
----- pid 5085 at 2015-05-14 15:24:09 -----
     Cmd line: com.example.servicedemo
     DALVIK THREADS:
     (mutexes: tll=0 tsl=0 tscl=0 ghl=0)
     "main" prio=5 tid=1 TIMED_WAIT
       | group="main" sCount=1 dsCount=0 obj=0x40ae3490 self=0x15cad58
       | sysTid=5085 nice=0 sched=0/0 cgrp=default handle=1074472136
       | schedstat=( 0 0 0 ) utm=17 stm=6 core=0
       at java.lang.VMThread.sleep(Native Method)
       at java.lang.Thread.sleep(Thread.java:1047)
       at java.lang.Thread.sleep(Thread.java:1029)
       at com.example.servicedemo.MyReceiver.onReceive(MyReceiver.java:19)
       
   这两个信息是如何输出的:在ams中的appNotResponding函数:
       synchronized (this) {
//在某些情况下忽略ANR
            // PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.
            if (mShuttingDown) {
                Slog.i(TAG, "During shutdown skipping ANR: " + app + " " + annotation);
                return;
            } else if (app.notResponding) {
                Slog.i(TAG, "Skipping duplicate ANR: " + app + " " + annotation);
                return;
            } else if (app.crashing) {
                Slog.i(TAG, "Crashing app skipping ANR: " + app + " " + annotation);
                return;
            }
         //.....
// Log the ANR to the main log.

StringBuilder info = mStringBuilder;
        info.setLength(0);
        info.append("ANR in ").append(app.processName);
        if (activity != null && activity.shortComponentName != null) {
            info.append(" (").append(activity.shortComponentName).append(")");
        }

info.append("\n");
        if (annotation != null) {
            info.append("Reason: ").append(annotation).append("\n");
        }
        if (parent != null && parent != activity) {
            info.append("Parent: ").append(parent.shortComponentName).append("\n");
        }

//指定traces文件保存地址
File tracesFile = dumpStackTraces(true, firstPids, processStats, lastPids);
String cpuInfo = null;
        if (MONITOR_CPU_USAGE) {
            updateCpuStatsNow();
            synchronized (mProcessStatsThread) {
                cpuInfo = mProcessStats.printCurrentState(anrTime);
            }
//anr之前cpu负载信息
            info.append(processStats.printCurrentLoad());
            info.append(cpuInfo);
        }
        //anr之后cpu负载信息 
        info.append(processStats.printCurrentState(anrTime));
//将anr信息加入到dropBox中(管理log的工具)
addErrorToDropBox("anr", app, activity, parent, annotation, cpuInfo, tracesFile, null);
// Unless configured otherwise, swallow ANRs in background processes & kill the process.
//读取用户配置,是否显示后台进程anr对话框。不显示就直接杀死

boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
                Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;

synchronized (this) {
//设置为后台不显示anr dialong

if (!showBackground && !app.isInterestingToUserLocked() && app.pid != MY_PID) {
                Slog.w(TAG, "Killing " + app + ": background ANR");
                EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
                        app.processName, app.setAdj, "background ANR");
                Process.killProcessQuiet(app.pid);//杀死进程
                return;
            }

// Bring up the infamous App Not Responding dialog,发送信息给mHandler,提示可以显示ANR对话框
            Message msg = Message.obtain();
            HashMap map = new HashMap();
            msg.what = SHOW_NOT_RESPONDING_MSG;
            msg.obj = map;
            map.put("app", app);
            if (activity != null) {
                map.put("activity", activity);
            }
            mHandler.sendMessage(msg);
mHandler中handleMessage函数中相应的处理为:
case SHOW_NOT_RESPONDING_MSG: {
                synchronized (ActivityManagerService.this) {                                              
Dialog d = new AppNotRespondingDialog(ActivityManagerService.this,
                            mContext, proc, (ActivityRecord)data.get("activity"));
                    d.show();//显示anr对话框           
                }            
            } break;
     如何避免:ANR产生的原因是因为在主线程中执行了耗时的操作,所以将耗时操作方法在子线程中执行,可以使用handler+thread 或者AsyncTask方式。
     同时为了给用户好的体验,也有几个建议:

1.执行耗时操作时,给用户ui提示,比如进度对话框
2.游戏类app用子线程计算。
3.应用程序的初始化,可以使用一个splash或者过场动画,这种使用的很多比如一开始打开app时显示一张欢迎图片(天天动听),这样既给用户app正在响应的感觉,同时可以执行耗时操作。

5.UI线程和非UI线程的交互方式相关推荐

  1. android 组件 线程,Android UI线程和非UI线程

    UI线程及Android的单线程模型原则 当应用启动,系统会创建一个主线程(main thread). 这个主线程负责向UI组件分发事件(包括绘制事件),也是在这个主线程里,你的应用和Android的 ...

  2. SWT的UI线程和非UI线程

    为什么80%的码农都做不了架构师?>>>    要理解UI线程,先要了解一下"消息循环"这个概念.链接是百度百科上的条目,简单地说,操作系统把用户界面上的每个操作 ...

  3. Android开发之UI线程和非UI线程

    这里又是老生畅谈的话了,前边已经有多篇文章针对线程进行探究解释,Android开发过程中线程的体现更是淋漓尽致.Android开发过程中涉及到的线程从大类上分可以归为两类:UI线程和非UI线程.本篇就 ...

  4. 守护线程与非守护线程的区别

    守护线程与非守护线程 最近在看多线程的Timer章节,发现运用到了守护线程,感觉Java的基础知识还是需要补充. Java分为两种线程:用户线程和守护线程 所谓守护线程是指在程序运行的时候在后台提供一 ...

  5. Java中的守护线程和非守护线程(转载)

    <什么是守护线程,什么是非守护线程> Java有两种Thread:"守护线程Daemon"(守护线程)与"用户线程User"(非守护线程). 用户线 ...

  6. 守护线程和非守护线程

    直觉上来讲,守护线程和main相关 Java中有两种线程,一种是用户线程,另一种是守护线程. 用户线程是指用户自定义创建的线程,主线程停止,用户线程不会停止(另一条执行路径) 守护线程当进程不存在或主 ...

  7. 多线程基础-守护线程与非守护线程

    守护线程与非守护线程 1.线程分类: 2.程序线程: 3.主线程与非守护线程 4.主线程与守护线程 5.主线程.守护线程.非守护线程 6. java虚拟机是如何退出的? 1.线程分类: 守护线程 非守 ...

  8. java守护线程与非守护线程

    java守护线程与非守护线程 代码逻辑 当前网上结论 1.主线程结束,守护线程跟着结束 2.只要存在任何一个非守护线程,守护线程就全部工作 测试代码 第一段代码 第二段代码 结论 代码逻辑 代码里面包 ...

  9. 守护线程是什么?守护线程和非守护线程的区别是?守护线程的作用是?

    守护线程是区别于用户线程,用户线程即我们手动创建的线程,而守护线程是程序运行的时候在后台提供一种通用服务的线程.垃圾回收线程就是典型的守护线程. 守护线程和非守护线程的区别是 public stati ...

  10. Android UI线程和非UI线程

    UI线程及Android的单线程模型原则 当应用启动,系统会创建一个主线程(main thread). 这个主线程负责向UI组件分发事件(包括绘制事件),也是在这个主线程里,你的应用和Android的 ...

最新文章

  1. SAP S4HANA 账户组的配置里'Int.Std.Grping'选项没勾选导致ABAP程序报错
  2. PostgreSQL 10.1 手册_部分 III. 服务器管理_第 16 章 从源代码安装_16.5. 安装后设置...
  3. mysql游标遍历修改_mysql使用游标遍历数据进行批量针对性更新数据,急求mysql大神解答...
  4. RCE、exp、Exploit、Exploit Pack、exp-gui、Payload、MetaSploit都是啥
  5. 【安卓开发 】Android初级开发(六)Activity生命周期
  6. 大连市打造数字人民币应用示范村
  7. java指的是什么_java什么是实例意思指的是
  8. pythonm 用法-------list实现购物车
  9. linux 静态库、共享库
  10. IntelliJ IDEA快速自动生成Junit测试类
  11. 计算机简单进制转换题目,计算机数制转换题!(1011011)2 =( )10=( )16 =( )8(110111101)2 =( )10=(...
  12. 两个向量的夹角公式_两向量夹角(求两个向量的夹角公式)
  13. swagger的详细注解
  14. Tbase 源码 (六)
  15. 心理学与生活 - 文化与社会
  16. 维基百科著名程序员列表大全
  17. 网关系统就该这么设计(万能通用),贼稳!
  18. 【风变编程】第五课笔记
  19. natapp 配置微信小程序开发需要的网络环境
  20. 视觉镜头上的闪回画面

热门文章

  1. 手把手教你做智能LED灯(三) 手机控制端开发
  2. MySQL-高级语句
  3. 抖音短视频数据抓取实战系列(二)——Fiddler安装配置以及模拟器监测环境配置
  4. 吴恩达机器学习课程全(python实现)
  5. python毕业设计作品基于django框架 景区购票系统毕设成品(7)中期检查报告
  6. 2、基于51单片机智能交流电表抄表OLED屏
  7. python为视频加logo_如何用python 在视频上添加自己的logo
  8. 乳制品追溯系统实现的好处
  9. VBA While Wend循环
  10. 【Linux】文本编辑器Vim