搭建ndk环境之前,请保证你能新建android项目并能在手机上正常运行
通过AS自动生成ndk环境
更新AS到较新的版本,通过File-setting-Androd SDK下载sdktools(这里不需要启动manager界面)
主要是这三项:cmake, lldb,ndk
待下载完成后,启动android studio,start a new project.
选中Include C++ Support,
如图
后续均可以默认,直到finish,等待自动生成。
然而,实际运行总那么不尽人意。下面讲讲本人在配置环境是遇到的坑。
坑1:cmake 3.6.3155560 报错,GLIBCXX_2.4.18 required by cmake
可能ndk和cmake的版本不匹配吧,然后,我用AS自己的manager去下载两个新工具,还是报错,同样的错误!
查看GLIBCXX发现
好吧,却是没有!在没网没root权限的linux上,尝试去安装3.4.18这个库,遗憾的发现只有rpm包,ok下载安装包安装(本人推测是可行的)。
但在ubuntu下不能直接安装rpm包,需要使用alien工具(下载命令:sudo apt-get install alien)将rpm转换成deb包安装(本人推测也是可行的)。
$ alien ***.rpm
$ dpkg -i ****.deb
但在没root权限的linux中,就算将alien安装成功,执行alien 转换 rpm安装包也会失败,非root用户不能转换文件格式。
所以,放弃这个方法了。方案转换为:使用ndk 手动build工程,手动操作也有一个好处,你知道各个步骤怎么协调的(不要太依赖自动话工具,除非你已经懂了自动化构建的流程,那个时候,你需要节约时间,是完全没有问题的)。
直接使用ndk build
坑2:将java文件生成h头文件失败
命令:javah jni ***.***.***.ClassName
这个命令执行的路径需要注意:
- AS 在**/src/main/java 下执行javah 命令
首先需要class文件,因此你需要先通过javac 编译这个java文件以便生成class文件。 - 者在project/build/intermediates/classes/debug/目录下执行
且class的名字一定要是全限定名称,即你的package+name(.class 注:参数是class文件,不是java文件,你可以通过AS工具栏上的build-MakeProject生成class文件,这个文件就在上述的classes/debug/目录下,且不需要后缀)
此时,你已经能得到一个h文件,里面包含了你声明的public native method(只是看起来怪怪的,仔细看你就能看懂了)。
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_lintan_jniapplication_JniJava */
#ifndef _Included_com_lintan_jniapplication_JniJava
#define _Included_com_lintan_jniapplication_JniJava
#ifdef __cplusplus
extern "C" {
#endif
/* * Class: com_lintan_jniapplication_JniJava * Method: max * Signature: (II)I */
JNIEXPORT jint JNICALL Java_com_lintan_jniapplication_JniJava_max
(JNIEnv *,jobject,jint,jint);
/* * Class: com_lintan_jniapplication_JniJava * Method: strFromJni * Signature: ()Ljava/lang/String; */
JNIEXPORT jstring JNICALL Java_com_lintan_jniapplication_JniJava_strFromJni
(JNIEnv *,jobject);
#ifdef __cplusplus
}
#endif
#endif
第15行末尾就是你的method名字,com开始就是你的包名类名等
而对应的我的java文件是这样的:
package com.lintan.jniapplication;
/** * Created by **** on 11/5/16. */
public class JniJava {
/** * 返回两个整数中较大的一个 */
public native int max(int a,int b);
/** * 看看熟悉的Hello World */
public native String strFromJni();
}
此时,你需要创建一个jni(cpp 也行)的文件夹,路径为”src/main/jni”
然后,你可以写你的C代码了,注意include的头文件
这是我的C文件
//
// Created by **** on 11/5/16.
//
#include <jni.h>
#include "com_lintan_jniapplication_JniJava.h"
JNIEXPORT jint JNICALL Java_com_lintan_jniapplication_JniJava_max
(JNIEnv * env,jobject obj,jint a,jint b) {
return (a > b) ? a : b;
}
JNIEXPORT jstring JNICALL Java_com_lintan_jniapplication_JniJava_strFromJni
(JNIEnv * env,jobject obj) {
return env->NewStringUTF("Hello World");
到这一步,代码写完了,怎么才能让java调用到这个max方法呢?
干货内容
在src/main/下执行ndk-build
报错,没有Android.mk文件!C语言的编译跟java不一样,so针对这段C代码需要用gcc 去编译,那么Android.mk文件写在哪儿?我觉得应该写到jni这个目录里。以后你也好看。
#以下两行代码必须,call my-dir 将返回Android.mk所在的目录
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# native这个单词要记住了,你以后添加的就是这个库
LOCAL_MODULE := "native"
# hello 这个是一个名字,可以随便起一个,此行代码也可以不要,注释而已
LOCAL_PACKAGE_NAME := hello
#这个就是C(CPP)文件了,里面是你的native方法的实现
LOCAL_SRC_FILES := CalculateTest.cpp
# 编译成共享库
include $(BUILD_SHARED_LIBRARY)
此时还没完,因为只告诉了需要编译,要编译出那些平台的呢,因此还需要在新建一个Application.mk的文件,编译出多个平台的so
#注意字别写错了,平台之间用空格隔开。
#就算ndk-build 没报错,在点击AS的Build是会报错,
#错误内容,大概是就setup.mk 说PROJECT_PATH NULL,这里注意。
#笔者当时将x86_64敲成了x86_62。。。
APP_ABI := x86_64 armeabi armeabi-v7a arm64-v8a x86 mips
然后ndk-build,成功后,你会发现在你的IDE工程窗口多一个libs目录,且里面就是你编译出来的so依赖库
好了,你可以用你的native方法了,笔者使用native方法的代码为:
static {
//加载你的so
//libs文件夹下的so名字是添加了前缀的,不用管。
System.loadLibrary("native");
}
// 这是包含native方法的类
JniJava jniJava = new JniJava();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = (TextView) findViewById(R.id.test);
//调用max方法
String txt = jniJava.strFromJni() + " from Jni !";
tv.setText(txt);
}
保险起见,还是在app的build.gradle文件(不是project的build.gradle)里添加依赖
compile fileTree(dir: 'libs',include: ['*.jar','**/*.so'])
此时,你可以尝试AS的build,没问题。
Run,的时候发现,界面crash掉了,log发现,System.loadLibrary报错。。。。
找不到这个so,哦,AS在Run的时候,还是已gradle的方式编译+run,那么,我们ndk编译出来的so不认识吗?
因此要告诉gradle(在app build.gradle)defaultConfig下添加如下代码
ndk{
moduleName "native"
abiFilters("x86","x86_64","armeabi","armeabi-v7a","arm64-v8a","mips","mips64")
}
到此,点击AS的熟悉Run绿色三角形箭头,success !
如果有讲得不对的地方,欢迎大家提出来,共同学习, 谢谢!