Android系统中aidl的理解和service的添加

概述

看看官方文档:
AIDL(Android 接口定义语言)与您可能使用过的其他 IDL 类似。 您可以利用它定义客户端与服务使用进程间通信 (IPC) 进行相互通信时都认可的编程接口。 在 Android 上,一个进程通常无法访问另一个进程的内存。 尽管如此,进程需要将其对象分解成操作系统能够识别的原语,并将对象编组成跨越边界的对象。 编写执行这一编组操作的代码是一项繁琐的工作,因此 Android 会使用 AIDL 来处理。

看到这我们就知道了aidl是什么了:Android 接口定义语言。它可以实现一种通信服务ipc。既然是语言,那么都支持什么数据类型?


// IMyAidlTestInterface.aidl
package com.sim.aidlTest;
// Declare any non-default types here with import statements
interface 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在使用时主要分为三步:

  1. 创建aidl接口
  2. 创建服务端
  3. 客户端调用

我创建了一个小的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-)