原创文章,转载请注明出处:http://blog.csdn.net/t5721654/article/details/7480696
Android系统本身提供了很多系统服务,如WindowManagerService,PowerManagerService等。下面描述一下添加一个系统服务的具体步骤。
1、 撰写一个aidl文件,定义服务的接口,将在编译过程中通过aidl工具生成对应的Java接口。一般系统服务的aidl文件都放在framework\base\core\java\android\os目录中。
以我所写的IMyTool.aidl为例。在.aidl中定义自己需要加入的方法,编写规则和java接口差不多,这里不多说。
2、 将aidl文件名添加到frameworks\base\目录下的Android.mk编译脚本文件中。
如:
LOCAL_SRC_FILES += \
core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl\
…\
core/java/android/os/IMyTool.aidl\
…
IMyTool.aidl即我加进去的aidl文件,加入后才能在make过程中编译到,否则将在后面的SystemServer添加系统服务时会报错提示找不到对应类。
3、 编写真正工作的服务类,继承IMyTool.Stub类(AIDL文件名.Stub,aidl生成的接口中的内部类,是一个Binder)。
服务类一般都放在framework\base\services\java\com\android\server目录中。
例如:
public class MyToolService extends IMyTool.Stub {
实现IMyTool.aidl中定义的接口。
}
4、 将自定义服务注册到SystemServer,使得开机过程中被添加。
在framework\base\services\java\com\android\server目录中的SystemServer中启动服务代码处加入:
try {
Slog.i(TAG, “MyToolService”);
ServiceManager.addService(Context.MY_TOOL_SERVICE,new MyToolService(context));// MyToolService构造函数自己定义,一般都会用到Context
} catch(Throwable e) {
Slog.e(TAG, “Failure startingMyToolService”, e);
}
上面代码中Context.MY_TOOL_SERVICE是自己在Context类中定义的常量,也就是给服务定义的名字,使用常量方便获取服务,而不需要记住注册服务时用的名字,且想换名字时只需改一个常量的值。
5、 由于在工程中添加了自己定义的类及常量,系统的api没有更新,因此需要先在工程中make clean然后make update-api,执行完后会发现frameworks\base\api\current.xml文件中多出自己定义的一些东西。 current.xml这个文件包含了所有系统所有能被应用层使用的类及其方法等。
之后再使用make编出来的固件及jar包就能包含自定义的接口。
编译后如何使用:
将编出来的jar包通过lib方式导入工程。jar包位置:out\target\common\obj\JAVA_LIBRARIES\framework_intermediates\classes.jar
调用以下代码获取自定义服务:
IMyTool myTool = IMyTool.Stub.asInterface(ServiceManager.getService(MY_TOOL_SERVICE));
MY_TOOL_SERVICE即在Context中定义的常量。获取到myTool后就可以调用在aidl文件中定义的接口了。
Android Binder机制—-实现自定义的系统服务
一.基于源码分析Binder机制:
Binder机制是Android系统中实现跨进程通信(IPC)的一种重要机制。可以说,Binder机制在android系统中无处不在,所以,要研究android源码,学好Binder机制极其重要。
在学习Binder机制之前,我们先试着摸索一下系统中一些相关的涉及到Binder机制的代码。
首先,先看看SystemServer.Java这个文件(基于android4.0源代码),该文件位于源码路径frameworks\base\services\java\com\android\server\SystemServer.java中,这个文件中有两个类,一个是SystemServer类(public),一个是线程类ServerThread。
SystemServer类的main函数中会先调用函数init1(),代码如下:
[java]
view plain
copy
- public class SystemServer {
- ….
- public static void main(String[] args) {
- ….
- init1(args);
- }
- ….
- }
init1()是native函数,它的定义为native public static void init1(String[] args);它的内部会进行一些与Dalvik虚拟机相关的初始化工作,执行完初始化工作后,其内部会调用java端的SystemServer类的init2()函数。
而SystemServer类中的init2()函数的实现代码如下:
[java]
view plain
copy
- public static final void init2() {
- Slog.i(TAG, “Entered the Android system server!”);
- Thread thr = new ServerThread();
- thr.setName(“android.server.ServerThread”);
- thr.start();
- }
该函数实现创建和启动ServerThread线程。
我们再来看看ServerThread线程类的run()方法中的部分代码,如下:
[java]
view plain
copy
- class ServerThread extends Thread {
- ….
- @Override
- public void run() {
- ….
- // Critical services…
- try {
- Slog.i(TAG, “Entropy Service”);
- ServiceManager.addService(“entropy”, new EntropyService());
- Slog.i(TAG, “Power Manager”);
- power = new PowerManagerService();
- ServiceManager.addService(Context.POWER_SERVICE, power);
- ….
- Slog.i(TAG, “Telephony Registry”);
- ServiceManager.addService(“telephony.registry”, new TelephonyRegistry(context));
- ….
- }
- }
- }
在该线程类的run()函数中,很多的try{}代码块里,有如上类似的代码,它创建一个服务类,然后将初始化的服务类作为参数传进ServiceManager的addService函数中。也就是说,在frameworks\base\services\java\com\android\server源码路径里的服务类基本上都是在这里启动的。
而且这些服务类,一般都是继承于“I服务类名.Stub”(如IVibratorService.Stub,但有些继承Binder),I服务类名(如 IVibratorService)是由aidl文件自动生成的。要想看系统中这些aidl文件生成的类(编译生成),可以在编译后的源码中,out文件 目录下查找。比如,我要找IVibratorService.aidl生成的java文件:进入out目录,在Linux终端输入命令:find -name “IVibratorService.java”。这样就可以找到该类了。
我们再来看看ServiceManager这个类(源码所在路径\frameworks\base\core\java\android\os\ServiceManager.java):
[java]
view plain
copy
- /** @hide */
- public final class ServiceManager {
- private static final String TAG = “ServiceManager”;
- private static IServiceManager sServiceManager;
- private static HashMap<String, IBinder> sCache = new HashMap<String, IBinder>();
- private static IServiceManager getIServiceManager() {
- if (sServiceManager != null) {
- return sServiceManager;
- }
- // Find the service manager
- sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
- return sServiceManager;
- }
- /**
- * Returns a reference to a service with the given name.
- *
- * @param name the name of the service to get
- * @return a reference to the service, or <code>null</code> if the service doesn’t exist
- */
- public static IBinder getService(String name) {
- try {
- IBinder service = sCache.get(name);
- if (service != null) {
- return service;
- } else {
- return getIServiceManager().getService(name);
- }
- } catch (RemoteException e) {
- Log.e(TAG, “error in getService”, e);
- }
- return null;
- }
- /**
- * Place a new @a service called @a name into the service
- * manager.
- *
- * @param name the name of the new service
- * @param service the service object
- */
- public static void addService(String name, IBinder service) {
- try {
- getIServiceManager().addService(name, service);
- } catch (RemoteException e) {
- Log.e(TAG, “error in addService”, e);
- }
- }
- /**
- * Retrieve an existing service called @a name from the
- * service manager. Non-blocking.
- */
- public static IBinder checkService(String name) {
- try {
- IBinder service = sCache.get(name);
- if (service != null) {
- return service;
- } else {
- return getIServiceManager().checkService(name);
- }
- } catch (RemoteException e) {
- Log.e(TAG, “error in checkService”, e);
- return null;
- }
- }
- /**
- * Return a list of all currently running services.
- */
- public static String[] listServices() throws RemoteException {
- try {
- return getIServiceManager().listServices();
- } catch (RemoteException e) {
- Log.e(TAG, “error in listServices”, e);
- return null;
- }
- }
- /**
- * This is only intended to be called when the process is first being brought
- * up and bound by the activity manager. There is only one thread in the process
- * at that time, so no locking is done.
- *
- * @param cache the cache of service references
- * @hide
- */
- public static void initServiceCache(Map<String, IBinder> cache) {
- if (sCache.size() != 0) {
- throw new IllegalStateException(“setServiceCache may only be called once”);
- }
- sCache.putAll(cache);
- }
- }
该类中提供一个getService(String name)的静态函数,参数name对应addService(String name, IBinder service)函数的name。name参数在SystemServer启动一个初始化的服务类,然后调用addService函数添加的时候指定(如ServiceManager.addService(“entropy”, new EntropyService()),name为“entropy”),那么当我们要在另外一个进程里(客户端)获取在SystemServer中启动运行的服务*(服务端),就需要调用ServiceManager类的getService(String name)函数了(如, 我们要在客户端获取EntropyService服务对象,那么就需要调用 ServiceManager.getService(“entropy”)),而通过getService获取的是一个IBinder,接着再通过调用 对应服务类的aidl文件生成的接口的内部类Stub的asInterface(iBinder)方法,(比如,IAccountManager mRemoteService = IAccountManager.Stub.asasInterface(iBinder)),参数iBinder就是通过 getService(String name)获取得到的对应的IBinder。这样,客户端就获取得到远程服务对象的代理,并不是服务类对象本身(具体后面会介绍)。
但是,我们发现,ServiceManager这个类是隐藏的(/** @hide */),也就是说在android SDK中不提供该类的接口。如果在开发的第三方应用中需要调用该类,只能通过反射来调用。
二.注册自定义的系统服务(Service类为客户端服务):
所谓的系统服务指可以使用getSystemService()方法获取的服务,即Binder服务,由继承Binder的类来实现。而我们开发第三方应用所常用的Service类服务为客户端服务。
当然,如果我们有需求,需要系统中注册运行一个自定义的系统服务,该服务在手机开机后执行一直运行着的工作(如,保存一些资源在该服务端,需要时,可以时 刻获取)。那么,通过上面的介绍,我们可以在SystemServer中注册我们自己编写的系统服务。比如:假如MyService是自己定义的需要注册 在SystemServer中的服务类,那么,我们可以在ServerThread类run函数中,try{}代码块添加如下代码: ServiceManager.addService(“myservice”, new MyService());
而 这个我们自定义的MyService,我们该如何去实现它,已到达类似其他系统服务类的效果呢。通过上面的介绍,我们知道,系统服务类一般都继承由 aidl文件生成的一个类的内部类(Stub)。抱着好奇的心态,我们不妨也类试试写一个IMyService的aidl文件,看看它生成的对应的 java文件中的代码实现。关于aidl文件的相关知识,可以去参考这篇博客:http://www.cnblogs.com/over140/archive/2011/03/08/1976890.html;
下面的代码是在eclipse下的一个android工程项目中,建一个名为IMyService的后缀名aidl文件,然后eclipse会自动在工程的gen目录中生成的java代码;
IMyService.aidl文件中的定义:
interface IMyService
{
String getName();
void setName(String name);
}
android工程gen目录中生成的IMyService.java文件代码:
[java]
view plain
copy
- /*
- * This file is auto-generated. DO NOT MODIFY.
- * Original file: D:\\WorkSpace\\RemoteServiceTest\\src\\com\\feixun\\hu\\IMyService.aidl
- */
- //IInterface是一个接口,提供一个asBinder()的方法声明,返回值为IBinder
- public interface IMyService extends android.os.IInterface {
- /** Local-side IPC implementation stub class. */
- //该内部类(一般称为桩)继承Biner,同时实现IMyService接口,也就说覆盖asBinder()方法
- public static abstract class Stub extends android.os.Binder implements
- IMyService {
- private static final java.lang.String DESCRIPTOR = “IMyService”;
- /** Construct the stub at attach it to the interface. */
- public Stub() {
- this.attachInterface(this, DESCRIPTOR);
- }
- /**
- * Cast an IBinder object into an IMyService interface, generating a
- * proxy if needed.
- */
- /*客户端通过调用该方法实现跨进程访问,参数为对应获取得到的远程服务IBinder,
- * 一般是通过ServiceManager.getService获取
- */
- public static IMyService asInterface(android.os.IBinder obj) {
- if ((obj == null)) {
- return null;
- }
- android.os.IInterface iin = (android.os.IInterface) obj
- .queryLocalInterface(DESCRIPTOR);
- //若需要获取的服务为本地服务,则直接返回。
- if (((iin != null) && (iin instanceof IMyService))) {
- return ((IMyService) iin);
- }
- /*若不是本地进程所属服务(即跨进程服务),则返回创建的Proxy对象
- * 传递的参数obj,赋值给Binder驱动中的mRemote引用
- */
- return new IMyService.Stub.Proxy(obj);
- }
- public android.os.IBinder asBinder() {
- return this;
- }
- /*服务端重载的OnTransact,参数code用于标识客户端期望调用服务端的哪个函数
- * 参数data和reply均为包裹,都由客户端提供
- * data负责打包由客户端调用服务端函数时传进的参数
- * reply负责打包执行服务端函数后返回的结果数据,供客户端读取
- * flags定义执行IPC调用的模式,flags=等于0时为双向,即服务端执行完后返回一定的数据,
- * 为1时单向,即服务端执行完后不返回任何数据。
- */
- @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_getName: {
- //某种校验,与客户端的writeInterfaceToken()对应
- data.enforceInterface(DESCRIPTOR);
- /*调用MyService类的getName,该类实现IMyService.Stub
- * 即为真正实现功能的远程服务类,getName方法的具体实现
- * 在MyService中
- */
- java.lang.String _result = this.getName();
- reply.writeNoException();
- //将getName返回值_result写入reply
- reply.writeString(_result);
- return true;
- }
- case TRANSACTION_setName: {
- data.enforceInterface(DESCRIPTOR);
- java.lang.String _arg0;
- //读取客户端发来的data包裹,进行拆解得到客户端传进的相关参数
- _arg0 = data.readString();
- //setName方法没有返回值,所以不需要reply向客户端返回数据
- this.setName(_arg0);
- reply.writeNoException();
- return true;
- }
- }
- return super.onTransact(code, data, reply, flags);
- }
- //Binder驱动实现的代理机制,供远程客户端使用
- private static class Proxy implements IMyService {
- //远程服务端在Binder驱动中对应的Binder引用
- private android.os.IBinder mRemote;
- Proxy(android.os.IBinder remote) {
- //remote来自远程服务端对应的obj
- mRemote = remote;
- }
- public android.os.IBinder asBinder() {
- return mRemote;
- }
- public java.lang.String getInterfaceDescriptor() {
- return DESCRIPTOR;
- }
- public java.lang.String getName() throws android.os.RemoteException {
- //申请得到_data,_reply包裹对象(相当于创建对象)
- android.os.Parcel _data = android.os.Parcel.obtain();
- android.os.Parcel _reply = android.os.Parcel.obtain();
- java.lang.String _result;
- try {
- //与服务端的enforceInterface对应
- _data.writeInterfaceToken(DESCRIPTOR);
- //通过mRemote重载远程服务的onTransact方法,调用服务端的getName()函数
- mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
- _reply.readException();
- //读取远程服务端getName函数返回的_result
- _result = _reply.readString();
- } finally {
- _reply.recycle();
- _data.recycle();
- }
- return _result;
- }
- public void setName(java.lang.String name)
- throws android.os.RemoteException {
- android.os.Parcel _data = android.os.Parcel.obtain();
- android.os.Parcel _reply = android.os.Parcel.obtain();
- try {
- _data.writeInterfaceToken(DESCRIPTOR);
- //往data包裹写入客户端传进的参数name,并发送到服务端
- _data.writeString(name);
- //通过mRemote重载远程服务的onTransact方法,调用服务端的setName()函数
- mRemote.transact(Stub.TRANSACTION_setName, _data, _reply, 0);
- _reply.readException();
- } finally {
- _reply.recycle();
- _data.recycle();
- }
- }
- }
- static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
- static final int TRANSACTION_setName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
- }
- public java.lang.String getName() throws android.os.RemoteException;
- public void setName(java.lang.String name)
- throws android.os.RemoteException;
- }
IMyService.java文件解析:
1.该接口是由自定义的IMyService.aidl文件自动生成的,IMyService实现IInterface接口,IInterface提供一个asBinder()的方法声明,返回值为IBinder。
2.Stub类为IMyService接口的内部类,该内部类(一般称为桩)继承Biner,同时实现IMyService接口,也就说覆盖asBinder()方法。该类主要由服务端来使用。
3.Stub类中又有一个内部类Proxy(代理),该类就是实现跨进程访问的重要类。客户端跨进程获取的代理对象就是Proxy。
4.Proxy类中有一个mRemote,该mRemote为远程服务在Binder对象中对应的引用,通过该引用实现与远程服务的连接。
aidl文件编写完后,接下来就是实现功能代码块的系统服务类的编写了,这里我们将该系统服务实现类命名为MyService,该类的实现代码如下:
[java]
view plain
copy
- import android.os.RemoteException;
- public class MyService extends IMyService.Stub
- {
- private String ServiceName;
- //重载IMyService接口的getName方法
- @Override
- public String getName() throws RemoteException
- {
- // TODO Auto-generated method stub
- return ServiceName;
- }
- //重载IMyService接口的setName方法
- @Override
- public void setName(String name) throws RemoteException
- {
- // TODO Auto-generated method stub
- ServiceName = name;
- }
- }
当然,自定义的系统服务类MyService中的函数,还可以通过调用一些native方法实现调用Android中间层的先关函数(JNI)。ok,系统服务类代码编写完后,接下来就是在ServerThread线程中注册该系统服务了,如下注册代码:
ServiceManager.addService(“myservice”, new MyService());
三.实现跨进程访问自定义的系统服务
系 统服务类的创建和注册已经完成,那么,该如何进行IPC调用呢。很显然,通过前面对IMyService.java文件的介绍和解析,我们知道,重点在于 IMyService.Stub的asInterfcace函数。比如,我们在另外一个进程中编写一个MyServiceManager类来实现获取远程 MyService服务,并实现调用相关的函数。代码如下:
[java]
view plain
copy
- import android.os.IBinder;
- import android.os.ServiceManager;
- public class MyServiceManager
- {
- private static IMyService mRemoteService;
- public static IMyService getService()
- {
- if(mRemoteService == null)
- {
- //参数对应在ServerThread中注册系统服务时指定的参数,即”myservice”
- IBinder iBinder = ServiceManager.getService(“myservice”);
- mRemoteService = IMyService.Stub.asInterface(iBinder);
- }
- return mRemoteService;
- }
- public static String getName()
- {
- getService().getName();
- }
- public static String setName(String name)
- {
- getService().setName(name);
- }
- }
这样,不同进程的客户端就可以通过该类来实现与系统服务的连接和调用了。
注:上 面的相关类实现都是基于源码环境开发的,而不是第三方开发。 MyService.java,IMyService.java,MyServiceManager.java等文件是存放在源码路径 frameworks\base\core\java\android\MyProject中,MyProject是自己创建的文件夹,编译也都是在 Linux环境下基于源码编译的。