Android Studio 配置使用 AIDL 跨应用通信示例
本文参考文献:《疯狂Android讲义 : 第2版 》
本文中的示例参考自 《[android跨进程通信(IPC):使用AIDL](http://blog.csdn.net/singwhatiwanna/article/details/17041691)》
本文中的两个 app 的示例源代码的 main 目录文件下载地址 密码: 2kwk
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 CREATOR = new Parcelable.Creator() { 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.aidlpackage com.example.toby.aidlservice;// Declare any non-default types here with import statementsparcelable Student;
- AIDL 接口文件 IMyService.aidl 代码:
// IMyService.aidlpackage com.example.toby.aidlservice;// Declare any non-default types here with import statementsimport com.example.toby.aidlservice.Student;interface IMyService { List 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 mStudents = new ArrayList<>(); //这里实现了aidl中的抽象函数 private final IMyService.Stub mBinder = new IMyService.Stub() { @Override public List 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"?>
服务端的 AndroidManifest.xml 文件的内容:
<?xml version="1.0" encoding="utf-8"?>
服务端的工程中的文件布局图:
服务端的工程中的文件布局图Client 端代码
正如前面提到的,AIDL 接口定义了两个进程之间的通信接口,因此不仅服务器端需要 AIDL 接口,客户端同样需要前面定义的 AIDL 接口,因此开发客户端的第一步就是将 Service 端的 AIDL 接口文件复制到客户端应用中。本例中我们同时还要复制 Student.java 文件到客户端。
拷贝完成之后的工程结构图如下:
拷贝完成之后的工程结构图** 注意,上图中的 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 资源文件夹下的文件不能有大写字符,会导致R.java无法生成
- Android接口安全 - RSA+AES混合加密方案
- android的文件系统结构简单介绍
- Android NFS 文件系统
- Android接口定义语言---AIDL(一)
- Android中VideoView播放当前工程中视频文件的方法