前一篇Android进阶——使用远程服务AIDL实现进程间简单通信小结(一)简单介绍了AIDL的由来和一些基本的语法知识,结合例子相信对于入门AIDL应该没有什么问题了,前篇讲到AIDL应用主要可以分为三大场景:普通AIDL、带有远程回调接口的AIDL和需要引用自定义Parcelable的AIDL,由于篇幅问题仅仅完成了第一个最简单的场景,这篇就完成剩下的两个场景的应用。
package com.crazymo.remoteserver.base;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
/**
* Auther: Crazy.Mo on 2018/5/7 13:09
* Summary:所有远程服务必须继承的父类
*/
public abstract class AbstractService extends Service {
protected IBinder mBinder;
@Nullable
@Override
public IBinder onBind(Intent intent) {
if(mBinder==null){
mBinder=initBinder();
}
return mBinder;//与客户端成功连接上的时候返回给客户端使用的对象
}
protected abstract IBinder initBinder();
}
在Android5.1之后,仅仅通过setAction无法匿名启动服务了而且还会引发异常,需要做处理。
package com.crazymo.client;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.util.Log;
import java.util.List;
/**
* Auther: Crazy.Mo on 2018/5/3 9:35
* Summary:这是为了处理高版本下仅仅通过action 匿名启动服务引发的异常
*/
public class AIDLUtil {
/**
*
* @param pContext
* @param pConnection 实现ServiceConnection接口的类
* @param action 要启动服务的action
* @return
*/
public static boolean bindAIDLService(Context pContext, ServiceConnection pConnection, String action){
boolean isBind=false;
if(pContext!=null && action!=null && pConnection!=null) {
try {
Intent intent = new Intent(getExplicitIntent(pContext, new Intent().setAction(action)));
isBind = pContext.bindService(intent, pConnection, Context.BIND_AUTO_CREATE);
}catch (Exception e){
Log.e("AIDL",e.getMessage());
}
}
return isBind;
}
private static Intent getExplicitIntent(Context context, Intent implicitIntent) {
// Retrieve all services that can match the given intent
PackageManager pm = context.getPackageManager();
List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);
// Make sure only one match was found
if (resolveInfo == null || resolveInfo.size() != 1) {
return null;
}
// Get component info and create ComponentName
ResolveInfo serviceInfo = resolveInfo.get(0);
String packageName = serviceInfo.serviceInfo.packageName;
String className = serviceInfo.serviceInfo.name;
ComponentName component = new ComponentName(packageName, className);
// Create a new intent. Use the old one for extras and such reuse
Intent explicitIntent = new Intent(implicitIntent);
// Set the component to be explicit
explicitIntent.setComponent(component);
return explicitIntent;
}
}
和普通Java 回调接口语法不同,需要单独使用一个AIDL文件来定义回调接口。
// IRemoteCallback.aidl
package com.crazymo.remoteserver.callback;
interface IRemoteCallback {
void afterSpeak(String msg);
}
和不需要回调的接口的AIDL不同,这里必须要定义注册和反注册回调的方法,同时通过import 引入对应的aidl回调
// IRemoteCallbackApi.aidl
package com.crazymo.remoteserver;
//引入回调接口
import com.crazymo.remoteserver.callback.IRemoteCallback;
interface IRemoteCallbackApi {
void speak(String msg);
void registerListener(IRemoteCallback callBack);//注册
void unRegisterListener(IRemoteCallback callBack);//销毁
}
触发回调,需要借助**RemoteCallbackList(一些接口集合用于执行列表中对象的回调函数,主要用于为服务端和客户端通信)**来实现,所谓注册就是将回调接口添加到RemoteCallbackList里,销毁则是从RemoteCallbackList里移除,至于用于在回调的AIDL里做什么不应该由服务提供者来确定,所以无须实现具体业务逻辑。
package com.crazymo.remoteserver.service;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
import com.crazymo.remoteserver.IRemoteCallbackApi;
import com.crazymo.remoteserver.base.AbstractService;
import com.crazymo.remoteserver.callback.IRemoteCallback;
/**
* Auther: Crazy.Mo on 2018/5/7 17:27
* Summary:
*/
public class RemoteCallApiService extends AbstractService {
private final static String TAG="RemoteService";
/**
* 和普通AIDL的Service实现不同,添加的代码中比较关键的有两点:注册和反注册远程回调接口
* 即将回调对象元素加入或移 除mRemoteCallbackList这个对象中的一个集合,执行回调,将回调方法放在需要执行的地方
**/
private static RemoteCallbackList<IRemoteCallback> mRemoteCallbackList = new RemoteCallbackList<>();
@Override
protected IBinder initBinder() {
if(mBinder==null){
mBinder=new RemoteCallBinder();
}
return mBinder;
}
private static final class RemoteCallBinder extends IRemoteCallbackApi.Stub{
@Override
public void speak(String msg) throws RemoteException {
Log.e(TAG,"【服务端】 线程"+Thread.currentThread().getName()+"接到客户端的字符串:"+msg);
try {
Thread.sleep(100);
int count = 0;
Log.e(TAG, "mRemoteCallbackList: " + mRemoteCallbackList+",mRemoteCallbackList.mCallBack:"+mRemoteCallbackList);
/**
*准备开始调用当前注册的回调,这将创建一个回调列表的副本,你可以从使用getBroadcastItem检索条目,而且
* 一次只能激活一个广播,所以你必须确保总是从同一个线程调用这个或者是自己做同步。完成后必须调用finishBroadcast。
* 返回值:callbacks的大小;如果 mBroadcastCount > 0 说明之前调用过beginBroadcast(),则会抛出异常;
**/
count = mRemoteCallbackList.beginBroadcast();
if (count == 0) {
return;
}
try {
for (int i = 0; i < count; i++) {
Log.e(TAG,"【服务端】 线程"+Thread.currentThread().getName()+"触发回调方法");
mRemoteCallbackList.getBroadcastItem(i).afterSpeak("今天晴,23℃");
}
} catch (RemoteException e) {
e.printStackTrace();
} finally {
mRemoteCallbackList.finishBroadcast();//必须执行否则有可能会出现状态不合法异常,mBroadcastCount <0 说明之前掉用过finishBroadcast() 则会抛出异常。
}
} catch (InterruptedException pE) {
pE.printStackTrace();
}
}
@Override
public void registerListener(IRemoteCallback callBack) throws RemoteException {
if (mRemoteCallbackList == null) {
return;
}
mRemoteCallbackList.register(callBack);//注册回调添加到mRemoteCallbackList 里
}
@Override
public void unRegisterListener(IRemoteCallback callBack) throws RemoteException {
if(mRemoteCallbackList!=null) {
mRemoteCallbackList.unregister(callBack);
}
}
}
}
<service android:name=".service.RemoteCallApiService"
android:process=":remotecall">
<intent-filter>
<action android:name="com.crazymo.aidl.callback"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>
无论是本地服务还是远程服务,本质上都是Service机制,按照匿名启动Service的步骤来就可以了,所以AIDL的是使用步骤都是:先连接——>在连接回调里初始化远程接口对象——>再通过远程接口对象调用远程方法
package com.crazymo.client;
import android.app.Activity;
import android.content.ComponentName;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import com.crazymo.remoteserver.IRemoteApi;
import com.crazymo.remoteserver.IRemoteCallbackApi;
import com.crazymo.remoteserver.IUserMgrApi;
import com.crazymo.remoteserver.bean.User;
import com.crazymo.remoteserver.callback.IRemoteCallback;
import java.util.List;
//为了节约篇幅,我没有做重连处理,也省掉了一些逻辑,实际应用中为了健壮性应该根据具体情况参照前一篇普通AIDL应用的例子进行完善
public class MainActivity extends Activity {
private final static String TAG="RemoteService";
private final static String ACTION_CALLBACK="com.crazymo.aidl.callback";
private boolean isUnbindRemoteApiConn=false;
private RemoteApiConn mRemoteApiConn=new RemoteApiConn();//只要是进程通信都需要实现,因为系统是在这个ServiceConnecttion类的相关方法通过回调返回Ibinder对象的
private RemoteCallbackApiConn mCallbackApiConn=new RemoteCallbackApiConn();
private IRemoteCallbackApi mRemoteCallbackApi;
//实现远程回调接口
private IRemoteCallback mIRemoteCallback=new IRemoteCallback.Stub() {
@Override
public void afterSpeak(String msg) throws RemoteException {
Log.e(TAG,"【客户端】线程"+Thread.currentThread().getName()+"远程回调返回的信息:"+msg);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void connCallbackAIDL(View view) {
boolean isBind;
isBind=AIDLUtil.bindAIDLService(this,mCallbackApiConn,ACTION_CALLBACK);
Log.e(TAG,"【客户端】线程"+Thread.currentThread().getName()+"绑定远程带有回调的远程接口(成功true,失败false): "+isBind);
}
public void testCallbackAIDL(View view) {
if(mRemoteCallbackApi!=null){
try {
Log.e(TAG,"【客户端】线程"+Thread.currentThread().getName()+"调用带有回调的远程接口,发送:今天天气怎么样");
mRemoteCallbackApi.speak("今天天气怎么样");
} catch (RemoteException pE) {
pE.printStackTrace();
}
}
}
private final class RemoteCallbackApiConn implements ServiceConnection{
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mRemoteCallbackApi=IRemoteCallbackApi.Stub.asInterface(service);
try {
mRemoteCallbackApi.registerListener(mIRemoteCallback);
} catch (RemoteException pE) {
pE.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
if(mRemoteCallbackApi!=null){
try {
mRemoteCallbackApi.unRegisterListener(mIRemoteCallback);
} catch (RemoteException pE) {
pE.printStackTrace();
}
}
}
}
}
Android 中进程之间的通信最终都是基于Binder机制的,而AIDL是Binder机制中的一部分,进程之间要想通过AIDL 进行数据传输,发送进程方就必须先把数据进行序列化操作,Android 中序列化没有采用Java 传统的Serializer 方案,而是使用了一种性能更高的独有方案Parcelable(因为传统的Serializer方案的序列化和反序列化操作都是由JVM去完成的,对于开发者来说是黑盒的,在做反序列化时需要去遍历根据字节等判断边界,而Parcelable 则是直接提供序列化和反序列化接口方法,这些接口直接通过JNI 调用本地方法存储到Parcel中,最终存储到Native层),总之AIDL传输非基本类型及集合类型数据时都需要通过Parcelable类型。
为了引入实现了Parcelable 的JavaBean到AIDL层,得先建立一个和JavaBean 同名的AIDL文件(一对一的关系),如果需要引入多个JavaBean就得建立多个对应的AIDL文件,这些没有Interface的AIDL文件在编译时就不会生成对应的Java文件,仅仅是起描述Parcelable 类型的作用(不能定义Interface,有多少个Parcelable类型就需要有多少个这种类型的描述AIDL文件)。而且必须要先建立AIDL文件,再去Java 同一包名下建立同名的JavanBean。
// User.aidl
package com.crazymo.remoteserver.bean;
//为了引入JavaBean到AIDL层,得先建立一个和JavaBean 同名的aidl接口(一对一的关系),如果需要引入多个JavaBean就得建立对应的aidl接口
//这个文件的作用是引入了一个序列化对象 User 供其他的AIDL文件使用
//注意:User.aidl与User.java的包名应当是一样的
parcelable User;
package com.crazymo.remoteserver.bean;
import android.os.Parcel;
import android.os.Parcelable;
public class User implements Parcelable {
private String mName;
private int mAge;
public User() {
}
public void setName(String pName) {
mName = pName;
}
public void setAge(int pAge) {
mAge = pAge;
}
protected User(Parcel in) {
mName = in.readString();
mAge = in.readInt();
}
/**
* 这里new User 对象和下面writeToParcel方法的对象不在同一个进程内,不是同一个对象实例,
* 这里是接收进程的User对象实例
*/
public static final Creator<User> CREATOR = new Creator<User>() {
@Override
public User createFromParcel(Parcel in) {
return new User(in);
}
@Override
public User[] newArray(int size) {
return new User[size];
}
};
@Override
public int describeContents() {
return 0;
}
/**
* 序列化时,写的顺序要和下面读的顺序一致
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mName);
dest.writeInt(mAge);
}
public void readFromParcel(Parcel dest) {
mName = dest.readString();
mAge = dest.readInt();
}
@Override
public String toString() {
return "姓名:" + mName + " " + "年龄:" + mAge;
}
}
使用自定义类型前得先通过import 手动引入且作为参数时必须使用修饰符修饰,一般使用in
// IUserMgrApi.aidl
package com.crazymo.remoteserver;
import com.crazymo.remoteserver.bean.User;//引入
// Declare any non-default types here with import statements
interface IUserMgrApi {
void registUser(inout User user);
List<User> getUsers();
}
package com.crazymo.remoteserver.service;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import com.crazymo.remoteserver.IUserMgrApi;
import com.crazymo.remoteserver.base.AbstractService;
import com.crazymo.remoteserver.bean.User;
import java.util.ArrayList;
import java.util.List;
/**
* Auther: Crazy.Mo on 2018/5/7 15:44
* Summary:
*/
public class RemoteUserApiService extends AbstractService {
private final static String TAG="RemoteService";
private List<User> mUserList;
@Override
protected IBinder initBinder() {
if(mBinder==null){
mBinder=new RemoteUserServiceBinder();
}
return mBinder;
}
/**
* 继承AIDL文件里的Stub封装IBinder对象,可以理解为AIDL接口的实现
*/
private final class RemoteUserServiceBinder extends IUserMgrApi.Stub{
@Override
public void registUser(User user) throws RemoteException {
synchronized (this){
if(mUserList==null){
mUserList=new ArrayList<>();
}
if(user!=null){
mUserList.add(user);
Log.e(TAG, "【服务端】 线程"+Thread.currentThread().getName()+"处理客户端的注册请求"+user.toString());
}
}
}
@Override
public List<User> getUsers() throws RemoteException {
synchronized (this){
if(mUserList!=null){
Log.e(TAG, "【服务端】 线程"+Thread.currentThread().getName()+"处理客户端的获取用户集请求返回List集合");
return mUserList;
}else{
return null;
}
}
}
}
}
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.crazymo.remoteserver">
<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=".service.RemoteApiService"
android:process=":remote">
<intent-filter>
<action android:name="com.crazymo.aidl.comm"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>
<service android:name=".service.RemoteUserApiService"
android:process=":remoteuser">
<intent-filter>
<action android:name="com.crazymo.aidl.user"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>
<service android:name=".service.RemoteCallApiService"
android:process=":remotecall">
<intent-filter>
<action android:name="com.crazymo.aidl.callback"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>
</application>
</manifest>
无论是本地服务还是远程服务,本质上都是Service机制,按照匿名启动Service的步骤来就可以了,所以自定义Parcelable的AIDL的是使用步骤都是:先连接——>在连接回调里初始化远程接口对象——>再通过远程接口对象调用远程方法
//这里把三种场景使用的完整代码全都贴在一起,做个对比
package com.crazymo.client;
import android.app.Activity;
import android.content.ComponentName;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import com.crazymo.remoteserver.IRemoteApi;
import com.crazymo.remoteserver.IRemoteCallbackApi;
import com.crazymo.remoteserver.IUserMgrApi;
import com.crazymo.remoteserver.bean.User;
import com.crazymo.remoteserver.callback.IRemoteCallback;
import java.util.List;
public class MainActivity extends Activity {
private final static String TAG="RemoteService";
private final static String ACTION_COMM="com.crazymo.aidl.comm";
private final static String ACTION_PARCE="com.crazymo.aidl.user";
private final static String ACTION_CALLBACK="com.crazymo.aidl.callback";
private boolean isUnbindRemoteApiConn=false;
private RemoteApiConn mRemoteApiConn=new RemoteApiConn();//只要是进程通信都需要实现,因为系统是在这个ServiceConnecttion类的相关方法通过回调返回Ibinder对象的
private RemoteUserApiConn mRemoteUserApiConn=new RemoteUserApiConn();
private RemoteCallbackApiConn mCallbackApiConn=new RemoteCallbackApiConn();
private IRemoteApi mRemoteApi;
private IUserMgrApi mUserMgrApi;
private IRemoteCallbackApi mRemoteCallbackApi;
private IRemoteCallback mIRemoteCallback=new IRemoteCallback.Stub() {
@Override
public void afterSpeak(String msg) throws RemoteException {
Log.e(TAG,"【客户端】线程"+Thread.currentThread().getName()+"远程回调返回的信息:"+msg);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onDestroy() {
super.onDestroy();
disconnectRemoteApiComm();
}
private void disconnectRemoteApiComm() {
unbindService(mRemoteApiConn);
isUnbindRemoteApiConn=true;
}
public void connAIDL(View view) {
connectAIDL();
}
private void connectAIDL() {
boolean isBind;
isBind= AIDLUtil.bindAIDLService(this,mRemoteApiConn,ACTION_COMM);
Log.e(TAG,"【客户端】线程"+Thread.currentThread().getName()+"绑定远程接口(成功true,失败false): "+isBind);
if(!isBind){
Toast.makeText(this,"连接远程服务发生异常",Toast.LENGTH_SHORT).show();
}
}
public void useAIDL(View view) {
if(mRemoteApi==null){
connectAIDL();
}
try {
if(mRemoteApi!=null) {//这里有必要
Log.e(TAG, "【客户端】线程"+Thread.currentThread().getName()+"通过AIDL调用远程接口,客户端:小鸡炖蘑菇");
String result=mRemoteApi.getRemoteStr();
Log.e(TAG, "【客户端】线程"+Thread.currentThread().getName()+"服务端响应请求返回:" +result );
}
} catch (RemoteException pE) {
pE.printStackTrace();
}
}
public void connUserAIDL(View view) {
boolean isBind;
isBind= AIDLUtil.bindAIDLService(this,mRemoteUserApiConn,ACTION_PARCE);
Log.e(TAG,"【客户端】线程"+Thread.currentThread().getName()+"绑定远程用户管理接口(成功true,失败false): "+isBind);
if(!isBind){
Toast.makeText(this,"连接远程用户管理服务发生异常",Toast.LENGTH_SHORT).show();
}
}
public void testUserAIDL(View view) {
try {
User user=new User();
user.setName("CrazyMo");
user.setAge(1);
Log.e(TAG, "【客户端】线程"+Thread.currentThread().getName()+"通过AIDL调用远程用户管理接口注册用户:"+user.toString());
mUserMgrApi.registUser(user);
Log.e(TAG, "【客户端】线程"+Thread.currentThread().getName()+"通过AIDL调用远程用户管理接口查询用户集");
List<User> list=mUserMgrApi.getUsers();
if(list!=null){
for (User tmp :list){
Log.e(TAG, "【客户端】线程"+Thread.currentThread().getName()+"远程用户管理接口返回的用户数据:"+tmp.toString());
}
}
} catch (RemoteException pE) {
pE.printStackTrace();
}
}
public void connCallbackAIDL(View view) {
boolean isBind;
isBind=AIDLUtil.bindAIDLService(this,mCallbackApiConn,ACTION_CALLBACK);
Log.e(TAG,"【客户端】线程"+Thread.currentThread().getName()+"绑定远程带有回调的远程接口(成功true,失败false): "+isBind);
}
public void testCallbackAIDL(View view) {
if(mRemoteCallbackApi!=null){
try {
Log.e(TAG,"【客户端】线程"+Thread.currentThread().getName()+"调用带有回调的远程接口,发送:今天天气怎么样");
mRemoteCallbackApi.speak("今天天气怎么样");
} catch (RemoteException pE) {
pE.printStackTrace();
}
}
}
private final class RemoteApiConn implements ServiceConnection{
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mRemoteApi=IRemoteApi.Stub.asInterface(service);//初始化远端服务对象实例
}
@Override
public void onServiceDisconnected(ComponentName name) {
if(isUnbindRemoteApiConn){
mRemoteApi=null;//主动断开直接置为null
}else{
while(mRemoteApi==null) {
connectAIDL();//做重连
}
}
}
}
private final class RemoteUserApiConn implements ServiceConnection{
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mUserMgrApi= IUserMgrApi.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
mUserMgrApi=null;
}
}
private final class RemoteCallbackApiConn implements ServiceConnection{
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mRemoteCallbackApi=IRemoteCallbackApi.Stub.asInterface(service);
try {
mRemoteCallbackApi.registerListener(mIRemoteCallback);
} catch (RemoteException pE) {
pE.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
if(mRemoteCallbackApi!=null){
try {
mRemoteCallbackApi.unRegisterListener(mIRemoteCallback);
} catch (RemoteException pE) {
pE.printStackTrace();
}
}
}
}
}
AIDL的使用到此基本结束了,要记住AIDL本质上也是一种特殊的Service,所以使用它的基本流程和使用本地服务基本一致先连接——>在连接回调里初始化远程接口对象——>再通过远程接口对象调用远程方法。源码传送门