由于编写demo的需要,了解了一下jni接口的实现。本文适合像我熟悉c++却对安卓比较小白速度上手jni。
总体流程
我们搞定c++模块后希望在android层被调用,但实际上接口需要在android层先定好,然后根据android层的接口再转化成jni接口,然后在这些jni接口里使用我们的c++模块代码。可以用中间生成的文件来表示这个流程:
test.java -> test.class -> test.h –> test.c
最后在test.c里面调用我们的c++代码。
准备
如果不够详细,百度可以了,下面那些都很常见的操作。
1. 需要安装一个android studio
2. 配置jdk环境
我们可以直接使用android studio自带的jre。在安装目录下就有(例如我的D:\Android\Android Studio\jre)
在windows上配置以下3个环境变量
JAVA_HOME D:\Android\Android Studio\jre
CLASSPATH %JAVA_HOME%\lib
Path %JAVA_HOME%\bin
path一般已经有的,在后面添加就好了。
3. 配置ndk环境
https://www.jianshu.com/p/708e6bf68ae9
4.建两个目录来分别存放as项目和so生成项目
java层接口
我们先用as建立一个工程放在jni-as下。
然后建立一个java类,这个就是java层的接口了。
package com.test.jnitest;
/**
* Created by Administrator on 2018/3/26.
*/
public class jnitestimpl {
static {
System.loadLibrary("jnitest"); //defaultConfig.ndk.moduleName
}
public native String getVersionString();
}
jnitest是你之后要生成的so库名
我在这先定一个简单的接口函数getVersionString,作用是返回一个版本号
然后在MainActivity里面使用它
package com.test.jnitest;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private TextView tv_version_;
private jnitestimpl jti_;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv_version_ = (TextView) this.findViewById(R.id.tv_version);
jti_ = new jnitestimpl();
tv_version_.setText(jti_.getVersionString());
}
}
可以把自建的helloworld text元素带上id tv_version,或者你再建一个。
然后build。
生成对应的c++头文件
build完后,jni-as\app\build\intermediates\classes\debug\com\test\jnitest\jnitestimpl.class就是java接口类生成的class文件,可以用这个来生成对应的c++头文件。
在jni-ndk下建立一个jni文件夹,打开命令行cd到这目录执行
javah -classpath ../../jni-as/app/build/intermediates/classes/debug -jni com.test.jnitest.jnitestimpl
会生出一个头文件内容为
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_test_jnitest_jnitestimpl */
#ifndef _Included_com_test_jnitest_jnitestimpl
#define _Included_com_test_jnitest_jnitestimpl
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_test_jnitest_jnitestimpl
* Method: getVersionString
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_test_jnitest_jnitestimpl_getVersionString
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
ndk-build出so文件
利用上步生成的头文件我们可以创建一个cpp文件(起名jnitestimpl.cpp)来实现接口函数
//jnitestimpl.cpp
#include "com_test_jnitest_jnitestimpl.h"
JNIEXPORT jstring JNICALL Java_com_test_jnitest_jnitestimpl_getVersionString(JNIEnv *env, jobject obj){
return env->NewStringUTF("jnitest 0.0.1");
}
jni目录下,建立两个ndk-build的配置文件:Android.mk Application.mk
Android.mk
# Copyright (C) 2009 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := jnitest
LOCAL_SRC_FILES := jnitestimpl.cpp
include $(BUILD_SHARED_LIBRARY)
Application.mk
#APP_ABI := all
APP_ABI := armeabi armeabi-v7a
#APP_PLATFORM := android-21
关于这些ndk-build的文档说明,可参考ndk安装目录下的docs。
看一下目录下的东西:
然后在jni-ndk目录下执行
ndk-build
完成后可在jni-ndk\libs下看到生成的so了。
好了,该生产的都搞定了,接下来配置一下就可以运行app了。
as项目编译运行
将以上生成的jni-ndk\libs复制到-jni-as\app\libs。
在app的build.gradle中添加
然后同步一下as项目。
Run app,搞定!!!