面试的时分,面试官经常同你随意侃侃Activity
的发动形式,但Activity
发动牵扯的知识点其实许多,并非能单单用四个发动形式就能概括的,
默认的发动形式的体现会跟着Intent Flag
的设置而改变,因而侃Activity
发动形式大多走流程装逼,最多结合项目遇到的问题,随意刁难一下面试者,并不太简单把控,或许最后,面试官跟面试者的答案都是错了,
比如在Service
中有必要经过设置FLAG_ACTIVITY_NEW_TASK
才干发动Activity
,这个时分发动Activit
会有什么样的体现呢?就这一个问题,答案就要分好几个场景:
Activity
的taskAffinity
特点的Task
栈是否存在- 假如存在,要看
Activity
是否存现已存在于该Task
- 假如现已存在于该
taskAffinity
的Task
,要看其是不是其rootActivity
- 假如是其
rootActivity
,还要看发动该Activity
的Intent
是否跟当时intent
持平
不同场景,所体现的行为都会有所不同,再比如singleInstance
特点,假如设置了,咱们都知道只有一个实例,将来再发动会复用,可是假如运用Intent.FLAG_ACTIVITY_CLEAR_TASK
来发动,仍然会重建,并非完全恪守singleInstance
的说明,还有不同Flag
在叠加运用时分也会有不同的体现,单一而论Activity
发动形式其实是很难的。本文也仅仅是触及部分发动形式及Flag
,更多组合跟场景要自己看源码或许实验来处理了。
1.面试连环炮之说说 Android 的四种发动形式
-
standard
这是Activity
的默认发动形式,每次激活Activity
的时分都会创立一个新的Activity
实例,并放入使命栈中。 运用场景:根本绝大多数地方都可以用。
-
singleTop
这可能也是非常常用的launchMode
了。假如在使命的栈顶正好存有该Activity
的实例,则会经过调用onNewIntent()
办法进行重用,否则就会同 standard 形式一样,创立新的实例并放入栈顶。即使栈中现已存在了该 Activity 的实例,也会创立新的实例,即:A -> B ->A,此刻栈内为 A -> B -> A,但 A -> B ->B ,此刻栈内为 A -> B。一句话概述便是:当且仅当发动的Activity
和上一个Activity
一致的时分才会经过调用onNewIntent()
办法重用Activity
。 运用场景:资讯阅读类 APP 的内容界面。
-
singleTask
这个launchMode
专门用于处理上面singleTop
的别的一种情况,只需栈中现已存在了该Activity
的实例,就会直接调用onNewIntent()
办法来完成重用实例。重用时,直接让该Activity
的实例回到栈顶,而且移除之前它上面的一切Activity
实例。假如栈中不存在这样的实例,则和standard
形式相同。即: A ->B -> C -> D -> B,此刻栈内变成了 A -> B。而 A -> B -> C,栈内还是 A -> B -> C。 运用场景:浏览器的主页面,或许大部分 APP 的主页面。
-
singleInstance
在一个新栈中创立该Activity
的实例,并让多个运用共享该栈中的该Activity
实例。一旦该形式的Activity
实例现已存在于某个栈中,任何运用再激活该Activity
时都会重用该栈中的实例,是的,依然是调用onNewIntent()
办法。其效果相当于多个运用共享一个运用,不管是谁激活,该 Activity 都会进入同一个运用中。但值得引起留意的是:singleInstance
不要用于中心页面,假如用户中心页面,跳转会出现很难过的问题。 这个在实践开发中我暂未遇到过,不过 Android 系统的来电页面,多次来电均是运用的同一个Activity
。
四种形式的背书式了解记忆讲完了,你以为这样就结束了吗?
对,我也一度是这样以为的。
2.面试连环炮之说说 Intent标签起什么效果呢? 简单说一说
咱们除了需求知道在 AndroidManifest.xml
里边设置 android:launchMode
特点,咱们还需求了解下面这几个Intent
标签的用法。
在 Android 中,咱们除了在清单文件 AndroidManifest.xml
中装备 launchMode
,当然可以用 Intent
标签说事儿。发动 Activity
,咱们需求传递一个 Intent
,完全可以经过设置 Intent.setFlags(int flags)
来设置发动的 Activity
的发动形式。
需求留意的是:经过代码来设置 Activity
的发动形式的方式,优先级比清单文件设置更高。
FLAG_ACTIVITY_NEW_TASK
这个标识会使新发动的Activity
独立创立一个Task
。
FLAG_ACTIVITY_CLEAR_TOP
这个标识会使新发动的Activity
查看是否存在于Task
中,假如存在则铲除其之上的Activity
,使它取得焦点,并不重新实例化一个Activity
,一般结合FLAG_ACTIVITY_NEW_TASK
一起运用。
FLAG_ACTIVITY_SINGLE_TOP
等同于在launcherMode
特点设置为singleTop
。
3.Android 的发动原理,他的流程是什么样的
总的流程图:
1.2.1.进程A与AMS的交互进程
此处以跨进程发动Activity
分析一下源码流程:
①A调用startActivity
时,需求与AMS交互,此刻需求需求获取到AMS的署理方针Binder也便是上图的AMP,
经过ActivityManagerNative.getDefault()
取得,并调用AMP的startActivity
办法,然后会经过mRemote.transact
办法进行Binder通信,在AMS的onTransact
办法里边会获取到恳求的Activity
参数信息:
mRemote.transact(START_ACTIVITY_TRANSACTION,data,reply,0);
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags){
switch(code){
case START_ACTIVITY_TRANSACTION:{ startActivity(app,callingPackage,intent,...)
}
}
}
②AMS里边的startActivity
办法最主要会去调用startSpecificActivityLocked
函数,在此函数里边会去判别方针进程是否现已存在,而且方针向AMS注册过它自己的ApplicationThread
也便是上图ATP署理方针,假如这两个条件都满意会去调用realStartActivityLocked
办法,这个办法咱们后边再看。假如上述条件不满意时,会去调用mService.startProcessLocked(r.processName,...)
办法发动进程。
-
startProcessLocked
办法首要调用Process.start("android.app.ActivityThread",)
办法会向Zygote
发送一个发动进程的恳求,并告知Zygote
进程发动之后,加载ActivityThread
这个类的进口main
函数,发动完成后回来进程的pid,并向AMS的Handler发送一个延迟音讯,为的是要求方针进程发动后,10秒钟内需求向AMS陈述,否则的话AMS就会铲除方针进程的相关信息。 -
Process.start
办法会去调用startViaZygote(processClass,)
函数,这个函数主要做了两件事,一件便是打开通往Zygote
的Socket
,第二件事便是经过Socket
发送发动进程参数。 -
Zygote
端主要逻辑是在runOnce
函数,函数内调用Zygote.forkAndSpecialize(...)
创立子进程,创立完成之后就分别在父进程和子进程里边做各自的工作.
父进程经过
hanleParentProc(pid)
把子进程的pid
经过Socket
发送给AMS子进程调用
handleChildProc
函数,做一些通用的初始化,比如启用Binder
机制;执行运用程序的进口函数,也便是ActivityThread
的Main
函数.
-
ActivityThread
的main
函数,里边会创立一个ActivityThread
方针,并调用thread.attach(false)
,为的是向AMS签到,上面第一条里边有提到。 -
attach
办法里边,其实是一个跨进程的调用,首要经过
IActivityManager mgr = ActivityManagerNative.getDefault();
获取到AMS的Binder署理方针,然后调用
IActivityManager mgr = ActivityManagerNative.getDefault();
mAppThread
是运用端的一个Binder方针ApplicationThread
,也便是最上面一张图的ATP,这样AMS端就可以调用运用端了。
-
attachApplication
办法里边,最主要有两个办法,一个是经过传入的ApplicationThread
方针,调用bindApplication
初始化Application
方针,另一个便是经过
mStactSupervisor.attachApplicationLoacked(app);
初始化挂起的
Activity
方针。
- 在
attachApplicationLoacked
函数里,会调用
ActivityRecord hr = stack.topRunningActivityLocked(null);
其间要理解AMS里边有两个栈,一个是
Launch
桌面栈,一个便是非桌面栈mFocusedStack
,此处的stack
便是mFocusedStack
,它会将栈顶的ActivityRecord
回来出来,咱们的方针Activity
早就放置在了栈顶,仅仅一直没有初始化。然后调用办法,来发动Activity
假如咱们不是发动别的一个进程,而是同一进程,那么这第二大部分就不会存在了,而是直接调用realStartActivityLocked
办法。
realStartActivityLocked(hr,app,true,true);
写到这儿是不是有许多码牛的小伙伴们现已坚持不下去了。还剩最后几个过程
① realStartActivityLocked
函数会调用app.thread.scheduleLaunchActivity(new Intent(r.intent),...)
;也便是经过之前注册的Binder方针ATP,调用scheduleLaunchActivity
函数,在scheduleLaunchActivity
函数里边:
ActivityClientRecord r = new ActivityClientRecord();
...
sendMessage(H.LAUNCH_ACTIVITY,r);
封装了一个
ActivityClientRecord
音讯,然后丢到主线程的Handler(mH)里。
②在主线程里边
final ActivityClientRecord r = (ActivityClientRecord)msg.obj ;
r.packageInfo = getPackageInfoNoCheck(...);
handleLaunchActivity(r,null);
getPackageInfoNoCheck
函数主要是用来生成一个LoadedApk
方针,它用来保存咱们的apk信息,因为后边咱们需求一个ClassLoader
去加载Apk里边的Activity
类,所以这儿提前准备好。
③handleLaunchActivity
里边分为两个部分,一个是performLaunchActivity
函数,一个是handleResumeActivity
函数。
performLaunchActivity
Activity activity = mInstrumentation.newActivity(...);
//回来之前创立好的
Application app = r.packageInfo.makeApplication(false,mInstrumentation);
//生成ContextImpl
Context appContext = createBaseContextForActivity(r,activity);
//给activity绑定上下文和一些初始化的工作,如createPhoneWindow
activity.attach(appContext,...);
mInstrumentation.callActivityOnCreate(activity,r.state); //生命周期的OnCreate
activity.performStart(); //生命周期的OnStart
return activity
④handleResumeActivity
:
-> r.activity.performResume()
-> mInstrumentation.callActivityOnResume(this);
-> activity.onResume()