Android从程序员到架构师之路4

本文学习自高焕堂老师的Android从程序员到架构师之路系列教学视频

98 - 认识Android核心服务a

1. 区分核心服务与App服务

• 在Android平台,上层App的SDK-based Service与底层的核心服务有很大的区别,只是许多人都将它们混在一起了。
• 于此,兹说明Android里有2种服务:
	1. 应用层的应用服务,通称为SDK-based Service(或称为 App服务)。
	2. 系统层的核心服务,通称为Core Service(或称为System Service)。

App服务

• 至于App服务(即SDK-based Service)则属于App的一部份,是开机完成后,用户加载并开启某App时,才会启动该App里的服务。这种App服务的特征是,它会定义成为Service的一个子类别。
• 如下图的myService子类别,它继承Android应用框架里的Service基类。

D01-00

• 在Android开机过程中,就会启动数十个核心服务。当开机完成时,全部核心服务也都启动完成了。
• 接着,才能启动App,并执行之。所以,核心服务的启动时间早于App的启动时间。

D01-01

• AMS(ActivityManagerService)启动一个App的时候,就会通过Socket请求Zygote来fork一个进程给这个即将要启动的App。

• 也就是说,当App服务启动时,核心服务早已经启动存在了。
• 因此,myService可以透过MediaPlayer和JNI来绑定(Bind)和调用核心服务MediaPlayerService。

D01-02

• 其中的myService就是一个App服务;而MediaPlayerService就是一个核心服务。到此,已经初步介绍了核心服务的特性,让你能对分辨核心服务和App服务的微妙区别了。

D01-03

2. 回忆App服务

• 核心服务是系统层的「前装型」服务
	–大多以C++类别实现,有些以Java类别实现。
	–可透过ServiceManager来建立和绑定(Bind)核心服务。
	–绑定后,可透过IBinder接口去执行其函数。
• App服务是应用层的「后装型」服务
	–以Java层的Service的应用子类别实现。所有的App服务都是由ActivityManagerService核心服务所掌管。
	–在应用层(如Activity)可调用Android框架里Context类别的startService()和bindService()函数去绑定App服务。
	–绑定后,可透过IBinder接口去执行其函数。

D01-04

99 - 认识Android核心服务b

3. 区分两种核心服务:Android Service与Native Service

• 核心服务可以用Java撰写;也可以用C++撰写。
• 以Java撰写的核心服务通称为Android Service(例如AudioService和SensorService等);
• 以C++撰写的核心服务则通称为Native Service (例如MediaPlayerService和CameraService等)。

• Android Service又称为Java Service,是实现于应用框架层(Framework)里的。
• 这里所讲的Service是系统服务(System Service),又称为Server;其与App开发上所讨论的Service不同。
• Android Service以Java撰写。

D01-05

• Native Service则是实现于Runtime层里的Server。在系统服务开发上,我们有二个途径,一个是写成Android Service、其再透过JNI与HAL驱动沟通;另一个途径是,跳过Android Service,让App透过JNI直接与Native Service沟通。

D01-06

D01-07

4. 核心服务的特性

• 其中,核心服务属于Android平台的系统服务模块,在开机过程,就会启动数十个核心服务;例如MediaPlayerService、AudioService、SensorService等。

D01-08

• 关于核心服务的启动,就是在开机时,加载Linux kernel部分,进行Kernel-Space的初始化,然后加载硬件驱动程序,就启动了Linux系统。随后切换至User-Space,创建init进程,读取 init.rc文档,依据其内容的指示而先启动Native Service,再启动Android Service。就完成核心服务的启程序了。
• 由Linux内核启动用户空间的Init进程 解析脚本文件:Init.rc
Init.rc是Android的初始化脚本
• 由Init进程创建ServiceManager和Zygote进程。
• 由Zygote创建(VM和)SystemServer进程。

D01-09

核心服务的特性

• 核心服务通常在特定的进程里执行。
• 必须提供IBinder接口,让App进行跨进程的绑定(Bind)和呼叫。
• 因为共享,所以必须确保多线裎安全(Thread-safe)
• 以C++类别或Java类别定义,诞生其对象,请ServiceManager(简称SM)将该对象参考值加入到Binder Driver里。
• App可请SM协助而远距绑定某核心服务,此时SM会传IBinder接口给App。
• App可透过IBinder::transact()函数来与核心服务互传讯息。
• 核心服务启动在先,应用程序启动在后。
• 核心服务能以C++撰写;也可以用Java撰写(如AudioService)。
• 核心服务与App服务不同,App服务不会加入到Binder Driver里。

100 - 认识Android核心服务c

5. 简介几个Android Service

• 在Android Service里,常见的有如ActivityManager、LocationManager等。兹针对其中的几个常见服务介绍如下:
• Activity Manager(活动管理器):提供所有的应用程序的生命周期,以及Activity之间的互动之服务。
• WindowManager(窗口管理器):提供由关频幕窗口之管理服务。
• ResourceManager(资源管理器):管理程序使用的各项资源,例如:字符串、图片、UI布局(Layout)定义档。
• LocationManager(位置管理器):提供位置服务(Location-based Service)。
• TelephonyManager(电话管理器):提供有关手机通话的服务,例如关闭电话等。
• 其它服务。

6. 简介几个Native Service

• Native Service是以C++语言所撰写的核心服务。
• 在Native Service里,大家比较熟悉得有如ServiceManager、MediaPlayerService、Zygote、CameraService等等。
• 兹针对其中的几个常见服务介绍如下:
• ServiceManager(服务管理器):协助登录与绑定系统服务。
• ServiceManager是Android平台里的一个基本模块。在Android系统启动过程中,它就会被登录到Binder Kernel里,成为天字第一号的可提供远距服务之模块。
• 使用C/C++撰写Android的NativeService模块时,可以透过defaultServiceManager()函数来取得ServiceManager模块的IServiceManager接口之指针。例如,在CameraService.cpp程序里的instantiate()函数,其内容如下:

// CameraService.cpp
void CameraService::instantiate() {
 defaultServiceManager()->addService(
 String16("media.camera"), new CameraService());
}

• 它使用了defaultServiceManager()函数来取得ServiceManager模块,然后呼叫其IServiceManager接口里的addService()函数来将自己对象(即CameraService类别之对象)传给ServiceManager模块,请它登录到Binder Kernel里,成为一个可提供远距服务之模块(即CameraService模块)。
• Zygote服务:Android最内层有个Zygote服务。它是Android Java层的孵化器。Zygote就在幕后复制孵化(Fork)出一个子进程来给这些Java层的系统服务或应用程序。所以,Android的应用程序,以及上述的各项服务,也都是由zygote所繁殖出来的。Zygote服务的架构位置如下图:

D01-10

• CameraService(摄像服务):其实作于libcameraservice.so共享库里,与底层Camera 硬件的 HAL驱动程序衔接,藉由Linux的 videodev接口来撷取视讯。
• 其它服务。

7. 天字第一号的核心服务: ServiceManager

• 天字第一号的核心服务就是ServiceManager(简称SM)。当Android系统启动时,就会优先将之登记到Binder Driver里,如下图:

D01-11

• 随后,陆续会有更多的核心服务呼叫SM的addService()函数去登录到BD里。例如,ActivityManagerService也是透过SM而登录到BD里。再如,MediaPlayerService也继续透过SM去登录到BD里,如下图:

D01-12

• 当Android系统启动完成(主要核心服务也启动完成)之后,就可以启动及执行应用程序了。
• 执行应用程序时,各App服务(亦即Service的子类)都由ActivityManagerService来掌管。
• 例如,App里的myActivity等类别可以使用bindService()来绑定(Bind)到myService服务,然后透过IBinder接口而远程呼叫到myService。之后,myService再透过JNI而呼叫到MediaPlayerService,进而呼叫到更底层的驱动模块。

D01-13

101 - 认识Android核心服务d

8. 核心服务的共通API:IBinder接口

• 就以ServiceManager(简称SM)为例子,由于SM跑在自己的进程里,为了让别的模块可以来呼叫它的服务,它必需提供共通的IBinder接口,才能让其它进程里的模块来进行IPC沟通。如下图:

D01-14

• 此刻,如果另一个进程里的C++ Client想与SM互动时,就会透过更底层的ProcesState服务在自己的进程里,诞生一个BpBinder对象,作为BinderDriver的分身(即Proxy对象)。
• 让C++ Client能透过BpBinder而与Binder Driver互动,进而跨进程地与SM沟通,所以BpBinder也间接地扮演SM的分身的角色。如下图所示:

D01-15

9. BBinder基类实现IBinder接口

• 在C++层实现核心服务时,可继承BBinder父类别而拥有IBinder接口;然后透过ServiceManager而登录到Binder Kernel里,如下图:

D01-16

• 由于核心服务通常执行于独立的进程里,所以它的Client模块(如下图的MediaPlayer.cpp类别)通常在另一个进程里,与MediaPlayerService之间是跨进程的,如下图:

D01-17

• C++层的Client模块(如下图的MediaPlayer.cpp类别)就透过ServiceManager来绑定核心服务,如下图:

D01-18

• Java层的Client模块(如下图的myActivity.java类别)也能透过ServiceManager来绑定核心服务,如下图:

D01-19

• 同样的C++层机制,也能绑定Java层的SDK-Service。如下图:

D01-20

102 - 撰写你的第一个核心服务a

1. 为什么要写核心服务呢?

• 为什么需要学习撰写核心服务呢? 其常见的理由是:
	1. 核心服务是 Android 框架裡最接近Linux/Driver 的部分。为了充分发挥硬件设备的差異化特性,核心服务是让上层Java 应用程序來使用Driver/HW Device 特色的重要管道。
	2. 在开机过程中,就可以启动核心服务(例如汉字输入法服务等),让众多应用程序來共享之。
	3. 由于共享,所以能有效降低 Java 应用程序的大小(Size)。

2. 亲自开发一个Native Service:SQRService

D02-00

• 此核心服务命名为SQRService,它做简单的整數平方(Square)运算。其开发要点为:
	1.核心服务通常在特定的进程(Process)裡执行。
	2. 必须提供 IBinder 接口,让应用程序可以进行跨进程的绑定(Binding)和调用。
	3. 因为共享,所以必须确保多线裎安全(Thread-safe)。
	4. 以 C++類定义,诞生其对象,透过SM 之协助,将该对象參考值传给IServiceManager::addService()函數,就加入到Binder Driver 裡了。
	5. 应用程序可透过 SM之协助而远距绑定该核心服务,此时SM会回传IBinder接口给应用程序。
	6. 应用程序可透过 IBinder::transact()函數來与核心服务互传资料。

亲自撰写
阶段一:撰写服务 以C++撰写一个SQRService

• 步骤1.1: 以C++撰写一个SQRService,它的工作是将一个数值(例如5)做开平方的运算,然后传出其计算结果(例如5的平方值为25)。于是,撰写一个C++类别(名称为SQRService),如下述的程序码:

// SQRService.h
#include <stdint.h>
#include <sys/types.h>
#include <utils/Parcel.h>
#ifndef ANDROID_MISOO_SQRSERVICE_H
#define ANDROID_MISOO_SQRSERVICE_H
#include <utils.h>
#include <utils/KeyedVector.h>
#include <ui/SurfaceComposerClient.h>
namespace android {
class SQRService : public BBinder
 {
 public:
 static int instantiate();
 virtual status_t onTransact(uint32_t, const Parcel&,
 Parcel*, uint32_t);
 SQRService();
 virtual ~SQRService();
 };
};
#endif

D02-01

在java层,Binder类实现了IBinder接口
在C++层,BBinder类实现IBinder接口

// SQRService.cpp
#include <utils/IServiceManager.h>
#include <utils/IPCThreadState.h>
#include <utils/RefBase.h>
#include <utils/IInterface.h>
#include <utils/Parcel.h>
#include "SQRService.h"
namespace android {
enum {
 SQUARE = IBinder::FIRST_CALL_TRANSACTION,
 };
int SQRService::instantiate(){
 LOGE("SQRService instantiate");
 int r = defaultServiceManager()->addService(
 String16("misoo.sqr"), new SQRService());
 LOGE("SQRService r = %d\n", r);
 return r;
 }
SQRService::SQRService(){ LOGV("SQRService created"); }
SQRService::~SQRService(){ LOGV("SQRService destroyed"); }
status_t SQRService::onTransact(
 uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
 switch(code) {
 case SQUARE: {
 int num = data.readInt32();
 reply->writeInt32(num * num);
 LOGE("onTransact::CREATE_NUM.. n=%d\n", num);
 return NO_ERROR;
 }
 break;
 default:
 LOGE("onTransact::default\n");
 return BBinder::onTransact(code, data, reply, flags);
 }}
}; // namespace android

• 步骤1.2: 撰写完成之后,就可以编辑一个Make档案,如下:

// Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
SQRService.cpp
LOCAL_C_INCLUDES := \
$(JNI_H_INCLUDE)
LOCAL_SHARED_LIBRARIES := \
libutils
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE := libSQRS01
include $(BUILD_SHARED_LIBRARY)

• 执行此Android.mk 就可进行编译(Compile)和连结(Link),来产出libSQRS01.so 共享库。

• 步骤1.3: 使用 C++撰写可独立执行的addserver.cpp 程序。此程序先创建SQRService的对象,然后将它Binder Driver 裡。此addserver.cpp 的内容如下:

暂时不该写init.rc文档,以及重新启动设备,先使用addserver程序来测试这个核心服务

// addserver.cpp
#include <sys/types.h>
#include <unistd.h>
#include <grp.h>
#include <utils/IPCThreadState.h>
#include <utils/ProcessState.h>
#include <utils/IServiceManager.h>
#include <utils/Log.h>
#include <private/android_filesystem_config.h>
#include "../libadd/SQRService.h"
//#include <libadd/SQRService.h>
using namespace android;
int main(int argc, char** argv)
{
 sp<ProcessState> proc(ProcessState::self());
 sp<IServiceManager> sm = defaultServiceManager();
 LOGI("ServiceManager: %p", sm.get());
 SQRService::instantiate();

 ProcessState::self()->startThreadPool();
 IPCThreadState::self()->joinThreadPool();
}

• 上述步骤,分别建立了libSQRS01.so 和addserver 可执行程序。
• 然后将libSQRS01.so 放入Android 仿真器的/system/lib 裡。并且,将addserver 程序放入Android仿真器的/system/bin 裡。
• 执行 addserver 程序,就将SQRService 对象加入Binder Driver 裡。

D02-02

103 - 撰写你的第一个核心服务b

先不要杀掉addserver进程

3. 撰写SQR.CPP 來使用SQRService

阶段二:撰写一个本地层client,以c++撰写一个SQR.CPP

• 在本节里,兹撰写一个SQR.CPP程序使用SQRService。
• 步骤2.1:使用 C++开发一个名为SQR.cpp的C++层应用程序,它透过ServiceManager去绑定(Bind)了SQRService。
• 然后调用IBinder::transact()函数,进而调用SQRService核心服务的onTransact()去进行「平方」的服务。其架构图如下:

D02-03

• 于是,使用C++撰写一个SQR类,如下:
// SQR.h
#ifndef ANDROID_MISOO_SQR_H
#define ANDROID_MISOO_SQR_H
namespace android {
class SQR {
 private:
 const void getSQRService();
 public:
 SQR();
 int execute(int n);
 };
}; //namespace
#endif // ANDROID_MISOO_SQR_H

// SQR.cpp
#include <utils/IServiceManager.h>
#include <utils/IPCThreadState.h>
#include "SQR.h"
namespace android {
 sp<IBinder> m_ib;
 SQR::SQR(){
 getSQRService();
}
const void SQR::getSQRService(){
 sp<IServiceManager> sm = defaultServiceManager();
 m_ib = sm->getService(String16("misoo.sqr"));
 LOGE("SQR:getSQRService %p\n",sm.get());
 if (m_ib == 0)
 LOGW("SQRService not published, waiting...");
 return;
}

int SQR::execute(int n) {
 Parcel data, reply;
 data.writeInt32(n);
 LOGE("SQR::execute\n");
 m_ib->transact(0, data, &reply);
 int num = reply.readInt32();
 return num;
 }
}; //namespace BpBinder

• 在构造式SQR()里调用getSQRService來获得服务(亦即绑定服务)。
• 取得服务时 ServiceManager 传回BpBinder 的IBinder 接口。
• 而execute()函数则实际调用Ibinder接口的transat()函数,并转而调用onTransact()函数。

4. 撰写Java层Client來使用核心服务

阶段三:撰写一个java层client,以java撰写一个sqr01.java

• Java层的应用程序可以透过JNI NativeCode来调用上图里的SQR类别,进而取得SQRService的服务。其架构图如下:

D02-04

• 步骤3.1: 使用C语言撰写JNI Native Code,来当作Java应用程序与SQR.cpp之间的桥梁,如下:

/* com_misoo_service_sqr01.cpp */
#include "com_misoo_service_sqr01.h"
#include <utils/Log.h>
#include <utils/IPCThreadState.h>
#include <utils/ProcessState.h>
#include "../core_service/SQRService.h"
#include "SQR.h"
using namespace android;
JNIEXPORT jint JNICALL
Java_com_misoo_service_sqr01_execute(JNIEnv *env, jobject thiz,
 jint x) {
char s[] = "Java_com_misoo_service_sqr01_execute!!!";
LOGE("%s X = %d\n", s, x);
SQR* sqrObj = new SQR();
int num = sqrObj->execute(x);
return num;
 }

• 执行 Android.mk 档,产出libSQRS01_jni.so 共享程序库,就可以加载手机或模拟器裡执行了。
• 执行时,Native 函數调用SQR,转而调用BpBinder,如下图所示。

D02-05

• 设计Java层的应用类别来调用上述的JNI Native Code,就完成串接Java应用程序与硬件驱动的目标了。
• 于是,撰写一个与JNI Native Code相对应的Java类别:

// sqr01.java
package com.misoo.service;
public class sqr01
{
static
 { System.loadLibrary("libSQRS01_jni"); }
public static native int execute(int x);
}

• 当此sqr01类别被加载到内存时,就会把libSQRS01_jni.so也加载进来,于是sqr01类别就与libSQRS01_jni.so结合起来了;而且sqr01.execute()函数就能调用到libSQRS01_jni.so里的JNI Native函数了。

D02-06

104 - 撰写你的第一个核心服务c

5. 撰写一个Java层Activity

D02-07
D02-08
D02-09

这不是好的架构设计

• 此时,C/C++层都是被动的、被调用的,这是古典的架构思维,其控制点在App上。

新潮架构设计

• 新潮架构设计思维:如何将控制点下移到C/C++层呢? 例如,下移到JNI本地层。

D02-10

• C层拥有控制点的必备表现是:
	1. 从C创建Java对象
	2. 从C调用Java层函数

阶段四:撰写一个java层Activity,撰写一个Android的myActivity.java

议题:

• 接下来,一个典型的议题是:如果myActivity想使用SQRService服务,该如何规划呢?

方案一:
• 最简单的途径是:让myActivity透过sqr01.java,再经由JNI本地函数去调用到SQRService的onTranscat()函数。如下图:

D02-11

方案二:善用BinderProxy对象
• 由myActivity透过JNI Native函数去绑定核心服务。
• 然后,在Java层诞生一个BpBinder对象的分身:即BinderProxy对象。如下图:

D02-12

方案二的实现:

• C层拥有控制点的必备表现是:
	1. 从C创建Java对象
	2. 从C调用Java层函数

• 步骤4.1: 此时,myActivity必须透过JNI Native函数去绑定核心服务,然后由JNI Native函数在Java层诞生一个BpBinder对象的分身:即BinderProxy对象。如下图:

D02-13

架构设计:sqr05的角色

• myActivity不宜含有native函数,于是委托sqr05来提供native函数,创建Java对象,然后回传接口给myActivity。

使用javaObjectForIBinder()函数

• 在Android里,提供了一个JNI Native 模块,内涵一个javaObjectForIBinder()函数,它能协助诞生Java层的BinderProxy对象,做为BpBinder对象的分身。

• 撰写JNI Native模块

// sqr05.java
// ………
public class sqr05
{
static
 { System.loadLibrary("SQRS05_jni"); }
public native final IBinder bindCoreService();
}

/* com_misoo_service_sqr05.cpp */
//...........
sp<IBinder> m_ib;
JNIEXPORT jobject JNICALL
Java_com_misoo_service_sqr05_bindCoreService(JNIEnv *env, jobject
thiz){
 LOGE("bindCoreService");
 sp<IServiceManager> sm = defaultServiceManager();
 m_ib = sm->getService(String16("misoo.sqr"));
 LOGE("SM:getService %p\n",sm.get());
 if (m_ib == 0){
 LOGW("SQRService not published, waiting..."); return 0;
 }
 jobject jbi = javaObjectForIBinder(env, m_ib);
 if (jbi == 0) { LOGE("javaObjectForIBinder jbi = 0"); return 0; }
 return jbi;
}

• 由javaObjectForIBinder()诞生Java层的BinderProxy对象。
• BinderProxy对象里的mObject属性指向BpBinder的IBinder接口。
• JNI Native模块里的gBinderProxyOffsets.mObject公用属性存有BinderProxy类别里的mObject属性的ID值(不是指针)。如下图:

D02-14

D02-15

• 于是,在从myActivity类别里,就能透过BinderProxy对象而调用JNI Native模块,转而远距调用SQRService核心服务了。如下图:

D02-16

• 撰写myActivity

// myActivity.java
// ……..ndroid.os.IBinder;
public class myActivity extends Activity implements OnClickListener {
private Button btn, btn2;
@Override
 //………
 }
public void onClick(View v) {
 switch(v.getId()){
• 撰写myActivity
 case 101:
 sqr05 sqr = new sqr05();
 IBinder m_ib = sqr.bindCoreService();
 int code = 0;
 Parcel data = Parcel.obtain();
 data.writeInt(11);
 Parcel reply = Parcel.obtain();
 int flags = 0;
 try {
 m_ib.transact(code, data, reply, flags);
 } catch (Exception e) { e.printStackTrace(); }
 setTitle("Value = " + String.valueOf(reply.readInt()));
 break;
 case 102: finish(); break;
 }
}}

• myActivity执行到指令:
 sqr05 sqr = new sqr05();
 IBinder m_ib = sqr.bindCoreService();
• 就委托sqr05来调用Native的bindCoreService()函数:
 Java_com_misoo_service_sqr05_bindCoreService(JNIEnv*, jobject)
• 这bindCoreService()函数内含指令:
 sp<IServiceManager> sm = defaultServiceManager();
 m_ib = sm->getService(String16("misoo.sqr"));

• 于是,bindCoreService()函数绑定了SQRService核心服务。
• 再执行指令:
 jobject jbi = javaObjectForIBinder(env, m_ib);
• 这bindCoreService()函数就诞生了Java层的BinderProxy对象。
• 最后执行到指令: return jbi;
• 这bindCoreService()函数就将BinderProxy对象的IBinder接口回传给myActivity类别。
• 于是,myActivity类别可以透过此IBinder接口而调用BinderProxy的transact()函数,转而调用SQRService核心服务的
onTransact()函数。
• 实现了下图的结构了

D02-17

106 - 撰写你的第一个核心服务e

6. 优化设计

• 撰写myActivity的人,也撰写sqr05.java及其 JNI 本地函数(下图),这增加了myActivity撰写人的负担。该如何呢?

D02-18

• 可以将sqr05.java部分写在myActivity的基类里,让myActivity来继承之,就迎刃而解了。

D02-19

架构设计思维

D02-20

// myActivity.java
// ……..
class mySqr extends sqr05 {
 public static void bindCS(ServiceConnection conn){
 IBinder ib = sqr.bindCoreService();
 conn.onServiceConnected(null, ib);
 }}
public class myActivity extends Activity implements OnClickListener {
 private IBinder m_ib;
 private mySqr sqr;
 @Override public void onCreate(Bundle savedInstanceState) {
 //……..
 sqr = new mySqr();
 sqr.bindCS(mConnection);
 }
private ServiceConnection mConnection
 = new ServiceConnection() {
 public void onServiceConnected(ComponentName className,
 IBinder ibinder) {
 m_ib = ibinder;
}
 @Override
 public void onServiceDisconnected( ComponentName name) { }
 };
public void onClick(View v) {
 // ………
 m_ib.transact(code, data, reply, flags);
 // ………
 }
}

• 这bindCS()包装了基类的bindCoreService()本地函数。bindCS()反过来调用myActivity的onServiceConnected()函数。

107 - Native核心服务的Proxy-Stub设计模式a

1. 复习:Java层的Proxy-Stub设计模式

为什么要Stub类呢?

D02-21

为什么要proxy-stub模式呢?

D02-22

proxy-stub模式:Binder应用范例

D02-23

• onTransact()就是EIT造形里的<I>
• 这是标准的EIT造形,其<I>是支持<基类/子类>之间IoC调用的接口。
• 运用曹操(Stub)类,形成两层EIT(两层框架)。

D02-24

D02-25

Binder应用范例:编码与译码

D02-26

D02-27

• 编好码之后,就将这编码值当做第1个参数传给IBinder接口的transact()函数。
• 于是编码值就跨进程地传递到myBinder类里的onTransact()函数了。

D02-28

例如myBinder的代码:

public class myBinder extends Binder{
 @Override public boolean onTransact( int code, Parcel data,
 Parcel reply, int flags) throws android.os.RemoteException {
 switch( code ){
 case 1:
// 将Message丢到子线程的MQ to play MP3
Message msg = myService.h.obtainMessage(1,1,1, “Play”);
 myService.h.sendMessage(msg); break;
 case 2:
// 将Message丢到子线程的MQ to stop playing
msg = myService.h.obtainMessage(1,1,1, “Stop”);
 myService.h.sendMessage(msg); break;
 }
 return true;
}}

• 其代码就是对code进行“译码”动作。
• 如果code值為1就執行<Play>動作;如果code值為2就執行<Stop>動作。

Proxy类担任编码。Stub类担任译码

• 回想,之前介绍过,在Java应用框架层里,使用了AIDL架构实现Proxy-Stub设计模式来封装IBinder接口,以便产生更亲切贴心的新接口。
• 并且担任<编码>和<译码>的工作。

D02-29

大大增加了app开发者的负担

108 - Native核心服务的Proxy-Stub设计模式b

2. 从IBinder接口说起

D02-30

D02-31

在java层,Binder类实现了IBinder接口
在C++层,BBinder类实现IBinder接口

D02-32

Proxy类在哪里呢? App Service

D02-33

D02-34

Proxy类在哪里呢? Native Core Service

D02-35

D02-36

Native Service 都有通用性API:即IBinder接口

• 在撰写Native Service时,因为它必须提供
IBinder接口给远程的Client来调用,所以
通常都会继承BBinder框架基类,如下:
 class MyNativeService extends BBinder
 {
 //……….
 }

D02-37

• 此时,MyNativeService提供了IBinder接口,让Client模块来从远程调用。如下图所述:

D02-38

如何协助产生Proxy_Stub类?

• 在 Java层使用aidl.exe工具来生成ProxyStub类;
• 在C/C++层使用模板(Template)来协助产生Proxy_Stub类。

109 - Native核心服务的Proxy-Stub设计模式c

3. 使用模板,产生Stub类,BnInterface<I>模板

D02-39

如何考虑<人>的<分工>

• 由框架开发者来撰写Proxy-Stub类,才能减轻开发者的负担。
• 框架分为:<天子框架>和<曹操框架>。
• 因此,应该由两者(天子或曹操)之一来撰写Proxy-Stub类。

接口知识取得的难题

• 但是,有个难题:接口的内容必须等到<买主>来了才会知道。
• 在框架开发阶段,买主还没来,接口的知识无法取得,又如何定义接口呢? 没有接口定义,又如何撰写Stub和Proxy类呢?

D02-40

• 好办法是:“强龙(天子或曹操)撰写代码(在先) ;然后,地头蛇(App开发者)定义接口(在后)。”

在编程上,有什么技术可以实现这个办法?

• 技术之一是:類別模板(class template)
例如,强龙撰写模板:
template< class T >
class SomeClass
{
 private:
 T data;
 public:
 SomeClass() { }
 void set(T da)
 { data = da; }
}; 

• 地头蛇利用模板来生成一个类:
• 由于接口(interface)是一种特殊的类(class),所以也可以定义模板如下:
SomeClass<Integer> x;
template<interface I>
class BinderProxy
 {
 // ………
 }; 

• 地头蛇利用模板来生成一个类:
BinderProxy<IPlayer> proxy;

Android提供的:BnInterface<I>和BpInterface<I>模板

• Android SDK提供了BnInterface<T>类模板,此模板定义于frameworks/base/include/binder/IInterface.h文件中,如下:

template<typename INTERFACE>
class BnInterface :public INTERFACE, public BBinder {
 public:
 virtual sp<IInterface> queryLocalInterface(const
 String16& _descriptor);
 virtual String16 getInterfaceDescriptor() const;
 protected:
 virtual IBinder* onAsBinder();
 };

• 基于这个模板,并定义接口如下:
class IMyService :public IInterface {
 public:
 DECLARE_META_INTERFACE(MyService);
 virtual void sv1(…) = 0;
 virtual void sv1(…) = 0;
 virtual void sv1(…) = 0;
};

• 此时可使用BnInterafce<T>模板来产生BnInterafce<IMyService>类别。如下:BnInterface<IMyService>
• 它一方面继承了Binder框架基类来得到IBinder接口。同时。也继承了IMyService接口所定义的sv1(), sv2()和sv3()函数。
• 基于这个模板产生的类别,就可衍生出Stub类别,如下:

class BnMyService : public BnInterface<IMyService>
{
//………..
}
• 如下图所示:

D02-41

• 基于这个Stub类别(即BnMyService),我们只要撰写MyNativeService类别,它来继承上述的BnMyService类别即可,如下定义:
class MyNativeService : public BnMyService
{
//………..
}
• 如下图所示:

D02-42

110 - Native核心服务的Proxy-Stub设计模式d

4. 举例:以既有的Native Service为例



5. 使用模板,产生Proxy类
6. 让Client使用Proxy类的新接口

111 - Native核心服务的Proxy-Stub设计模式e

112 - 亲自开发SQRService的Proxy-Stub设计模式a
113 - 亲自开发SQRService的Proxy-Stub设计模式b
114 - 亲自开发SQRService的Proxy-Stub设计模式c

115 - Android Service的Proxy-Stub设计模式a
116 - Android Service的Proxy-Stub设计模式b
117 - Android Service的Proxy-Stub设计模式c

118 - 核心服务Callback的IBinder接口a
119 - 核心服务Callback的IBinder接口b


版权声明:本文为gordon_sun原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。