Android Studio 配置使用 AIDL 跨应用通信示例
本文参考文献:《疯狂Android讲义 : 第2版 》
本文中的示例参考自 《[android跨进程通信(IPC):使用AIDL](http://blog.csdn.net/singwhatiwanna/article/details/17041691)》
本文中的两个 app 的示例源代码的 main 目录文件下载地址 密码: 2kwk
![](https://img.haomeiwen.com/i4011078/45e488ae484526ae.png)
Android 远端调用 Service 是先定义一个远端调用接口,然后为该接口提供一个实现类即可。
客户端访问 Service 时,Android 并不是直接返回 Service 对象给客户端 —— 与本地绑定 Service 时类似,Service 只是将 Service 的代理对象(IBinder对象)通过 onBind() 方法返回给客户端。因此 Android 的 AIDL 远程接口的实现类就是那个 IBinder 实现类。
与绑定本地 Service 不同的是,本地 Service 的 onBind() 方法会直接把 IBinder 对象本身传给客户端的 ServiceConnection 的 onServiceConnected 方法的第二个参数。但远程 Service 的 onBind() 方法只是将 IBinder 对象的代理传给客户端的 ServiceConnection 的 onServiceConnected 方法的第二个参数。
当客户端获取了远程 Service 的 IBinder 对象的代理之后,接下来就可通过该 IBinder 对象去回调远程 Service 的属性或方法了。
创建 AIDL 文件
AIDL 接口定义语言的语法十分简单,这种接口定义语言并不是一种真正的编程语言,它只是定义两个进程之间的通信接口:
- AIDL 定义接口的源代码必须以 .aidl 结尾;
- AIDL 接口中用到数据类型,除了基本类型、String、List、Map、CharSequence 之外,其他类型全部都需要导包,即使它们在同一个包中也需要导包。
AIDL 使用示例
Service 端代码
本示例中要使用的用于提供给客户和服务端使用的 Student.java 类文件的内容:
package com.example.toby.aidlservice;
import java.util.Locale;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Created by toby on 17-4-28.
*/
public final class Student implements Parcelable {
public static final int SEX_MALE = 1;
public static final int SEX_FEMALE = 2;
public int sno;
public String name;
public int sex;
public int age;
public Student() {
}
public static final Parcelable.Creator<Student> CREATOR = new
Parcelable.Creator<Student>() {
public Student createFromParcel(Parcel in) {
return new Student(in);
}
public Student[] newArray(int size) {
return new Student[size];
}
};
private Student(Parcel in) {
readFromParcel(in);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(sno);
dest.writeString(name);
dest.writeInt(sex);
dest.writeInt(age);
}
public void readFromParcel(Parcel in) {
sno = in.readInt();
name = in.readString();
sex = in.readInt();
age = in.readInt();
}
@Override
public String toString() {
return String.format(Locale.ENGLISH, "Student[ %d, %s, %d, %d ]", sno, name, sex, age);
}
}
接下来需要在工程中右键创建 AIDL 文件,并创建如下两个 AIDL 接口文件:
- AIDL 接口文件 Student.aidl 代码:
// Student.aidl.aidl
package com.example.toby.aidlservice;
// Declare any non-default types here with import statements
parcelable Student;
- AIDL 接口文件 IMyService.aidl 代码:
// IMyService.aidl
package com.example.toby.aidlservice;
// Declare any non-default types here with import statements
import com.example.toby.aidlservice.Student;
interface IMyService {
List<Student> getStudent();
void addStudent(in Student student);
}
服务端的 MyService.java 代码:
package com.example.toby.aidlservice;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
/**
* Created by toby on 17-4-28.
*/
public class MyService extends Service {
private final static String TAG = "MyService";
private static final String PACKAGE_CLIENT = "com.example.toby.aidlclient";
private boolean mCanRun = true;
private List<Student> mStudents = new ArrayList<>();
//这里实现了aidl中的抽象函数
private final IMyService.Stub mBinder = new IMyService.Stub() {
@Override
public List<Student> getStudent() throws RemoteException {
synchronized (mStudents) {
return mStudents;
}
}
@Override
public void addStudent(Student student) throws RemoteException {
synchronized (mStudents) {
if (!mStudents.contains(student)) {
mStudents.add(student);
}
}
}
//在这里可以做权限认证,return false意味着客户端的调用就会失败,比如下面,只允许包名为com.example.test的客户端通过,
//其他apk将无法完成调用过程
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
String packageName = null;
String[] packages = MyService.this.getPackageManager().
getPackagesForUid(getCallingUid());
if (packages != null && packages.length > 0) {
packageName = packages[0];
}
Log.d(TAG, "onTransact: " + packageName);
if (!PACKAGE_CLIENT.equals(packageName)) {
return false;
}
return super.onTransact(code, data, reply, flags);
}
};
@Override
public void onCreate() {
Thread thr = new Thread(null, new ServiceWorker(), "BackgroundService");
thr.start();
synchronized (mStudents) {
for (int i = 1; i < 6; i++) {
Student student = new Student();
student.name = "student#" + i;
student.age = i * 5;
mStudents.add(student);
}
}
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, String.format("on bind,intent = %s", intent.toString()));
return mBinder;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
mCanRun = false;
super.onDestroy();
}
class ServiceWorker implements Runnable {
long counter = 0;
@Override
public void run() {
// do background processing here.....
while (mCanRun) {
Log.d("scott", "" + counter);
counter++;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
服务端的 MainActivity.java 是自动生成的代码:
package com.example.toby.aidlservice;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
服务端的 activity_main.xml 是自动生成的代码:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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"
tools:context="com.example.toby.aidlservice.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="This is Service!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
服务端的 AndroidManifest.xml 文件的内容:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.toby.aidlservice">
<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">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name="com.example.toby.aidlservice.MyService"
android:process=":remote"
android:exported="true" >
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<action android:name="com.example.toby.aidlservice.MyService" />
</intent-filter>
</service>
</application>
</manifest>
服务端的工程中的文件布局图:
![](https://img.haomeiwen.com/i4011078/e36f8d7039656f73.png)
Client 端代码
正如前面提到的,AIDL 接口定义了两个进程之间的通信接口,因此不仅服务器端需要 AIDL 接口,客户端同样需要前面定义的 AIDL 接口,因此开发客户端的第一步就是将 Service 端的 AIDL 接口文件复制到客户端应用中。本例中我们同时还要复制 Student.java 文件到客户端。
拷贝完成之后的工程结构图如下:
![](https://img.haomeiwen.com/i4011078/f4ec278bc671a10c.png)
** 注意,上图中的 Student.java 以及两个 AIDL 文件的包名目录结构。 **
客户端绑定远程 Service 与绑定本地 Service 的区别并不大,同样只需要两步:
- 创建 ServiceConnection 对象;
- 以 ServiceConnection 对象作为参数,调用 Context 的 bindService() 方法绑定远程 Service 即可。
与绑定本地 Service 不同的是,绑定远程 Service 的 ServiceConnection 并不能直接获取 Service 的 onBind() 方法所返回的对象,它只能返回 onBind() 方法所返回的对象的代理,因此在 ServiceConnection 的 onServiceConnected 方法中需要通过如下代码进行处理:
mIMyService = IMyService.Stub.asInterface(service);
客户端的 MainActivity.java 代码:
package com.example.toby.aidlclient;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import com.example.toby.aidlservice.IMyService;
import com.example.toby.aidlservice.Student;
public class MainActivity extends AppCompatActivity {
private static final String ACTION_BIND_SERVICE = "com.example.toby.aidlservice.MyService";
private IMyService mIMyService;
private ServiceConnection mServiceConnection = new ServiceConnection()
{
@Override
public void onServiceDisconnected(ComponentName name)
{
mIMyService = null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service)
{
//通过服务端onBind方法返回的binder对象得到IMyService的实例,得到实例就可以调用它的方法了
mIMyService = IMyService.Stub.asInterface(service);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intentService = new Intent(ACTION_BIND_SERVICE);
intentService.setPackage("com.example.toby.aidlservice");
intentService.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
MainActivity.this.bindService(intentService, mServiceConnection, BIND_AUTO_CREATE);
}
public void showDialog(String message)
{
new AlertDialog.Builder(MainActivity.this)
.setTitle("scott")
.setMessage(message)
.setPositiveButton("确定", null)
.show();
Log.d("","Toby===============================");
}
@Override
protected void onDestroy() {
if (mIMyService != null) {
unbindService(mServiceConnection);
}
super.onDestroy();
}
public void btnClick(View view) {
try {
if(mIMyService != null) {
Student student = mIMyService.getStudent().get(0);
showDialog(student.toString());
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
客户端的主布局文件的代码:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.toby.aidlclient.MainActivity">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/button1"
android:text="@string/app_name"
android:onClick="btnClick"
/>
</android.support.constraint.ConstraintLayout>
程序的运行效果如下:
![](https://img.haomeiwen.com/i4011078/6b30b50d42d0b308.png)