`
410063005
  • 浏览: 177870 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

对Android App UI线程的一点理解

 
阅读更多

1. ANR

ANRs (“Application Not Responding”),意思是”应用没有响应“

发生ANR最常见的一种情况: UI线程中执行长时间的任务,如IO或网络操作, 导致UI线程5秒内没有响应输入事件

 

参考 http://blog.csdn.net/leilu2008/article/details/6689405

 

2. 如何避免ANR

防止UI线程中执行长时间的任务,避免UI线程阻塞。 以Activity为例,我们应该避免在Activity的生命周期方法中执行耗时操作。

3. 问题

Android系统创建UI线程时,同时在这个线程中启动了一个消息循环(Looper), Looper.loop()源码注释指出该方法可能阻塞。 这里的loop()显然是在UI线程中执行,不会阻塞UI线程吗?为什么没有引起ANR?

 

ActivityThread.java

 

    public static final void main(String[] args) {
        ...
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
        ...
        Looper.loop();
        ...
   }

 

Looper.java

 

    public static final void loop() {
        Looper me = myLooper();
        MessageQueue queue = me.mQueue;
        
        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();
        
        while (true) {
            Message msg = queue.next(); // might block
            //if (!me.mRun) {
            //    break;
            //}
            if (msg != null) {
                if (msg.target == null) {
                    // No target is a magic identifier for the quit message.
                    return;
                }
                if (me.mLogging!= null) me.mLogging.println(
                        ">>>>> Dispatching to " + msg.target + " "
                        + msg.callback + ": " + msg.what
                        );
                msg.target.dispatchMessage(msg);
                if (me.mLogging!= null) me.mLogging.println(
                        "<<<<< Finished to    " + msg.target + " "
                        + msg.callback);
                
                // Make sure that during the course of dispatching the
                // identity of the thread wasn't corrupted.
                final long newIdent = Binder.clearCallingIdentity();
                if (ident != newIdent) {
                    Log.wtf("Looper", "Thread identity changed from 0x"
                            + Long.toHexString(ident) + " to 0x"
                            + Long.toHexString(newIdent) + " while dispatching to "
                            + msg.target.getClass().getName() + " "
                            + msg.callback + " what=" + msg.what);
                }
                
                msg.recycle();
            }
        }
    }

 

4. Looper不会阻塞UI线程

Looper不会阻塞UI线程。 原因是,UI线程消息循环开始后, UI线程内发生的所有操作(以Activity为例,比如Activity的生命周期的管理)都是通过这个消息循环来完成。即,Activity的生命回调方法(onCreate, onResume)最终都会以一种间接的方式由这个消息循环来调用。 

 

参考  http://stackoverflow.com/questions/6984263/android-looper-and-call-stack 

http://stackoverflow.com/questions/5193913/handlers-messagequeue-looper-do-they-all-run-on-the-ui-thread

 

5. 分析

 

Activity启动时相关的几个类

 

注意图中的ActivityManagerProxy和ActivityManagerService,后者为服务端, 前者可以看作它的远程代理。 ActivityThread通过这个代理进行IPC调用。另外, ActivityThread的一个类型为ApplicationThread(可以看出, 它间接地继承自Binder, 并且是ActivityThread的内部类)成员变量作为远程调用的参数被传递给了ActivityManagerService, 这里的ApplicationThread是ActivityManagerService能够调用Activity的回调方法的关键。

 

Activity启动的时序图

 

由上图可以看出,一系列调用过程最终结果是一个相应Message进入MessageQueue, 由ActivityThread的一个Handler成员变量进行消息的处理。其处理流程基本可归纳为


handleXXXActivity() ----> performXXXActivity()---->Instrument.callActivityOnXXX()--->Activity.onXXX()


这里的Activit.onXXX()就是我们编写的Activity生命周期方法。

 

所以说Activity的生命周期方法是通过UI线程的消息循环来完成, 并不存在Looper阻塞UI线程的问题。我们需要注意的就是避免在这些回调方法中执行耗时操作。

 

  • 大小: 50 KB
  • 大小: 70 KB
0
0
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics