欢迎访问移动开发之家(rcyd.net),关注移动开发教程。移动开发之家  移动开发问答|  每日更新
页面位置 : > > > 内容正文

Android IPC 之 AIDL(一)

来源: 开发者 投稿于  被查看 47978 次 评论:165

Android IPC 之 AIDL(一)


IPC是Inter-Process Communication的缩写,即跨进程通信。Android中跨进程通信有多种方式,如文件共享、使用ContentProviderBroadcast、和Socket等。比较复杂的情况下,常用的两种方式为MessengerAIDL,而Messenger的底层实现又是AIDL。

首先不看别的,先来看一下AIDL是如何使用的。

假设我们现在有一个两数相加的任务,客户端没办法完成(别问我它为什么完不成==,咱举栗子简单点哈~),需要将任务交给另一个进程中的服务端完成,再从服务端获取到该任务的结果。

我们首先如下图方式创建一个AIDL接口:

新建文件,选择AIDL

Android Studio会自动为它生成一个路径,如下图:

这里写图片描述

在该文件中声明一个接口以及一个我们想让服务端实现的接口方法。如下:

package com.vera.aidltest;

interface IMyAdd {
   int myAdd(int num_a,int num_b);
}

注意,并不是所有数据类型都能在AIDL文件中使用,AIDL文件只支持以下几种数据类型:

Java 中的基本数据类型 String 和CharSequence List 和 Map ,且List和Map 对象的元素必须是AIDL支持的数据类型 AIDL 自动生成的接口 ,需要导入(import,即使同处于一个包中) 实现android.os.Parcelable 接口的类的对象. 需要导入(import,即使同处于一个包中),且必须新建一个与其同名的AIDL文件,并在文件中声明该类为Parcelable

在接口定义好后,系统将为我们生成一个Java文件,AS下是在app\build\generated\source\aidl\debug目录下,生成的代码如下:

package com.vera.aidltest;
public interface IMyAdd extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.vera.aidltest.IMyAdd
{
private static final java.lang.String DESCRIPTOR = "com.vera.aidltest.IMyAdd";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
 * Cast an IBinder object into an com.vera.aidltest.IMyAdd interface,
 * generating a proxy if needed.
 */
public static com.vera.aidltest.IMyAdd asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.vera.aidltest.IMyAdd))) {
return ((com.vera.aidltest.IMyAdd)iin);
}
return new com.vera.aidltest.IMyAdd.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_myAdd:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.myAdd(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.vera.aidltest.IMyAdd
{
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;
}
@Override public int myAdd(int num_a, int num_b) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(num_a);
_data.writeInt(num_b);
mRemote.transact(Stub.TRANSACTION_myAdd, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_myAdd = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public int myAdd(int num_a, int num_b) throws android.os.RemoteException;
}

代码很长很凌乱的样子……嗯,本节我们先不看它,只需要知道它是根据AIDL文件生成的一个接口IMyAdd,包含一个继承自Binder的静态内部抽象类Stub(咦,这么连起来说总有哪里怪怪的……),并且声明了myAdd()方法。为什么是抽象类呢,因为它实现了IMyAdd接口却并没有真正实现,那放到哪里实现呢?当然是我们的服务端咯。

接下来,我们可以就可以来写客户端和服务端的代码了,那么,我们这里的客户端和服务端指的是什么呢?就本例来说,它们分别是一个Activity和一个Service,这里我们将它们放在了同一个应用中,只不过通过某种方法使其运行在不同的进程。更多情况下它们并不运行在同一个应用中,这时候我们需要将整个aidl文件夹的内容复制一份,这是因为客户端和服务端的AIDL包结构需要保持一致,否则将会出现反序列化不成功的结果,那么跨进程通信将无法进行。

那么我们现在开始写服务端的代码,新建一个Service名为ServerService如下:

package com.vera.aidltest;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

public class ServerService extends Service {

   private Binder mBinder=new IMyAdd.Stub(){
       @Override
       public int myAdd(int num_a, int num_b) throws RemoteException {
           int result=num_a+num_b;
           Log.d("ServerService","the result of "+num_a+" and "+num_b+" is "+result);
           return result;
       }
   };

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}

ServerService很简单,它只是创建了一个Binder对象并在onBinder()方法中将其返回。该Binder对象就是我们实现了接口方法的Stub对象。好啦,现在我们可以在myAdd()方法中愉快地进行我们的操作啦。在这里我们只是得到num_a和num_b的和并将其打印,最后再返回结果。

另外,我们需要将该Service设置在一个独立的进程中,不然还怎么玩跨进程通信~
更改AndroidManifest.xml如下:


好了,服务端的创建已经完成啦,我们现在来看客户端。客户端要做些什么呢?,我们来看代码:

package com.vera.aidltest;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class ClientActivity extends AppCompatActivity {

    Button mButton;

    private ServiceConnection myConnection=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            IMyAdd mMyAdd=IMyAdd.Stub.asInterface(iBinder);
            try {
                int result=mMyAdd.myAdd(1,2);
                Log.d("ClientActivity","get the result is "+result);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_client);
        mButton=(Button)findViewById(R.id.activity_client_mbutton);

        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent=new Intent(ClientActivity.this,ServerService.class);
                bindService(intent,myConnection, Context.BIND_AUTO_CREATE);
            }
        });
    }
    @Override
    protected void onDestroy(){
        unbindService(myConnection);
        super.onDestroy();
    }
}

首先,我们创建了一个ServiceConnection的匿名类并实例化一个对象,然后,我们又设置了在Button点击之后通过bindService()方法绑定服务。bindService()方法接收三个参数,第一个是在这之前我们创建的Intent对象,第二个是ServiceConnection的实例对象,第三个参数是一个标志位,BIND_AUTO_CREATE表示在Activity和Service进行绑定后,服务将自动创建。然后我们重写了onDestroy()方法,将Activity和Service解除绑定。

重点是在创建的ServiceConnection匿名类里!在这个类里我们重写了onServiceConnected()方法和onServiceDisconnected()方法,这两个方法分别会在Activity与Service成功绑定和解除绑定的时候调用。在onServiceConnected()方法里,我们调用了Stub()的asInterface()方法,该方法返回一个Binder代理对象,并向上转型成为客户端接受的AIDL接口类型的对象!

好啦,拿到了这个对象,现在只差一步调用方法的事啦,我们来试一试1+2等于多少叭~

运行程序,在点击Button之后,查看日志打印信息,如下图:

这里写图片描述

这里写图片描述

在com.vera.aidltest进程中,客户端打印出得到的结果为3,在com.vera.aidltest:remote进程中,服务端打印出1+2等于3(我英语不好……),通信成功啦!

嗯好,那我们来总结一下,具体的步骤吧:

首先建立一个AIDL文件,在其中定义一个接口,接口中应包含我们希望服务端实现的方法的声明。 其次,在服务端中将实现好方法的Stub对象通过onBind()方法返回 最后,在客户端中将服务绑定,并重写ServiceConnection类中的方法,在其中获得Binder对象,调用服务端的接口方法。

这其中我们屏蔽了很多细节,只谈了使用方法,下一节婷子会把更具体的部分再贴出来,第一篇技术博客,有什么不对的地方还请留言哦,么么哒~

用户评论