AIDL入门

一直听说AIDL但是自己还没有真正使用过,因为没有碰到过使用它的场景,但这并不能成为我不去了解它的借口。现在还没有了解的可以和我一起进行了解(仅限于入门,大神可以移步了)

什么是AIDL

什么是AIDL呢?它的全称是Android Interface Definition Language,中文名字就是Android接口定义语言。Android中的进程间不能进行内存共享,所以不同的进程间的通信,Android采用远程过程调度,同时使用一种定义接口与公开接口的方式来实现。从而达到跨进程间的通信,这样的方式成为AIDL。

实现AIDL的步骤

总体来说我把其分为3个步骤

  • 创建相应的.aidl文件,与需要的接口
  • 实现创建的接口
  • 向客户端暴露实现的接口,实现调用

下面来详细的说说各个步骤的实现

创建.aidl文件与接口

Android Studio中新建项目,在项目的src目录下新建一个.aidl文件

AIDL入门_第1张图片
1.png

这个新建的.aidl文件是定义远程进程所以暴露的接口,我这里命名为IRemoteService。创建完之后,我们能在src/main/aidl中找到这个文件,下面是我们暴露一个sayHello的接口,它的类型是HelloMessage,是一个对象类型。

// IRemoteService.aidl
package com.idisfkj.aidldemo;

// Declare any non-default types here with import statements
import com.idisfkj.aidldemo.HelloMessage;
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);

    HelloMessage sayHello();
}

现在我们来创建HelloMessage类,在我们日常创建文件的main/java中创建。

package com.idisfkj.aidldemo;

import android.os.Parcel;
import android.os.Parcelable;

public class HelloMessage implements Parcelable{
    private String message;
    private int pId;

    public HelloMessage(String message, int pId) {
        this.message = message;
        this.pId = pId;
    }

    protected HelloMessage(Parcel in) {
        message = in.readString();
        pId = in.readInt();
    }

    public static final Creator CREATOR = new Creator() {
        @Override
        public HelloMessage createFromParcel(Parcel in) {
            return new HelloMessage(in);
        }

        @Override
        public HelloMessage[] newArray(int size) {
            return new HelloMessage[size];
        }
    };

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public int getpId() {
        return pId;
    }

    public void setpId(int pId) {
        this.pId = pId;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel parcel, int i) {
        parcel.writeString(message);
        parcel.writeInt(pId);
    }
}

它包括两个属性字段messagepId,代表下面我们要传递的信息与方便查看的进程ID。这里实现了Parcelable接口,进行序列化。因为使用到了Parcelable所以我们要回到aidl目录,再新建一个与HelloMessage相同名字的.aidl文件,内容为

// HelloMessage.aidl
package com.idisfkj.aidldemo;

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

parcelable HelloMessage;

最后在前面新建的IRemoteService.aidl文件中导入该类型的包。(可以查看前面贴的代码,我已经实现了),这样所需的.aidl文件与接口都已经建好了,最后再Build -> Make Project(如果你是在Module中则Build -> Make Module App)。完了之后我们就能在build/generated/source/aidl中查看生成的IRemoteService。打开之后能看到如下信息

public interface IRemoteService extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.idisfkj.aidldemo.IRemoteService
{
private static final java.lang.String DESCRIPTOR = "com.idisfkj.aidldemo.IRemoteService";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
 * Cast an IBinder object into an com.idisfkj.aidldemo.IRemoteService interface,
 * generating a proxy if needed.
 */
public static com.idisfkj.aidldemo.IRemoteService asInterface(android.os.IBinder obj)
{
 //省略

会发现它实现了一个静态抽象类Stub,它继承于Binder同时实现我们定义的IRemoteService,这样我们可以通过New IRemoteService.Stub()来实现我们定义的接口,同时通过asInterface来获取其代理,调用接口方法。

接口的实现

首先建一个Service,通过ServiceonBind的方法来传递绑定的IBinder,上面已经说了Stub继承与BinderBinder实现了IBinder接口,所以我们可以使用Stub

public class RemoteService extends Service {
    public RemoteService() {
    }

    IRemoteService.Stub mBinder = new IRemoteService.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }

        @Override
        public HelloMessage sayHello() throws RemoteException {
            return new HelloMessage("当前线程:" + Thread.currentThread().toString() + "\n"
                    + "当前线程id:" + Thread.currentThread().getId() + "\n"
                    + "主线程id:" + getMainLooper().getThread().getId(), Process.myPid());
        }
    };

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return mBinder;
    }
}

调用

MainActivity中绑定开启Service,实现ServiceConnection,在onServiceConnected中通过iRemoteService = IRemoteService.Stub.asInterface(iBinder);来获取调用的代理,最后调用iRemoteService.sayHello()获取通信的信息。

public class MainActivity extends AppCompatActivity {

    private TextView mPidText;
    private IRemoteService iRemoteService;
    private boolean mBind = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mPidText = (TextView) findViewById(R.id.pid_text);
        mPidText.setText("本地id:" + Process.myPid());
    }

    @Override
    protected void onStart() {
        super.onStart();
        Intent intent = new Intent(MainActivity.this, RemoteService.class);
        bindService(intent, conn, BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        unbindService(conn);
        mBind = false;
    }

    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            iRemoteService = IRemoteService.Stub.asInterface(iBinder);
            mBind = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            iRemoteService = null;
            mBind = false;
        }
    };

    public void myClick(View view) {
        switch (view.getId()) {
            case R.id.show_pid_bt:
                if (mBind) {
                    try {
                        Log.v("TAG", "远程id:" + iRemoteService.sayHello().getpId());
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
                break;
            case R.id.show_message_bt:
                if (mBind) {
                    try {
                        Log.v("TAG", "Message:" + iRemoteService.sayHello().getMessage());
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
                break;
        }
    }
}

当然在AndroidManifest.xml中要对Service进行配置


注意因为要模仿远程间的通信,所以要将process设置成remote,如果不设置当然也可以,但这就不是不同进程中的通信了,aidl就没什么意义了

来看下效果吧

AIDL入门_第2张图片
2.png
3.png

客户端进程是26951而服务端进程为26974,说明他们处于不同的进程,这样AIDL也就实现了。当然如上所述将process去掉,就会发现,客户端与服务端的进程就是一样的了。这个就留个读者自行测试了。

源代码下载:https://github.com/idisfkj/AIDLDemo
更多分享请移步:https://idisfkj.github.io

关注

怪谈时间到了

你可能感兴趣的:(AIDL入门)