Android Activity详解

提示:
1.本文有较多超链接与文字引用,若侵权请及时联系。
2.可通过目录进行快速查询想了解的内容。

一、Activity简介

  • Activity展示为可视化用户界面,通过程序与用户交互的窗口。
    如我们打开手机联系人,我们所看到的的界面就是Activity,联系人里面的内容如老王、老李等消息人列表以及各种按钮都是放在Activity这个容器里面的,我们可以在这个Activity上通过滑动列表,点击按钮等操作来和应用程序进行交互。

  • 一个Activity占据当前的窗口,响应所有窗口事件,具备控件、菜单等界面元素。
    负责接收用户的操作,点击、滑动等操作都是通过Activity来传递给相应的组件,但是这些组件必须放在Activity里面。

  • Activity需要保存数据和调用系统等功能、妥善管理生命周期和实现界面之间的跳转逻辑等。
    在应用里一般会有多个界面,一个界面对应一个Activity,Activity自行管理这个界面的生命周期,也就是管理这个界面什么时候创建、显示、销毁等。界面之间的跳转即不同Activity之间的跳转,通常通过Intent来进行显示或隐式跳转。

  • 对于开发者而言,一般创建Activity的子类如public class MainActivity extends AppCompatActivity,在其基础上定义界面布局、添加业务逻辑等。

二、创建Activity

在合适的包(package)中点击右键->new->Activity->选择合适的Activity。

这里选择了Empty Activity进行创建
在这里插入图片描述
在ActivityName中输入Activity的名字,输入名字后Android Studio会自动帮你生成Activity对应的Layout的名字,当然,你也可以自行修改Layout的名字。

选择Package name 注意package name全部为小写。

选择Source Language

让我们来看一下刚刚创建的Activity文件

package packagename.activity;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import packagename.R;

public class EmptyActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);  //实现父类的onCreate()方法
        setContentView(R.layout.activity_empty);  //加载Layout视图
    }
}

上面的代码都是Android Studio帮我们自动生成的。其中:

  • import android.support.v7.app.AppCompatActivity;
    表示import了一个support.v7下的一个AppCompatActivity类,因为我在app目录下的build.gradle中依赖的是implementation 'com.android.support:appcompat-v7:27.1.1',所以Android Studio会自动帮我们import android.support.v7.app.AppCompatActivity,如果你的项目使用的是Androidx,则import的会是另外一个类,这里就不展开描述。

  • public class EmptyActivity extends AppCompatActivity
    刚刚创建的EmptyActivity是继承AppCompatActivity的。
    https://blog.csdn.net/javazejian/article/details/51932562

  • 帮我们重写了父类的OnCreate()方法,OnCreate()方法属于Activity生命周期里的七大方法之一(下面我们会讲Activity的生命周期),这个方法会在Activity创建的时候被调用。

三、Activity的生命周期

Android通过Activity栈的方式来管理Activity

Activity通过生命周期的方法来管理自己的创建与销毁

参考及引用文章:
Android之Activity生命周期浅析(一)
Android之Activity生命周期浅析(二)

3.1Activity的形态

  • Active/Running:
    Activity处于活动状态,此时Activity处于栈顶,对用户可见且可与用户进行交互。

  • Pause:
    当Activity失去焦点时,或被一个新的非全屏的Activity,或被一个透明的Activity放置在栈顶时,Activity就转化为Paused状态。但我们需要明白,此时Activity只是失去了与用户交互的能力,其所有的状态信息及其成员变量都还存在,只有在系统内存紧张的情况下,才有可能被系统回收掉。

  • Stopped:
    当一个Activity被另一个Activity完全覆盖时,被覆盖的Activity就会进入Stopped状态,此时它不再可见,但是跟Paused状态一样保持着其所有状态信息及其成员变量。

  • Killed

  • 当Activity被系统回收掉时,Activity就处于Killed状态。

3.2Activity生命周期流程图

Activity生命周期流程图

3.3生命周期里的方法

所谓的典型的生命周期就是在有用户参与的情况下,Activity经历从创建,运行,停止,销毁等正常的生命周期过程。

  • onCreate :
    该方法是在Activity被创建时回调,它是生命周期第一个调用的方法,我们在创建Activity时一般都需要重写该方法,然后在该方法中做一些初始化的操作,如通过setContentView设置界面布局的资源,初始化所需要的组件信息等。

  • onStart :
    此方法被回调时表示Activity正在启动,此时Activity已处于可见状态,只是还没有在前台显示,因此无法与用户进行交互。可以简单理解为Activity已显示而我们无法看见罢了。

  • onResume :
    当此方法回调时,则说明Activity已在前台可见,可与用户交互了(处于前面所说的Active/Running形态),onResume方法与onStart的相同点是两者都表示Activity可见,只不过onStart回调时Activity还是后台无法与用户交互,而onResume则已显示在前台,可与用户交互。当然从流程图,我们也可以看出当Activity停止后(onPause方法和onStop方法被调用),重新回到前台时也会调用onResume方法,因此我们也可以在onResume方法中初始化一些资源,比如重新初始化在onPause或者onStop方法中释放的资源。

  • onPause :
    此方法被回调时则表示Activity正在停止(Paused形态),一般情况下onStop方法会紧接着被回调。但通过流程图我们还可以看到一种情况是onPause方法执行后直接执行了onResume方法,这属于比较极端的现象了,这可能是用户操作使当前Activity退居后台后又迅速地再回到到当前的Activity,此时onResume方法就会被回调。当然,在onPause方法中我们可以做一些数据存储或者动画停止或者资源回收的操作,但是不能太耗时,因为这可能会影响到新的Activity的显示——onPause方法执行完成后,新Activity的onResume方法才会被执行。

  • onStop :
    一般在onPause方法执行完成直接执行,表示Activity即将停止或者完全被覆盖(Stopped形态),此时Activity不可见,仅在后台运行。同样地,在onStop方法可以做一些资源释放的操作(不能太耗时)。

  • onRestart :
    表示Activity正在重新启动,当Activity由不可见变为可见状态时,该方法被回调。这种情况一般是用户打开了一个新的Activity时,当前的Activity就会被暂停(onPause和onStop被执行了),接着又回到当前Activity页面时,onRestart方法就会被回调。

  • onDestroy :
    此时Activity正在被销毁,也是生命周期最后一个执行的方法,一般我们可以在此方法中做一些回收工作和最终的资源释放。

  • 生命周期方法小结:
    当Activity启动时,依次会调用onCreate(),onStart(),onResume(),而当Activity退居后台时(不可见,点击Home或者被新的Activity完全覆盖),onPause()和onStop()会依次被调用。当Activity重新回到前台(从桌面回到原Activity或者被覆盖后又回到原Activity)时,onRestart(),onStart(),onResume()会依次被调用。当Activity退出销毁时(点击back键),onPause(),onStop(),onDestroy()会依次被调用,到此Activity的整个生命周期方法回调完成。现在我们再回头看看之前的流程图,应该是相当清晰了吧。嗯,这就是Activity整个典型的生命周期过程。

3.4关于生命周期的一些问题和解析

1.在Activity中弹出Toast或者Dialog会使Activity调用onPause()方法吗?

不会,无论是Toast还是Dialog,他们的出现对Activity的生命周期没有任何影响。

因为我们在创建Toast或者Dialog的时候,需要传入一个context类型的参数,这个参数也就说明我们的Toast或Dialog是在这个上下文创建的,我们的Activity也是正在交互的,并不会执行onPause()。

为什么Dialog弹出以后,activity就无法捕捉触摸事件了?

2.从Activity A 跳转到 透明背景的Activity B再结束Activity B,生命周期的方法会怎样?

(1)首先打开Activity A 会调用 ActivityA的onCreat、onStart、onResume

(2)从Activity A 跳转到 透明背景的Activity B,会调用Activity A 的onPause,Activity B 的onCreate、onStart、onResume

(3)结束Activity B ,会调用Activity B 的onPause,然后调用 Activity A 的onResume,再调用Activity B 的onStop、onDestroy。

3.从Activity A 跳转到透明背景的Activity B后,点击Activity A的按钮还会有响应吗?

没有了喔,从生命周期来看,Activity B压入到了Activity栈的栈顶,Activity A已经进入了Pause形态,失去了与用户进行交互的能力。

四、Activity启动流程

4.1Binder机制

具体内容可参考再谈Android Binder跨进程通信原理

Android系统将进程分为了用户(Zygote)进程和系统(SystemServer)进程以及各种应用进程等,为了能够实现各种进程之间的通讯,Android系统采用了自己的进程间通讯方式“Binder机制”。其中主要有四种角色Client、Server、Binder Driver、Service Manager。

下面图中显示了四种角色之间的关系。
在这里插入图片描述
简单来说就是Android Application的Client进程想获取Android Application的Server进程的服务的话,需要进行以下步骤:

1.Server进程通过Binder驱动向ServiceManager进程注册服务。

Server进程创建一个Binder对象,其中Binder实体是Server进程在Binder驱动中的具体存在形式,该对象保存Server和ServiceManager的信息,Binder驱动通过内核空间的Binder实体找到用户空间的Server对象。

2.Client进程通过Binder驱动向ServiceManager进程获取响应的Service信息(Service信息其实就是Server进程创建的Binder对象的一个代理对象)。

3.Client进程根据获取到的Service信息(Binder的代理对象)通过Binder驱动建立与该Server进程通信的链路,并开始使用服务。

4.2启动Activity的流程

参考及引用资料:
一张图搞定,Activity的启动流程

Android源码解析之(十四)Activity启动流程

Android Activity启动流程分析
在这里插入图片描述

4.2.1从源码看Activity启动流程

1.不管是从桌面进入应用主界面,还是在应用里进入一个新的Activity,最终都会调用Activity.startActivity()方法。

2.startActivity()方法源码

@Override
    public void startActivity(Intent intent) {
        this.startActivity(intent, null);
    }
    @Override
    public void startActivity(Intent intent, @Nullable Bundle options) {
        if (options != null) {
            startActivityForResult(intent, -1, options);
        } else {
            startActivityForResult(intent, -1);
        }
    }

有几个重载的startActivity()方法,但最终都会执行到Activity.startActivityForResult方法。如果是调用startActivity(intent)启动Activity,那么requestCode参数则传入-1,表示不需要返回Activity的数据。

3.startActivityForResult()方法源码

 public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
            @Nullable Bundle options) {
        if (mParent == null) {
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);
            if (ar != null) {
                mMainThread.sendActivityResult(
                    mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                    ar.getResultData());
            }
            if (requestCode >= 0) {
                // If this start is requesting a result, we can avoid making
                // the activity visible until the result is received.  Setting
                // this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
                // activity hidden during this time, to avoid flickering.
                // This can only be done when a result is requested because
                // that guarantees we will get information back when the
                // activity is finished, no matter what happens to it.
                mStartedActivity = true;
            }

            cancelInputsAndStartExitTransition(options);
            // TODO Consider clearing/flushing other event sources and events for child windows.
        } else {
            if (options != null) {
                mParent.startActivityFromChild(this, intent, requestCode, options);
            } else {
                // Note we want to go through this method for compatibility with
                // existing applications that may have overridden it.
                mParent.startActivityFromChild(this, intent, requestCode);
            }
        }
    }

在上面的代码中,mParent进行了一个判Null操作,由于我们是第一次启动Activity,所以这里的mParent为空,所以会执行if分之,然后调用mInstrumentation.execStartActivity方法,并且这里需要注意的是,有一个判断逻辑。即:

if (requestCode >= 0) {
    mStartedActivity = true;
}

前面说过,调用startActivityForResult的时候只有requestCode的值大于等于0,onActivityResult才会被回调。

3.Instrumentation.execStartActivity()源码

首先看一下Instrumentation,Instrumentation是android系统中启动Activity的一个实际操作类,也就是说Activity在应用进程端的启动实际上就是Instrumentation执行的。那为什么是这样呢?实际上acitivty的启动分为应用进程端的启动和SystemServer服务进程端的启动的,多个应用进程相互配合最终完成了Activity在系统中的启动的,而在应用进程端的启动实际的操作类就是Intrumentation来执行的。

execStartActivity函数源码:

public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
        IApplicationThread whoThread = (IApplicationThread) contextThread;
        Uri referrer = target != null ? target.onProvideReferrer() : null;
        if (referrer != null) {
            intent.putExtra(Intent.EXTRA_REFERRER, referrer);
        }
        if (mActivityMonitors != null) {
            synchronized (mSync) {
                final int N = mActivityMonitors.size();
                for (int i=0; i<N; i++) {
                    final ActivityMonitor am = mActivityMonitors.get(i);
                    ActivityResult result = null;
                    if (am.ignoreMatchingSpecificIntents()) {
                        result = am.onStartActivity(intent);
                    }
                    if (result != null) {
                        am.mHits++;
                        return result;
                    } else if (am.match(who, null, intent)) {
                        am.mHits++;
                        if (am.isBlocking()) {
                            return requestCode >= 0 ? am.getResult() : null;
                        }
                        break;
                    }
                }
            }
        }
        try {
            intent.migrateExtraStreamToClipData();
            intent.prepareToLeaveProcess(who);
            int result = ActivityManager.getService()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
        return null;
    }

如上,execStartActivity主要有以下几个参数:
this,为启动Activity的对象;
contextThread,为Binder对象,是主进程的context对象;
token,也是一个Binder对象,指向了服务端一个ActivityRecord对象;
target,为启动的Activity;
intent,启动的Intent对象;
requestCode,请求码;
options,参数;
通过,execStartActivity方法可以发现,该方法主要调用ActivityManager.getService()方法,继续查看ActivityManager.getService源码。

/**  
 * @hide  
 */  
public static IActivityManager getService() {
        return IActivityManagerSingleton.get();
    } 

//继续查看  

private static final Singleton<IActivityManager> IActivityManagerSingleton =  
        new Singleton<IActivityManager>() {  
            @Override  
            protected IActivityManager create() {  
                final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);  
                final IActivityManager am = IActivityManager.Stub.asInterface(b);  
                return am;  
            }  
        };  

 //...code  

}

可以发现,IActivityManager.Stub.asInterface(b),那么继续看asInterface方法的实现:

static public IActivityManager asInterface(IBinder obj) {
        if (obj == null) {
            return null;
        }
        IActivityManager in =
            (IActivityManager)obj.queryLocalInterface(descriptor);
        if (in != null) {
            return in;
        }

        return new ActivityManagerProxy(obj);
    }

asInterface()返回一个ActivityManagerProxy对象,ActivityManagerProxy继承与IActivityManager,到了这里就引出了我们android系统中很重要的一个概念:Binder机制。

IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE)

好吧,最后直接返回一个ActivityManagerProxy对象,而ActivityManagerProxy继承与IActivityManager,到了这里就引出了我们android系统中很重要的一个概念:Binder机制。我们知道应用进程与SystemServer进程属于两个不同的进程,进程之间需要通讯,android系统采取了自身设计的Binder机制,这里的ActivityManagerProxy和ActivityManagerNative都是继承与IActivityManager的而SystemServer进程中的ActivityManagerService对象则继承与ActivityManagerNative。简单的表示:
Binder接口 –> ActivityManagerNative/ActivityManagerProxy –> ActivityManagerService;

这样,ActivityManagerNative与ActivityManagerProxy相当于一个Binder的客户端ActivityManagerService相当于Binder的服务端,这样当ActivityManagerNative调用接口方法的时候底层通过Binder driver就会将请求数据与请求传递给server端,并在server端执行具体的接口逻辑。需要注意的是Binder机制是单向的,是异步的,也就是说只能通过client端向server端传递数据与请求而不同等待服务端的返回,也无法返回,那如果SystemServer进程想向应用进程传递数据怎么办?这时候就需要重新定义一个Binder请求以SystemServer为client端,以应用进程为server端,这样就是实现了两个进程之间的双向通讯。

好了,说了这么多我们知道这里的ActivityManagerNative是ActivityManagerService在应用进程的一个client就好了,通过它就可以滴啊用ActivityManagerService的方法了。

所以,ActivityManager.getService().startActivity(…)获取了ActivityManagerProxy对象调用了ActivityManagerProxy的startActivity方法,其实最终调用的是ActivityManagerService的startActivity方法。

以上其实都是发生在应用进程中,下面开始调用的ActivityManagerService的执行时发生在SystemServer进程。

来看一下,ActivityManagerService$startActivity的相关源码。

@Override  
public final int startActivity(IApplicationThread caller, String callingPackage,  
        Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,  
        int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {  
    return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,  
            resultWho, requestCode, startFlags, profilerInfo, bOptions,  
            UserHandle.getCallingUserId());  
}  

ActivityManagerService启动Activity主要会涉及到以下的一些方法。

ActivityManagerService.startActivity() 
ActvityiManagerService.startActivityAsUser() 
ActivityStackSupervisor.startActivityMayWait() 
ActivityStackSupervisor.startActivityLocked() 
ActivityStackSupervisor.startActivityUncheckedLocked() 
ActivityStackSupervisor.startActivityLocked() 
ActivityStackSupervisor.resumeTopActivitiesLocked() 
ActivityStackSupervisor.resumeTopActivityInnerLocked() 

当ActivityManagerService调用startActivity方法后,该方法调用的是startActivityAsUser(),该方法的源码如下:

@Override  
public final int startActivityAsUser(IApplicationThread caller, String callingPackage,  
        Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,  
        int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {  
    enforceNotIsolatedCaller("startActivity");  
    userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),  
            userId, false, ALLOW_FULL_ONLY, "startActivity", null);  
    // TODO: Switch to user app stacks here.  
    return mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent,  
            resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,  
            profilerInfo, null, null, bOptions, false, userId, null, null,  
            "startActivityAsUser");  
}  

可以看到这里只是进行了一些关于userid的逻辑判断,然后就调用mStackSupervisor.startActivityMayWait方法,该方法涉及的源码比较多,下面截取一些核心的代码实现:

final int startActivityMayWait(IApplicationThread caller, int callingUid,
            String callingPackage, Intent intent, String resolvedType,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IBinder resultTo, String resultWho, int requestCode, int startFlags,
            ProfilerInfo profilerInfo, WaitResult outResult, Configuration config,
            Bundle options, boolean ignoreTargetSecurity, int userId,
            IActivityContainer iContainer, TaskRecord inTask) {
            ...

            int res = startActivityLocked(caller, intent, resolvedType, aInfo,
                    voiceSession, voiceInteractor, resultTo, resultWho,
                    requestCode, callingPid, callingUid, callingPackage,
                    realCallingPid, realCallingUid, startFlags, options, ignoreTargetSecurity,
                    componentSpecified, null, container, inTask);
            ...
            return res;
    }

该方法在启动Activity后执行了一些逻辑判断后,最终调用startActivityLocked方法。

final int startActivityLocked(IApplicationThread caller,
            Intent intent, String resolvedType, ActivityInfo aInfo,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IBinder resultTo, String resultWho, int requestCode,
            int callingPid, int callingUid, String callingPackage,
            int realCallingPid, int realCallingUid, int startFlags, Bundle options,
            boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
            ActivityContainer container, TaskRecord inTask) {
        int err = ActivityManager.START_SUCCESS;

        ...
        err = startActivityUncheckedLocked(r, sourceRecord, voiceSession, voiceInteractor,
                startFlags, true, options, inTask);

        ...
        return err;
    }

这个方法中主要构造了ActivityManagerService端的Activity对象–>ActivityRecord,并在执行了一些逻辑判断后调用了startActivityUncheckedLocked方法。

final int startActivityUncheckedLocked(final ActivityRecord r, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags,
            boolean doResume, Bundle options, TaskRecord inTask) {
        ...
        ActivityStack.logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task);
        targetStack.mLastPausedActivity = null;
        targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options);
        if (!launchTaskBehind) {
            // Don't set focus on an activity that's going to the back.
            mService.setFocusedActivityLocked(r, "startedActivity");
        }
        return ActivityManager.START_SUCCESS;
    }

startActivityUncheckedLocked方法中主要执行了不同启动模式不同栈的处理,并最后调用了startActivityLocked的重载方法。

final void startActivityLocked(ActivityRecord r, boolean newTask,
            boolean doResume, boolean keepCurTransition, Bundle options) {
        ...
        if (doResume) {
            mStackSupervisor.resumeTopActivitiesLocked(this, r, options);
        }
    }

而这个startActivityLocked方法主要执行初始化了windowManager服务,然后调用resumeTopActivitiesLocked方法。

boolean resumeTopActivitiesLocked(ActivityStack targetStack, ActivityRecord target,
            Bundle targetOptions) {
        if (targetStack == null) {
            targetStack = mFocusedStack;
        }
        // Do targetStack first.
        boolean result = false;
        if (isFrontStack(targetStack)) {
            result = targetStack.resumeTopActivityLocked(target, targetOptions);
        }

        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
            final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
                final ActivityStack stack = stacks.get(stackNdx);
                if (stack == targetStack) {
                    // Already started above.
                    continue;
                }
                if (isFrontStack(stack)) {
                    stack.resumeTopActivityLocked(null);
                }
            }
        }
        return result;
    }

可以发现经过循环逻辑判断之后,该函数最终又调用了resumeTopActivityLocked方法,

final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
        if (mStackSupervisor.inResumeTopActivity) {
            // Don't even start recursing.
            return false;
        }

        boolean result = false;
        try {
            // Protect against recursion.
            mStackSupervisor.inResumeTopActivity = true;
            if (mService.mLockScreenShown == ActivityManagerService.LOCK_SCREEN_LEAVING) {
                mService.mLockScreenShown = ActivityManagerService.LOCK_SCREEN_HIDDEN;
                mService.updateSleepIfNeededLocked();
            }
            result = resumeTopActivityInnerLocked(prev, options);
        } finally {
            mStackSupervisor.inResumeTopActivity = false;
        }
        return result;
    }

继续调用resumeTopActivityInnerLocked方法:

private boolean resumeTopActivityInnerLocked(ActivityRecord prev, Bundle options) {

        ...
        if (mResumedActivity != null) {
            if (DEBUG_STATES) Slog.d(TAG_STATES,
                    "resumeTopActivityLocked: Pausing " + mResumedActivity);
            pausing |= startPausingLocked(userLeaving, false, true, dontWaitForPause);
        }        ...
        return true;
    }

经过一系列处理逻辑之后最终调用了startPausingLocked方法,这个方法作用就是让系统中栈中的Activity执行onPause方法。

执行栈顶Activity的onPause方法过程:

ActivityStack.startPausingLocked()
IApplicationThread.schudulePauseActivity()
ActivityThread.sendMessage()
ActivityThread.H.sendMessage();
ActivityThread.H.handleMessage()
ActivityThread.handlePauseActivity()
ActivityThread.performPauseActivity()
Activity.performPause()
Activity.onPause()
ActivityManagerNative.getDefault().activityPaused(token)
ActivityManagerService.activityPaused()
ActivityStack.activityPausedLocked()
ActivityStack.completePauseLocked()
ActivityStack.resumeTopActivitiesLocked()
ActivityStack.resumeTopActivityLocked()
ActivityStack.resumeTopActivityInnerLocked()
ActivityStack.startSpecificActivityLocked

好吧,方法比较多也比较乱,首先来看startPausingLocked方法:

final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping, boolean resuming,
            boolean dontWait) {
        ...
        if (prev.app != null && prev.app.thread != null) {
            if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev);
            try {
                EventLog.writeEvent(EventLogTags.AM_PAUSE_ACTIVITY,
                        prev.userId, System.identityHashCode(prev),
                        prev.shortComponentName);
                mService.updateUsageStats(prev, false);
                prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
                        userLeaving, prev.configChangeFlags, dontWait);
            } catch (Exception e) {
                // Ignore exception, if process died other code will cleanup.
                Slog.w(TAG, "Exception thrown during pause", e);
                mPausingActivity = null;
                mLastPausedActivity = null;
                mLastNoHistoryActivity = null;
            }
        } else {
            mPausingActivity = null;
            mLastPausedActivity = null;
            mLastNoHistoryActivity = null;
        }
        ...
    }

可以看到这里执行了pre.app.thread.schedulePauseActivity方法,通过分析不难发现这里的thread是一个IApplicationThread类型的对象,而在ActivityThread中也定义了一个ApplicationThread的类,其继承了IApplicationThread,并且都是Binder对象,不难看出这里的IAppcation是一个Binder的client端而ActivityThread中的ApplicationThread是一个Binder对象的server端,所以通过这里的thread.schedulePauseActivity实际上调用的就是ApplicationThread的schedulePauseActivity方法。

未完待续…

五、Activity的启动模式

参考及引用资料:
Android中Activity的启动模式(LaunchMode)和使用场景

细谈Activity四种启动模式

前提知识

1.一个应用程序通常会有多个Activity,这些Activity都会在Manifest文件中注册,而且可以设置对应的action(如MainActivity的action),我们可以通过action来启动对应Activity(隐式启动)。

<action android:name="android.intent.action.MAIN" />

2.一个应用程序可以说由一系列组件组成,这些组件以进程为载体,相互协作实现App功能。

3.任务栈(Task Stack)或者叫退回栈(Back Stack)介绍:

3.1.任务栈用来存放用户开启的Activity。

3.2.在应用程序创建之初,系统会默认分配给其一个任务 栈(默认一个),并存储根Activity。

3.3.同一个Task Stack,只要不在栈顶,就是onStop状态:

在这里插入图片描述

3.4.任务栈的id自增长型,是Integer类型。

3.5.新创建Activity会被压入栈顶。点击back会将栈顶Activity弹出,并产生新的栈顶元素作为显示界面(onResume状态)。

3.6.当Task最后一个Activity被销毁时,对应的应用程序被关闭,清除Task栈,但是还会保留应用程序进程(狂点Back退出到Home界面后点击Menu会发现还有这个App的框框。个人理解应该是这个意思),再次点击进入应用会创建新的Task栈。

4.Activity的affinity:

4.1.affinity是Activity内的一个属性(在ManiFest中对应属性为taskAffinity)。默认情况下,拥有相同affinity的Activity属于同一个Task中。

4.2.Task也有affinity属性,它的affinity属性由根Activity(创建Task时第一个被压入栈的Activity)决定。

4.3.在默认情况下(我们什么都不设置),所有的Activity的affinity都从Application继承。也就是说Application同样有taskAffinity属性。

<application
        android:taskAffinity="gf.zy"

4.4.Application默认的affinity属性为Manifest的包名。

5.1为什么需要启动模式

在Android开发中,默认的情况下,当我们多次启动同一个Activity,系统会创建多个实例放入任务栈中。当我们点击返回(back)键,这些Activity实例又将从任务栈中一一移除,遵循的原则是“后进先出”(先进后出)。

这里我们考虑一个问题,当我们多次启动同一个Activity,如从Activity A 启动 Activity A从Activity A 启动 Activity B 再启动 Activity A。系统也会创建多个实例Activity A放入任务栈中,这样岂不是很耗费内存资源?为了解决这一问题,Android为Actiivty提供了启动模式。

Activity的启动模式有四种:standard、singleTop、singleTask和singleInstance。

5.2启动模式的分类

5.2.1 standard:标准模式

在这里插入图片描述
这种启动模式为标准模式,也是默认模式。在这种模式下,每当我们启动一个Activity,系统就会创建相应的实例,不管这个实例是否已经存在。所以在这种模式下,一个栈中可以有多个实例,每个实例也都有自己的任务栈。而且是谁启动了此Activity,那么这个Activity就运行在启动它的Activity所在的栈中。

使用方法:
在Manifest相应的Activity中配置 android:launchMode=“standard”

<activity
     android:name=".StandardActivity"
     android:launchMode="standard" >
</activity>

适用场景:
普通的Activity

5.2.2singleTop:栈顶复用模式

在这里插入图片描述
在这种启动模式下,如果要启动的Activity已经处于栈的顶部,那么此时系统就不会创建新的实例,而是直接打开此页面,同时它的onNewIntent(Intent intent)
会被执行,我们可以通过Intent进行传值,来执行一些操作。而且onCreate(),onStart()方法不会被调用。

使用方法:
在Manifest相应的Activity中配置 android:launchMode=“singleTop”

<activity
       android:name=".SingleTopActivity"
       android:launchMode="singleTop">
</activity>

适用场景:
假如一个新闻客户端,在通知栏收到了3条推送,点击每一条推送会打开新闻的详情页,如果为默认的启动模式的话,点击一次打开一个页面,会打开三个详情页,这肯定是不合理的。如果启动模式设置为singleTop,当点击第一条推送后,新闻详情页已经处于栈顶,当我们第二条和第三条推送的时候,只需要通过Intent传入相应的内容即可,并不会重新打开新的页面,这样就可以避免重复打开页面了。

总结:
1、当前栈中已有该Activity的实例并且该实例位于栈顶时,不会创建实例,而是复用栈顶的实例,并且会将Intent对象传入,回调onNewInten()方法;
2、当前栈中已有该Activity的实例但是该实例不在栈顶时,其行为和standard启动模式一样,依然会创建一个新的实例;
3、当前栈中不存在该Activity的实例时,其行为同standard启动模式。

5.2.3singleTask:栈内复用模式

在这里插入图片描述
在这个模式下,如果栈中存在这个Activity的实例就会复用这个Activity,不管它是否位于栈顶,复用时,会将它上面的Activity全部出栈,因为singleTask本身自带clearTop这种功能。并且会回调该实例的onNewIntent()方法。其实这个过程还存在一个任务栈的匹配,因为这个模式启动时,会在自己需要的任务栈中寻找实例,这个任务栈就是通过taskAffinity属性指定。如果这个任务栈不存在,则会创建这个任务栈。不设置taskAffinity属性的话,默认为应用的包名。

使用方法:
在Manifest相应的Activity中配置 android:launchMode=“singleTask”

<activity
       android:name=".SingleTaskActivity"
       android:launchMode="singleTask">
</activity>

适用场景:
SingleTask这种启动模式最常使用的就是一个APP的首页,因为一般为一个APP的第一个页面,且长时间保留在栈中,所以最适合设置singleTask启动模式来复用。

总结:
在复用的时候,首先会根据taskAffinity去找对应的任务栈:
1、如果不存在指定的任务栈,系统会新建对应的任务栈,并新建Activity实例压入栈中。
2、如果存在指定的任务栈,则会查找该任务栈中是否存在该Activity实例
a、如果不存在该实例,则会在该任务栈中新建Activity实例。
b、如果存在该实例,则会直接引用,并且回调该实例的onNewIntent()方法。并且任务栈中该实例之上的Activity会被全部销毁。

5.2.4singleInstance:单实例模式

在这里插入图片描述
单实例模式,顾名思义,只有一个实例。该模式具备singleTask模式的所有特性外,与它的区别就是:

1.没有创建过目标Activity时:
这种模式下的Activity会单独占用一个Task栈,为目标Activity分配一个新的affinity,并创建一个新的Task栈,将目标Activity放入新的Task,并让目标Activity获得焦点。具有全局唯一性,即整个系统中就这么一个实例。

2.已经创建过目标Activity:
由于栈内复用的特性,后续的请求均不会创建新的Activity实例,而是将以前创建过的Activity唤醒(对应Task设为Foreground状态),把它所在的任务调度到前台,重用这个实例。。除非这个特殊的任务栈被销毁了。

使用方法:
在Manifest相应的Activity中配置 android:launchMode=“singleInstance”

<activity
       android:name=".SingleInstanceActivity"
       android:launchMode="singleInstance">
</activity>

适用场景:
电话拨号盘页面,通过自己的应用或者其他应用打开拨打电话页面 ,只要系统的栈中存在该实例,那么就会直接调用。

总结:
启动该模式Activity的时候,会查找系统中是否存在:
1、不存在,首先会新建一个任务栈,其次创建该Activity实例。
2、存在,则会直接引用该实例,并且回调onNewIntent()方法。
特殊情况:该任务栈或该实例被销毁,系统会重新创建。

5.3 在Java代码上设置启动模式(动态设置)

从上面我们已经得知设置启动模式的第一种方法(静态设置):
通过清单文件AndroidManifest设置Activity的launchMode属性

现在要介绍的是第二种方法(动态设置):
通过在Intent中设置标志来实现设置Activity的启动模式

5.3.1动态设置启动模式的方法

如果我们要设置要启动的Activity的启动模式的话,只需要在startActivity前这样:

intent.setFlags(.......);

然后在里面添加对应的Flag就好,那么接下来我们介绍几个常见的Flag。

5.3.2常见的Flag:

FLAG_ACTIVITY_NEW_TASK : 首先会查找是否存在和被启动的Activity具有相同的亲和性的任务栈(即taskAffinity,注意默认情况下同一个应用程序中的activity的亲和性一样),如果有,则直接把这个栈整体移动到前台,并保持栈中的状态不变,即栈中的activity顺序不变,如果没有,则新建一个栈来存放被启动的activity。

FLAG_ACTIVITY_SINGLE_TOP :为Activity指定“singleTop”启动模式,其效果和在XML中指定启动模式一样

FLAG_ACTIVITY_CLEAR_TOP :例:先A->B->C->D 现在从D转到B。在同一任务栈中所有位于B上面的Activity(C、D)都要出栈。如果B在AndroidManifest的启动模式是默认的,则会把之前的B实例finish掉,重新创建一个B。如果B在AndroidManifest的启动模式是singleTop,则不会重新创建,而是调用之前B实例的onNewIntent();

FLAG_EXCLUDE_FROM_RECENTS :此Activity不会出现在历史Activity列表中,不希望用户通过历史列表回到Activity。其效果和在XML中指定Activity的属性:android:excludeFromRecents=”true” 一样

NEW_TASK+SINGLETOP+CLEAR_TOP加起来就像singleTask一样。

实例:

	Intent in = new Intent(context , MainActivity.class);
	in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_CLEAR_TOP);
	startActivity(in);

期待补充…

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值