本文讲解android p系统中的JAVA层使用Jni调用C++ Libraries层的方法;并且新增自己的so库和自己的Jni调用代码,以便熟悉安卓系统中的Jni调用方法。

Android P系统中的Jni通过动态注册的方式,请参考Jni基础方法解析:

安卓原生代码解析

使用音频架构中的AudioTrack为例,AudioTrack是framework层供上层应用创建AudioTrack的接口API,以便上层应用App使用AudioTrack进行播放某种音频,使用AudioTrack向下层调用的过程:

1.framework层java代码

代码路径:/frameworks/base/media/java/android/media/AudioTrack.java

创建AudioTrack对象,执行构造函数,主要是传递播放音频的一些参数,并且向C++ Libraries层创建一个AudioTrack对象。从下面代码中可以看到,native_setup()就是调用了Jni接口,从而调用到C++ Libraries层,我们往下看native_setup的定义。

private AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
            int mode, int sessionId, boolean offload) throws IllegalArgumentException {
    super(attributes, AudioPlaybackConfiguration.PLAYER_TYPE_JAM_AUDIOTRACK);
	
	//省略一部分代码
	...

    /* native层初始化
    * 这里调用了jni代码,/frameworks/base/core/jni/android_media_AudioTrack.cpp中android_media_AudioTrack_setup()函数
    * 最终将调用Audio Library层,frameworks/av/media/libaudioclient/AudioTrack.cpp,创建native AudioTrack object
    * 在Audio Library层,将执行lpTrack->set(...)函数,后面将详细讲解set()函数
    */
    // native initialization
    int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes,
            sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat,
            mNativeBufferSizeInBytes, mDataLoadMode, session, 0 /*nativeTrackInJavaObj*/,
            offload);
    if (initResult != SUCCESS) {
        loge("Error code "+initResult+" when initializing AudioTrack.");
        return; // with mState == STATE_UNINITIALIZED
    }

	//省略一部分代码
   	...
}

可以看到AudioTrack.java中定义了一些native 接口:

private native final int native_setup(...);
    private native final void native_finalize();
    public native final void native_release();
    private native final void native_start();
2.framework层jni代码

代码路径:/frameworks/base/core/jni/android_media_AudioTrack.cpp

android_media_AudioTrack.cpp中采用动态注册方式注册Jni接口。

android_media_AudioTrack.cpp就是AudioTrack的Jni文件,该Jni文件添加在\frameworks\base\core\jni\Android.bp文件中进行编译,该部分Jni编译成libandroid_runtime.so文件,同时,我们可以看到Android.bp中导入了libaudioclient.so这个库,这个库就是实现AudioTrack的C++层代码。

android_media_AudioTrack.cpp定义了JNI的数据结构,JNINativeMethod是一个Java和 C++函数的映射表数组,并在其中描述了函数的参数和返回值;

static const JNINativeMethod gMethods[] = {
    // name,              signature,     funcPtr
    {"native_start",         "()V",      (void *)android_media_AudioTrack_start},
    {"native_stop",          "()V",      (void *)android_media_AudioTrack_stop},
    {"native_pause",         "()V",      (void *)android_media_AudioTrack_pause},
    {"native_flush",         "()V",      (void *)android_media_AudioTrack_flush},
    {"native_setup",     "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJZ)I", (void *)android_media_AudioTrack_setup},
    {"native_finalize",      "()V",      (void *)android_media_AudioTrack_finalize},

	//省略一部分代码
	//...
}

我们可以看到native_setup方法所映射的C++方法是android_media_AudioTrack_setup方法。

接下来我们看android_media_AudioTrack_setup方法的实现:

//包含了libaudioclient.so中的头文件 #include <media/AudioTrack.h>

static jintandroid_media_AudioTrack_setup(){
	//省略一部分代码
	//...
	
	// C++层代码,创建了一个AudioTrack对象,并返回lpTrack,lpTrack可以去设置音频参数和注册回调函数
    // create the native AudioTrack object
    lpTrack = new AudioTrack();
    
	//省略一部分代码
	//...
}
3.C++ Libraries层代码

代码路径:frameworks/av/media/libaudioclient/AudioTrack.cpp

在Jni的Android.bp中,我们看到编译包含了引入libaudioclient.so这个库,Jni直接可以调用AudioTrack.cpp中的函数。

lpTrack = new AudioTrack();中的AudioTrack就是libaudioclient.so中的对象。

新增自己的JNI调用

上一章节讲到的是Android 9.0系统中的Jni调用,依赖于写好的libandroid_runtime.so库和C++层libaudioclient.so,但是实际开发项目中,有可能需要些我们自己的so库和提供给上层的Jni接口。下面主要讲解我们自己新增加的Jni调用。

上一章节我们从Java --> Jni --> C++的流程进行分析的,我们新增JNI的时候,我们采用C++ --> Jni --> Java 的方式进行讲解。

1.C++ Libraries库代码编写

首先我们在\framework\目录下新建一个文件夹jnidemo,并在jnidemo下新建一个AdCamera.cpp的C++文件。

根据名字可以看出AdCamera.cpp中是一些关于Camera摄像头的一些操作,我们增加一些实现方法。
AdCamera.cpp代码:

#define LOG_TAG "AdCamera"
#define LOG_NDEBUG 0

#include "AdCamera.h"
#include <utils/Log.h>

// ----------------------------------------------------------------------------
using namespace android;
//构造函数
AdCamera::AdCamera(){
	ALOGI("AdCamera");
}

//构造函数
AdCamera::AdCamera(int32_t id, int32_t facing, int32_t orientation, char *path)
    : mId(id)
{
	ALOGI("%s:AdCamera,mId=%d: new camera device", __func__, mId);
	ALOGI("%s:AdCamera,facing=%d: new camera device", __func__, facing);
	ALOGI("%s:AdCamera,orientation=%d: new camera device", __func__, orientation);
	strncpy(mDevPath, path, CAMAERA_FILENAME_LENGTH);
    mDevPath[CAMAERA_FILENAME_LENGTH-1] = 0;
}
//析构函数
AdCamera::~AdCamera()
{
	ALOGI("%s:%d: destroy camera device", __func__, mId);
}

//供上层应用打开的接口
int32_t AdCamera::openDev()
{
    ALOGI("%s:%d: Opening camera device", __func__, mId);
    return 0;
}

AdCamera.h代码:

#ifndef _ADCAMERA_H_
#define _ADCAMERA_H_

#include <inttypes.h>
#include <string.h>

#define CAMAERA_FILENAME_LENGTH 256
namespace android {
	class AdCamera 
	{
	public:
		AdCamera();
	    AdCamera(int32_t id, int32_t facing, int32_t orientation, char* path);
	    ~AdCamera();
		int32_t openDev();
	private:
		char mDevPath[CAMAERA_FILENAME_LENGTH];
		// Identifier used by framework to distinguish cameras
	    int32_t mId;
	};
}; // namespace android
#endif // ADCAMERA_H_

接下来我需要编写Android.bp文件,Android.bp编译文件如下:

cc_library_headers {
	name: "libAdCamera_headers",
	vendor_available: true,
    header_libs: [
        "libbase_headers",
    ],
    export_header_lib_headers: [
        "libbase_headers",
    ],
}

cc_library_shared {
    name: "libAdCamera",

	srcs: [
		"AdCamera.cpp",
	],

    shared_libs: [
        "liblog",
	],

    header_libs: ["libAdCamera_headers"],
    export_header_lib_headers: ["libAdCamera_headers"],
}

这样在\framework\jnidemo\下面进行mm编译,就可以编译生成libAdCamera.so文件。

2.framework层JNI接口的增加

Jni接口增加在/frameworks/base/core/jni目录下,新增:
\frameworks\base\core\jni\android_media_AdCamera.cpp
\frameworks\base\core\jni\android_media_AdCamera.h

android_media_AdCamera.cpp代码如下:

#include "android_media_AdCamera.h"

#define LOG_TAG "AdCamera"
#define LOG_NDEBUG 0
#include <nativehelper/JNIHelp.h>
#include <nativehelper/JniConstants.h>
#include "core_jni_helpers.h"
#include <utils/Log.h>
#include <AdCamera.h>

// ----------------------------------------------------------------------------
using namespace android;

//Jni动态注册
//上层Java调用Class路径
static const char* const kClassPathName = "android/media/AdCamera";

// ----------------------------------------------------------------------------
static void android_jnidemo_AdCamera_openDev(JNIEnv *env, jobject thiz)
{
	ALOGI("android_jnidemo_AdCamera_openDev");
	
	char* path= (char*)"/dev/video0";
	
	AdCamera *lpAdCamera = new AdCamera(0,0,0,path);
	lpAdCamera->openDev();
	//openDev();
}

// ----------------------------------------------------------------------------
// Jni动态注册
static const JNINativeMethod gMethods[] = {
    // name,                  signature,     funcPtr
    {"native_open_dev",         "()V",      (void *)android_jnidemo_AdCamera_openDev},
};

// ----------------------------------------------------------------------------
// Jni动态注册
// 需要在AndroidRuntime.cpp中加入REG_JNI(register_android_jnidemo_AdCamera),进行初始化
// AndroidRuntime.cpp中加入extern int register_android_jnidemo_AdCamera(JNIEnv *env);
int register_android_jnidemo_AdCamera(JNIEnv *env)
{
	// must be first
    int res = RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
    
    // get jclass
    //jclass adCameraClass = FindClassOrDie(env, kClassPathName);
    
    ALOGI("register_android_jnidemo_AdCamera");
    
    return res;
}

android_media_AdCamera.h代码如下:

#ifndef ANDROID_JNIDEMO_ADCAMERA_H
#define ANDROID_JNIDEMO_ADCAMERA_H

#include "jni.h"
#include <utils/StrongPointer.h>

namespace android {

	class AdCamera;
	
}; // namespace android
#endif // ANDROID_JNIDEMO_ADCAMERA_H

这部分的代码是加载libandroid_runtime.so库中的,并没有新建自己的Jni的so库。所以一部分Jni动态注册是在AndroidRuntime.cpp中完成的。注册函数register_android_jnidemo_AdCamera()也是在AndroidRuntime.cpp中完成的。

\frameworks\base\core\jni\Android.bp中加入文件android_media_AudioTrack.cpp,编译生成最新的libandroid_runtime.so库文件。

3.Java层代码

Java层代码放在了开放API,media模块里面,路径为:frameworks\base\media\java\android\media.

frameworks\base\media\java\android\media\AdCamera.java代码如下:

package android.media;
import android.util.Log;

public class AdCamera{
	private static final String TAG = "android.media.AdCamera";
	public AdCamera(){
		Log.d(TAG, "sunxiaolin,shine:AdCamera,native_open_dev=");
		//调用Jni接口函数
		native_open_dev();
	}

	public int printAdCamera(){
		Log.d(TAG, "sunxiaolin,shine:printAdCamera");
		return 2;
	}

	//Jni层JNINativeMethod 中注册的Jni接口函数
	private native final void native_open_dev();
}

上层应用程序就可以通过**import android.media.AdCamera;**来使用AdCamera.java文件,从而调用Jni接口,最后调用libAdCamera.so中的函数。当然这个上层应用必须在系统中编译,才能使用API类AdCamera.java。

总结

上面通过Android P系统中的AudioTrack流程,讲解了从java层到jni接口层到so库C++层的一个调用过程;
也自己新建了libAdCamera.so库文件,讲解了AdCamera从java层到jni接口层到so库C++层的一个调用过程;

Jni的基础请参考:

后续会讲解更详细的JNI语法和高级用法。