开启多进程
<service
android:name=".MyService"
android:label="@string/app_name"
android:process=":remote"/>
如果被设置的进程名是以一个冒号开头的,则这个新的进程对于这个应用来说是私有的。
<service
android:name=".MyService"
android:label="@string/app_name"
android:process="com.example.liuwangshu.myprogress.remote"/>
这是一个全局进程,不同应用中的各种组件可以共享一个进程,从而减少资源的占用。
开启多进程会使Application运行两次,我们继承Application,解决的方法就是得到每个进程的名称,如果进程的名称和我们应用的进程名称相同则做我们应用的操作,如果不是则做其他进程的操作。
用Messenger进行进程间通信
Messenger(注意不是Message)是一种轻量级的IPC方案并对AIDL 进行了封装。Messenger是以串行的方式来处理客户端发来的信息,如果有大量的消息发到服务端,服务端仍然一个一个的处理再响应客户端显然是不合适的。另外,Messenger用来进程间进行数据传递但是却不能满足跨进程的方法调用,接下来我们来使用AIDL来实现跨进程方法调用。
用AIDL进行进程间通信
用ContentProvider进行进程间通信
ContentProvider为存储和获取数据提供统一的接口,它可以在不同的应用程序之间共享数据,本身就是适合进程间通信的。ContentProvider底层实现也是Binder,但是使用起来比AIDL要容易许多。系统也预制了很多的ContentProvider,例如通讯录,音视频等,这些操作本身就是跨进程进行通信。
多进程带来的问题
Application多次创建
静态变量和单例模式完全失效
线程同步机制完全失效
SP的可靠性降低
1,多进程相关小结
2,带你一起剖析Android AIDL跨进程通信的实现原理(已阅)
3,Android跨进程通信,深入浅出AIDL(已阅)
4,Android进程间通信(已阅)
5,Android多进程使用场景(已阅)
6,Android AIDL使用详解(已阅)
7,三步掌握 Android 中的 AIDL(已阅)
8,Android 多进程通信之几个基本问题(已阅)
10,你真的理解AIDL中的in,out,inout么?(已阅)
观点
首先多进程开发能为应用解决了OOM问题,Android对内存的限制是针对于进程的,这个阈值可以是48M、24M、16M等,视机型而定,所以,当我们需要加载大图之类的操作,可以在新的进程中去执行,避免主进程OOM。
多进程不光解决OOM问题,还能更有效、合理的利用内存。我们可以在适当的时候生成新的进程,在不需要的时候及时杀掉,合理分配,提升用户体验。减少系统被杀掉的风险。
多进程还能带来一个好处就是,单一进程崩溃并不影响整体应用的使用。例如我在图片浏览进程打开了一个过大的图片,java heap 申请内存失败,但是不影响我主进程的使用,而且,还能通过监控进程,将这个错误上报给系统,告知他在什么机型、环境下、产生了什么样的Bug,提升用户体验。
多进程不一定适合所有的应用,合理利用分配进程,使程序更加稳定,才是我们追求的目标。
应用内跨进程通信,绑定服务可以通过显示意图来绑定,但是如果是跨应用的进程间通信,那么就需要用到隐式意图了。 这里有一点需要注意的就是,在5.0以后隐式意图开启或者绑定service要setPackage(Service的包名),不然会报错。
你可以用Messager处理简单的跨进程通信,但是高并发量的要用AIDL。
默认实现Parcelable的模版只支持in ,如果需要需要支持out或inout需要手动实现readFromParcel方法。
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.name);
dest.writeInt(this.price);
}
//手动实现这个方法
public void readFromParcel(Parcel dest) {
//注意,这里的读取顺序要writeToParcel()方法中的写入顺序一样
name = dest.readString();
price = dest.readInt();
}
埋坑与完善
该篇文章内容不多,但是处处皆是精华,尤其是以下3条建议,以防引起惨案。
xxx.aidl 中不能存在同方法名不同参数的方法。
xxx.aidl 中实体类必须要有指定的tag。
在Android Studio里写完aidl文件还需要在build.gradle文件中android{}方法内添加aidl路径。
sourceSets { main { java.srcDirs = ['src/main/java', 'src/main/aidl'] } }
Android 通过这个特性提供 RemoteCallbackList,让我们用来存储监听接口集合, 这个RemoteCallbackList 内部自动实现了线程同步的功能,而且它的本质是一个 ArrayMap,所以我们用它来绑定/解绑时,不需要做额外的线程同步操作。
private RemoteCallbackList<IDemandListener> demandList = new RemoteCallbackList<>();
@Override
public void registerListener(IDemandListener listener) throws RemoteException {
demandList.register(listener);
}
@Override
public void unregisterListener(IDemandListener listener) throws RemoteException {
demandList.unregister(listener);
}
最后,我们通过 Handler 来进行定时发送消息。(handler 并不能精准的做定时任务,因为 handler 在发送和接收的过程中会有时间损耗) 。
另外我们需要通过 beginBroadcast() 来获取 RemoteCallbackList中元素的个数,同时 beginBroadcast() 和 finishBroadcast() 必须要配对使用,哪怕仅仅只是获取一下这个集合的元素个数。
