盒子
盒子
文章目录
  1. 什么是AIDL
  2. 实现AIDL的步骤
  3. 创建.aidl文件与接口
  4. 接口的实现
  5. 调用

AIDL入门

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

什么是AIDL

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

实现AIDL的步骤

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

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

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

创建.aidl文件与接口

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

效果图

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 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中创建。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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<HelloMessage> CREATOR = new Creator<HelloMessage>() {
@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文件,内容为

1
2
3
4
5
6
// 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。打开之后能看到如下信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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()获取通信的信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
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进行配置

1
2
3
4
5
<service
android:name=".RemoteService"
android:enabled="true"
android:exported="true"
android:process=":remote"></service>

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

来看下效果吧

效果

效果

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

源代码下载地址:https://github.com/idisfkj/AIDLDemo

转载请指明出处idisfkj博客:https://idisfkj.github.io

支持一下
赞赏是一门艺术