本文采用情景分析的方法来分析应用程序如何使用通知灯,所谓情景分析,就是抓住一条主线,只关心与我们相关的代码,忽略不相关的代码.这个方法是我从LINUX内核源代码情景分析中学到的,在此向两位前辈毛德操,胡希明致敬.

应用程序中如何使用通知灯

NotificationManager manager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);

Notification notification = new NotificationCompat.Builder(this)

notification.ledARGB = Color.GREEN;// 控制 LED 灯的颜色,一般有红绿蓝三种颜色可选

notification.ledOnMS = 1000;// 指定 LED 灯亮起的时长,以毫秒为单位

notification.ledOffMS = 1000;// 指定 LED 灯暗去的时长,也是以毫秒为单位

notification.flags = Notification.FLAG_SHOW_LIGHTS;// 指定通知的一些行为,其//中就包括显示LED 灯这一选项

manager.notify(1, notification)

首先可以看到应用程序中首先获得一个系统服务NOTIFICATION_SERVICE,然后构造一个notification,其中就包括了对led闪烁条件的初始化,最后调用manager.notifi将通知发出去.

Framework中如何调用

那就先看看NOTIFICATION_SERVICE服务是在哪里注册的,搜索它.可以看到在ContextImpl.java中注册了NOTIFICATION_SERVICE.我查看的代码是Android L,Android M有些许不同,但大同小异.

registerService(NOTIFICATION_SERVICE, new ServiceFetcher() {

public Object createService(ContextImpl ctx) {

final Context outerContext = ctx.getOuterContext();

return new NotificationManager(

new ContextThemeWrapper(outerContext,

Resources.selectSystemTheme(0,

outerContext.getApplicationInfo().targetSdkVersion,

com.android.internal.R.style.Theme_Dialog,

com.android.internal.R.style.Theme_Holo_Dialog,

com.android.internal.R.style.Theme_DeviceDefault_Dialog,

com.android.internal.R.style.Theme_DeviceDefault_Light_Dialog)),

ctx.mMainThread.getHandler());

}});

这段代码是在ContextImpl.java中的静态代码块中,也就是说它会是这个文件中最先执行的部分.再来看看registerService具体是如何实现的.

private static void registerService(String serviceName, ServiceFetcher fetcher) {

if (!(fetcher instanceof StaticServiceFetcher)) {

fetcher.mContextCacheIndex = sNextPerContextServiceCacheIndex++;

}

SYSTEM_SERVICE_MAP.put(serviceName, fetcher);

}

可以看到这个函数会把我们上面传进去的匿名类ServiceFetcher放入一个Hash Map.放入哈希表后,下次就可以根据serviceName来取fetcher.也就是应用程序中getSystemService来获取服务的过程

public Object getSystemService(String name) {

ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);

return fetcher == null ? null : fetcher.getService(this);

}

这里获得了一个fetcher并调用它的getService函数.这里的fetcher就是我们前面注册时传入的匿名类.看看fetcher实现.

static class ServiceFetcher {

public Object getService(ContextImpl ctx) {

...

//这里只保存了关键代码

service = createService(ctx);

cache.set(mContextCacheIndex, service);

return service;

}

}

这里会调用service的createService函数,而上面定义的createService会返回一个NotificationManager实例.

所以getSystemService会返回NotificationManager实例.

接先来再看看应用程序中的manager.notify是如何实现的.

//NotificationManager.java

public void notify(String tag, int id, Notification notification)

{

INotificationManager service = getService();

...

try {

service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,stripped, idOut, UserHandle.myUserId());//这里涉及到Binder远程调用

}

} catch (RemoteException e) {

}

}

看下getService函数实现

static public INotificationManager getService()

{

if (sService != null) {

return sService;

}

IBinder b = ServiceManager.getService("notification");

sService = INotificationManager.Stub.asInterface(b);

return sService;

}

看到这里就需要对Android的Binder机制比较了解才行了,后面我会陆续推出讲解Binder机制的文章,也不知道有没有人看哈,没人看也没关系,反正就当给自己工作学习过程留个记录,(^__^) 嘻嘻……

回归正题,我们来看看”notification”是在哪里注册的.

//SystemServer.java

mSystemServiceManager.startService(NotificationManagerService.class);

//startService会调用NotificationManagerService的onStart函数

publishBinderService(Context.NOTIFICATION_SERVICE, mService);//这里就把'notification'服务注册进去了.

来看看mService里面有什么.

private final IBinder mService = new INotificationManager.Stub(){

...

public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,

Notification notification, int[] idOut, int userId) throws RemoteException {

enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),

Binder.getCallingPid(), tag, id, notification, idOut, userId);

}

...

}

再来看看enqueueNotificationInternal函数

/*和Led操作相关的*/

buzzBeepBlinkLocked(r);

/*然后会调用到*/

updateLightsLocked();

/*进而调用到*/

mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,ledOnMS, ledOffMS);

那mNotificationLight是谁,以及setFlashing是如何实现的呢?

final LightsManager lights = getLocalService(LightsManager.class);

mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);

lights是从一个ArrayMap里面get到的,那在哪里put的呢,可以搜索’LightsManager.class’发现

//LightsService.java

public void onStart() {

publishBinderService("hardware", mLegacyFlashlightHack);

publishLocalService(LightsManager.class, mService);

}

在LightsService.java里面会注册本地服务.LightsService就实现了上面的setFlashing函数

@Override

public void setFlashing(int color, int mode, int onMS, int offMS) {

synchronized (this) {

setLightLocked(color, mode, onMS, offMS, BRIGHTNESS_MODE_USER);

}

}

private void setLightLocked(int color, int mode, int onMS, int offMS, int brightnessMode) {

if (color != mColor || mode != mMode || onMS != mOnMS || offMS != mOffMS) {

if (DEBUG) Slog.v(TAG, "setLight #" + mId + ": color=#"

+ Integer.toHexString(color));

mColor = color;

mMode = mode;

mOnMS = onMS;

mOffMS = offMS;

Trace.traceBegin(Trace.TRACE_TAG_POWER, "setLight(" + mId + ", " + color + ")");

try {

setLight_native(mNativePointer, mId, color, mode, onMS, offMS, brightnessMode);

} finally {

Trace.traceEnd(Trace.TRACE_TAG_POWER);

}

}

终于看到setLight_native函数了,它在JNI层.

JNI层中操作LED

JNI层其实也只是一层过渡,它向上为JAVA层提供操作硬件的接口,向下调用HAL层,然后在HAL层中具体操作硬件.

static void setLight_native(JNIEnv *env, jobject clazz, jlong ptr,

jint light, jint colorARGB, jint flashMode, jint onMS, jint offMS, jint brightnessMode)

{

Devices* devices = (Devices*)ptr;

light_state_t state;

if (light < 0 || light >= LIGHT_COUNT || devices->lights[light] == NULL) {

return ;

}

memset(&state, 0, sizeof(light_state_t));

state.color = colorARGB;

state.flashMode = flashMode;

state.flashOnMS = onMS;

state.flashOffMS = offMS;

state.brightnessMode = brightnessMode;

{

ALOGD_IF_SLOW(50, "Excessive delay setting light");

devices->lights[light]->set_light(devices->lights[light], &state);

}

其实JNI层,没什么好说的,主要是通过硬件访问服务来操作硬件,关于硬件访问服务,可以看我以前写的博文.

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐