当前位置: 移动技术网 > IT编程>移动开发>Android > Android系统服务(SystemService)简介

Android系统服务(SystemService)简介

2020年03月09日  | 移动技术网IT编程  | 我要评论

风水世家219,程立人,9c8947

什么是systemservice

我们在android开发过程中经常会用到各种各样的系统管理服务,如进行窗口相关的操作会用到窗口管理服务windowmanager,进行电源相关的操作会用到电源管理服务powermanager,还有很多其他的系统管理服务,如通知管理服务notifacationmanager、振动管理服务vibrator、电池管理服务batterymanager…… 这些manager提供了很多对系统层的控制接口。对于app开发者,只需要了解这些接口的使用方式就可以方便的进行系统控制,获得系统各个服务的信息,而不需要了解这些接口的具体实现方式。而对于framework开发者,则需要了解这些manager服务的常用实现模式,维护这些manager的接口,扩展这些接口,或者实现新的manager。

image

一个简单的systemservice

我们从一个简单的系统服务vibrator服务来看一下一个系统服务是怎样建立的。

vibrator服务提供的控制手机振动的接口,应用可以调用vibrator的接口来让手机产生振动,达到提醒用户的目的。

从android的官方文档中可以看到vibrator只是一个抽象类,只有4个抽象接口:

  • bstract 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) {
    return systemserviceregistry.getsystemservice(this, name);
}

frameworks/base/core/java/android/app/systemserviceregistry.java
(systemserviceregistry是 android 6.0之后才有的,android 6.0 之前的代码没有该类,下面的代码是直接写在contextimpl里的)

 public static object getsystemservice(contextimpl ctx, string name) {
    servicefetcher<?> fetcher = system_service_fetchers.get(name);
    return fetcher != null ? fetcher.getservice(ctx) : null;
}

system_service_map是一个hashmap,通过我们服务的名字name字符串,从这个hashmap里取出一个servicefetcher,再return这个servicefetcher的getservice()。servicefetcher是什么?它的getservice()又是什么?既然他是从system_service_map这个hashmap里get出来的,那就找一找这个hashmap都put了什么。

通过搜索systemserviceregistry可以找到如下代码:

private static <t> void registerservice(string servicename, class<t> serviceclass,
        servicefetcher<t> servicefetcher) {
    system_service_names.put(serviceclass, servicename);
    system_service_fetchers.put(servicename, servicefetcher);
}

这里往system_service_map里put了一对string与servicefetcher组成的key/value对,registerservice()又是从哪里调用的?继续搜索可以发现很多类似下面的代码:

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() {
    ...
    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搜索,或者在androidxref搜索,可以发现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服务的整个实现流程:

  1. 定义一个抽象类vibrator,定义了应用中可以访问的一些抽象方法
frameworks/base/core/java/android/os/vibrator.java
  1. 定义具体的类systemvibrator继承vibrator,实现抽象方法

frameworks/base/core/java/android/os/systemvibrator.java

  1. 定义一个aidl接口文件ivibratorservice,定义系统服务接口

frameworks/base/core/java/android/os/ivibratorservice.aidl

  1. 定义服务vibratorservice,实现ivibratorservice定义的接口

frameworks/base/services/java/com/android/server/vibratorservice.java

  1. 将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);
}
  1. 在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) {
        }
        ...
    }
}
  1. 在context里定义一个代表vibrator服务的字符串

frameworks/base/core/java/android/content/context.java

public static final string vibrator_service = "vibrator";
  1. 在contextimpl里添加systemvibrator的实例化过程

frameworks/base/core/java/android/app/contextimpl.java

registerservice(vibrator_service, new servicefetcher() {
public object createservice(contextimpl ctx) {
    return new systemvibrator(ctx);
}});  
  1. 在应用中使用vibrator的接口
vibrator mvibrator = (vibrator) getsystemservice(context.vibrator_service);
mvibrator.vibrate(500);
  1. 为保证编译正常,还需要将aidl文件添加到编译配置里

frameworks/base/android.mk

local_src_files += \
...
core/java/android/os/ivibratorservice.aidl \

system service 新加接口

如果我们需要实现一个新的系统服务,就可以按照上面的步骤在系统中扩展出一个新的服务,并给应用层提供出使用接口。如果想在vibrator里添加一个新的接口,需要下面3步:

  1. 在ivibratorservice添加接口;
  2. 在vibratorservice添加接口的实现;
  3. 在vibrator及systemvibrator里扩展新的接口;

这样应用中就可以使用vibrator的新接口了。

应用层与 system service 通信

上面的实现我们看到的只是从应用层通过服务代理,调用系统服务的接口,如果我们想反过来,将系统服务的状态通知给应用层,该怎么做呢?

  • 方法一:使用broadcast

我们知道使用broadcast广播可以实现跨进程的消息传递,一些系统服务也使用了这种方法。如电池管理服务batterymanagerservice,收到底层上报的电池状态变化信息时,就将当前的电池状态封装在一个intent里,action为android.intent.action.battery_changed。应用只要注册一个对应的broadcastreceiver就可以收到battermanagerservice发送的电池状态信息。

  • 方法二:使用aidl

从上面我们可以知道,通过aidl定义一套接口,由系统服务端实现这些接口,应用端使用一个相应的代理就可以访问系统服务的接口,那反过来让应用端实现aidl接口,系统服务端使用代理调用应用端的接口可不可以呢?答案是yes。那么接下来的问题是怎么让系统服务得到这个代理。我们再来看一个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) {
        ...
    }
}

原来是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
  1. 注意多线程访问

每个系统服务在系统进程中只有一个实例,而且应用中系统服务的代理也是单例的,而且应用端的访问,在系统进程都是使用独立的线程进行响应,所以访问同一个系统服务的接口时必然会出现多个线程或者多个进程同时访问的情况。为保证系统服务的线程安全,需要对系统服务的进程进行多线程访问的保护,目前主要有两种实现线程安全的方法:

一种是通过同步锁机制,锁住一个对象实例(一般是这个服务对象本身),这样这个服务同一时间只能响应一个访问请求,如locationmanagerservice里:

public boolean callstatuschangedlocked(...) {
    ...
    synchronized (this) {
    ...
    }
}

另一种方法就是使用handler机制,这种服务一般会创建一个单独的线程,当有应用端访问请求到来时会向服务线程的handler里发送一个message,利用单线程顺序执行的特性,保证所有的访问都按顺序进行处理,但这种方法只适合单向的访问,不适合需要返回的双向访问。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网