本文讲解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语法和高级用法。