Android 学习 AIDL 的使用

今天,尝试这为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 进行通信了。

猜你喜欢

转载自blog.csdn.net/haonanren2bu2/article/details/48244387