内容比较杂,先说下需求:
应用层需要一套接口,这套接口完成与framework层的具体的Services通信,这套接口、还有Services具体需求会增加。添加的Services还要与native双向通信。
分解需求转化为android 系统开发需求:
1.应用层与framework通信涉及到多进程通信,所以需要添加一个Binder Services到SystemServer,编写AIDL,同时封装一个管理类,给应用层使用,这个管理类就是暴露给应用层的接口。
2.管理类和Services将打包成动态库,并在services模块 引入此动态库,添加Binder Services到SystemServer,同时这个动态库也提供给开发者用eclipse导入,应用层开发用。
3.添加的Services调用native 层,通过声明成native方法,然后native层实现即可;反过来我们可以传入一个Callbacks对象到native层,这样native层就可以调用Services里的方法。这需要通过jni实现。
你将了解:
单例模式应用
如何编写Binder Services
如何编译成动态库
如何在其他模块链接动态库
java 与 c++ 通过jni双向调用
package net.eshion.rfid;
import android.content.Context;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
public class RfidAdapter {
final Context mContext;
private static RfidAdapter INSTANCE;
private static IRfidAdapter mService;
private final static Object INSTANCE_LOCK = new Object();
private final static String TAG = "RfidAdapter";
private RfidAdapter(Context context) {
mContext = context;
}
public static synchronized RfidAdapter getRfidAdapter(Context context) {
synchronized (INSTANCE_LOCK) {
if (INSTANCE == null) {
INSTANCE = new RfidAdapter(context);
mService = getServiceInterface();
if(mService == null){
Log.e(TAG , "could not retrieve RFID service");
throw new UnsupportedOperationException();
}
}
return INSTANCE;
}
}
private static IRfidAdapter getServiceInterface() {
IBinder b = ServiceManager.getService("e_rfid");
if (b == null) {
return null;
}
return IRfidAdapter.Stub.asInterface(b);
}
public int test() {
try {
return mService.test();
} catch (RemoteException e) {
e.printStackTrace();
}
return -1;
}
}
getServiceInterface获得Binder Services,test方法实际调用的是Binder Services的test方法。
由上看出我们需要编写一个AIDL
IRfidAdapter.aidl:
package net.eshion.rfid;
/**
* @hide
*/
interface IRfidAdapter
{
int test();
}
RfidServices:
package net.eshion.rfid;
import android.content.Context;
import android.os.RemoteException;
import android.util.Log;
public class RfidServices extends IRfidAdapter.Stub{
private final static String TAG = "RfidServices";
final Context mContext;
final Callbacks callback = new Callbacks();
static {
System.loadLibrary("rfid_jni");
}
static native int nativeTest(Callbacks callback);
public RfidServices(Context context) {
mContext = context;
//nativeTest(callback);
}
@Override
public int test() throws RemoteException {
Log.e(TAG, "------test");
nativeTest(callback);
return 0;
}
/*
* Callbacks from native.
*/
private final class Callbacks {
@SuppressWarnings("unused")
public int callbackTest(long param) {
Log.e(TAG, "-Callbacks-----callbackTest -param=" + param);
return 1000;
}
}
}
static native int nativeTest(Callbacks callback); 将会调用C++中的方法,Callbacks的callbackTest方法是供C++回调的方法。下面会讲到。
将3个文件放在同一目录,写一个Android.mk打成动态库:
注意mk中要引入aidl编译.
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_SRC_FILES += \
java/net/eshion/rfid/IRfidAdapter.aidl \
LOCAL_MODULE:= net.eshion.rfid
include $(BUILD_JAVA_LIBRARY)
# additionally, build unit tests in a separate .apk
include $(call all-makefiles-under,$(LOCAL_PATH))
mk路径:build/target/product/
还需要将模块名 添加到init.rc的export BOOTCLASSPATH 中,否则系统还是找不到动态库
PRODUCT_PACKAGES := \
net.eshion.rfid
需要将Binder Services加到SystemServer.java的ServerThread run方法里:
RfidServices rfid = null;
rfid = new RfidServices(context);
ServiceManager.addService("e_rfid", rfid);
Slog.i(TAG, "Rfid Service");
同时,引入类:
import net.eshion.rfid.RfidServices;
建立一个jni目录,编写c++文件通过jni和java双向调用:
native.cpp:
#define LOG_TAG "net_eshion_RfidServices"
#include
#include
#include "jni.h"
static const char *classPathName = "net/eshion/rfid/RfidServices";
static struct {
jmethodID callbackTest;
} gCallbacksClassInfo;
static jint net_eshion_RfidServices_nativeTest(JNIEnv *env, jclass clazz, jobject callbacksObj) {
//JNIEnv* env = AndroidRuntime::getJNIEnv();
ALOGE("net_eshion_RfidServices-nativeTest 1");
jobject mCallbacksObj = env->NewGlobalRef(callbacksObj);
ALOGE("net_eshion_RfidServices-nativeTest 2");
jlong param = (jlong)100;
ALOGE("net_eshion_RfidServices-nativeTest 21 -callbacksObj=%p", callbacksObj);
jint temp = env->CallIntMethod(mCallbacksObj,
gCallbacksClassInfo.callbackTest,
param);
ALOGE("net_eshion_RfidServices-nativeTest 22");
int wmActions = (int)temp;
ALOGE("net_eshion_RfidServices-nativeTest 3 -temp=%d", temp);
int result = 500;
env->DeleteGlobalRef(mCallbacksObj);
ALOGE("net_eshion_RfidServices-nativeTest 4");
ALOGE("net_eshion_RfidServices-nativeTest ---wmActions= %d -result= %d",wmActions, result);
return result;
}
static JNINativeMethod methods[] = {
{"nativeTest", "(Lnet/eshion/rfid/RfidServices$Callbacks;)I", (void*)net_eshion_RfidServices_nativeTest },
};
/*
* Register several native methods for one class.
*/
static int registerNativeMethods(JNIEnv* env, const char* className,
JNINativeMethod* gMethods, int numMethods)
{
jclass clazz;
clazz = env->FindClass(className);
if (clazz == NULL) {
ALOGE("Native registration unable to find class '%s'", className);
return JNI_FALSE;
}
if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
ALOGE("RegisterNatives failed for '%s'", className);
return JNI_FALSE;
}
return JNI_TRUE;
}
/*
* Register native methods for all classes we know about.
*
* returns JNI_TRUE on success.
*/
static int registerNatives(JNIEnv* env)
{
if (!registerNativeMethods(env, classPathName,
methods, sizeof(methods) / sizeof(methods[0]))) {
return JNI_FALSE;
}
jclass clazz = env->FindClass("net/eshion/rfid/RfidServices$Callbacks");
gCallbacksClassInfo.callbackTest = env->GetMethodID(clazz, "callbackTest", "(J)I");
ALOGE("registerNativeMethods '%p'", gCallbacksClassInfo.callbackTest);
if (gCallbacksClassInfo.callbackTest == NULL) {
return JNI_FALSE;
}
return JNI_TRUE;
}
// ----------------------------------------------------------------------------
/*
* This is called by the VM when the shared library is first loaded.
*/
typedef union {
JNIEnv* env;
void* venv;
} UnionJNIEnvToVoid;
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
UnionJNIEnvToVoid uenv;
uenv.venv = NULL;
jint result = -1;
JNIEnv* env = NULL;
ALOGI("JNI_OnLoad");
if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) {
ALOGE("ERROR: GetEnv failed");
goto bail;
}
env = uenv.env;
if (registerNatives(env) != JNI_TRUE) {
ALOGE("ERROR: registerNatives failed");
goto bail;
}
result = JNI_VERSION_1_4;
bail:
return result;
}
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
# This is the target being built.
LOCAL_MODULE := librfid_jni
# All of the source files that we will compile.
LOCAL_SRC_FILES:= \
native.cpp
# All of the shared libraries we link against.
LOCAL_SHARED_LIBRARIES := \
libutils
# No static libraries.
LOCAL_STATIC_LIBRARIES :=
# Also need the JNI headers.
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE)
# No special compiler flags.
LOCAL_CFLAGS +=
include $(BUILD_SHARED_LIBRARY)
用eclipse编写一个android测试应用测试接口:
首先导入上面
net.eshion.rfid
动态库生成的jar包:
MainActivity.java:
package com.example.rfidtest;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
import net.eshion.rfid.RfidAdapter;
public class MainActivity extends Activity {
RfidAdapter mRfidAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRfidAdapter = RfidAdapter.getRfidAdapter(this);
int result = mRfidAdapter.test();
Log.e("RfidAdapter", "-----result="+ result);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
}