编写第一个Android Native Service :SQRService,求平方(Square)运算。
为什么有核心服务呢?
1. 核心服务是 Android 框架里最接近Linux/Driver 的部分。为了充分发挥硬件设备的差异化特性,核心服务是让上层Java 应用程序来使用Driver/HW Device 特色的重要管道。
2. 在开机过程中,就可以启动核心服务(例如汉字输入法服务等),让众多应用程序来共享之。
3. 由于共享,所以能有效降低 Java 应用程序的大小(Size)。
看图!
步骤:
I、编写Native Service:SQRService
II、编写SQR.cpp来使用SQRService
III、编写Java层Client来使用核心服务
IV、编写一个Java层Activity分析:
1、核心服务通常在特定的进程(Process)里执行。
2、必须提供IBinder接口,让应用程序可以进行跨进程的绑定(Binding)和调用。
3、因为共享,所以必须确保多线程安全(Thread-safe)。
4、以C++类定义,诞生其对象,通过SM的协助,将该对象参数传给IServiceManager::addService函数,就加入到Binder Driver里了。
5、应用程序可以通过SM的协助远距离绑定该核心服务,此时SM会回传IBinder接口给应用程序。
6、应用程序可通过IBinder::transact()函数来与核心服务互传资料。
I、
以C++编写一个SQRService,它的功能是将一个数值(例如5)做平方的运算,然后传出其计算结果(5^2 = 5*5 = 25)。
SQRService.h
//SQRService.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();
}
注意:在Java层,Binder类实现了IBinder接口;
在C++层,BBInder类实现了IBinder接口。
SQRService.cpp
//SQRService.cpp
namespace android{
enum{
SQUARE = IBinder::FIRST_CALL_TRANSACTION,
};
int SQRService::instantiate(){
LOGE("SQRService instantiate");
int r = defaultServiceManager()->addService(String16("com.devwang.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.. num = %d \n",num);
return NO_ERROR;
}
break;
default:
LOGE("onTransact::default\n");
return BBinder::onTransact(code,data,reply,flags);
}
}
}//namespace android
编写一个Make文件:
Android.mk
//Android.mk
LOCAL_MODULE:=libSQRS
执行此Android.mk就可进行编译(Cpmpile)和链接(Link),生成libSQRS.so共享库。
使用C++编写可独立执行的sqrserver.cpp程序。此程序先创建SQRService的对象,然后将它添加到Binder Driver里。
注意:
暂时不改写init.rc文件,以及重新启动设备(以防服务有错导致死机);
先使用sqrserver程序来测试这个核心服务。
sqrserver.cpp
//sqrserver.cpp
using namespace android;
int main(int argc, char const *argv[])
{
sp<ProcessState> proc(ProcessState::self);
sp<IServiceManager> sm = defaultServiceManager();
LOGI("ServiceManager:%p",sm.get());
SQRService::instantiate();
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
return 0;
}
上述步骤,分别建立了libSQRS.so和sqrserver可执行程序。
然后将libSQRS.so放入Android仿真器的/system/lib里。并且,将sqrserver程序放入Android仿真器的/sysytem/bin里。
执行sqrserver程序,就将SQRService对象加入Binder Driver里。
注意:
先不要杀掉sqrserver进程
II、
编写SQR.cpp来使用SQRService
编写一个本地层Client
以C++编写一个SQR.cpp的C++层应用程序,它可以通过ServiceManager去绑定(Bind)SQRService.
然后调用IBinder::transact()函数,进而调用SQRService核心服务的onTransact()去进行平方运算的服务。
SQR.h
//SQR.h
namespace android{
class SQR
{
private:
const void getSQRService();
public:
SQR();
int execute(int n);
}
}//namespace android
SQR.cpp
//SQR.cpp
namespace android{
sp<IBinder> m_ib;
SQR:SQR(){
getSQRService();
}
const void SQR::getSQRService(){
sp<IServiceManager> sm = defaultServiceManager();
m_ib = sm->getService(String16("com.devwang.sqr"));
LOGE("SQR:getSQRService %p\n",sm.get());
if(m_ib == 0){
LOGW("SQRService not published,wating...");
}
return;
}
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;
}
}
在构造函数SQR()里调用getSQRService来获得服务(亦即绑定服务)。
取得服务时ServiceManager传回BpBinder的IBinder接口。
而execute()函数则实际调用IBinder接口的transact()函数,并转而调用onTransact()函数。
III、
编写Java层Client来使用核心服务
编写一个Java层Client
以Java编写一个sqr.java
Java层的应用程序可以通过JNI Native Code来调用SQR类别,进而取得SQRService服务。
com_devwang_service_sqr.cpp
//com_devwang_service_sqr.cpp
using namespace ansroid;
JNIEXPORT jint JNICALL Java_com_devwang_service_sqr_execute(JNIEnv *env,jobject thiz,jint x){
char s[] = "Java_com_devwang_service_sqr_execute!!!";
LOGE("%s x = %d\n",s,x);
SQR* sqrObj = new SQR();
int num = sqrObj->execute(x);
return num;
}
执行Android.mk文件,生成libSQRS_jni.so共享库,就可以加载手机或模拟器里执行了。
执行时,Native函数调用SQR,转而调用BpBinder。
设计Java层的应用类别来调用上述的JNI Native Code,就完成串接Java应用程序与硬件驱动的目的了。
于是,编写一个与JNI Native Code相对应的Java类别。
sqr.java
//sqr.java
package com.devwang.service;
public class sqr{
static{
system.loadLibrary("libSQRS_jni");
}
public static native int execute(int x);
};
当此sqr类别被加载到内存时,就会把libSQRS_jni.so也加载进来,于是sqr类别就与libSQRS_jni.so结合起来了;而且sqr.execute()函数就能调用到libSQRS_jni.so里的JNI Natice函数了。
IV、
编写一个Java层Activity,如:sqrActivity.java,使用sqr.java中的native函数即OK。
视频教程:参照以下这三个,不得不说高老的视频很赞,得有强硬的基础才能看懂。