一直听说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
,是一个对象类型。
// 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);
}
}
它包括两个属性字段message
与pId
,代表下面我们要传递的信息与方便查看的进程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
,通过Service
的onBind
的方法来传递绑定的IBinder
,上面已经说了Stub
继承与Binder
而Binder
实现了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就没什么意义了
来看下效果吧
客户端进程是26951
而服务端进程为26974
,说明他们处于不同的进程,这样AIDL
也就实现了。当然如上所述将process
去掉,就会发现,客户端与服务端的进程就是一样的了。这个就留个读者自行测试了。
源代码下载:https://github.com/idisfkj/AIDLDemo
更多分享请移步:https://idisfkj.github.io