Android日积月累系列之七-IPC安卓进程间通讯
进程间通信 IPC :Inter-Process Communication
1 非多进程间Server通信
1.1定义一个Service
Service具体实现代码
class ServiceTest1 : Service() {
val logTag = "ServiceTest1"
override fun onBind(intent: Intent?): IBinder {
Log.d(logTag,"onBind")
(getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager).let { activityManager ->
activityManager.runningAppProcesses.forEach { processInfo->
if(processInfo.pid == android.os.Process.myPid()) {
Log.d(logTag,"processName:${processInfo.processName}")
}
}
}
return ServiceTest1Binder()
}
}
ServiceTest1Binder具体实现代码。
class TimerLiveData : LiveData<Int>() {
private var count = 0
private var counterJob: Job? = null
override fun onActive() {
super.onActive()
counterJob = MainScope().launch {
while (isActive) {
Log.d("TimerLiveData", "postValue:$count")
postValue(count++)
delay(1000)
}
}
}
override fun onInactive() {
super.onInactive()
if (counterJob?.isActive ?: false) {
counterJob?.cancel()
}
}
}
class ServiceTest1Binder : Binder() {
fun getLiveData(): LiveData<Int> = TimerLiveData()
}
在AndroidManifest.xml
声明Service
<service android:name=".service.ServiceTest1"/>
1.2绑定服务
class MainActivity : AppCompatActivity() {
private var isBound = false
private val serviceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
isBound = true
Log.d(tag,"onServiceConnected name:$name\tservice:$service")
Log.d(tag,"Binder:${service as? ServiceTest1Binder}")
Log.d(tag,"liveData:${(service as? ServiceTest1Binder)?.getLiveData()}")
(service as? ServiceTest1Binder)?.getLiveData()?.observe(this@MainActivity){
Log.d(tag,"count:$it")
}
}
override fun onServiceDisconnected(name: ComponentName?) {
Log.d(tag,"onServiceDisconnected name:$name")
isBound = false
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
startService(Intent(this@MainActivity, ServiceTest1::class.java))
bindService(
Intent(this@MainActivity, ServiceTest1::class.java),
serviceConnection,
Context.BIND_AUTO_CREATE
)
}
override fun onDestroy() {
unbindService(serviceConnection)
isBound = false
super.onDestroy()
}
}
绑定后,就会看到控制台每隔一秒打印一次累加后的数字
1.3把Service声明到另外一个进程运行
在AndroidManifest.xml
里,添加android:process=":remote"
<service android:name=".service.ServiceTest1"
android:process=":remote"/>
可以看到,服务仍可以绑定成功,因为已经处在不同的进程内,所以已经不能像之前那样正常通信了。
Binder:null
liveData:null
2.使用Messenger跨进程通信
把Service里的onBind方法,从返回一个自定义的Binder,变成返回一个Messenger.binder,在handleMessage里处理客户端发送过来的Message。
private var inMessenger: Messenger? = null
private var outMessenger: Messenger? = null
private val handlerThread = HandlerThread("Messenger")
inner class IncomingHandler(looper: Looper) : Handler(looper) {
override fun handleMessage(msg: Message) {
if (msg.replyTo is Messenger) {
//服务端获取到客户端的Messenger
outMessenger = msg.replyTo
} else {
outMessenger?.send(msg)
}
Log.d("ServiceTest1","msg what:${msg.what}\tmsg msg:${msg.obj}\tThread:${Thread.currentThread()}")
super.handleMessage(msg)
}
}
override fun onBind(intent: Intent?): IBinder? {
Log.d(logTag, "onBind")
(getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager).let { activityManager ->
activityManager.runningAppProcesses.forEach { processInfo ->
if (processInfo.pid == android.os.Process.myPid()) {
Log.d(logTag, "processName:${processInfo.processName}")
}
}
}
if (handlerThread.state == Thread.State.NEW) {
handlerThread.start()
}
inMessenger = Messenger(IncomingHandler(handlerThread.looper))
return inMessenger?.binder
}
Activity(客户端)里的ServiceConnection也进行相应的处理。
private val serviceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
isBound = true
Log.d(tag,"onServiceConnected name:$name\tservice:$service")
Log.d(tag,"Binder:${service as? ServiceTest1Binder}")
Log.d(tag,"liveData:${(service as? ServiceTest1Binder)?.getLiveData()}")
messenger = Messenger(service)
val message = Message.obtain()
message.what = 0
message.replyTo = Messenger(OutcomingHandler())
//客户端向服务端发送自身的Messenger,从而使服务端也可以向客户端发送信息。
messenger?.send(message)
TimerLiveData().observe(this@MainActivity){
if(!isBound) return@observe
val message = Message.obtain()
message.arg1 = it
//向服务端发送消息
messenger?.send(message)
}
}
override fun onServiceDisconnected(name: ComponentName?) {
Log.d(tag,"onServiceDisconnected name:$name")
isBound = false
messenger = null
}
}
现在可以从客户端(Activity)向服务端(Service)发送消息了,那么如何从服务端向客户端发送消息呢,很简单,onServiceConnected的时候,客户端也向服务端发送自己的Messenger就可以了
使用Messenger跨进程通信的优点是简单,并且消息处理是使用消息队列的,消息是一条一条的处理的,不需要考虑并发的问题。
3.使用AIDL进行进程间通信
AIDL:Android Interface Definition Language
3.1创建aidl文件
必须使用 Java 编程语言构建 .aidl
文件
编写完成后,Build->Make Module ,就会在build->generated->aidl_source_output_dir下看到生成的对应java文件
// IRemoteService.aidl
package com.x.y.z.android_kotlin;
// Declare any non-default types here with import statements
interface IRemoteService {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
}
3.2 绑定
绑定和普通绑定流程一样,只是在onServiceConnected
里,需要用IRemoteService.Stub.asInterface(service)
转换成应的接口,然后就可以客户端向服务端发送数据。
private val mConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
IRemoteService.Stub.asInterface(service).basicTypes(
1,2,false,1.0f,1.0,TimeUtils.date2String(Date())
)
}
override fun onServiceDisconnected(name: ComponentName?) {
}
}
3.3传递自定义的Parcelable方法
安卓sdk哪些类是Parcelable的子类呢,看到了熟悉的 Intent,Message
public final class MacAddress implements Parcelable {}
public class UsbConfiguration implements Parcelable {}
public class Address implements Parcelable {}
public class WifiInfo implements TransportInfo, Parcelable {}
public class ObservableParcelable<T extends Parcelable> extends ObservableField<T> implements Parcelable, Serializable {}
public class Notification implements Parcelable{}
public class Location implements Parcelable {}
public final class Bitmap implements Parcelable {}
public abstract class Uri implements Parcelable, Comparable<Uri> {}
public final class Message implements Parcelable {}
public final class Messenger implements Parcelable {}
public class Intent implements Parcelable, Cloneable {}
public class UsbDevice implements Parcelable {}
public class PackageInfo implements Parcelable {}
public class IntentFilter implements Parcelable {}
public class IntentSender implements Parcelable {}
public final class IntentSenderRequest implements Parcelable {}
先自定义一个类,并且实现Parcelable
//TestParcelableBean.java
public class TestParcelableBean implements Parcelable {
private String name;
private int index;
private long time;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.name);
dest.writeInt(this.index);
dest.writeLong(this.time);
}
public void readFromParcel(Parcel source) {
this.name = source.readString();
this.index = source.readInt();
this.time = source.readLong();
}
public TestParcelableBean() {
}
protected TestParcelableBean(Parcel in) {
this.name = in.readString();
this.index = in.readInt();
this.time = in.readLong();
}
public static final Creator<TestParcelableBean> CREATOR = new Creator<TestParcelableBean>() {
@Override
public TestParcelableBean createFromParcel(Parcel source) {
return new TestParcelableBean(source);
}
@Override
public TestParcelableBean[] newArray(int size) {
return new TestParcelableBean[size];
}
};
}
并且创建一个对应的aidl文件,以便AIDL可以找到它并知道它实现了 parcelable协议
// TestParcelableBean.aidl
package com.x.y.z;
parcelable TestParcelableBean;
引用自定义的Parcelable类
interface IRemoteService {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
void testParcelable(in TestParcelableBean parcelableBean);
}
然后就可以客户端向服务端发送数据了
IRemoteService.Stub.asInterface(service).apply {
basicTypes(
1, 2, false, 1.0f, 1.0, TimeUtils.date2String(Date())
)
testParcelable(TestParcelableBean().apply {
this.name = TimeUtils.date2String(Date())
this.time = Date().time
this.index = 1
})
}
3.4使用Bundle传递数据
如果您的 AIDL 接口包含接收Bundle作为参数(预计包含 Parcelable 类型)的方法,则在尝试从Bundle读取之前,请务必通过调用 Bundle.setClassLoader(ClassLoader)
设置Bundle的类加载器。否则,即使您在应用中正确定义 Parcelable 类型,也会遇到 ClassNotFoundException
。例如,
如果您有 .aidl
文件:
// IRectInsideBundle.aidl
package com.example.android;
/** Example service interface */
interface IRectInsideBundle {
/** Rect parcelable is stored in the bundle with key "rect" */
void saveRect(in Bundle bundle);
}
如下方实现所示,在读取 Rect
之前,ClassLoader
已在 Bundle
中完成显式设置
private val binder = object : IRectInsideBundle.Stub() {
override fun saveRect(bundle: Bundle) {
bundle.classLoader = classLoader
val rect = bundle.getParcelable<Rect>("rect")
process(rect) // Do more with the parcelable
}
}
3.5 处理调用AIDL方法时可能发生的异常
调用您在接口上定义的方法。您应始终捕获 DeadObjectException
异常,系统会在连接中断时抛出此异常。您还应捕获 SecurityException
异常,当 IPC 方法调用中两个进程的 AIDL 定义发生冲突时,系统会抛出此异常。
但是生成的java代码,只向外抛出了android.os.RemoteException
,
@Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException{}
@Override public void testParcelable(com.etonedu.aiprinter.service.TestParcelableBean parcelableBean) throws android.os.RemoteException{}
@Override
public android.os.IBinder asBinder() {
return null;
}
3.6 方法参数的形式发送匿名对象 实现双向通信
有关调用 IPC 服务的几点说明:
-
对象是跨进程计数的引用。
-
您可以方法参数的形式发送匿名对象。
//用于aidl间传递的回调
oneway interface IRemoteServiceCallback {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void onSuccessCallback(in TestParcelableBean parcelableBean);
void onFailCallback();
}
在另外的aidl中引用
interface IRemoteService {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
void testParcelable(in TestParcelableBean parcelableBean);
void registerCallback(IRemoteServiceCallback callback);
void unregisterCallback(IRemoteServiceCallback callback);
}
然后客户端和服务端之间就可能通过其他方法和回调方法,实现了双向通信了。
4.安卓开启多进程需要注意的一些事情
-
Application 会创建多个实例;
-
单例模式、静态变量失效;
-
线程同步机制失效; -
-
文件同步读写功能的可靠性下降;
判断是否是同一个进程
// 获取当前包名
String packageName = getPackageName();
// 获取当前进程名
String processName = getProcessName(android.os.Process.myPid());
private String getProcessName(int pid) {
ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> processInfos = activityManager.getRunningAppProcesses();
if (processInfos == null || processInfos.isEmpty()) {
return null;
}
for (ActivityManager.RunningAppProcessInfo processInfo : processInfos) {
if (processInfo.pid == pid) {
return processInfo.processName;
}
}
return null;
}
总结
如果不需要跨进程通信,使用Binder即可,
如果需要跨进程通信,但不需要多线程不需要并发,使用 Messenger 即可
如果需要跨进程,并且需要在服务中进行多线程,那么就需要用到AIDL