zoukankan      html  css  js  c++  java
  • android Application Component研究之Activity(二)

    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. }  
  • 相关阅读:
    tcp socket
    Spring MVC 复习笔记01
    Spring 复习笔记01
    mybatis 复习笔记03
    ActiveMQ实现负载均衡+高可用部署方案(转)
    JVM复习笔记
    JAVA NIO复习笔记
    java IO复习笔记
    并发编程复习笔记
    杂记复习笔记
  • 原文地址:https://www.cnblogs.com/feng9exe/p/5738602.html
Copyright © 2011-2022 走看看