文档章节

Activity 的 失去焦点 到销毁

SuShine
 SuShine
发布于 2015/06/24 13:54
字数 2101
阅读 3
收藏 0
点赞 0
评论 0

 当我们按下键盘上的Back键时,当前激活的Activity窗口就会被失去焦点,但是这时候它还没有被销毁,它的状态被设置为Stopped;当新的Activity窗口即将要显示时,它会通知WindowManagerService,这时候WindowManagerService就会处理当前处理Stopped状态的Activity窗口了,要执行的操作就是销毁它们了,在销毁的时候,就会注销它们之前所注册的键盘消息接收通道。

        新的Activity窗口通知WindowManagerService它即将要显示的过程比较复杂,但是它与我们本节要介绍的内容不是很相关,因此,这里就略过大部过程了,我们从ActvitiyRecord的windowsVisible函数开始分析。注意,这里的ActivityRecord是新的Activity窗口在ActivityManangerService的代表,而那些处于Stopped状态的Activity窗口

会放在ActivityStack类的一个等待可见的mWaitingVisibleActivities列表里面,事实于,对于那些Stopped状态的Activity窗口来说,它们是等待销毁,而不是等待可见。

        像前面一样,我们先来看一张应用程序注销键盘消息接收通道的过程的序列图,然后根据这个序列图来详细分析互一个步骤:


点击查看大图

        Step 1. ActivityRecord.windowsVisible

        这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityRecord.java文件中:

[java]  view plain copy
  1. class ActivityRecord extends IApplicationToken.Stub {  
  2.     ......  
  3.     boolean nowVisible;     // is this activity's window visible?  
  4.     boolean idle;           // has the activity gone idle?  
  5.     ......  
  6.   
  7.   
  8.     public void windowsVisible() {  
  9.         synchronized(service) {  
  10.             ......  
  11.   
  12.             if (!nowVisible) {  
  13.                 nowVisible = true;  
  14.                 if (!idle) {  
  15.                     .......  
  16.                 } else {  
  17.                     // If this activity was already idle, then we now need to  
  18.                     // make sure we perform the full stop of any activities  
  19.                     // that are waiting to do so.  This is because we won't  
  20.                     // do that while they are still waiting for this one to  
  21.                     // become visible.  
  22.                     final int N = stack.mWaitingVisibleActivities.size();  
  23.                     if (N > 0) {  
  24.                         for (int i=0; i<N; i++) {  
  25.                             ActivityRecord r = (ActivityRecord)  
  26.                                 stack.mWaitingVisibleActivities.get(i);  
  27.                             r.waitingVisible = false;  
  28.                             ......  
  29.                         }  
  30.                         stack.mWaitingVisibleActivities.clear();  
  31.   
  32.                         Message msg = Message.obtain();  
  33.                         msg.what = ActivityStack.IDLE_NOW_MSG;  
  34.                         stack.mHandler.sendMessage(msg);  
  35.   
  36.                     }  
  37.                 }  
  38.                 ......  
  39.             }  
  40.         }  
  41.     }  
  42.   
  43.     ......  
  44. }  
        应用程序中的每一个Activity在ActivityManagerService都有一个代表ActivityRecord,它们以堆栈的形式组织在ActivityManaerService中的ActivityStack中。一个即将要显示,但是还没有显示的Activity,它在ActivityManagerService中的ActivityRecord的成员变量nowVisible为false,而成员变量idle为ture,表示这个即将要显示的Activity窗口处于空闲状态。因此,在上面的这个函数中,会执行下面的语句:

[java]  view plain copy
  1. final int N = stack.mWaitingVisibleActivities.size();  
  2. if (N > 0) {  
  3.     for (int i=0; i<N; i++) {  
  4.         ActivityRecord r = (ActivityRecord)  
  5.         stack.mWaitingVisibleActivities.get(i);  
  6.         r.waitingVisible = false;  
  7.         ......  
  8.     }  
  9.     stack.mWaitingVisibleActivities.clear();  
  10.   
  11.     Message msg = Message.obtain();  
  12.     msg.what = ActivityStack.IDLE_NOW_MSG;  
  13.     stack.mHandler.sendMessage(msg);  
  14.   
  15. }  
        前面我们说过,当用户按下键盘上的Back键时,当前激活的Activity记录就被放在ActivityStack对象stack的成员变量mWaitingVisibleActivities中了,这时候就要对它进行处理了。首先是将它们的Activity记录的waitingVisible设置为false,然后就把它们从ActivityStack对象stack的成员变量mWaitingVisibleActivities清空,最后向ActivityStack对象stack发送一个ActivityStack.IDLE_NOW_MSG消息。这个消息最终是由ActivityStack类的activityIdleInternal函数来处理的。

        Step 2. ActivityStack.activityIdleInternal

        这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中:

[java]  view plain copy
  1. public class ActivityStack {  
  2.     ......  
  3.   
  4.     final void activityIdleInternal(IBinder token, boolean fromTimeout,  
  5.             Configuration config) {  
  6.         ......  
  7.   
  8.         ArrayList<ActivityRecord> stops = null;  
  9.         ......  
  10.   
  11.         int NS = 0;  
  12.         ......  
  13.   
  14.         synchronized (mService) {  
  15.             ......  
  16.   
  17.             // Atomically retrieve all of the other things to do.  
  18.             stops = processStoppingActivitiesLocked(true);  
  19.             NS = stops != null ? stops.size() : 0;  
  20.             ......  
  21.         }  
  22.   
  23.         int i;  
  24.   
  25.         ......  
  26.   
  27.         // Stop any activities that are scheduled to do so but have been  
  28.         // waiting for the next one to start.  
  29.         for (i=0; i<NS; i++) {  
  30.             ActivityRecord r = (ActivityRecord)stops.get(i);  
  31.             synchronized (mService) {  
  32.                 if (r.finishing) {  
  33.                     finishCurrentActivityLocked(r, FINISH_IMMEDIATELY);  
  34.                 } else {  
  35.                     ......  
  36.                 }  
  37.             }  
  38.         }  
  39.   
  40.         ......  
  41.     }  
  42.   
  43.     ......  
  44. }  
        这个函数首先会调用processStoppingActivitiesLocked函数把所有处于Stopped状态的Activity取回来,然后逐个分析它们,如果它们的ActivityRecord中的finishing成员变量为true,就说明这个Activity需要销毁了,于是,就调用finishCurrentActivityLocked函数来销毁它们。

        Step 3. ActivityStack.finishCurrentActivityLocked

        这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中:

[java]  view plain copy
  1. public class ActivityStack {  
  2.     ......  
  3.   
  4.     private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r,  
  5.             int mode) {  
  6.         ......  
  7.   
  8.         return finishCurrentActivityLocked(r, index, mode);  
  9.     }  
  10.   
  11.     private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r,  
  12.             int index, int mode) {  
  13.         ......  
  14.   
  15.         // make sure the record is cleaned out of other places.  
  16.         mStoppingActivities.remove(r);  
  17.         mWaitingVisibleActivities.remove(r);  
  18.         ......  
  19.   
  20.         final ActivityState prevState = r.state;  
  21.         r.state = ActivityState.FINISHING;  
  22.   
  23.         if (mode == FINISH_IMMEDIATELY  
  24.             || prevState == ActivityState.STOPPED  
  25.             || prevState == ActivityState.INITIALIZING) {  
  26.             // If this activity is already stopped, we can just finish  
  27.             // it right now.  
  28.             return destroyActivityLocked(r, true) ? null : r;  
  29.         } else {  
  30.             ......  
  31.         }  
  32.   
  33.         return r;  
  34.     }  
  35.   
  36.     ......  
  37. }  
        从上面的Step 2中传进来的参数mode为FINISH_IMMEDIATELY,并且这个即将要被销毁的Activity的状态为Stopped,因此,接下来就会调用destroyActivityLocked函数来销毁它。

        Step 4. ActivityStack.destroyActivityLocked

        这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中:

[java]  view plain copy
  1. public class ActivityStack {  
  2.     ......  
  3.   
  4.     final boolean destroyActivityLocked(ActivityRecord r,  
  5.             boolean removeFromApp) {  
  6.         ......  
  7.   
  8.         boolean removedFromHistory = false;  
  9.   
  10.         ......  
  11.   
  12.         final boolean hadApp = r.app != null;  
  13.   
  14.         if (hadApp) {  
  15.             ......  
  16.   
  17.             try {  
  18.                 ......  
  19.                 r.app.thread.scheduleDestroyActivity(r, r.finishing,  
  20.                     r.configChangeFlags);  
  21.             } catch (Exception e) {  
  22.                 ......  
  23.             }  
  24.   
  25.             ......  
  26.         } else {  
  27.             ......  
  28.         }  
  29.   
  30.         ......  
  31.   
  32.         return removedFromHistory;  
  33.     }  
  34.   
  35.     ......  
  36. }  
         在前面一篇文章 Android应用程序启动过程源代码分析 中,我们说到,每一个应用程序进程在ActivityManagerService中,都ProcessRecord记录与之对应,而每一个Activity,都是运行在一个进程上下文中,因此,在ActivityManagerService中,每一个ActivityRecord的app成员变量都应该指向一个ProcessRecord记录,于是,这里得到的hadApp为true。在ProcessRecord类中,有一个成员变量thread,它的类型为IApplicationThread。在文章 Android应用程序启动过程源代码分析 中,我们也曾经说过,每一个应用程序在启动的时候,它都会在内部创建一个ActivityThread对象,而在这个ActivityThread对象中,有一个成员变量mAppThread,它的类型为ApplicationThread,这是一个Binder对象,专门用来负责在应用程序和ActivityManagerService之间执行进程间通信工作的。应用程序在启动的时候,就会将这个Binder对象传递给ActivityManagerService,而ActivityManagerService就会把它保存在相应的ProcessRecord记录的thread成员变量中。因此,ProcessRecord记录的thread成员变量其实就是ApplicationThread对象的远程接口,于是,执行下面这个语句的时候:

[java]  view plain copy
  1. r.app.thread.scheduleDestroyActivity(r, r.finishing,  
  2.     r.configChangeFlags);  
        就会进入到ApplicationThread类中的scheduleDestroyActivity函数来。

        Step 5. ApplicationThread.scheduleDestroyActivity

        这个函数定义在frameworks/base/core/java/android/app/ActivityThread.java文件中:

[java]  view plain copy
  1. public final class ActivityThread {  
  2.     ......  
  3.   
  4.     private final class ApplicationThread extends ApplicationThreadNative {  
  5.         ......  
  6.   
  7.         public final void scheduleDestroyActivity(IBinder token, boolean finishing,  
  8.                 int configChanges) {  
  9.   
  10.             queueOrSendMessage(H.DESTROY_ACTIVITY, token, finishing ? 1 : 0,  
  11.                     configChanges);  
  12.         }  
  13.   
  14.         ......  
  15.     }  
  16.   
  17.     ......  
  18. }  
        这个函数调用外部类ActivityThread的queueOrSendMessage函数来往应用程序的消息队列中发送一个H.DESTROY_ACTIVITY消息,这个消息最终由ActivityThread类的handleDestroyActivity函数来处理。

        Step 6. ActivityThread.handleDestroyActivity

        这个函数定义在frameworks/base/core/java/android/app/ActivityThread.java文件中:

[java]  view plain copy
  1. public final class ActivityThread {  
  2.     ......  
  3.   
  4.     private final void handleDestroyActivity(IBinder token, boolean finishing,  
  5.             int configChanges, boolean getNonConfigInstance) {  
  6.         ......  
  7.   
  8.         ActivityClientRecord r = performDestroyActivity(token, finishing,  
  9.             configChanges, getNonConfigInstance);  
  10.         if (r != null) {  
  11.             WindowManager wm = r.activity.getWindowManager();  
  12.             View v = r.activity.mDecor;  
  13.             if (v != null) {  
  14.                 ......  
  15.   
  16.                 if (r.activity.mWindowAdded) {  
  17.                     wm.removeViewImmediate(v);  
  18.                 }  
  19.                   
  20.                 ......  
  21.             }  
  22.             ......  
  23.         }  
  24.   
  25.         ......  
  26.     }  
  27.   
  28.     ......  
  29. }  
        这里首先调用performDestroyActivity来执行一些销毁Activity的操作,期间就会调用Activity的onDestroy函数让Activity本身有机会执行一些销毁前的工作了。这里通过r.activity.getWindowManager函数返回的是一个LocalWindowManager对象,而通过r.activity.mDecor得到的是一个DecorView对象,这些都是在Activity启动的时候设置好的。函数最后调用LocalWindowManager对象wm的removeViewImmediate函员来从LocalWindowManager移除这个DecorView对象。

        Step 7. LocalWindowManager.removeViewImmediate

        这个函数定义在frameworks/base/core/java/android/view/Window.java文件中:

[java]  view plain copy
  1. public abstract class Window {  
  2.     ......  
  3.   
  4.     private class LocalWindowManager implements WindowManager {  
  5.         ......  
  6.   
  7.         public final void removeViewImmediate(View view) {  
  8.             mWindowManager.removeViewImmediate(view);  
  9.         }  
  10.   
  11.         ......  
  12.   
  13.         private final WindowManager mWindowManager;  
  14.     }  
  15.   
  16.     ......  
  17. }  
        LocalWindowManager类的成员变量mWindowManager是一个WndowManagerImpl对象,这个函数只是简单地调用WndowManagerImpl类的removeViewImmediate来进一步处理。

       Step 8. WndowManagerImpl.removeViewImmediate

       这个函数定义在frameworks/base/core/java/android/view/WindowManagerImpl.java文件中:

[java]  view plain copy
  1. public class WindowManagerImpl implements WindowManager {  
  2.     ......  
  3.   
  4.     public void removeViewImmediate(View view) {  
  5.         synchronized (this) {  
  6.             int index = findViewLocked(view, true);  
  7.             ViewRoot root = mRoots[index];  
  8.             ......  
  9.   
  10.             root.die(true);  
  11.               
  12.             ......  
  13.         }  
  14.     }  
  15.   
  16.     ......  
  17. }  
         这个函数首先是找到这个view所属的ViewRoot对象root,然后调用这个root对象的die函数来销毁它。

         Step 9. ViewRoot.die

         这个函数定义在frameworks/base/core/java/android/view/ViewRoot.java文件中:

[java]  view plain copy
  1. public final class ViewRoot extends Handler implements ViewParent,  
  2.         View.AttachInfo.Callbacks {  
  3.     ......  
  4.   
  5.     public void die(boolean immediate) {  
  6.         if (immediate) {  
  7.             doDie();  
  8.         } else {  
  9.             ......  
  10.         }  
  11.     }  
  12.       
  13.     ......  
  14. }  
        上面Step 8传进来的immediate参数为true,因此,这里直接调用doDie函数来进一步处理。

        Step 10. ViewRoot.doDie

        这个函数定义在frameworks/base/core/java/android/view/ViewRoot.java文件中:

[java]  view plain copy
  1. public final class ViewRoot extends Handler implements ViewParent,  
  2.         View.AttachInfo.Callbacks {  
  3.     ......  
  4.   
  5.     void doDie() {  
  6.         ......  
  7.   
  8.         synchronized (this) {  
  9.             ......  
  10.   
  11.             if (mAdded) {  
  12.                 mAdded = false;  
  13.                 dispatchDetachedFromWindow();  
  14.             }  
  15.         }  
  16.     }  
  17.       
  18.     ......  
  19. }  
        当我们把Activity窗口中的View添加到一个ViewRoot对象时,就会把它的成员变量mAdded设置为true,这样就表示这个ViewRoot中有View存在,于是,这里就会调用dispatchDetachedFromWindow函数来进一步处理。

        Step 11. ViewRoot.ispatchDetachedFromWindow

        这个函数定义在frameworks/base/core/java/android/view/ViewRoot.java文件中:

[java]  view plain copy
  1. public final class ViewRoot extends Handler implements ViewParent,  
  2.         View.AttachInfo.Callbacks {  
  3.     ......  
  4.   
  5.     void dispatchDetachedFromWindow() {  
  6.         ......  
  7.   
  8.         if (mInputChannel != null) {  
  9.             if (mInputQueueCallback != null) {  
  10.                 ......  
  11.             } else {  
  12.                 InputQueue.unregisterInputChannel(mInputChannel);  
  13.             }  
  14.         }  
  15.   
  16.         try {  
  17.             sWindowSession.remove(mWindow);  
  18.         } catch (RemoteException e) {  
  19.         }  
  20.   
  21.         ......  
  22.     }  
  23.   
  24.     ......  
  25. }  

本文转载自:http://blog.csdn.net/sfshine/article/details/9118191

共有 人打赏支持
SuShine
粉丝 118
博文 436
码字总数 88684
作品 0
青岛
后端工程师
Android生命周期——认识Activity

1、什么是Activity? Activity是一个应用程序组件,提供用户与程序交互的界面。 2、Activity如何创建使用: 继承Android的Activity类 重写方法 设置显示布局 在AndroidManifest文件中,注册A...

落叶-归根 ⋅ 2016/07/23 ⋅ 0

Android7.0 分屏下 Activity 与 Fragment 生命周期(一)

小菜前段时间整理了一篇关于我们真的了解 Activity 与 Fragment 的生命周期吗?的小博文,整理了基础版的关于 Activity 与 Fragment 的生命周期。 后来又一次被一个大大神问到在 Android7.0...

阿策神奇 ⋅ 06/11 ⋅ 0

Android生命周期

Activity生命周期: onCreate() :Activity创建时调用,有且只调用一次 onStart() : 紧跟onCreate()之后调用,目标是视图可见 onResume() : 在onStart()之后调用,目标是使视图控件获得焦点 ...

狼行千年 ⋅ 2015/05/17 ⋅ 0

快速掌握activity的生命周期

activity的生命周期不管是在面试还是在工作中我们都会经常遇到,这当然也是非常基础的,基础也很重要哦,学会activity的生命周期对我们以后开发更健壮的程序会有很大帮助。下面来看一下Activ...

晨曦之光 ⋅ 2012/03/13 ⋅ 0

Android 解读开源项目UniversalMusicPlayer(播放控制层)

版权声明:本文为博主原创文章,未经博主允许不得转载 源码:AnliaLee/android-UniversalMusicPlayer 首发地址:Anlia_掘金 大家要是看到有错误的地方或者有啥好的建议,欢迎留言评论 前言 ...

Anlia ⋅ 04/29 ⋅ 0

03_Activity 生命周期介绍【图解】

一、基本概念 Activity 负责创建一个窗口,程序员可以通过 setContentView(View)向这个窗口添加一些 UI组件。本文将介绍 Activiy 的生命周期,并且展示一个小程序来验证 Activity 的执行过程...

晨曦之光 ⋅ 2012/03/14 ⋅ 0

android TV 通过按键控制RecyclerView中的item的选中,移动,点击功能

前言 android tv开发中常常会用到recyclerview展示内容,并且要处理好按键控制item的选中,移动,点击功能,会遇到失去焦点,recyclerview 获取childView获取不到,出现null的问题。下面介绍...

MRYangY ⋅ 03/06 ⋅ 0

Android 面试技能树梳理

相信大家都有面试的经历,相对比面试官的问的一些问题其实都是基础的知识,但就是一些基础的知识我们也不是很完美的回答出来,我们也知道现在的开发人员很多,一家公司一个岗位就会有很多的开...

liu3364575 ⋅ 05/05 ⋅ 0

移动端解决input获取焦点软键盘弹出影响定位的问题

这是刚做前端时候写的文章,拿到简书上做记录吧!以免以后再遇到这样的坑。 在最近的一次H5页面开发中,发现在安卓端点击输入框的时候虚拟键盘会把最下边的‘保存’按钮顶上去。 在试了很多方...

愿爱无忧dk_ ⋅ 05/25 ⋅ 0

input 输入框被软键盘遮挡问题

1.安卓手机 在软键盘被弹起来的时候输入框不被遮挡,支持性良好 2.ios手机 在软键盘被弹起来的时候输入框被遮挡,体验很不好 在经过多次实验发现,ios手机输入框被弹起的瞬间是不被遮挡的,过...

爱喝水的小熊 ⋅ 06/06 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

vuex学习

1、getters基本用法: 在store.js里面用const声明我们的getters属性。 const getters={ count:function (state) { return state.count +=100; }} export default new Vuex.S......

大美琴 ⋅ 40分钟前 ⋅ 0

292. Nim Game - LeetCode

Question 292. Nim Game Solution 思路:试着列举一下,就能发现一个n只要不是4的倍数,就能赢。 n 是否能赢1 true2 true3 true4 false 不论删除几,对方都能一把赢5 t...

yysue ⋅ 52分钟前 ⋅ 0

G6 关系数据可视化图形库 简单使用

官网 https://antv.alipay.com/zh-cn/g6/1.x/index.html 效果 首先生成给定数目的小球,并设置随机的颜色 按照顺序,设置小球的角度以及坐标 设置定时器,每隔一定的时间修改小球的角度和坐标...

阿豪boy ⋅ 55分钟前 ⋅ 0

6.5 zip压缩工具 6.6 tar打包 6.7 打包并压缩

zip压缩工具 zip命令可以压缩目录和文件,-r 压缩目录。 zip使用方法 zip 1.txt.zip 1.txt //压缩文件 zip -r 123.zip 123/ //压缩目录 unzip 1.txt.zip //解压 unzip 123.zip -d /root/456...

Linux_老吴 ⋅ 今天 ⋅ 0

react-loadable使用跳坑

官方给react-loadable的定义是: A higher order component for loading components with dynamic imports. 动态路由示例 withLoadable.js import React from 'react'import Loadable fro......

pengqinmm ⋅ 今天 ⋅ 0

记录工作中遇到的坑

1、ios safari浏览器向下滚动会触发window resize事件

端木遗风 ⋅ 今天 ⋅ 0

桥接设计模式

1、概述: 将抽象部分与他的实现部分分离,这样抽象化与实现化解耦,使他们可以独立的变化 如何实现解耦的呢,就是通过提供抽象化和实现化之间的桥接结构 桥接模式将继承模式转化成关联关系,他降...

职业搬砖20年 ⋅ 今天 ⋅ 0

20.zip压缩 tar打包 打包并压缩

6月25日任务 6.5 zip压缩工具 6.6 tar打包 6.7 打包并压缩 6.5 zip压缩工具: zip支持压缩目录 zip压缩完之后原来的文件不删除 不同的文件内容其实压缩的效果不一样 文件内有很多重复的用xz压...

王鑫linux ⋅ 今天 ⋅ 0

double类型数据保留四位小数的另一种思路

来源:透析公式处理,有时候数据有很长的小数位,有的时候由在四位以内,如果用一般的处理方法,那么不足四位的小树会补充0到第四位,这样子有点画蛇添足的感觉,不太好看。所以要根据小数的...

young_chen ⋅ 今天 ⋅ 0

Django配置163邮箱出现 authentication failed(535)错误解决方法

最近用Django写某网站,当配置163邮箱设置完成后,出现535错误即:smtplib.SMTPAuthenticationError: (535, b'Error: authentication failed') Django初始配置邮箱设置 EMAIL_HOST = "smtp.1...

陈墨轩_CJX ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部