Android IPC 简介:
IPC(Inter-Process Communication),进程间通信。也就是两个进程之间进行数据交换的过程。Android 中一个应用的打开就相当于一个进程在虚拟机上的运行。
Android 中 IPC 使用场景:
-
1. 一个应用采用多进程方式运行。
-
2. 不同的应用之间通过 IPC 机制进行数据通信。
Android 中开启多进程模式:
正常情况下,在 Android 中的多进程模式是指同一个应用中存在多个进程的情况。通常,在 Android 中我们可以通过给四大组件(Activity、Service、BroadcastReceiver、ContentProvider)在 AndroidManifest.xml 中指定 android:process 属性来开启多进程(当然也有非常规操作,通过 JNI 在 native 层 fork() 一个进程)。在 Android 中除了四大组件之外,我们无法给单独的一个线程或者实体类指定其运行的特定进程(只能为 Android 中的四大组件指定)。
eg:
// 获取进程名方法
public static String getProcessName(Context context) {
int pid = android.os.Process.myPid();
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> runningApps = am.getRunningAppProcesses();
if (runningApps == null) {
return null;
}
for (ActivityManager.RunningAppProcessInfo procInfo : runningApps) {
if (procInfo.pid == pid) {
return procInfo.processName;
}
}
return null;
}
<activity android:name="com.cfm.processtest.ThirdActivity"
android:process="com.cfm.test2">
</activity>
<activity android:name="com.cfm.processtest.SecondActivity"
android:process=":test1">
</activity>
<activity android:name="com.cfm.processtest.FirstActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
FirstActivity.java -> SecondActivity.java -> ThirdActivity.java
Log 打印结果:
FirstActivity 进程名: com.cfm.processtest // 不指定 process 属性就默认当前应用的包名为进程名
SecondActivity 进程名: com.cfm.processtest:test1
ThirdActivity 进程名: com.cfm.test2
adb shell 结果:
通过结果可以看到我们通过两种命名方式开启了多进程:
1. 第一种是以 ":" 冒号开头的,这个是在当前的进程名前附加上当前的包名。
2. 第二种是完整的命名方式。
这两种命名的方式区别是,以冒号开头的进程属于当前应用的私有进程,其它应用的组件不可以和它跑在同一个进程中;而第二种方式产生的进程是全局进程,其它应用可以通过 shareUID 方式和它跑在同一个进程中。
关于 ShareUID 解释:
Android 系统默认会为每一个应用分配一个唯一的 UID(用户 ID),我们也可以自己指定应用的 UID,具有相同 UID 的应用可以进行共享数据,比如 data 目录的数据、组件信息等。如果两个应用之间具有相同的 ShareUID 且签名相同,那么它们还可以运行在同一个进程中,此时它们不仅可以访问对方的私有数据,比如 data 目录、组件信息等,还能共享内存数据。
eg: 具有相同 shareUID 但签名不同的两个应用访问对方 data 目录数据
ShareUIDTest2 应用:
// AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.cfm.shareuidtest2"
android:sharedUserId="com.cfm.test">
...
</manifest>
// MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SharedPreferences.Editor editor = getSharedPreferences("share", MODE_PRIVATE).edit();
editor.putString("name", "cfm");
editor.putString("dream", "open source");
editor.apply();
}
ShareUIDTest1 应用:
// AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.cfm.shareuidtest1"
android:sharedUserId="com.cfm.test">
...
</manifest>
// ManiActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
// 通过包名信息获取 shareuidtest2 的上下文对象
Context context = createPackageContext("com.cfm.shareuidtest2", Context.CONTEXT_IGNORE_SECURITY);
SharedPreferences preferences = context.getSharedPreferences("share", MODE_PRIVATE);
String name = preferences.getString("name", "ym");
String dream = preferences.getString("dream", "make money");
Log.d("cfmtest", "name: " + name);
Log.d("cfmtest", "dream: " + dream);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
// 打印的 Log 信息
cfmtest: name: cfm
cfmtest: dream: open source
Android 中多进程模式带来的问题:
主要造成以下 4 个问题:
1. 静态成员和单例模式完全失效
2. 线程同步机制完全失效
3. SharePreferences 的可靠性下降
4. Application 会多次创建
解释 1:
这个很容易理解,因为 Android System 在应用被启动之后会为每一个启动的应用都分配一个独立的虚拟机,或者说为每一个进程都分配一个独立的虚拟机。那么由于不同的虚拟机在内存分配上在不同的地址空间中,这就会导致在不同虚拟机中访问同一个对象会产生多份副本。所以当我们在一个应用中创建一个静态成员变量或者单例模式的对象时,实际上每个进程所获取到的都是这些对象相对应的副本,当我们对该对象进行操作时,实际上影响的只是当前进程对象副本的属性,而不会影响到最初创建的那个对象。所以多进程模式就无法通过这种方式来共享内存中的数据。
解释 2:
同 1 类似,既然它们都不是在同一块内存中,那么不管是锁对象还是锁全局类都无法保证线程同步,因为不同进程间,锁的压根就不是同一个对象,而是该对象所产生的多份副本。
解释 3:
因为 SharePreferences 不支持两个或两个以上的进程同时对其进行读写操作,这会导致一定几率的数据丢失。
解释 4:
还使用上面 FirstActivity.java -> SecondActivity.java -> ThirdActivity.java 的例子,并自定义一个 MyApplication:
public class MyApplication extends Application {
private static final String TAG = "MyApplication";
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "MyApplication 进程名: " + Untils.getProcessName(getApplicationContext()));
}
}
// AndroidManifest.xml
<application
android:name=".MyApplication"
...>
...
</application>
打印 Log 信息:
MyApplication 进程名: com.cfm.processtest
MyApplication 进程名: com.cfm.processtest:test1
MyApplication 进程名: com.cfm.test2
可以看到 Application 被多次创建了,其实原因很简单,就是因为 Android System 在创建每一个进程的时候都会创建一个新的虚拟机,而这个过程其实就类似于启动了一个新的应用一样,那么启动应用自然就会创建 Application。同时,这也证明了,在多进程模式下,不同进程的组件拥有独立的虚拟机、Application 以及内存空间。