码迷,mamicode.com
首页 > 移动开发 > 详细

android Application Component研究之Activity(二)

时间:2016-08-04 23:06:15      阅读:439      评论:0      收藏:0      [点我收藏+]

标签:

http://blog.csdn.net/windskier/article/details/7172710

 本文为原创文章,欢迎转载!转载时请注明出处:http://blog.csdn.net/windskier

    上篇文章分析完了task的创建或者复用,接下来接着分析activity在启动过程中还有哪些工作需要去完成?首先给出整个activity的过程图。

    技术分享

1. Starting Window

    当该activity运行在新的task中或者进程中时,需要在activity显示之前显示一个Starting Window。如上图所示的setAppStartingWindow()方法,这个Starting Window上并没有绘制任何的view,它就是一个空白的Window,但是WMS赋予了它一个animation。这个Starting Window的处理过程需要注意几点:

    ·1. 在AMS请求WMS启动Starting Window时,这个过程是被置在WMS的消息队列中,也就是说这个过程是一个异步的过程,并且需要将其置在WMS消息队列的队首。

     一般情况下,Starting Window是在activity Window之前显示的,但是由于是异步过程,因此从理论上来说activity Window较早显示是有可能的,如果这样的话,Starting Window将会被清除而不再显示。例如在addStartingWindow()@PhoneWindowManager.java方法调用addView之前做一个sleep操作,结果就可能不显示Starting Window。

setAppStartingWindow()@WindowManagerService.java

 

[java] view plain copy
  1. // The previous app was getting ready to show a  
  2. // starting window, but hasn‘t yet done so.  Steal it!  
  3. if (DEBUG_STARTING_WINDOW) Slog.v(TAG,  
  4.         "Moving pending starting from " + ttoken  
  5.         + " to " + wtoken);  
  6. wtoken.startingData = ttoken.startingData;  
  7. ttoken.startingData = null;  
  8. ttoken.startingMoved = true;  
  9. Message m = mH.obtainMessage(H.ADD_STARTING, wtoken);  
  10. // Note: we really want to do sendMessageAtFrontOfQueue() because we  
  11. // want to process the message ASAP, before any other queued  
  12. // messages.  
  13. mH.sendMessageAtFrontOfQueue(m);  
  14. return;  


    2. Starting Window是设置了Animation的

 

addStartingWindow()@PhoneWindowManager.java

 

[java] view plain copy
  1. final WindowManager.LayoutParams params = win.getAttributes();  
  2. params.token = appToken;  
  3. params.packageName = packageName;  
  4. params.windowAnimations = win.getWindowStyle().getResourceId(  
  5.         com.android.internal.R.styleable.Window_windowAnimationStyle, 0);  
  6. params.setTitle("Starting " + packageName);  


    3. Starting Window同普通的activity Window一样,均为一个PhoneWindow,其中包看着DecorView和ViewRoot。

 

addStartingWindow()@PhoneWindowManager.java

 

[java] view plain copy
  1.         try {  
  2.             Context context = mContext;  
  3.             boolean setTheme = false;  
  4.             //Log.i(TAG, "addStartingWindow " + packageName + ": nonLocalizedLabel="  
  5.             //        + nonLocalizedLabel + " theme=" + Integer.toHexString(theme));  
  6.             if (theme != 0 || labelRes != 0) {  
  7.                 try {  
  8.                     context = context.createPackageContext(packageName, 0);  
  9.                     if (theme != 0) {  
  10.                         context.setTheme(theme);  
  11.                         setTheme = true;  
  12.                     }  
  13.                 } catch (PackageManager.NameNotFoundException e) {  
  14.                     // Ignore  
  15.                 }  
  16.             }  
  17.             if (!setTheme) {  
  18.                 context.setTheme(com.android.internal.R.style.Theme);  
  19.             }  
  20.             //创建PhoneWindow  
  21.             Window win = PolicyManager.makeNewWindow(context);  
  22.             if (win.getWindowStyle().getBoolean(  
  23.                     com.android.internal.R.styleable.Window_windowDisablePreview, false)) {  
  24.                 return null;  
  25.             }  
  26.               
  27.             Resources r = context.getResources();  
  28.             win.setTitle(r.getText(labelRes, nonLocalizedLabel));  
  29.       
  30.             win.setType(  
  31.                 WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);  
  32.             // Force the window flags: this is a fake window, so it is not really  
  33.             // touchable or focusable by the user.  We also add in the ALT_FOCUSABLE_IM  
  34.             // flag because we do know that the next window will take input  
  35.             // focus, so we want to get the IME window up on top of us right away.  
  36.             win.setFlags(  
  37.                 WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE|  
  38.                 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|  
  39.                 WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,  
  40.                 WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE|  
  41.                 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|  
  42.                 WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);  
  43.       
  44.             win.setLayout(WindowManager.LayoutParams.MATCH_PARENT,  
  45.                                 WindowManager.LayoutParams.MATCH_PARENT);  
  46.       
  47.             final WindowManager.LayoutParams params = win.getAttributes();  
  48.             params.token = appToken;  
  49.             params.packageName = packageName;  
  50.             params.windowAnimations = win.getWindowStyle().getResourceId(  
  51.                     com.android.internal.R.styleable.Window_windowAnimationStyle, 0);  
  52.             params.setTitle("Starting " + packageName);  
  53.   
  54.             WindowManagerImpl wm = (WindowManagerImpl)  
  55.                     context.getSystemService(Context.WINDOW_SERVICE);  
  56.             View view = win.getDecorView();  
  57.   
  58.             if (win.isFloating()) {  
  59.                 // Whoops, there is no way to display an animation/preview  
  60.                 // of such a thing!  After all that work...  let‘s skip it.  
  61.                 // (Note that we must do this here because it is in  
  62.                 // getDecorView() where the theme is evaluated...  maybe  
  63.                 // we should peek the floating attribute from the theme  
  64.                 // earlier.)  
  65.                 return null;  
  66.             }  
  67.               
  68.             if (localLOGV) Log.v(  
  69.                 TAG, "Adding starting window for " + packageName  
  70.                 + " / " + appToken + ": "  
  71.                 + (view.getParent() != null ? view : null));  
  72. <span style="white-space: pre;">  </span>    //向WindowManager addView  
  73.             wm.addView(view, params);  
  74.   
  75.             // Only return the view if it was successfully added to the  
  76.             // window manager... which we can tell by it having a parent.  
  77.             return view.getParent() != null ? view : null;  
  78.         }   



    技术分享

 

2. 启动新进程

    如果新启动的activity需要运行在新的进程中,那么这个流程就涉及到了一个新进程的启动,由于画图的局限性,这个过程在上图中没有体现出来。

    所有的ProcessRecord被存储在mProcessNames变量中,以当前的进程的名字为索引。

@ActivityManagerService.java

 

[java] view plain copy
  1. final ProcessMap<ProcessRecord> mProcessNames  
  2.         = new ProcessMap<ProcessRecord>();  

 

  进程名字的确定有如下规则: 

如果Activity设置了Android:process属性,则processName为属性设置的值;    

 

@ComponentInfo.java

 

[java] view plain copy
  1. public String processName;  

    如果Activity没有设置android:process属性,那么Activity的processName为Application的processName。如果Application设置了process属性,那么processName为该值;如果没有设置,processName为Package的名字,即

 

@PackageItemInfo.java

 

[java] view plain copy
  1. public String packageName;  

整个进程启动的过程前面有一篇文章介绍过,就不在介绍。

3. Application Transition

    Application Transition是android在实现窗口切换过程中,为了提供更好的用户体验和特定的指示,来呈现出的过渡效果。一般情况下,Application Transition是一个动画效果。

    Application Transition有两种,一种是启动activity时的Transition动画,一种是启动一些widget时的Transition动画。

    Transition类型的设置通过函数prepareAppTransition()@WindowManagerService.java来进行.

    设置完Transition类型之后,通过executeAppTransition()@WindowManagerService.java函数来执行这个Transition。

   prepareAppTransition()-->executeAppTransition()-->performLayoutAndPlaceSurfacesLocked();

   具体的Transition的animation绘制过程在分析WMS再做分析。

   技术分享

3.1 activity Transition

    当启动一个activity时,系统会给它的window呈现提供一个animation,这个animation可以在frameworks/base/core/res/res/values/styles.xml中进行设置

 

[html] view plain copy
  1. <!-- Standard animations for a full-screen window or activity. -->  
  2. <style name="Animation.Activity">  
  3.     <item name="activityOpenEnterAnimation">@anim/activity_open_enter</item>  
  4.     <item name="activityOpenExitAnimation">@anim/activity_open_exit</item>  
  5.     <item name="activityCloseEnterAnimation">@anim/activity_close_enter</item>  
  6.     <item name="activityCloseExitAnimation">@anim/activity_close_exit</item>  
  7.     <item name="taskOpenEnterAnimation">@anim/task_open_enter</item>  
  8.     <item name="taskOpenExitAnimation">@anim/task_open_exit</item>  
  9.     <item name="taskCloseEnterAnimation">@anim/task_close_enter</item>  
  10.     <item name="taskCloseExitAnimation">@anim/task_close_exit</item>  
  11.     <item name="taskToFrontEnterAnimation">@anim/task_open_enter</item>  
  12.     <item name="taskToFrontExitAnimation">@anim/task_open_exit</item>  
  13.     <item name="taskToBackEnterAnimation">@anim/task_close_enter</item>  
  14.     <item name="taskToBackExitAnimation">@anim/task_close_exit</item>  
  15.     <item name="wallpaperOpenEnterAnimation">@anim/wallpaper_open_enter</item>  
  16.     <item name="wallpaperOpenExitAnimation">@anim/wallpaper_open_exit</item>  
  17.     <item name="wallpaperCloseEnterAnimation">@anim/wallpaper_close_enter</item>  
  18.     <item name="wallpaperCloseExitAnimation">@anim/wallpaper_close_exit</item>  
  19.     <item name="wallpaperIntraOpenEnterAnimation">@anim/wallpaper_intra_open_enter</item>  
  20.     <item name="wallpaperIntraOpenExitAnimation">@anim/wallpaper_intra_open_exit</item>  
  21.     <item name="wallpaperIntraCloseEnterAnimation">@anim/wallpaper_intra_close_enter</item>  
  22.     <item name="wallpaperIntraCloseExitAnimation">@anim/wallpaper_intra_close_exit</item>  
  23. </style>  

 

    activity启动的animation根据当前的activity所在的task状态有所不同,从上面的xml中的animation定义中就可以看出,它的分类:

    ★ 如果启动的activity运行在原来的task中,那么使用animation activityOpenEnterAnimation/activityOpenExitAnimation;

    ★ 如果启动的activity运行在新的task中,那么使用animation taskOpenEnterAnimation/taskOpenExitAnimation;

    ★ 如果结束的activity结束之后原来的task还存在,那么使用activityCloseEnterAnimation/activityCloseExitAnimation;

    ★ 如果结束的activity结束之后原来的task将不存在,也即次activity为task最后的activity,那么使用taskCloseEnterAnimation/taskCloseExitAnimation;

    ★ 一些特定的情况下,AMS需要将某个task move到最前面,例如上一篇文章中的task reparenting过程,此时使用taskToFrontEnterAnimation/taskToFrontExitAnimation;

    ★ 一些特定的情况下,AMS需要将某个task move到最底端,此时使用taskToBackEnterAnimation/taskToBackExitAnimation;

    ★ 如果当前的activity使用的theme中的参数android:windowShowWallpaper为true,此时的activity应该以当前的壁纸为背景,并且前一个显示的activity的背景不是当前的壁纸,此时使用wallpaperOpenEnterAnimation/wallpaperOpenExitAnimation/wallpaperCloseEnterAnimation/wallpaperCloseExitAnimation,

如下面activity所示:

    技术分享

    ★ 如果当前的activity使用的theme中的参数android:windowShowWallpaper为true,此时的activity应该以当前的壁纸为背景,并且前一个显示的activity的背景是当前的壁纸,此时使用wallpaperIntraOpenEnterAnimation/wallpaperIntraOpenExitAnimation/wallpaperIntraCloseEnterAnimation/wallpaperIntraCloseExitAnimation.

下面代码即是判断当前应该选择那些带有wallpaper的Transition类型。

performLayoutAndPlaceSurfacesLockedInner()@WindowManagerService.java

 

[java] view plain copy
  1. final int NC = mClosingApps.size();  
  2.  NN = NC + mOpeningApps.size();  
  3.  for (i=0; i<NN; i++) {  
  4.      AppWindowToken wtoken;  
  5.      int mode;  
  6.      if (i < NC) {  
  7.          wtoken = mClosingApps.get(i);  
  8.          mode = 1;  
  9.      } else {  
  10.          wtoken = mOpeningApps.get(i-NC);  
  11.          mode = 2;  
  12.      }  
  13.      if (mLowerWallpaperTarget != null) {  
  14.          if (mLowerWallpaperTarget.mAppToken == wtoken  
  15.                  || mUpperWallpaperTarget.mAppToken == wtoken) {  
  16.              foundWallpapers |= mode;  
  17.          }  
  18.      }  
  19.      if (wtoken.appFullscreen) {  
  20.          WindowState ws = wtoken.findMainWindow();  
  21.          if (ws != null) {  
  22.              // If this is a compatibility mode  
  23.              // window, we will always use its anim.  
  24.              if ((ws.mAttrs.flags&FLAG_COMPATIBLE_WINDOW) != 0) {  
  25.                  animLp = ws.mAttrs;  
  26.                  animToken = ws.mAppToken;  
  27.                  bestAnimLayer = Integer.MAX_VALUE;  
  28.              } else if (ws.mLayer > bestAnimLayer) {  
  29.                  animLp = ws.mAttrs;  
  30.                  animToken = ws.mAppToken;  
  31.                  bestAnimLayer = ws.mLayer;  
  32.              }  
  33.          }  
  34.      }  
  35.  }  
  36.   
  37.  if (foundWallpapers == 3) {  
  38.      if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,  
  39.              "Wallpaper animation!");  
  40.      switch (transit) {  
  41.          case WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN:  
  42.          case WindowManagerPolicy.TRANSIT_TASK_OPEN:  
  43.          case WindowManagerPolicy.TRANSIT_TASK_TO_FRONT:  
  44.              transit = WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_OPEN;  
  45.              break;  
  46.          case WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE:  
  47.          case WindowManagerPolicy.TRANSIT_TASK_CLOSE:  
  48.          case WindowManagerPolicy.TRANSIT_TASK_TO_BACK:  
  49.              transit = WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_CLOSE;  
  50.              break;  
  51.      }  
  52.      if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,  
  53.              "New transit: " + transit);  
  54.  } else if (oldWallpaper != null) {  
  55.      // We are transitioning from an activity with  
  56.      // a wallpaper to one without.  
  57.      transit = WindowManagerPolicy.TRANSIT_WALLPAPER_CLOSE;  
  58.      if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,  
  59.              "New transit away from wallpaper: " + transit);  
  60.  } else if (mWallpaperTarget != null) {  
  61.      // We are transitioning from an activity without  
  62.      // a wallpaper to now showing the wallpaper  
  63.      transit = WindowManagerPolicy.TRANSIT_WALLPAPER_OPEN;  
  64.      if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,  
  65.              "New transit into wallpaper: " + transit);  
  66.  }  

 

3.2 widget  Transition

    每个widget在启动时的animation和activity不一样,并且在frameworks/base/core/res/res/values/styles.xml中可以设置不同widget。

    

 

[java] view plain copy
  1. private boolean applyAnimationLocked(WindowState win,  
  2.         int transit, boolean isEntrance) {  
  3.     if (win.mLocalAnimating && win.mAnimationIsEntrance == isEntrance) {  
  4.         // If we are trying to apply an animation, but already running  
  5.         // an animation of the same type, then just leave that one alone.  
  6.         return true;  
  7.     }  
  8.   
  9.     // Only apply an animation if the display isn‘t frozen.  If it is  
  10.     // frozen, there is no reason to animate and it can cause strange  
  11.     // artifacts when we unfreeze the display if some different animation  
  12.     // is running.  
  13.     if (!mDisplayFrozen && mPolicy.isScreenOn()) {  
  14.         int anim = mPolicy.selectAnimationLw(win, transit);  
  15.         int attr = -1;  
  16.         Animation a = null;  
  17.         if (anim != 0) {  
  18.             a = AnimationUtils.loadAnimation(mContext, anim);  
  19.         } else {  
  20.             switch (transit) {  
  21.                 case WindowManagerPolicy.TRANSIT_ENTER:  
  22.                     attr = com.android.internal.R.styleable.WindowAnimation_windowEnterAnimation;  
  23.                     break;  
  24.                 case WindowManagerPolicy.TRANSIT_EXIT:  
  25.                     attr = com.android.internal.R.styleable.WindowAnimation_windowExitAnimation;  
  26.                     break;  
  27.                 case WindowManagerPolicy.TRANSIT_SHOW:  
  28.                     attr = com.android.internal.R.styleable.WindowAnimation_windowShowAnimation;  
  29.                     break;  
  30.                 case WindowManagerPolicy.TRANSIT_HIDE:  
  31.                     attr = com.android.internal.R.styleable.WindowAnimation_windowHideAnimation;  
  32.                     break;  
  33.             }  
  34.             if (attr >= 0) {  
  35.                 a = loadAnimation(win.mAttrs, attr);  
  36.             }  
  37.         }  
  38.         if (DEBUG_ANIM) Slog.v(TAG, "applyAnimation: win=" + win  
  39.                 + " anim=" + anim + " attr=0x" + Integer.toHexString(attr)  
  40.                 + " mAnimation=" + win.mAnimation  
  41.                 + " isEntrance=" + isEntrance);  
  42.         if (a != null) {  
  43.             if (DEBUG_ANIM) {  
  44.                 RuntimeException e = null;  
  45.                 if (!HIDE_STACK_CRAWLS) {  
  46.                     e = new RuntimeException();  
  47.                     e.fillInStackTrace();  
  48.                 }  
  49.                 Slog.v(TAG, "Loaded animation " + a + " for " + win, e);  
  50.             }  
  51.             win.setAnimation(a);  
  52.             win.mAnimationIsEntrance = isEntrance;  
  53.         }  
  54.     } else {  
  55.         win.clearAnimation();  
  56.     }  
  57.   
  58.     return win.mAnimation != null;  
  59. }  

 

4. Activity启动

    文章的前面的内容中分析的一直是AMS对一个新启动的activity的管理,activity在AMS中的形态是以ActivityRecord的形式来管理的,下面的时序图中则是描绘了应用中一个activity的创建并启动的过程。

技术分享

 

5. Activity pausing过程

    Activity pausing过程有3种情况:

    1. 第一种情况是从一个activity启动另一个activity的同时,也伴随着前一个activity的pause过程。

resumeTopActivityLocked()@ActivityStack.java

 

[java] view plain copy
  1. // We need to start pausing the current activity so the top one  
  2. // can be resumed...  
  3. if (mResumedActivity != null) {  
  4.     if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: need to start pausing");  
  5.     startPausingLocked(userLeaving, false);  
  6.     return true;  
  7. }  

 

    2. 第二种情况是当PowerManagerService要求AMS休眠或者设备shutDown时;

@ActivityStack.java

 

[java] view plain copy
  1. void pauseIfSleepingLocked() {  
  2.     if (mService.mSleeping || mService.mShuttingDown) {  
  3.         if (!mGoingToSleep.isHeld()) {  
  4.             mGoingToSleep.acquire();  
  5.             if (mLaunchingActivity.isHeld()) {  
  6.                 mLaunchingActivity.release();  
  7.                 mService.mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);  
  8.             }  
  9.         }  
  10.   
  11.         // If we are not currently pausing an activity, get the current  
  12.         // one to pause.  If we are pausing one, we will just let that stuff  
  13.         // run and release the wake lock when all done.  
  14.         if (mPausingActivity == null) {  
  15.             if (DEBUG_PAUSE) Slog.v(TAG, "Sleep needs to pause...");  
  16.             if (DEBUG_USER_LEAVING) Slog.v(TAG, "Sleep => pause with userLeaving=false");  
  17.             startPausingLocked(false, true);  
  18.         }  
  19.     }  
  20. }  

 

    3.第三种情况是一个activity finish过程中。这个下面再介绍。

下图为第一种情况的时序图,整个pausing过程的是相同的,因此以一种情况的时序图来体现activity的pausing过程。

技术分享

 

6. Activity Stoping 过程

     我们知道,当Activity不可见时会执行stoping的过程,下面我们就来分析以下一个activity是怎么来进行stop的。下面给出整个stop过程的时序图:

    技术分享

    在stop中有一个很重要的概念就是activity idle状态,不论是activity被新启动的activity完全覆盖,还是activity被finish,也就是activity的stop过程以及finsh过程,均是在最新被resume的activity已经resume完成之后才去处理。

    我们可以想象一下,每个应用程序的主线程ActivityThread中,当没有任何的消息待处理时,此时我们可以认为此时的已被resumed的activity状态时空闲的,没有任何的人机交互。因此android设计者将前一个被完全覆盖不可见的或者finish的activity的stop或finish操作放在此时来处理。这样做是合情合理,毕竟stop或者finish一个activity以及显示新的activity之间的关系是同步,是必须有先后顺序的,为了达到更好的用户体验,理所当然应该是先显示新的activity,然后采取stop或者finish旧的activity。为了实现这个目的,android设计者使用了MessageQueue的这个IdleHandler机制。

    首先,我们看一下MessageQueue的IdleHandler机制。

next ()@MessageQueue.java

 

[java] view plain copy
  1. // Run the idle handlers.  
  2. // We only ever reach this code block during the first iteration.  
  3. for (int i = 0; i < pendingIdleHandlerCount; i++) {  
  4.     final IdleHandler idler = mPendingIdleHandlers[i];  
  5.     mPendingIdleHandlers[i] = null; // release the reference to the handler  
  6.   
  7.     boolean keep = false;  
  8.     try {  
  9.         keep = idler.queueIdle();  
  10.     } catch (Throwable t) {  
  11.         Log.wtf("MessageQueue", "IdleHandler threw exception", t);  
  12.     }  
  13.   
  14.     if (!keep) {  
  15.         synchronized (this) {  
  16.             mIdleHandlers.remove(idler);  
  17.         }  
  18.     }  
  19. }  

    在ActivityThread线程的Looper中,Looper会不停的去查找消息队列中是否有消息需要处理,如果没有任何的消息待处理,那么将查看当前的消息队列是否有IdleHandler注册,如果有逐个执行这些IdleHandler。
    明白了IdleHandler的机制,回过头来了看ActivityThread的IdleHandler的注册过程,代码如下。

 

handleResumeActivity()@ActivityThread.java

 

[java] view plain copy
  1. r.nextIdle = mNewActivities;  
  2. mNewActivities = r;  
  3. if (localLOGV) Slog.v(  
  4.     TAG, "Scheduling idle handler for " + r);  
  5. Looper.myQueue().addIdleHandler(new Idler());  

 

7. Activity finishing过程

    用户从application结束当前的activity,如按back键;

   技术分享

    如同activity不可见时的处理一样,activity的finishing过程同样是在新的activity被resume之后才去执行,但是存在一种情况,当mHistory栈中存在多个(多于4个)activity时,假如此时user以很快的速度去按back键,并且在第一个需resume的activity尚未被resume完成时,已经被user触发了多次back键,此时应该怎么处理finish过程呢?

    按照上面的逻辑来看,user不停的以很快的速度去触发back键,直到回到home activity,这种情况下ActivityThread的Looper一直会有消息需要处理,根本不可能去处理它的IdleHandler,也就不可能去处理各个activity的finish过程,直到回到home activity之后才能有空闲去处理。我们可以想象一下如果按照这个逻辑去操作的话,会有什么问题?

    设想一下,我们累计了多个activity在ActivityThread的Looper在idle状态下处理,那么这个过程将是比较长的,假如此时又有user触发了启动actibity的操作,那么ActivityThread将会同时处理累计的activity的finish过程,同时又需要处理activity的启动过程,那么这么做的结果只能是给用户带来系统很慢的用户体验。因此上面的finish逻辑需要进行一定的矫正与修改。

    AMS在累计的activity超过3个时,就会强制调用Idle处理操作。这么做就有效的消耗了累计的activity的finish过程,就很大程度上减轻了上述所说的问题。

finishCurrentActivityLocked()@ActivityStack.java

 

[java] view plain copy
  1. // First things first: if this activity is currently visible,  
  2. // and the resumed activity is not yet visible, then hold off on  
  3. // finishing until the resumed one becomes visible.  
  4. if (mode == FINISH_AFTER_VISIBLE && r.nowVisible) {  
  5.     if (!mStoppingActivities.contains(r)) {  
  6.         mStoppingActivities.add(r);  
  7.         Slog.d(TAG, "finishCurrentActivityLocked mStoppingActivities size:" + mStoppingActivities.size());  
  8.         if (mStoppingActivities.size() > 3) {  
  9.             // If we already have a few activities waiting to stop,  
  10.             // then give up on things going idle and start clearing  
  11.             // them out.  
  12.             Message msg = Message.obtain();  
  13.             msg.what = IDLE_NOW_MSG;  
  14.             mHandler.sendMessage(msg);  
  15.         }  
  16.     }  
  17.     r.state = ActivityState.STOPPING;  
  18.     mService.updateOomAdjLocked();  
  19.     return r;  
  20. }  

    同样的问题也存在与activity启动过程中,假如user以很快的速度去不停的启动activity,那么被覆盖的activity的stop过程很上述的finish过程一样,也会不停的累计,出现相同的问题。解决的思路也是一致的。
completePauseLocked()@ActivityStack.java

 

 

[java] view plain copy
    1. mStoppingActivities.add(prev);  
    2. if (mStoppingActivities.size() > 3) {  
    3.     // If we already have a few activities waiting to stop,  
    4.     // then give up on things going idle and start clearing  
    5.     // them out.  
    6.     if (DEBUG_PAUSE) Slog.v(TAG, "To many pending stops, forcing idle");  
    7.     Message msg = Message.obtain();  
    8.     msg.what = IDLE_NOW_MSG;  
    9.     mHandler.sendMessage(msg);  
    10. }  

android Application Component研究之Activity(二)

标签:

原文地址:http://www.cnblogs.com/feng9exe/p/5738602.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!