前言
关于AIDL和Binder已经“听说”好多年了,在实际项目中一直没有使用到,只停留在理论的阶段,最近打算对项目进行多进程拆分尝试,于是对Binder的概念重新梳理一下,发现有些概念早已模糊不清,借此机会重新造个轮子,写一下尝试过程中遇到的问题。
先跳过原理,我们来看看怎么实现最简单的ADIL
假如你还不熟悉ADIL和Binder,请参考Binder学习指南.
这篇文章我看了两遍。本文写的思路和它差不多,只不过带上了实践代码和个人理解,基础概念在上文中已描述的非常清楚了。
顺便推荐下Weishu's Notes的文章,主要讲插件化原理的,每一篇个人都觉得写的很棒;
提供一个Service
编写方法和步骤非常简单:
编写ISwordServiceInterface.aidl;定义提供的接口(也就是你这个Service提供给外部的接口);
在这里我们提供了callActivity()和getServerString()的这两个方法;
// ISwordServiceInterface.aidl
package hook.sword.com.swordserviceserver;
import android.content.Context;
// Declare any non-default types here with import statements
interface ISwordServiceInterface {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void callActivity();
String getServerString();
}
执行gradle build;会在app/build/generated/source/aidl/debug/***/ISwordServiceInterface的java类;
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: /Users/mu/sword/aidlgithub/SwordServiceServer/app/src/main/aidl/hook/sword/ com/swordserviceserver/ISwordServiceInterface.aidl
*/
package hook.sword.com.swordserviceserver;
// Declare any non-default types here with import statements
public interface ISwordServiceInterface extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements hook.sword.com.swordserviceserver.ISwordServiceInterface {
private static final java.lang.String DESCRIPTOR = "hook.sword.com.swordserviceserver.ISwordServiceInterface";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an hook.sword.com.swordserviceserver.ISwordServiceInterface interface,
* generating a proxy if needed.
*/
public static hook.sword.com.swordserviceserver.ISwordServiceInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof hook.sword.com.swordserviceserver.ISwordServiceInterface))) {
return ((hook.sword.com.swordserviceserver.ISwordServiceInterface) iin);
}
return new hook.sword.com.swordserviceserver.ISwordServiceInterface.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 {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_callActivity: {
data.enforceInterface(DESCRIPTOR);
this.callActivity();
reply.writeNoException();
return true;
}
case TRANSACTION_getServerString: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.getServerString();
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements hook.sword.com.swordserviceserver.ISwordServiceInterface {
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;
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override
public void callActivity() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_callActivity, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public java.lang.String getServerString() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getServerString, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_callActivity = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getServerString = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public void callActivity() throws android.os.RemoteException;
public java.lang.String getServerString() throws android.os.RemoteException;
}
这个Java类主要看两个类,一个是Proxy,一个是Stub;
Stub 继承了Binder,具有夸进程的通讯能力(内核赋予的);它是可抽象类,我们需要实现它,然后在Service的onBinder返回它,Service就具有了跨进程通讯能力。
Proxy和它的名字一样,是一个代理类;如果读了Binder学习指南这篇文字,你对这个应该非常熟悉了。简单来说,当使用者通过Binder请求Service服务能力的时候,Binder会返回一个代理对象,使用者调用Proxy的接口,再由Binder通知真正的对象Stub来进行执行。
编写实现类 SwordServiceImpl extends ISwordServiceInterface.Stub;
package hook.sword.com.swordserviceserver;
import android.content.Context;
import android.content.Intent;
import android.os.RemoteException;
/**
* Author: lwh
* Date: 11/2/16 16:49.
*/
public class SwordServiceImpl extends ISwordServiceInterface.Stub {
private Context mContext;
public SwordServiceImpl(Context context){
mContext = context;
}
@Override
public void callActivity() throws RemoteException {
Intent intent = new Intent();
intent.setClass(mContext,ServerActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
}
@Override
public String getServerString() throws RemoteException {
return "Hi,I am from Server!";
}
}
编写 SwordService extends Service ;将onBind实现返回 实现类;
package hook.sword.com.swordserviceserver;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
/**
* Author: lwh
* Date: 11/2/16 16:48.
*/
public class SwordService extends Service {
private SwordServiceImpl mSwordServiceImpl;
@Override
public void onCreate() {
super.onCreate();
mSwordServiceImpl = new SwordServiceImpl(getApplicationContext());
/*Intent intent = new Intent(getApplicationContext(),ServerActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);*/
}
@Override
public IBinder onBind(Intent intent) {
return mSwordServiceImpl;
}
}
在Mainfest声明action;
service android:name="hook.sword.com.swordserviceserver.SwordService"
android:exported="true"
android:process=":CoreService"
>
android:exported=true意味着服务对外部是开放的;
android:process=":CoreService"此服务运行在独立进程,进程名叫CoreService;
完成启动服务
//啟動服務
Intent intent = new Intent();
intent.setClass(this,SwordService.class);
startService(intent);
至此,我们已经编写并完成了一个独立进程Service的运行;它提供了两个接口供外部调用;
接下来我们看外部如何使用。
在这里遇到两个问题比较迷惑一点:
(1)当运行问独立进程的Service之后,在手机的“正在运行”的进程中,只看到一个进程,不知道是什么原理,求解惑~。
(2)当把Service声明为独立进程之后,不适用aidl其实也是可以和本工程的类进行直接相互引用的,感觉上没什么问题,不知道有什么弊端没有。
使用Service
与Service同工程的项目也可以演示调用远程Service,但此时返回的代理对象是本地的,这里为了真正区分夸进程通信,我们建立一个client 工程来调用我们刚才建立的Service,看看是否有效;
拷贝aidl文件
将Server工程的aidl文件夹拷贝到客户端目录一致的地方;然后执行gradle build生成java文件;
这个和server工程的java文件是一模一样的;
启动连接服务
Bundle args = new Bundle();
Intent intent = new Intent("hook.sword.com.swordserviceserver.SwordService");
intent.setPackage("hook.sword.com.swordserviceserver");
intent.putExtras(args);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Log.d(TAG,"連接服務成功:");
mISwordServiceInterface = ISwordServiceInterface.Stub.asInterface(iBinder);
Toast.makeText(getApplicationContext(),"服務連接成功",Toast.LENGTH_SHORT).show();
getString();
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
Log.d(TAG,"連接服務失敗:");
}
};
在这里有一行代码mISwordServiceInterface = ISwordServiceInterface.Stub.asInterface(iBinder)需要解释一下原理:
iBinder是內核传递过来的;
我们来看ISwordServiceInterface.Stub.asInterface这个方法,在生成的那个Java类里:
public static hook.sword.com.swordserviceserver.ISwordServiceInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof hook.sword.com.swordserviceserver.ISwordServiceInterface))) {
return ((hook.sword.com.swordserviceserver.ISwordServiceInterface) iin);
}
return new hook.sword.com.swordserviceserver.ISwordServiceInterface.Stub.Proxy(obj);
}
它的逻辑是告诉Binder找一下本地有没有叫DESCRIPTOR的本地服务,如果有的话,就返回;
如果没有的话,就创建一个代理对象返回;而这个代理对象的接口跟真正代理的接口伪装的一模一样,让使用者用起来感觉在使用真正的服务一样,他们内部通过Binder的transact来传输和交互;
调用服务提供的方法:
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(mISwordServiceInterface!=null){
getString();
}
}
});
findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(mISwordServiceInterface!=null){
callActivity();
}
}
});
private void getString(){
try {
String serverString = mISwordServiceInterface.getServerString();
Toast.makeText(getApplicationContext(),"獲取服務端字符串:"+serverString,Toast.LENGTH_SHORT).show();
Log.d(TAG,"獲取服務端字符串:"+serverString);
}catch (Exception ex){
ex.printStackTrace();
}
}
private void callActivity(){
try{
mISwordServiceInterface.callActivity();
}catch (Exception ex){
ex.printStackTrace();
}
}
Server和Client代码已提交github,欢迎下载
QQ:452825089
mail:452825089@qq.com
wechat:ice3897315
blog:iceAnson.github.io