欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

android ActivityManagerService 源码分析----Activity管理(二) ActivityManagerService 

程序员文章站 2022-05-16 09:53:31
...

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

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

    android ActivityManagerService 源码分析----Activity管理(二)
            
    
    
        ActivityManagerService 

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 plaincopy
 
 
  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 plaincopy
 
 
  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 plaincopy
 
 
  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.         }   



    android ActivityManagerService 源码分析----Activity管理(二)
            
    
    
        ActivityManagerService 

 

2. 启动新进程

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

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

@ActivityManagerService.java

 

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

 

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

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

 

@ComponentInfo.java

 

[java] view plaincopy
 
 
  1. public String processName;  

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

 

@PackageItemInfo.java

 

[java] view plaincopy
 
 
  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再做分析。

   android ActivityManagerService 源码分析----Activity管理(二)
            
    
    
        ActivityManagerService 

3.1 activity Transition

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

 

[html] view plaincopy
 
 
  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所示:

    android ActivityManagerService 源码分析----Activity管理(二)
            
    
    
        ActivityManagerService 

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

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

performLayoutAndPlaceSurfacesLockedInner()@WindowManagerService.java

 

[java] view plaincopy
 
 
  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 plaincopy
 
 
  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的创建并启动的过程。

android ActivityManagerService 源码分析----Activity管理(二)
            
    
    
        ActivityManagerService 

 

5. Activity pausing过程

    Activity pausing过程有3种情况:

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

resumeTopActivityLocked()@ActivityStack.java

 

[java] view plaincopy
 
 
  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 plaincopy
 
 
  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(falsetrue);  
  18.         }  
  19.     }  
  20. }  

 

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

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

android ActivityManagerService 源码分析----Activity管理(二)
            
    
    
        ActivityManagerService 

 

6. Activity Stoping 过程

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

    android ActivityManagerService 源码分析----Activity管理(二)
            
    
    
        ActivityManagerService 

    在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 plaincopy
 
 
  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 plaincopy
 
 
  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键;

   android ActivityManagerService 源码分析----Activity管理(二)
            
    
    
        ActivityManagerService 

    如同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 plaincopy
 
 
  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 plaincopy
 
 
  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. }  
相关标签: ActivityManagerService