android framework 添加Binder Services,链接动态库,以及使添加的Services与native双向调用

内容比较杂,先说下需求:

应用层需要一套接口,这套接口完成与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;
    }

}

RfidAdapter类是上述所说的管理类,是单例模式,提供getRfidAdapter方法,返回对象。

getServiceInterface获得Binder Services,test方法实际调用的是Binder Services的test方法。


由上看出我们需要编写一个AIDL

IRfidAdapter.aidl:

package net.eshion.rfid;

/**
 * @hide
 */
interface IRfidAdapter
{
    int test();
}

还需要一个Binder Services去实现AIDL的接口

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))

还需要将动态库的module加入到core.mk或者common.mk或者具体项目配置的mk, 这样system/framework/ 下才会生成jar包

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;
}

编写一个mk,编译这个cpp文件成动态库:

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包:

android framework 添加Binder Services,链接动态库,以及使添加的Services与native双向调用_第1张图片

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;
	}

}

完成测试!


你可能感兴趣的:(android,jni,framework,Binder,Services)