什么鬼!单例居然失效了,一个地方设置值,另个地方居然取不到,这怎么可能?没道理啊!排查半天,发现这两就不在一个进程里,才恍然大悟……
什么是进程
按照操作系统中的描述:进程一般指一个执行单元,在 PC 和移动设备上指一个程序或者一个应用。
为什么要使用多进程
我们都知道,系统为 APP 每个进程分配的内存是有限的,如果想获取更多内存分配,可以使用多进程,将一些看不见的服务、比较独立而又相当占用内存的功能运行在另外一个进程当中。
目录结构预览
先放出最终实践后的目录结构,有个大概印象,后面一一介绍。
如何使用多进程
AndroidManifest.xml 清单文件中注册 Activity、Service 等四大组件时,指定 android:process 属性即可开启多进程,如:
|
|
说明:
1、com.wuxiaolong.androidprocesssample
,主进程,默认的是应用包名;
2、android:process=":process1"
,“:”开头,是简写,完整进程名包名 + :process1
;
3、android:process="com.wuxiaolong.androidprocesssample.process2"
,以小写字母开头的,属于全局进程,其他应用可以通过 ShareUID 进行数据共享;
4、进程命名跟包名的命名规范一样。
进程弊端
Application 多次创建
我们自定义一个 Application 类,onCreate
方法进行打印 Log.d("wxl", "AndroidApplication onCreate");
,然后启动 Process1Activity:
看到确实被创建两次,原因见:android:process 的坑,你懂吗?多数情况下,我们都会在工程中自定义一个 Application 类,做一些全局性的初始化工作,因为我们要区分出来,让其在主进程进行初始化,网上解决方案:
AndroidUtil:
静态成员和单例模式失效
创建一个类 SingletonUtil:
在 MainActivity 进行设置:
Process1Activity 取值,打印:
发现打印 userId=0
,单例模式失效了,因为这两个进程不在同一内存了,自然无法共享。
进程间通信
文件共享
既然内存不能共享,是不是可以找个共同地方,是的,可以把要共享的数据保存 SD 卡,实现共享。首先将 SingletonUtil 实现 Serializable 序列化,将对象存入 SD 卡,然后需要用的地方,反序列化,从 SD 卡取出对象,完整代码如下:
SingletonUtil
|
|
序列化和反序列化
|
|
需要权限:
|
|
MainActivity 序列写入
|
|
Process1Activity 反序列化取值
|
|
AIDL
AIDL,Android 接口定义语言,定义客户端与服务端进程间通信,服务端有处理多线程时,才有必要使用 AIDL,不然可以使用 Messenger ,后文介绍。
单个应用,多个进程
服务端
AIDL 传递数据有基本类型 int,long,boolean,float,double,也支持 String,CharSequence,List,Map,传递对象需要实现 Parcelable 接口,这时需要指定 in(客户端数据对象流向服务端)、out (数据对象由服务端流向客户端)。
1、Userbean.java
|
|
2、UserBean.aidl
Userbean.java 同包下创建对应的 UserBean.aidl 文件,与 aidl 调用和交互。
|
|
3、IUserManager.aidl
4、服务类
新建 AIDLService 继承 Service,并且实现 onBind() 方法返回一个你实现生成的 Stub 类,把它暴露给客户端。Stub 定义了一些辅助的方法,最显著的就是 asInterface(),它是用来接收一个 IBinder,并且返回一个 Stub 接口的实例 。
|
|
AndroidManifest 注册:
以上创建完毕,build clean 下,会自动生成 aidl 对应的 java 类供客户端调用。
客户端
1、app/build.gradle
需要指定 aidl 路径:
|
|
2、启动服务,建立联系
打印:
|
|
多个应用,多进程
和上面基本差不多,把服务端和客户端分别创建的两个项目,可以互相通信,注意点:
1、服务端创建好的 aidl 文件,带包拷贝到客户端项目中;
2、客户端启动服务是隐式启动,Android 5.0 中对 service 隐式启动有限制,必须通过设置 action 和 package,代码如下:
AndroidManifest 注册:
|
|
启动服务:
|
|
使用 Messenger
Messenger 可以在不同的进程传递 Message 对象,而我们可以在 Message 对象中放入我们所需要的数据,这样就能实现进程间通信了。Messenger 底层实现是 AIDL,对 AIDL 做了封装, 不需要处理多线程,实现步骤也分为服务端和客户端,代码如下:
服务端
MessengerService:
|
|
AndroidManafest.xml 注册:
|
|
客户端
MainActivity
打印信息:
|
|
最后
《Android开发艺术探索》一书关于 Android 进程间通信这块,还有 ContentProvider、Socket 方式,由于篇幅所限,这里不一一介绍了,有兴趣可以自行查看。如果需要这次 Sample 的源码,可在我的公众号「吴小龙同学」回复:「AndroidProcessSample」获取。
参考
《Android开发艺术探索》
Android 中的多进程,你值得了解的一些知识
Android使用AIDL实现跨进程通讯(IPC)