Ubuntu 12.04 搭建ndk编译环境

来源:互联网 发布:厦门软件企业 编辑:程序博客网 时间:2024/04/30 02:07

搭建ndk环境之前,请保证你能新建android项目并能在手机上正常运行

通过AS自动生成ndk环境

更新AS到较新的版本,通过File-setting-Androd SDK下载sdktools(这里不需要启动manager界面)
主要是这三项:cmake, lldb,ndk
待下载完成后,启动android studio,start a new project.
选中Include C++ Support,
如图 选中Include C++ Support
后续均可以默认,直到finish,等待自动生成。

然而,实际运行总那么不尽人意。下面讲讲本人在配置环境是遇到的坑。

坑1:cmake 3.6.3155560 报错,GLIBCXX_2.4.18 required by cmake
可能ndk和cmake的版本不匹配吧,然后,我用AS自己的manager去下载两个新工具,还是报错,同样的错误!
查看GLIBCXX发现
查看是否真的缺少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
这个命令执行的路径需要注意:

  1. AS 在**/src/main/java 下执行javah 命令
    首先需要class文件,因此你需要先通过javac 编译这个java文件以便生成class文件。
  2. 者在project/build/intermediates/classes/debug/目录下执行
    且class的名字一定要是全限定名称,即你的package+name(.class 注:参数是class文件,不是java文件,你可以通过AS工具栏上的build-MakeProject生成class文件,这个文件就在上述的classes/debug/目录下,且不需要后缀)

.h文件生成后,就开始写C(/C++)代码了

此时,你已经能得到一个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 __cplusplusextern "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依赖库

生成的libs依赖

好了,你可以用你的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 !

如果有讲得不对的地方,欢迎大家提出来,共同学习, 谢谢!

0 0
原创粉丝点击