本篇文章概述分析Android四大组件的机制,插入本人其他的博客链接方便阅读,以及为接下来写四大组件的内部工作过程铺垫。
1 Android四大组件的运行状态
Android四大组件包括Activity、Service、BroadcastReceiver和ContentProvider。除了BroadcastReceiver以外,其他三种组件都必须在AndroidManifest中注册,对于BroadcastReceiver来说,既可以在AndroidManifest中注册(静态注册),也可以通过代码来注册(动态注册)。在调用方式上,Activity、Service和BroadcastReceiver需要借助Intent,而ContentProvider无须借助Intent。
1.1 四大组件都可以开启多进程
1.1.1 如何开启并查看多进程
Android四大组件都可以开启多进程,需要在AndroidManifest.xml里面添加android:process=””,process的值可以自由设置,没有指定process属性运行在默认进程中,默认进程名是包名。例子如下:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.seniorlibs.ipc">
<application
android:name=".MyApplication"
<activity
android:name=".MainActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- 当前应用的私有进程(常用) -->
<activity
android:name=".SecondActivity"
android:configChanges="screenLayout"
android:label="@string/app_name"
// 以":"开头的进程属于当前应用的私有进程,其他应用的组件不可以和它跑在同一个进程中。
android:process=":remote" />
<!-- 全局进程 -->
<activity
android:name=".ThirdActivity"
android:configChanges="screenLayout"
android:label="@string/app_name"
// 以"."开头的进程属于全局进程,其他应用的组件可以通过sharedUserId方式和它跑在同一个进程中。
android:process="com.seniorlibs.ipc.remote" />
</application>
</manifest>
可以在DDMS视图中查看进程信息,还可以用shell来查看,命令为:adb shell ps 或者 adb shell “ps | grep com.seniorlibs.ipc”。(注意:adb shell 之后的内容要用引号引起来,就不会报adb ‘grep’ 不是内部或外部命令错误。)
$ adb shell "ps | grep com.seniorlibs.ipc"
u0_a85 6768 1349 1417592 50864 SyS_epoll_ 00000000 S com.seniorlibs.ipc
u0_a85 6839 1349 1413888 49096 SyS_epoll_ 00000000 S com.seniorlibs.ipc:remote
u0_a85 6858 1349 1413888 48884 SyS_epoll_ 00000000 S com.seniorlibs.ipc.remote
1.1.2 多进程造成的问题
(1)
静态成员和单例模式完全失效
。因为每个进程都分配一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,导致在不同的虚拟机访问同一个类的对象会产生多份副本,所以进程间不可以通过内存来共享数据。
(2)
线程同步机制完全失效
。类似1,既然不在同一块内存,不管是锁对象还是锁全局类都无法保证线程同步,因为不同进程锁的不是同一个对象。
(3)
SharePreferences的可靠性下降
。因为SharePreferences不支持两个进程同时去执行写操作,否则会导致一定几率的数据丢失。
(4)
Application会多次创建
。因为系统在创建新的进程同时分配独立的虚拟机,其实是启动一个应用的过程(相当于重新启动),就会创建新的Application。所以,在Application初始化第三方sdk时需要判断是否是“主进程”正在执行。
public class Application extends BaseApplication {
private final static String LOG_TAG = "Application ";
private static String PROCESS_MAIN = "com.main"; // 主进程名称
private static String PROCESS_MESSAGE = "com.h:message"; // 消息进程名称
@Override
public void onCreate() {
super.onCreate();
}
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
@Override
protected void init() {
String curProcessName = ProcessUtils.getCurProcessName(this);
initData(curProcessName);
}
@Override
protected void unInit() {
String curProcessName = ProcessUtils.getCurProcessName(this);
unInitData(curProcessName);
}
/**
* 初始化数据
*/
private void initData(String curProcessName) {
if (PROCESS_MAIN.equals(curProcessName)) {
// 初始化线程池
// 初始化HTTP请求管理
// 初始化广告sdk
// 初始化自定义生命周期回调
this.registerActivityLifecycleCallbacks(new CustomActivityLifecycleCallback());
}
if (PROCESS_MAIN.equals(curProcessName) || PROCESS_MESSAGE.equals(curProcessName)) {
PushUtils.initPush(this); // 初始化推送
}
}
/**
* 反初始化数据
*/
private void unInitData(String curProcessName) {
if (PROCESS_MAIN.equals(curProcessName) || PROCESS_MESSAGE.equals(curProcessName)) {
// 释放线程池资源
// 释放HTTP请求资源
}
}
}
1.2 Provider相关???
1.2.1 利用ShareUserID共享数据(私有暴露)
在Android里面每个app都有一个唯一的linux user ID,则这样权限就被设置成该应用程序的文件只对该用户可见,只对该应用程序自身可见。用到SharedUserId让两个apk使用相同的userID,这样它们共享一个虚拟机,就可以看到对方的文件。
注意:打包时签名文件必须相同
。除非使用一个系统自带软件的ShareUID,例如Contact,那么无须第三方签名。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mythou.serviceID"
android:versionCode="1"
android:versionName="1.0"
android:sharedUserId="com.mythou.share">
//第二个应用程序的menifest文件代码如下:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mythou.clientID"
android:versionCode="1"
android:versionName="1.0"
android:sharedUserId="com.mythou.share">
//从package=“com.mythouclientID”的程序获取package="com.mythou.serviceID"的程序的context:
Context context = this.createPackageContext("com.mythou.serviceID", Context.CONTEXT_IGNORE_SECURITY);
String str = context.getString(R.string.appname);
1.2.2 利用StartActivity启动其他应用的组件(完全暴露)
android:exported=”true”的作用,而一旦设置了intentFilter之后,exported就默认为true,除非强制设为false。没有设置intentFilter时,它的exported属性默认仍然是false,也就不会暴露数据。
exported=”true”:provider需要提供给其他application,甚至是第三方application使用;
exported=”false”:provider只有自己的application或者同一个UID的application使用。
另外组件是Provider时:当Android sdk的最小版本为16或者更低时它的默认值是true,如果是17和以上的版本默认值是false。可以通过Android:exported=“fasle” 和 permission来限制当前应用Provider是否会被其它应用获取。
2 Activity机制
2.1 定义与作用
(1)
Activity主要作用是:展示一个界面并和用户交互,它扮演的是一种前台界面的角色。
(2)Activity是一种展示型组件,主要是向用户展示一个界面,并且可以接收用户的输入信息从而和用户进行交互。对用户来说,Activity就是Android应用的全部,因为其他三大组件对用户来说是不可感知的。
(3)Activity只有一种运行模式–启动状态,它的启动由Intent触发,其中Intent分为显式启动和隐式启动,显式启动明确地指向一个Activity,隐式启动则指向一个或多个目标Activtity组件。详细阅读:
Android基础之IntentFilter匹配规则与隐式启动Activity
2.2 内容详解
Android基础之Activity生命周期+Activity难点
Android基础之Activity四种启动模式和task相关
2.4 内部工作过程
3 Service机制
3.1 定义与作用
(1)
Service组件的主要作用是:在后台执行计算任务,执行任务的结果可以和外界进行通信。
(2)Service是一种计算型组件,用于在后台执行一系列计算任务。由于Service组件工作在后台,因此用户无法直接感知到它的存在。Activity组件只有一种运行模式-启动状态,但是Service组件却有两种状态:启动状态和绑定状态。①通过startService启动的服务处于
启动状态
,它的内部可以执行一些后台计算,并且不需要和外界有直接的交互。②通过bindService启动的服务处于
绑定状态
,Service内部同样也可以执行后台计算,但是处于这种状态的Service可以通过ServiceConnection和外界进行通信。
3.2 内容详解
4 BroadcastReceiver机制
4.1 定义与作用
(1)
BroadcastReceiver组件的主要作用是:消息的传递,该消息的传递可以在应用内,也可以在应用之间,它的角色是一个消息的传递者。
(2)BroadcastReceiver是一种消息型组件,用于在不同组件乃至不同应用之间传递消息。BroadcastReceiver同样无法被用户所感知,因为它工作在系统内部。BroadcastReceiver也叫做广播,广播的注册方式有两种:静态注册和动态注册。静态注册指在AndroidManifest中注册广播,这种广播在应用安装时被系统解析,此种形式的广播不需要应用启动就可以接收到相应的广播。动态广播需要通过Context.registerReceiver()来实现,并且在不需要的时候通过Context.unRegisterReceiver()解除广播,此种形态的广播必须要应用启动才能注册并接收广播。在实际开发中通过Context的一系列send方法来发送广播,被发送的广播会被系统(Activity Manager Service)发送给合适的广播接受者,依据是intent-filter/permission。由此发现,BroadcastReceiver可以用来实现低耦合的观察者模式,一般来说不需要停止,也没有停止的概念。由于BroadcastReceiver运行在主线程,所以它不适合用来执行耗时操作。
4.2 内容详解
Android基础之四大组件-BroadcastReceiver解析
5 ContentProvider机制
5.1 定义与作用
(1)
ContentProvider组件的主要作用是:作为一个平台,提供数据的共享,并且提供数据的增删改查功能。主要应用于应用之间的数据共享场景。
(2)ContentProvider是一种数据共享型组件,用于向其他组件乃至其他应用共享数据。同样的,它也无法被用户所感知。对于ContentProvider组件来说,它的内部需要实现增删改查这四种操作,在它的内部维持了一份数据集合,这个数据集合既可以通过数据库来实现,也可以采用其他任何类型来实现,比如List和Map。需要注意的是,ContentProvider内部的insert、delete、update和query方法需要处理好线程同步,因为这几个方法都是在Binder线程池中被调用的。ContentProvider组件不需要手动停止。
5.2 内容详解
Android基础之四大组件-深入理解ContentProvider
6 参考资料
《Android开发艺术探索》