内容比较杂,先说下需求:
应用层需要一套接口,这套接口完成与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 <utils/Log.h> #include <stdio.h> #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; } }