目录
一、AIDL理解
每个应用程序都运行在自己的独立进程中,并且可以启动另一个应用进程的服务,而且经常需要在不同的进程间传递数据对象。
在Android平台,一个进程不能直接访问另一个进程的内存空间,所以要想对话,需要将对象分解成操作系统可以理解的基本单元,并且有序的通过进程边界。
AIDL(Android Interface Definition Language)
用于生成可以在Android设备上两个进程之间进行进程间通信(interprocess communication,IPC)的代码。
如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序列化的参数。
参考官网介绍: https://developer.android.com/guide/components/aidl
二、使用方法
使用AIDL进行进程间通信实例(远程Service):客户端通过绑定服务,并获取服务端返回的IBinder 对象, 得到服务端定义的AIDL 的实例对象,可以对服务端进行调用操作。
实例如图:
RemoteService (服务端)
1. 新建一个工程 (选则No Activity), 工程名为 RemoteService
2. 在src/main/java 目录的主包下, 创建一个文件名为RemoteService.java, 它继承 Service
3. src/main 目录的主包下, 右键 new -> AIDL -> 输入文件名为 Student, Student.aidl 在 src/main/aidl 目录下
// Student.aidl
package com.example.remoteservice;
// Declare Student so AIDL can find it and knows that it implements
// the parcelable protocol.
parcelable Student;
4. src/main/java 目录的主包下, 右键 new -> Java Class -> 输入文件名为 Student, 并实现Parcelable接口( 进程间传递非默认类型的对象)
并实现打包数据(writeToParcel) 和 解包对象Creator
package com.example.remoteservice;
import android.os.Parcel;
import android.os.Parcelable;
public class Student implements Parcelable {
private int id;
private String name;
private float score;
public Student(int id, String name, float score) {
this.id = id;
this.name = name;
this.score = score;
}
protected Student(Parcel in) {
id = in.readInt();
name = in.readString();
score = in.readFloat();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public float getScore() {
return score;
}
public void setScore(float score) {
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", score=" + score +
'}';
}
@Override
public int describeContents() {
return 0;
}
//打包, 创建一个Student 对象
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeString(name);
dest.writeFloat(score);
}
//解包, 创建一个Student 对象
public static final Creator<Student> CREATOR = new Creator<Student>() {
@Override
public Student createFromParcel(Parcel in) {
return new Student(in);
}
@Override
public Student[] newArray(int size) {
return new Student[size];
}
};
}
5. src/main 目录的主包下, 右键 new -> AIDL -> IStudentService, 会有IStudentService.aidl 在 src/main/aidl 目录下, 并手动导入Student 类
// IStudentService.aidl
package com.example.remoteservice;
// Declare any non-default types here with import statements
// Studio 自动提示需要导入非默认类型的类
import com.example.remoteservice.Student;
interface IStudentService {
Student getStudentById(int id); // 需要手动import Student , 否则报错:.1-13: Failed to resolve 'Student'
}
6. Build -> Clean project -> 成功后, Build -> Make project ,成功后
则在目录:RemoteService\app\build\generated\aidl_source_output_dir\debug\out, 自动生成 IStudentService接口文件
注意它的内部静态类 Stub 和 asInterface() 方法, 其中前者供服务端在onBind时返回, 或者供客服端获取服务端的服务实例对象
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package com.example.remoteservice;
public interface IStudentService extends android.os.IInterface
{
/** Default implementation for IStudentService. */
public static class Default implements com.example.remoteservice.IStudentService
{
@Override public com.example.remoteservice.Student getStudentById(int id) throws android.os.RemoteException
{
return null;
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.example.remoteservice.IStudentService
{
private static final java.lang.String DESCRIPTOR = "com.example.remoteservice.IStudentService";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.remoteservice.IStudentService interface,
* generating a proxy if needed.
*/
public static com.example.remoteservice.IStudentService asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.remoteservice.IStudentService))) {
return ((com.example.remoteservice.IStudentService)iin);
}
return new com.example.remoteservice.IStudentService.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
{
java.lang.String descriptor = DESCRIPTOR;
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getStudentById:
{
data.enforceInterface(descriptor);
int _arg0;
_arg0 = data.readInt();
com.example.remoteservice.Student _result = this.getStudentById(_arg0);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.example.remoteservice.IStudentService
{
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 com.example.remoteservice.Student getStudentById(int id) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.example.remoteservice.Student _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(id);
boolean _status = mRemote.transact(Stub.TRANSACTION_getStudentById, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getStudentById(id);
}
_reply.readException();
if ((0!=_reply.readInt())) {
_result = com.example.remoteservice.Student.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
public static com.example.remoteservice.IStudentService sDefaultImpl;
}
static final int TRANSACTION_getStudentById = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
public static boolean setDefaultImpl(com.example.remoteservice.IStudentService impl) {
// Only one user of this interface can use this function
// at a time. This is a heuristic to detect if two different
// users in the same process use this function.
if (Stub.Proxy.sDefaultImpl != null) {
throw new IllegalStateException("setDefaultImpl() called twice");
}
if (impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static com.example.remoteservice.IStudentService getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
public com.example.remoteservice.Student getStudentById(int id) throws android.os.RemoteException;
}
7. RemoteService.java 中创建 Binder 对象, 即创建 IStudentService.stub (Binder的子类)的对象 并实现 AIDL中声明的通信接口, 并把 Binder 对象在onBind返回
package com.example.remoteservice;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import androidx.annotation.Nullable;
public class RemoteService extends Service {
private static final String TAG = "RemoteService";
@Override
public void onCreate() {
super.onCreate();
log("onCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
log("onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
log("onBind");
return binder;
}
private final IStudentService.Stub binder = new IStudentService.Stub() {
@Override
public Student getStudentById(int id) throws RemoteException {
return new Student(id, "Tom", 75.0f);
}
};
@Override
public boolean onUnbind(Intent intent) {
log("onUnbind");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
log("onDestroy");
}
private void log(String msg){
Log.d(TAG, msg);
}
}
8. AndroidManifest 中定义service 的action
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.remoteservice">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme" >
<service android:name=".RemoteService">
<intent-filter>
<action android:name="com.example.remoteservice.bindservice"/>
</intent-filter>
</service>
</application>
</manifest>
ServiceClient(客户端)
1. 创建一个 Empty Activity 的工程Serviceclient
2. 布局xml( 提供 绑定按钮、 解绑按钮、调用远程服务方法按钮 以及 id 输入框)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:layout_width="match_parent"
android:layout_height="60dp"
android:onClick="bindRemoteService"
android:text="bind remote service"/>
<EditText
android:id="@+id/edit_text"
android:layout_width="match_parent"
android:layout_height="60dp"/>
<Button
android:layout_width="match_parent"
android:layout_height="60dp"
android:onClick="invokeRemoteService"
android:text="调用远程服务端的方法"/>
<Button
android:layout_width="match_parent"
android:layout_height="60dp"
android:onClick="unbindRemoteService"
android:text="unbind remote service"/>
</LinearLayout>
其中 onClick 对应的方法,会在Activity 中实现
3. 拷贝 RemoteService 中 src/main 下 aidl文件夹, 到工程 Serviceclient 的src/main 目录下, 即生成新的目录 src/main/aidl
4. 拷贝 RemoteService 中 的Student.java 到工程中Serviceclient, 注意包名(即路径要一样):src/main/java/com/example/remoteservice
也就是在RemoteService 中 aidl相关的文件拷贝一份到ServiceClient 中, 并且相对路径需要一致
5. Activity中 实现绑定、解绑、调用服务操作
package com.example.serviceclient;
import androidx.appcompat.app.AppCompatActivity;
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.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import com.example.remoteservice.IStudentService;
import com.example.remoteservice.Student;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private IStudentService mService = null;
private ServiceConnection mServiceConnection;
private EditText mEditText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mEditText = findViewById(R.id.edit_text);
}
public void bindRemoteService(View view) {
if (mServiceConnection == null) {
log("bindRemoteService");
mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
log("bindRemoteService -onServiceConnected");
mService = IStudentService.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
log("bindRemoteService -onServiceDisconnected");
mService = null;
}
};
//需要显示调用
Intent intent = new Intent("com.example.remoteservice.bindservice");
intent.setPackage("com.example.remoteservice");
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
}
}
public void invokeRemoteService(View view) {
String id = mEditText.getText().toString();
if (mService == null || TextUtils.isEmpty(id)) {
log("invokeRemoteService but service is null or id is empty");
return;
}
try {
Student student = mService.getStudentById(Integer.parseInt(id));
if (student != null) {
log("invokeRemoteService success: " + student.toString());
} else {
log("invokeRemoteService but student is null");
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void unbindRemoteService(View view) {
if (mServiceConnection != null) {
unbindService(mServiceConnection);
mServiceConnection = null;
mService = null;
log("unbindRemoteService successfully");
} else {
log("already unbindRemoteService, no need");
}
}
private void log(String message) {
Log.d(TAG, message);
}
}
其中Service 的绑定需要显示调用,即创建一个带Action(在RemoteService 的AndroidManifest 声明时,定义的action) 的Intent, 并且设置它的包名(setPackage),
或者设置类名(包名+带详细路径的类名):
Intent setClassName(@NonNull String packageName, @NonNull String className)
否则会报错:
Caused by: java.lang.IllegalArgumentException: Service Intent must be explicit: Intent { act=com.example.remoteservice.bindservice }
参考: https://stackoverflow.com/questions/27842430/service-intent-must-be-explicit-intent