Android之Service设置android:process作用
原文地址 blog.csdn.net
在AndroidManifest.xml中定义service时会看到这样的代码android:process=”:remote”,例如:
1. <service
2. android:
3. android:enabled="true"
4. android:exported="false"
5. android:process=":remote" />
这个代码是什么意思呢?
我们先来了解一下Service在AndroidManifest.xml中的声明语法,其格式如下:
<service android:enabled=["true" | "false"]
android:exported=["true" | "false"]
android:icon="drawable resource"
android:isolatedProcess=["true" | "false"]
android:label="string resource"
android:
android:permission="string"
android:process="string" >
. . .
- android:exported:代表是否能被其他应用隐式调用,其默认值是由service中有无intent-filter决定的,如果有intent-filter,默认值为true,否则为false。为false的情况下,即使有intent-filter匹配,也无法打开,即无法被其他应用隐式调用。
- android:name:对应Service类名
- android:permission:是权限声明
- android:process:是否需要在单独的进程中运行,当设置为android:process=”:remote”时,代表Service在单独的进程中运行。注意“:”很重要,它的意思是指要在当前进程名称前面附加上当前的包名,所以“remote”和”:remote”不是同一个意思,前者的进程名称为:remote,而后者的进程名称为:App-packageName:remote。
- android:isolatedProcess :设置 true 意味着,服务会在一个特殊的进程下运行,这个进程与系统其他进程分开且没有自己的权限。与其通信的唯一途径是通过服务的API(bind and start)。
- android:enabled:是否可以被系统实例化,默认为 true因为父标签 也有 enable 属性,所以必须两个都为默认值 true 的情况下服务才会被激活,否则不会激活。
1)默认情况(不指定process属性)
1、一个app只运行在一个进程中,进程名字为包名。
2、一个service(所有组件都一样)只作为一个线程运行在app的进程中,没有自己独立的进程。
2)****设置android:process="xxxx"后
1、设置了这行代码,系统就会为service创建新的进程
service将运行在这个新的独立的进程,它所在的apk依旧运行在原来进程。这样就实现了Android使用多进程
2、属性值可以随意定义
xxxx是自定义的,上面代码的remote是随便写的
3、当属性值以冒号开头 :,如 android:process = “ :xxxx ”
表示:将为 app 创建一个私有进程,其他 app 无法访问,进程名称是:包名: xxxx
4、当属性值以小写字母开头,如 android:process = “xxxx”
表示:这个进程是对外公开的,其他app可以访问它,进程名称是:xxxx
注意:a)
b)设置
c)若
下面援引官方说明文档:
3)多进程引发的问题
静态成员和单例失效:每个进程保持各自的静态成员和单例,相互独立。
线程同步机制失效:每个进程有自己的线程锁。
SharedPreferences可靠性下降:不支持并发写,会出现脏数据。
Application多次创建:不同进程跑在不同虚拟机,每个虚拟机启动会创建自己的Application,自定义Application时生命周期会混乱。
综上,不同进程拥有各自独立的虚拟机,Application,内存空间,由此引发一系列问题,因此在跨进程通信开发时,要注意避开上述问题。
重点来了,因为设置了 android:process 属性将组件运行到另一个进程,相当于另一个应用程序,所以在另一个线程中也将新建一个 Application 的实例。因此,每新建一个进程 Application 的 onCreate 都将被调用一次。 如果在 Application 的 onCreate 中有许多初始化工作并且需要根据进程来区分的,那就需要特别注意了。
让我们到 Framework 中看看新建进程的逻辑,请打开老罗的博客 : Android系统在新进程中启动自定义服务过程(startService)的原理分析
详细介绍了新进程启动的过程,其中我们重点看到 Step 17. ActivityThread.handleCreateService中
`1. public final class ActivityThread {
3. ......
5. private final void handleCreateService(CreateServiceData data) {
6. // If we are getting ready to gc after going to the background, well
7. // we are back active so skip it.
8. unscheduleGcIdler();
10. LoadedApk packageInfo = getPackageInfoNoCheck(
11. data.info.applicationInfo);
12. Service service = null;
13. try {
14. java.lang.ClassLoader cl = packageInfo.getClassLoader();
15. service = (Service) cl.loadClass(data.info.name).newInstance();
16. } catch (Exception e) {
17. if (!mInstrumentation.onException(service, e)) {
18. throw new RuntimeException(
19. "Unable to instantiate service " + data.info.name
20. + ": " + e.toString(), e);
21. }
22. }
24. try {
25. if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
27. ContextImpl context = new ContextImpl();
28. context.init(packageInfo, null, this);
30. Application app = packageInfo.makeApplication(false, mInstrumentation);
31. context.setOuterContext(service);
32. service.attach(context, this, data.info.name, data.token, app,
33. ActivityManagerNative.getDefault());
34. service.onCreate();
35. mServices.put(data.token, service);
36. try {
37. ActivityManagerNative.getDefault().serviceDoneExecuting(
38. data.token, 0, 0, 0);
39. } catch (RemoteException e) {
40. // nothing to do.
41. }
43. } catch (Exception e) {
44. if (!mInstrumentation.onException(service, e)) {
45. throw new RuntimeException(
46. "Unable to create service " + data.info.name
47. + ": " + e.toString(), e);
48. }
49. }
50. }
52. ......
54. }` ![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
看到这行 Application app = packageInfo.makeApplication(false, mInstrumentation); 在这里创建了 Application 。
解决方案
获取当前运行进程的名称:
1. 方案1
2. public static String getProcessName(Context cxt, int pid) {
3. ActivityManager am = (ActivityManager) cxt.getSystemService(Context.ACTIVITY_SERVICE);
4. List<RunningAppProcessInfo> runningApps = am.getRunningAppProcesses();
5. if (runningApps == null) {
6. return null;
7. }
8. for (RunningAppProcessInfo procInfo : runningApps) {
9. if (procInfo.pid == pid) {
10. return procInfo.processName;
11. }
12. }
13. return null;
14. }
15. 目前网上主流的方法,但效率没有方案2高,感谢由王燚同学提供的方案2
17. 方案2
18. public static String getProcessName() {
19. try {
20. File file = new File("/proc/" + android.os.Process.myPid() + "/" + "cmdline");
21. BufferedReader mBufferedReader = new BufferedReader(new FileReader(file));
22. String processName = mBufferedReader.readLine().trim();
23. mBufferedReader.close();
24. return processName;
25. } catch (Exception e) {
26. e.printStackTrace();
27. return null;
28. }
29. }
30. 然后在 Application 的 onCreate 中获取进程名称并进行相应的判断,例如:
32. String processName = getProcessName(this, android.os.Process.myPid());
34. if (!TextUtils.isEmpty(processName) && processName.equals(this.getPackageName())) {//判断进程名,保证只有主进程运行
35. //主进程初始化逻辑
36. ....
37. }