Android日积月累系列之七-安卓进程间通讯-IPC-AIDL

85 阅读6分钟

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