Android四大组件是指 Activity、Service(服务)、BroadcastReceiver(广播)、ContentProvider。
在注册方面,Activity、Service、ContentProvider 必须在 AndroidManifest 中注册,而 BroadcastReceiver 既可以在 AndroidManifest 中注册,也可以通过代码来注册。如下图所示:
在调用方式上,Activity、Service、BroadcastReceiver 需要借助 Intent,而 ContentProvider 不需要借助 Intent。
1. Activity
Activity 是一种展示型组件,起作用就在于向用户直接展示一个界面,并且接收用户操作信息从而进行交互。Activity 是唯一一种可以直接感知的组件,可以说,在用户看来 Activity 就是一个Android应用的全部。
1.1 Activity 的启动方式
Activity 启动方式有两种,显式启动和隐式启动:
显式启动
明确指定一个 Activity 组件,正如我们平常大部分时间所使用的方式:(文中全部代码使用Kotlin)
startActivity(Intent(this, SampleActivity::class.java))
或者startActivityForResult(Intent(this, SampleActivity::class.java), 0)
隐式启动
指向一个或多个 Activity 组件,隐式启动需要我们再 AndroidManifest 中为 Activity 配置 intent-filter ,比如:
<!--Activity注册-->
<activity android:name=".SampleActivity">
<intent-filter>
<action android:name="sampleActivity"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
这个时候,我们可以使用如下方法启动 SampleActivity
// 创建Intent,并制定action
val intent = Intent("sampleActivity")
startActivity(intent)
隐式启动这种方式,相信我们平常也是用的比较多的,比如调用系统拨号功能。
1.2 Activity 的启动模式
Activity 的启动模式有4种,standard、singleTop、singleTask、singleInstance
- standard 标准模式,系统的默认启动模式。每次启动 Activity,都会在当前栈中创建一个实例。
- singleTop 栈顶复用模式。启动 Activity 时,如当前任务栈顶已经存在该 Activity 的实例,则不会重新创建,同时它的 onNewIntent 方法被调用。
- singleTask 栈内复用模式。这是一种单实例模式,只要 Activity 在一个任务栈中存在,多次启动时都不会重新创建实例,而是将栈顶到该 Activity 实例之前的 Activity 全部 finish,使该 Activity 处于栈顶,并调用其 onNewIntent 方法。
- singleInstance 单实例模式。这是一种加强的 singleTask 模式,除了拥有 singleTask 全部特征外,此模式的 Activity 只能单独位于一个任务栈中。
1.3 Activity 的启动过程
关于 Activity 的启动过程,这里就简单文字描述下,就不贴源码了,关于源码的解析,有机会单独记录。
启动 Activity 都是调用 startActivity(ForResult) 方法,所以,就从 startActivity(ForResult) 方法开始:
- startActivity(ForResult) 方法有多种重载方式,但它们最终都会调用
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options)
- startActivityForResult 方法中调用 Instrumentation 的
public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options, UserHandle user)
,该方法的参数中传入了 mMainThread.getApplicationThread(),它的类型是 ApplicationThread,ApplicationThread 是 ActivityThread 的一个内部类。 - Instrumentation.execStartActivity 方法中,由 ActivityManagerNative.getDefault() 的 startActivity 方法完成 Activity 启动。ActivityManagerService(下面简称AMS)继承自 ActivityManagerNative,而 ActivityManagerNative 继承自 Binder 并实现了 IActivityManager 这个 Binder 接口,因此 AMS 也是一个 Binder,它是 IActivityManager 的具体实现。由于 ActivityManagerNative.getDefault() 其实是一个 IActivityManager 类型的 Binder 对象,因此它的具体实现是 AMS。所以,Activity 的启动过程有转移到了 AMS 中。
- AMS 的 startActivity 方法调用 ActivityStackSupervisor 的 startActivityMayWait 方法。startActivityMayWait 中又调用 startActivityLocked 方法。startActivityLocked 又调用了 startActivityUncheckedLocked 方法。接着 startActivityUncheckedLocked 又调用了 ActivityStack 的 resumeTopActivitiesLocked 方法。
- 这个时候启动过程已经从 ActivityStackSupervisor 转移到了 ActivityStack,ActivityStack 的 resumeTopActivitiesLocked 又会调用 resumeTopActivityInnerLocked 方法,resumeTopActivityInnerLocked 又调用了 ActivityStackSupervisor 的 startSpecificActivityLocked 方法。
- 此时,启动过程又回到了 ActivityStackSupervisor,在 startSpecificActivityLocked 方法中,调用 realStartActivityLocked 方法。
- 在 realStartActivityLocked 中,有这样一段代码
app.thread.scheduleLaunchActivity(**)
,app.thread 的类型为 IApplicationThread,其实现者是 ActivityThread 中的内部类 ApplicationThread。饶了一大圈,Activity 的启动过程最终回到了 ApplicationThread 中,ApplicationThread 通过 scheduleLaunchActivity 方法来启动 Activity。 - scheduleLaunchActivity 做的事情也很简单,就是发送一个启动 Activity 的消息交由 Handler 处理,这个 Handler 有一个很简洁的名字:H。
- H 对 Activity 启动消息的处理是调用 ActivityThread 的 handleLaunchActivity 方法来实现 Activity 的启动。
- 最终,在 handleLaunchActivity 中,调用 performLaunchActivity 方法完成 Activity 对象的创建和启动,之后,又通过 HandlerResumeActivity 方法来调用被启动 Activity 的 onResume 这一生命周期方法。
2. Service
Service 是一种计算型组件,用于在后台执行耗时操作。由于 Service 组件工作在后台,因此用户无法直接感知到它的存在。
2.1 Service 的运行模式
Activity 组件只有一种运行模式,即 Activity 处于启动状态,Service 组件略有不同,它有两种状态:启动状态和绑定状态。
-
启动状态
Service 内部可以做一些后台计算,并且不需要和外界有直接的交互。该运行模式可以使用 Context 的startService(Intent)
方法启动Service。 -
绑定状态
Service 内部同样可以进行后台计算,但是处于这种状态时,外界可以很方便地和 Service 组件进行通信。该运行模式使用 Context 的bindService(Intent, ServiceConnection, int)
方法启动 Service。
2.2 Service 的启动过程
2.2.1 startService 方式
示例代码:startService(Intent(this, SampleService::class.java))
Context 的实现类只有 ContextWrapper,所以该 Service 的启动从 ContextWrapper 的 startService 方法开始:
- startService 方法调用 ContextImpl 的 startService 方法。
- 在 ContextImpl 中,startService 方法会调用 startServiceCommon 方法,而 startServiceCommon 方法又通过调用
ActivityManagerNative.getDefault().startService(**)
来启动一个 Service。对于 ActivityManagerNative.getDefault() 这个对象,在 Activity 的启动过程中第 3 点分析了,它实际上就是 AMS (ActivityManagerService),这里就不在重复说明了。需要注意的是,通过 AMS 来启动 Service 的行为是一个远程过程调用。 - 在 AMS 的 startService 中,会通过 ActiveServices 对象来完成 Service 后续的启动过程。也就是调用 ActiveServices 的 startServiceLocked 方法。
- 在 ActiveServices 的 startServiceLocked 方法尾部会调用 startServiceInnerLocked 方法,startServiceInnerLocked 又调用了 realStartServiceLocked 方法。
- 在 realStartServiceLocked 方法中,首先通过 app.thread 的 scheduleCreateService 方法来创建并调用其 onCreate,接着再调用 sendServiceArgsLocked 方法来调用 Service 的其它方法,比如 onStartCommand,这两个过程均是进程间通信。
- 由 Activity 的启动过程第 10 点可知,app.thread 是 ApplicationThread 的实例,也就是调用了 ApplicationThread 的 scheduleCreateService 方法来创建 Service。这个过程和 Activity 的启动过程是类似的,都是通过发送消息给 Handler H 来完成。
- H 通过 ActivityThread 的 handleCreateService 方法来完成 Service 的最终启动。handleCreateService 主要完成如下几件事:
- 通过类加载器创建 Service 的实例。
- 创建 Application 对象并调用其 onCreate,当然 Application 的创建过程只有一次。
- 创建 ContextImpl 对象并通过 Service 的 attach 方法建立二者之间的联系,这个过程和 Activity 实际上是类似的,毕竟 Service 和 Activity 都是一个 Context。
- 最后调用 Service 的 onCreate 方法并将 Service 对象存储到 ActivityThread 中的一个列表。
- 由于 Service 的 onCreate 方法被执行了,这也意味着 Service 已经启动了。除此之外, ActivityThread 中还会通过 handleServiceArgs 方法调用 Service 的 onStartCommand 方法。
2.2.2 bindService 方式
示例代码:
val service = Intent(this, SampleService::class.java)
bindService(service, mServiceConnection, Service.BIND_AUTO_CREATE)
和 startService 一样,bindService 的过程也是从 ContextWrapper 开始的:
- bindService 方法调用 ContextImpl 的 bindService 方法,然后 bindService 方法调用 bindServiceCommon 方法。
- 在 bindServiceCommon 中,首先将客户端的 ServiceConnection 对象转化为 ServiceDispatcher.InnerConnection 对象。接着通过 AMS 来完成 Service 的具体绑定过程,方式是调用
ActivityManagerNative.getDefault().bindService(**)
。 - AMS 的 bindService 方法调用 ActiveServices 的 bindServiceLocked 方法,bindServiceLocked 方法再调用 bringUpServiceLocked,bringUpServiceLocked 又会调用 realStartServiceLocked 方法。
- realStartServiceLocked 方法的逻辑和
2.2.1
中的逻辑类似,最终都是通过 ApplicationThread 来完成 Service 实例的创建并执行其 onCreate 方法。和 startService 方式不同的是,Service 的绑定过程会调用app.thread
的 scheduleBindService 方法。 - 和上述类似,scheduleBindService 也是通过发送消息给 Handler H 来中转的。
- H 接收到绑定 Service 的消息时,调用 ActivityThread 的 handleBindService 方法来处理。
- 在 handleBindService 中,首先根据 Service 的 token 取出 Service 对象,然后调用 Service 的 onBind 方法。接着通过 ActivityManagerNative.getDefault() 的 publishService 方法调用客户端的 ServiceConnection 中的 onServiceConnection。
3 BroadcastReceiver
BroadcastReceiver 也叫广播接收者,是一种消息型组件,用于在不同的组件甚至不同的应用之间传递消息。 BroadcastReceiver 同样是用户无感知的。
3.1 BroadcastReceiver 的注册方式
BroadcastReceiver 的注册方式有两种,静态注册和动态注册。
-
静态注册
指在 AndroidManifest 中注册广播,这种广播在应用安装时被系统解析,不需要启动应用就可以收到相应的广播。应用的开机启动就是用到了这类 BroadcastReceiver 来监听手机的启动。注册代码可参考上文的图。 -
动态注册
在代码中调用 Context.registerReceiver() 来实现注册。此类注册要在不使用的时候调用 Context.unRegisterReceiver() 进行反注册,否则容易因持有 Context 实例造成内存泄漏。
4. ContextProvider
ContentProvider 是一种数据共享型组件,用于向其它组件甚至其它应用共享数据。由于只是用来组件或者应用间共享数据,用户同样无法直接感知。
一个 ContentProvider 组件内部要实现数据的增删改查四种操作,其内部维持着一份数据集合。这个数据集合有多种实现方式,ContentProvider 本身对其没有任何要求,常见的使用数据库来实现。
四大组件是否可以开启多进程
上面对Android的四大组件的作用进行了一个大致的说明,由于篇幅原因,这里只简单描述了下 Activity 和 Service 启动过程,找机会再整理下 BroadcastReceiver 和 ContentProvider 的工作过程。
我们知道,Android 应用是可以开启多个进程的,就是在 AndroidManifest 中使用 android:process 属性,比如要给某一 Activity 指定运行进程,则在其 <activity> 标签中添加 android:process 属性即可。那么,其它的三种组件是否也可以为其指定运行进程呢?也就是说,Android的四大组件是否都可以开启多进程?
这里我大胆假设小心求证下,我先假设都可以,并且开启方式都和 Activity 相同,如果不行的话,再根据问题进行相应调整,并最终得出结论。
demo:
AndroidManifest中注册
<!--Activity注册-->
<activity android:name=".SampleActivity"
android:process=":SampleActivity">
<intent-filter>
<action android:name="sampleActivity"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<!--Service注册-->
<service
android:name=".SampleService"
android:process=":SampleService"/>
<!--BroadcastReceiver静态注册-->
<receiver
android:name=".SampleReceiver"
android:process=":SampleReceiver">
<intent-filter>
<action android:name="com.cy.receiver.sample"/>
</intent-filter>
</receiver>
<!--ContentProvider注册-->
<provider
android:name=".SampleContentProvider"
android:process=":SampleContentProvider"
android:authorities="com.cy.multiprocess.SampleContentProvider"
android:exported="true">
</provider>
MainActivity中启动 Activity、Service,发送广播,访问ContentProvider
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.d("MainActivity", "主进程号为:${getCurProcessName()}")
// 启动 Activity
startActivity(Intent(this, SampleActivity::class.java))
// 启动 Service
val service = Intent(this, SampleService::class.java)
startService(service)
// 发送广播
sendBroadcast(Intent("com.cy.receiver.sample"))
// 访问ContentProvider
val uri = Uri.parse("content://com.cy.multiprocess.SampleContentProvider")
contentResolver.query(uri, null, null, null, null)
}
}
/**
* Context 的扩展方法:获取当前进程号
*/
fun Context.getCurProcessName(): String? {
val pid = android.os.Process.myPid()
val mActivityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
for (appProcess in mActivityManager.runningAppProcesses) {
if (appProcess.pid == pid) {
return appProcess.processName
}
}
return null
}
最后在各自生命周期方法中打印进程名,运行结果如下:
由图可知,每个组件所运行的进程id、进程名和主进程都不一样,所以结论是:Android的四大组件都可以开启多进程。