Android系统中aidl的理解和service的添加
概述
看看官方文档:
AIDL(Android 接口定义语言)与您可能使用过的其他 IDL 类似。 您可以利用它定义客户端与服务使用进程间通信 (IPC) 进行相互通信时都认可的编程接口。 在 Android 上,一个进程通常无法访问另一个进程的内存。 尽管如此,进程需要将其对象分解成操作系统能够识别的原语,并将对象编组成跨越边界的对象。 编写执行这一编组操作的代码是一项繁琐的工作,因此 Android 会使用 AIDL 来处理。
看到这我们就知道了aidl是什么了:Android 接口定义语言。它可以实现一种通信服务ipc。既然是语言,那么都支持什么数据类型?
// IMyAidlTestInterface.aidlpackage com.sim.aidlTest;// Declare any non-default types here with import statementsinterface IMyAidlTestInterface { /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. * / void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString); }
这是由AndroidStudio自动生成的aidl,可以看到aidl支持的基本类型。int,long,boolean,float,double,String。
默认情况下,AIDL 支持下列数据类型:
- Java 编程语言中的所有原语类型(如 int、long、char、boolean 等等)
- String
- CharSequence
- List
List 中的所有元素都必须是以上列表中支持的数据类型、其他 AIDL 生成的接口或您声明的可打包类型。 可选择将 List 用作“通用”类(例如,List)。另一端实际接收的具体类始终是 ArrayList,但生成的方法使用的是 List 接口。 - Map
Map 中的所有元素都必须是以上列表中支持的数据类型、其他 AIDL 生成的接口或您声明的可打包类型。 不支持通用 Map(如 Map
下面我们来看一下应用层aidl的使用,我认为aidl在使用时主要分为三步:
- 创建aidl接口
- 创建服务端
- 客户端调用
我创建了一个小的demo用来练习aidl的使用,下面是主要的代码:
步骤一:创建aidl接口
package com.sim.aidlTest;// Declare any non-default types here with import statementsinterface IMyAidlInterface { void testMethod(); }
在接口中我只添加了一个方法。
步骤二:创建服务端
创建一个Service,Service中创建一个类继承AIDL接口中的Stub类并实现Stub中的抽象方法,最后不要忘记在onBind中返回这个类的对象。
public class MyAIDLService extends Service { private static final String TAG = "MyAIDLService"; private final IMyAidlInterface.Stub mBinder=new IMyAidlInterface.Stub(){ @Override public void testMethod() throws RemoteException { Log.d(TAG,"testMethod: this is myAIDLTest"); } }; @Override public IBinder onBind(Intent intent) { } }
步骤三:创建客户端
在客户端中绑定该Service,将Service返回的Binder对象转换成AIDL接口所属的类型,接着直接调用AIDL的方法。
private ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.e(TAG, "onServiceConnected"); } @Override mMyAIDL = null; }};
btnStartMethod.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { try { mMyAIDL.testMethod(); } catch (RemoteException e) { Toast.makeText(MainActivity.this, "服务被异常杀死,请重新开启。", Toast.LENGTH_SHORT).show(); } }});
其实在系统中使用aidl只需多一步。
需要在Android.mk中加上aidl的路径。
LOCAL_SRC_FILES += aidl路径
以上就是aidl的使用方式。
这样实现的Service是可以跨应用绑定并通信的,只不过在使用时需要注意的是要将这个aidl文件拷贝到另一个应用中,并且要确保包名不能改变,然后就可以在新应用里绑定原应用的Service了。
添加系统服务
但是看完之后感觉,什么时候在系统中一用aidl?还是不是特别清晰。
接下来介绍一下如何添加系统服务。添加系统服务当然还是用到了aidl,所以我也先从添加aidl开始。
步骤1:在frameworks/base/core/java/android/os/目录下添加一个自定义的aidl文件,我添加了一个名为IMyTestSystemService.aidl的文件。
package android.os;interface IMyTestSystemService{ String getTestMethod();}
因为只是测试,所以只添加了一个方法。
步骤2:在frameworks/base/services/core/java/com/android/server/添加与aidl对应的Service文件
package com.android.server;import android.os.IMyTestSystemService;public class MyTestSystemService extends IMyTestSystemService.Stub { private static String TAG = "MyTestSystemService"; public MyTestSystemService() {} @Override public String getTestMethod() { return TAG+": getTestMethod()"; }}
步骤3:将MyTestSystemService加入到SystemtemServer启动进程。系统服务大都从SystemServer启动,如果想要添加的服务开机就启动那么就需要在SystemServer中添加启动逻辑。
为了方便引用,现在Context.java中添加了一个常量。
public static final String MY_TEST_SYSTEM_SERVICE = “my_test_system”;
然后在
frameworks/base/services/java/com/android/server/SystemServer.java中的startOtherService()方法中添加如下代码:
try { Slog.i(TAG,"My Test SystemService"); myTestService = new MyTestSystemService(); ServiceManager.addService(Context.MY_TEST_SYSTEM_SERVICE, myTestService); } catch (Throwable e) { Slog.e(TAG, "Failure starting My Test SystemService", e); }
4.添加Manager文件frameworks/base/core/java/android/app/MyTestManager.java
public class MyTestManager { IMyTestSystemService mService; public MyTestManager(Context ctx,IMyTestSystemService service){ mService=service; } public String getTestMethod(){ try{ return mService.getTestMethod(); }catch(Exception e){ Log.e("MyTestManager",e.toString()); e.printStackTrace(); } return null; }}
在Manager文件中调用Service的方法
5.在SystemServiceRegistry.java中注册服务
registerService(Context. MY_TEST_SYSTEM_SERVICE, MyTestManager.class, new CachedServiceFetcher() { @Override public MyTestManager createService(ContextImpl ctx) { IBinder b = ServiceManager.getService(Context. MY_TEST_SYSTEM_SERVICE); IMyTestSystemService service = IMyTestSystemService.Stub.asInterface(b); return new MyTestManager (ctx, service); }});
这段代码是添加在SystemServiceRegistry的静态代码块中的
6.在mk文件中添加aidl文件路径
frameworks/base/Android.mk
7.在external/sepolicy/service_contexts中添加:
my_test_system u:object_r:my_test_system_service:s0
8.在external/sepolicy/service.te中添加
service.te主要用来定义我们自己服务的类型,不同厂商的定制可能导致该路径不同在该文件中已经定义了很多service类型,只需要照着画就行了。
type my_test_system_service, system_api_service, system_server_service, service_manager_type;
9.完成上述步骤之后,需要make update-api
然后编译framework.jar,services.jar,以及boot.img。
在替换了新编译的文件后,可以自己进行测试,测试代码如下:
MyTestManager mTestManager = (MyTestManager)getSystemService(Context.MY_TEST_SYSTEM_SERVICE); Log.e(“xijun","mTestManager : "+mTestManager); Log.e(“xijun","mTestManager.getTestMethod() = "+mTestManager.getTestMethod());
获取到MyTestManager 对象后,通过调用其内部的方法来验证是否成功。
也可以通过命令 adb shell services list 来获取系统服务列表,查看自定义服务是否存在。
getSystemService()的流程
接下来我想说明一下getSystemService()的流程,在Activity内我们可以直接调用getSystemService()来获得系统服务,由于Activity继承自Context,所以这个方法实际上是Context留出来的接口。
Context源码:
public abstract Object getSystemService(@ServiceName @NonNull String name);
而这个方法的实现是写在ContextImpl里的,源码如下:
public Object getSystemService(String name) { return SystemServiceRegistry.getSystemService(this, name); }
SystemServiceRegistry这个类就是我们注册服务的类,其内部实现如下
public static Object getSystemService(ContextImpl ctx, String name) { ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name); return fetcher != null ? fetcher.getService(ctx) : null; }
这里我们可以看到他是从SYSTEM_SERVICE_FETCHERS中取出了一个ServiceFetcher类型的对象。
而在源码中SYSTEM_SERVICE_FETCHERS是一个HashMap,其定义如下
private static final HashMap<?>> SYSTEM_SERVICE_FETCHERS = new HashMap<?>>();
而向这个Map中put值的方法如下:
private static void registerService(String serviceName, Class serviceClass, ServiceFetcher serviceFetcher) { SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName); SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher); }
这个方法就是我们注册服务的方法,也就是说如果想要在getSystemService时找到自定义的服务就需要在此处进行注册。
同时我们也看到这个HashMap中键值是servicename与serviceFetcher对象。这个ServiceFetcher是SystemServiceRegistry类中的一个接口,其内容如下:
static abstract interface ServiceFetcher<T> { T getService(ContextImpl ctx);}
而我们在注册时传入的是一个CachedServiceFetcher对象,这是实现了ServiceFetcher接口的内部类
static abstract class CachedServiceFetcher<T> implements ServiceFetcher<T> { private final int mCacheIndex; public CachedServiceFetcher() { mCacheIndex = sServiceCacheSize++; } @Override @SuppressWarnings("unchecked") public final T getService(ContextImpl ctx) { final Object[] cache = ctx.mServiceCache; synchronized (cache) { // Fetch or create the service. Object service = cache[mCacheIndex]; if (service == null) { service = createService(ctx); cache[mCacheIndex] = service; } return (T)service; } } public abstract T createService(ContextImpl ctx); }
我们在创建CachedServiceFetcher时实现了其CreateService方法,SystemServiceRegistry中getSystemService()方法中调用到fetcher.getService(ctx)时,就会执行createService方法,也就是在这时创建了MyTestManager对象
总结
aidl的使用我认为可以把它分为两类:
一种是我们在应用层开发中的关于两个应用之间进程互相调用。
一种是系统开发中的进程间信息通讯。比如内核层面的进程和应用之间的调用。
比如添加系统服务这种。是系统开发之后供第三方应用调用。像上面模拟的getSystemService调用。提供了接口,让所有第三方调用。
但是为什么用这种方法,第三方应用而不是直接用Jni来直接调取?其实系统提供的这种getSystemService是提供给众多第三方的应用的。如果用Jni这种,只是会和其中的某一个第三方调用,其他第三方应用并没有提供接口来调用。
这是我的理解,如有错误之处,敬请指出。
相关文章:
你真的理解Android AIDL中的in,out,inout么?
Android:学习AIDL,这一篇文章就够了
更多相关文章
- android版PDA通过USB与.net应用程序通讯,实现离线版android应用同
- Android下的Java之interface接口泛型 动态获取泛型的类型
- 【android】音乐播放器之service服务设计
- 基于Android移动终端的搜索客户端应用【团队项目】
- Android:学习AIDL,这一篇文章就够了(下)
- Android无障碍服务三 创建辅助功能服务
- Android(安卓)错误信息捕获发送至服务器【整理】
- Android学习笔记:服务(Service)
- 个人简历制作——Android自动升级&个人“服务器”搭建