今天,尝试这为Service 设定新的进程时,遇到了 Fatal 的Bug.
AndroidManifest.xml 设置如下:
<service
android:name=".MyService"
android:process=":remote"
android:enabled="true"
android:exported="false">
通过 process设置 Service 运行的进程 remote,当 startService运行service时,查看系统的进程,就会发现多了一个 remote 的进程。但,同时也遇到了新的问题,当 bindService时,抛出了如下的异常:
java.lang.ClassCastException: android.os.BinderProxy cannot be cast to demo.example.com.servicedemo.MyService$MyBinder
at demo.example.com.servicedemo.MainActivity$1.onServiceConnected(MainActivity.java:50)
at android.app.LoadedApk$ServiceDispatcher.doConnected(LoadedApk.java:1223)
at android.app.LoadedApk$ServiceDispatcher$RunConnection.run(LoadedApk.java:1240)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
这是 process引起的,当Activity与Service运行在不同的进程中时,如果要绑定二者,就会抛出这样的异常。但是如果我们有这样的需求,需要Activity 和Service 进行跨进程通信时,又该怎么办呢?
可以使用AIDL封装Binder。
什么是 AIDL
AIDL: Android Interface Definition Language,即Android接口定义语言。
如何使用 AIDL
一, 定义 .aidl 文件
AIDL文件的格式类似与 Java 中的接口 interface,我们需要在我们的工程目录下创建一个以 .aidl 结尾的文件 IPerson.aidl.
// IMyAidlInterface.aidl
interface IMyAidlInterface {
String sayHello(String content);
}
我们以 Android Studio为例,创建好之后,系统会自动生成一个文件进行进程间通信,类似与下面的格式
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: /AIDLDemo/app/src/main/aidl/demo/example/com/aidldemo/IMyAidlInterface.aidl
*/
package demo.example.com.aidldemo;
// Declare any non-default types here with import statements
public interface IMyAidlInterface extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements demo.example.com.aidldemo.IMyAidlInterface
{
private static final java.lang.String DESCRIPTOR = "demo.example.com.aidldemo.IMyAidlInterface";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an demo.example.com.aidldemo.IMyAidlInterface interface,
* generating a proxy if needed.
*/
public static demo.example.com.aidldemo.IMyAidlInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof demo.example.com.aidldemo.IMyAidlInterface))) {
return ((demo.example.com.aidldemo.IMyAidlInterface)iin);
}
return new demo.example.com.aidldemo.IMyAidlInterface.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_sayHello:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
java.lang.String _result = this.sayHello(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements demo.example.com.aidldemo.IMyAidlInterface
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
/**
* 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);
@Override public java.lang.String sayHello(java.lang.String content) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(content);
mRemote.transact(Stub.TRANSACTION_sayHello, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_sayHello = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
/**
* 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);
public java.lang.String sayHello(java.lang.String content) throws android.os.RemoteException;
}
二, 定义我们的Service文件,并定义返回对象
继承自 Service, 和之前类似,稍有不同的是我们需要实例化 IMyAidlInterface.Stub 方法,实现之前接口定义的方法。并在 onBind 中返回供客户端调用。
private IMyAidlInterface.Stub myAidlStub = new IMyAidlInterface.Stub() {
@Override
public String sayHello(String content) throws RemoteException {
Toast.makeText(MyService.this, content, Toast.LENGTH_LONG).show();
return content;
}
};
@Override
public IBinder onBind(Intent intent) {
return myAidlStub;
}
三, MainActivity 中获取 ConnectionService 引用。
private IMyAidlInterface myAidlInterface;
ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
这样 Activity 就可以与远程 Service 进行通信了。