目录:
- 什么是SystemService
- SystemService使用实例Vibrator
- System Service实现流程
- System Service 新加接口
- 应用层与 System Service 通信方式
- 实现 System Service 的注意事项
什么是SystemService
我们在Android开发过程中经常会用到各种各样的系统管理服务,如进行窗口相关的操作会用到窗口管理服务WindowManager
,进行电源相关的操作会用到电源管理服务PowerManager
,还有很多其他的系统管理服务,如通知管理服务NotifacationManager
、振动管理服务Vibrator
、电池管理服务BatteryManager
…… 这些Manager提供了很多对系统层的控制接口。对于App开发者,只需要了解这些接口的使用方式就可以方便的进行系统控制,获得系统各个服务的信息,而不需要了解这些接口的具体实现方式。而对于Framework开发者,则需要了解这些Manager服务的常用实现模式,维护这些Manager的接口,扩展这些接口,或者实现新的Manager。
SystemService使用实例Vibrator
我们从一个简单的系统服务Vibrator
服务来看一下一个系统服务是怎样建立的。Vibrator
服务提供的控制手机振动的接口,应用可以调用Vibrator
的接口来让手机产生振动,达到提醒用户的目的。
从Android的官方文档中可以看到Vibrator
只是一个抽象类,只有4个抽象接口:
- abstract void cancel() 取消振动
- abstract boolean hasVibrator() 是否有振动功能
- abstract void vibrate(long[] pattern, int repeat) 按节奏重复振动
- abstract void vibrate(long milliseconds) 持续振动
应用中使用振动服务的方法也很简单,如让手机持续振动500毫秒:
Vibrator mVibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
mVibrator.vibrate(500);
Vibrator
使用起来很简单,我们再来看一下实现起来是不是也简单。
从文档中可以看到Vibrator
只是定义在android.os
包里的一个抽象类,在源码里的位置即frameworks/base/core/java/android/os/Vibrator.java,那么应用中实际使用的是哪个实例呢?应用中使用的Vibrator
实例是通过Context的一个方法getSystemService(Context.VIBRATOR_SERVICE)
获得的,而Context
的实现一般都在ContextImpl
中,那我们就看一下ContextImpl
是怎么实现getSystemService
的:
frameworks/base/core/java/android/app/ContextImpl.java
@Override
public Object getSystemService(String name) {
ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
return fetcher == null ? null : fetcher.getService(this);
}
SYSTEM_SERVICE_MAP
是一个HashMap
,通过我们服务的名字name
字符串,从这个HashMap
里取出一个ServiceFetcher
,再return
这个ServiceFetcher
的getService()
。ServiceFetcher
是什么?它的getService()
又是什么?既然他是从SYSTEM_SERVICE_MAP
这个HashMap
里get
出来的,那就找一找这个HashMap
都put
了什么。
通过搜索ContextImpl.java可以找到如下代码:
private static void registerService(String serviceName, ServiceFetcher fetcher) {
if (!(fetcher instanceof StaticServiceFetcher)) {
fetcher.mContextCacheIndex = sNextPerContextServiceCacheIndex++;
}
SYSTEM_SERVICE_MAP.put(serviceName, fetcher);
}
这里往SYSTEM_SERVICE_MAP
里put
了一对String
与ServiceFetcher
组成的key/value对,registerService()
又是从哪里调用的?继续搜索可以发现很多类似下面的代码:
// ContextImpl.java
static {
...
registerService(Context.VIBRATOR_SERVICE, Vibrator.class,
new CachedServiceFetcher<Vibrator>() {
@Override
public Vibrator createService(ContextImpl ctx) {
return new SystemVibrator(ctx);
}});
...
SystemServiceRegistry
的static
代码块里通过registerService
注册了很多的系统服务,其中就包括我们正在调查的VIBRATOR_SERVICE
,通过结合上面的分析代码可以可以知道getSystemService(Context.VIBRATOR_SERVICE)
得到的是一个SystemVibrator
的实例,通过查看SystemVibrator
的代码也可以发现SystemVibrator
确实是继承自Vibrator
:
public class SystemVibrator extends Vibrator {
...
}
我们再从SystemVibrator
看一下系统的振动控制是怎么实现的。以hasVibrator()
为例,这个是查询当前系统是否能够振动,在SystemVibrator
中它的实现如下:
public boolean hasVibrator() {
...
try {
return mService.hasVibrator();
} catch (RemoteException e) {
}
...
}
这里直接调用了一个mService.hasVibrator()
。mService
是什么?哪来的?搜索一下可以发现:
private final IVibratorService mService;
public SystemVibrator(Context context) {
...
mService = IVibratorService.Stub.asInterface(
ServiceManager.getService("vibrator"));
}
mService
是一个IVibratorService
,我们先不去管IVibratorService.Stub.asInterface
是怎么回事,先看一下IVibratorService
是什么。搜索一下代码发现这并不是一个java文件,而是一个aidl文件:
frameworks/base/core/java/android/os/IVibratorService.aidl
AIDL (Android Interface Definition Language) 是Android中的接口定义文件,为系统提供了一种简单跨进程通信方法。IVibratorService
中定义了几个接口,SystemVibrator
中使用的也是这几个接口,包括我们刚才使用的hasVibrator()
interface IVibratorService
{
boolean hasVibrator();
void vibrate(...);
void vibratePattern(...);
void cancelVibrate(IBinder token);
}
这里又只是接口定义,接口实现在哪呢?通过在frameworks/base目录下进行grep搜索,可以发现IVibratorService
接口的实现在frameworks/base/services/java/com/android/server/VibratorService.java
public class VibratorService extends IVibratorService.Stub
可以看到 VibratorService
实现了IVibratorService
定义的所有接口,并通过JNI调用到native层,进行更底层的实现。更底层的实现不是这篇文档讨论的内容,我们需要分析的是VibratorService
怎么成为系统服务的。那么VibratorService
是怎么注册为系统服务的呢?在SystemServer
里面:
VibratorService vibrator = null;
...
//实例化VibratorService并添加到ServiceManager
traceBeginAndSlog("StartVibratorService");
vibrator = new VibratorService(context);
ServiceManager.addService("vibrator", vibrator);
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
...
//通知服务系统启动完成
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "MakeVibratorServiceReady");
try {
vibrator.systemReady();
} catch (Throwable e) {
reportWtf("making Vibrator Service ready", e);
}
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
这样在SystemVibrator
里就可以通过下面的代码连接到VibratorService
,与底层的系统服务进行通信了:
IVibratorService.Stub.asInterface(ServiceManager.getService("vibrator"));
mService
相当于IVibratorService
在应用层的一个代理,所有的实现还是在SystemServer
的VibratorService
里。
看代码时可以发现registerService
是在static
代码块里静态调用的,所以getSystemServcr
获得的各个Manager也都是单例的。
System Service实现流程
从上面的分析,我们可以总结出Vibrator服务的整个实现流程:
- 定义一个抽象类
Vibrator
,定义了应用中可以访问的一些抽象方法
frameworks/base/core/java/android/os/Vibrator.java - 定义具体的类
SystemVibrator
继承Vibrator
,实现抽象方法
frameworks/base/core/java/android/os/SystemVibrator.java - 定义一个AIDL接口文件
IVibratorService.aidl
,定义系统服务接口
frameworks/base/core/java/android/os/IVibratorService.aidl - 定义服务
VibratorService
,实现IVibratorService
定义的接口
frameworks/base/services/java/com/android/server/VibratorService.java
public class VibratorService extends IVibratorService.Stub
- 将VibratorServicey添加到系统服务
frameworks/base/services/java/com/android/server/SystemServer.java
VibratorService vibrator = null;
...
//实例化VibratorService并添加到ServiceManager
Slog.i(TAG, "Vibrator Service");
vibrator = new VibratorService(context);
ServiceManager.addService("vibrator", vibrator);
...
//通知服务系统启动完成
try {
vibrator.systemReady();
} catch (Throwable e) {
reportWtf("making Vibrator Service ready", e);
}
- 在
SystemVibrator
中通过IVibratorService
的代理连接到VibratorService
,这样SystemVibrator
的接口实现里就可以调用IVibratorService
的接口:
frameworks/base/core/java/android/os/SystemVibrator.java
private final IVibratorService mService;
...
public SystemVibrator() {
...
mService = IVibratorService.Stub.asInterface(
ServiceManager.getService("vibrator"));
...
public boolean hasVibrator() {
...
try {
return mService.hasVibrator();
} catch (RemoteException e) {
}
...
}
}
- 在
Context
里定义一个代表Vibrator服务的字符串
frameworks/base/core/java/android/content/Context.java
public static final String VIBRATOR_SERVICE = "vibrator";
- 注册
SystemVibrator
,在ContextImpl
里添加SystemVibrator
的实例化过程
frameworks/base/core/java/android/app/ContextImpl.java
registerService(VIBRATOR_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
return new SystemVibrator(ctx);
}});
- 在应用中使用
Vibrator
的接口
Vibrator mVibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
mVibrator.vibrate(500);
- 为保证编译正常,还需要将AIDL文件添加到编译配置里
frameworks/base/Android.mk
LOCAL_SRC_FILES += \
...
core/java/android/os/IVibratorService.aidl \
System Service 新加接口
如果我们需要实现一个新的系统服务,就可以按照上面的步骤在系统中扩展出一个新的服务,并给应用层提供出使用接口。如果想在Vibrator
里添加一个新的接口,需要下面3步:
- 在
IVibratorService.aidl
添加接口; - 在
VibratorService
添加接口的实现; - 在
Vibrator
及SystemVibrator
里扩展新的接口;
这样应用中就可以使用Vibrator
的新接口了。
应用层与 System Service 通信方式
上面的实现我们看到的只是从应用层通过服务代理,调用系统服务的接口,如果我们想反过来,将系统服务的状态通知给应用层,有以下几种方式:
1. 使用Broadcast
我们知道使用Broadcast广播可以实现跨进程的消息传递,一些系统服务也使用了这种方法。如电池管理服务BatteryManagerService
,收到底层上报的电池状态变化信息时,就将当前的电池状态封装在一个Intent
里,action
为android.intent.action.BATTERY_CHANGED
。应用只要注册一个对应的BroadcastReceiver
就可以收到BatterManagerService
发送的电池状态信息。
2.使用AIDL
从上面我们可以知道,通过AIDL
定义一套接口,由系统服务端实现这些接口,应用端使用一个相应的代理就可以访问系统服务的接口,那反过来让应用端实现AIDL
接口,系统服务端使用代理调用应用端的接口可不可以呢?答案是YES。那么接下来的问题是怎么让系统服务得到这个代理。我们再来看一个LocationManager
的例子。
LocationManager
是系统的定位服务,应用通过LocationManager
可以获得设备当前的地理位置信息。下面是LocationManager
的使用代码片段:
//获得定位服务
LocationManager locationManager =
(LocationManager) getSystemService(Context.LOCATION_SERVICE);
//定义定位监听器
LocationListener locationListener = new LocationListener() {
public void onLocationChanged(Location location) {
//监听到位置信息
}
...
};
//注册监听器
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
0, 0, locationListener);
从上面的代码可以看到,我们创建了一个位置监听器LocationListener
,并将这个监听器在LocationManager
里进行了注册。当系统定位到系统的位置后,就会回调监听器的onLocationChanged()
,将位置信息通知给监听器。LocationListener
就是一个系统服务调用应用层接口的例子,我们就研究一下LocationListener
的实现方式。
我们先从LocationManager
怎么注册LocationListener
开始研究:
frameworks/base/location/java/android/location/LocationManager.java
private final ILocationManager mService;
...
private void requestLocationUpdates(LocationRequest request,
LocationListener listener, Looper looper, PendingIntent intent) {
...
// wrap the listener class
ListenerTransport transport = wrapListener(listener, looper);
try {
mService.requestLocationUpdates(request, transport,
intent, packageName);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException", e);
}
}
可以看到LocationListener
被重新封装成了一个ListenerTransport
,然后传递给了ILocationManager
,从前面的分析可以猜测到这个ILocationManager
应该就是LocationManagerService
的一个代理。那么ListenerTransport
又是什么呢?搜索LocationManager.java可以找到:
private class ListenerTransport extends ILocationListener.Stub {
...
@Override
public void onLocationChanged(Location location) {
...
}
}
原来ListenerTransport
是ILocationListener.Stub
的一个继承实现,那么ILocationListener
应该就是一个AIDL接口定义:
frameworks/base/location/java/android/location/ILocationListener.aidl
oneway interface ILocationListener
{
void onLocationChanged(in Location location);
...
}
而在LocationManagerService
里只要调用ILocationListener
的方法就可以将消息传递给应用层的监听:
mListener.onLocationChanged(new Location(location));
实现 System Service 的注意事项
1. 注意防止阻塞
应用层访问系统服务提供的接口时会有两种情况:
- 一种是应用调用端需要等待服务实现端处理完成,返回处理结果,这样如果服务端发生阻塞,那么应用端也会发生阻塞,因此在实现服务端的实现时要注意不要发生阻塞。
- 另一种是调用端不需要等待服务端返回结果,调用完成后直接返回
void
,这样服务端发生阻塞不会影响到应用端,这样的单向的接口在AIDL
里定义时需要添加oneway
关键字,如:
oneway void statusBarVisibilityChanged(int visibility);
对于需要在服务端调用,在应用端实现的接口,考虑到系统的稳定性以及安全性,一般都会设计成上面的第二种,即AIDL里所有的接口都是单向的,如上面的ILocationListener
oneway interface ILocationListener
2.注意多线程访问
每个系统服务在系统进程中只有一个实例,而且应用中系统服务的代理也是单例的,而且应用端的访问,在系统进程都是使用独立的线程进行响应,所以访问同一个系统服务的接口时必然会出现多个线程或者多个进程同时访问的情况。为保证系统服务的线程安全,需要对系统服务的进程进行多线程访问的保护,目前主要有两种实现线程安全的方法:
- 一种是通过同步锁机制,锁住一个对象实例(一般是这个服务对象本身),这样这个服务同一时间只能响应一个访问请求,如
LocationManagerService
里:
public boolean callStatusChangedLocked(...) {
...
synchronized (this) {
...
}
}
- 另一种方法就是使用
Handler
机制,这种服务一般会创建一个单独的线程,当有应用端访问请求到来时会向服务线程的Handler
里发送一个Message
,利用单线程顺序执行的特性,保证所有的访问都按顺序进行处理,但这种方法只适合单向的访问,不适合需要返回的双向访问。