天天看点

Android进阶之Service远程通信AIDLService的远程通信AIDL

Service的远程通信AIDL

先解释下什么是AIDL,AIDL (Android Interface Definition Language)是一种IDL 语言,用于生成可以在Android设备上两个进程之间进行进程间通信(IPC)的代码。如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序列化的参数。

AIDL IPC机制是面向接口的、轻量级的。通过AIDL定义的接口可以实现服务器端与客户端的IPC通信。

使用AIDL实现IPC(进程间通讯)

说明:

以下代码我用的是最新版的SDK,android6.0 api

创建.aidl文件

// IAppServiceRemoteBinder.aidl
package me.dengfengdecao.android.servicedemo;

// Declare any non-default types here with import statements

interface IAppServiceRemoteBinder {
    /**
     * 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 syncData(String data);
}
           

其中 syncData方法用来和客户端同步数据,测试用的。

实现AIDL接口

在RemoteService中的onBind方法中返回aidl接口中Stub内部抽象类的实现,这里使用了匿名对象:

@Override
public IBinder onBind(Intent intent) {
    Log.d(TAG, "onBind");
    return new IAppServiceRemoteBinder.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
            Log.d(TAG, "basicTypes: ");
        }

        // data:从客户端同步过来的数据
        @Override
        public void syncData(String data) throws RemoteException {
            Log.d(TAG, "syncData: 要同步的数据:" + data);
            defaultdata += data;
        }
    };

}
           

查看自定义的aidl接口可知Stub为aidl的抽象内部类,并且它继承自Binder类和实现我们自定义的aidl接口。

/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements me.dengfengdecao.android.servicedemo.IAppServiceRemoteBinder
           

这个内部抽象类是必须要实现的。

注意:

服务端AIDL的任何修改都必须的同步到所有的客户端,否则客户端调用服务端得接口可能会导致程序异常(因为此时客户端此时可能会调用到服务端已不再支持的接口)。

这样,onBind方法就返回了一个Stub类的对象,该对象为service提供了RPC接口。该对象会被传递到实现了ServiceConnection接口的类的public void onServiceConnected(ComponentName name, IBinder service)方法中。

实现ADIL接口时需要注意一下几点:

  • 不能保证所有对aidl接口的调用都在主线程中执行,所以必须考虑多线程调用的情况,也就是必须考虑线程安全。
  • 默认IPC调用是同步的。如果已知IPC服务端会花费很多毫秒才能完成,那就不要在Activity或View线程中调用,否则会引起应用程序挂起(Android可能会显示“应用程序未响应”对话框),可以试着在独立的线程中调用。
  • 不会将异常返回给调用方

下一步中该对象将会向客户端公开,这样客户端就可以通过该对象与该service进行交互了。

向客户端暴露接口

在完成了接口的实现后需要向客户端暴露接口了,也就是发布服务,实现的方法是继承 Service,然后实现以Service.onBind(Intent)返回一个实现了接口的类对象。

下面的代码片断表示了暴露IRemoteService接口给客户端的方式。

@Override
public IBinder onBind(Intent intent) {
    Log.d(TAG, "onBind");
    return new IAppServiceRemoteBinder.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
            Log.d(TAG, "basicTypes: ");
        }

        // data:从客户端同步过来的数据
        @Override
        public void syncData(String data) throws RemoteException {
            Log.d(TAG, "syncData: 要同步的数据:" + data);
            defaultdata += data;
        }
    };

}
           

即在继承了Service的类中的onBind方法中返回aidl接口的Binder对象。

然后在manifest中注册远程服务:

<service
        android:name=".RemoteService"
        android:process=":remote"
        >
        <intent-filter>
            <action android:name="me.dengfengdecao.action.REMOTE_SERVICE"/>
        </intent-filter>
 </service>

android:process=":remote",指定创建一个远程服务在新的进程中。
           

注意,最后要将远程服务端的aidl copy到客户端,包括完整的包名。

现在,如果客户端(ClientActivity)调用bindService()来连接该服务端(RemoteService) ,客户端的onServiceConnected()回调函数将会获得从服务端(RemoteService )的onBind()返回的Binder对象。

ClientActivity清单(为了易读,删除了view代码):

public class ClientActivity extends AppCompatActivity implements View.OnClickListener, ServiceConnection {

    private static final String TAG = "ClientActivity";

    // 远程服务的AIDL
    private IAppServiceRemoteBinder mRemoteBinder;

    private Intent mIntent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_client);

        // 隐式intent启动远程服务
        mIntent = new Intent();
        mIntent.setAction("me.dengfengdecao.action.REMOTE_SERVICE");

    }

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        Log.d(TAG, "onServiceConnected");
        // 接收从服务端传递过来的IBinder对象
        mRemoteBinder = IAppServiceRemoteBinder.Stub.asInterface(service);
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        Log.d(TAG, "onServiceDisconnected: ");

    }
}
           

ok,现在启动ClientActivity如下:

Android进阶之Service远程通信AIDLService的远程通信AIDL

绑定到远程服务,日志如下:

Android进阶之Service远程通信AIDLService的远程通信AIDL

可以看到,从远程服务那里接收到了IBinder对象。

切换到远程端,日志如下:

Android进阶之Service远程通信AIDLService的远程通信AIDL

输入要同步到远程端的数据,日志如下:

Android进阶之Service远程通信AIDLService的远程通信AIDL

切换回ClientActivity 解绑服务,日志如下:

Android进阶之Service远程通信AIDLService的远程通信AIDL

可以看到,和远程服务断开了关联,切换到系统正在运行的app:

Android进阶之Service远程通信AIDLService的远程通信AIDL

可以确定,确实和远程服务断开了连接。

如果对远程传输对象感兴趣请看官方文档:

http://developer.android.com/intl/zh-cn/guide/components/aidl.html#PassingObjects

还可以和郭霖前辈的这篇博文一起学习:

http://blog.csdn.net/guolin_blog/article/details/9797169

OK,如果有任何问题请指出,谢谢!

部分内容参考:

http://www.cnblogs.com/hibraincol/archive/2011/09/06/2169325.html