Android Studio上进行NDK编程之Hello world

  • Post author:
  • Post category:其他



Android Studio很早之前就支持NDK编程了,但是资料比较少,照着网上的写,一大堆的坑,各种错误一堆。




经过不断的努力,弄出了第一个能正常奔跑的NDK示例——Hello world。现整理如下:




项目地址:

https://github.com/Mingyueyixi/SimpleNDK


我的配置:




Android Studio 2.3


Gradle 3.3


NDK 14


OpenJDK 8




话说,应该是目前最新的配置了。




总结一下流程:



1、下载与安装NDK(最好连CMake,LLDB也下了)



2、配置NDK环境变量(如果需要到处运行)



3、为项目配置NDK



4、编写java以及需要相关的C/C++代码



5、编译C/C++代码生成.so库文件



6、引入库文件,编译/调试/运行APP。






——————-





1、第一件要事就是下载NDK。


可以直接在Android Studio下载,也可以单独去下载。Android Studio下载的NDK默认放置于“android-sdk/ndk-bundle”路径下。


NDK版本集合:


https://developer.android.google.cn/ndk/downloads/revision_history.html





NDK的官方示例:

https://github.com/googlesamples/android-ndk

(NDK10自带,13、14没看见)




在Android Studio下载更方便,Android Studio下载的是最新版——NDK14。顺便连CMake和LLDB也下载了。


CMake


: C/C++的


外部构建工具,可以提示代码什么的。



LLDB: 调试本地代码的工具。







如图:














2、配置NDK环境变量。


如果想要想java命令一样在CMD上,各种路径下到处跑ndk的命令,需要设置。最好设置。


类似java,将NDK安装目录丢到Path中即可。如:


新建变量


变量名:NDK_HOME


变量值:“D:\Programming\Android\android-sdk\ndk-bundle”










3、为项目配置NDK



在开的项目的Android Studio中依次打开“File——Project Structure”,设置NDK路径并确定:









或者直接在项目的“local.properties”文件中添加NDK路径:




## This file is automatically generated by Android Studio.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must *NOT* be checked into Version Control Systems,
# as it contains information specific to your local configuration.
#
# Location of the SDK. This is only used by Gradle.
# For customization when using a Version Control System, please read the
# header note.
#Wed Mar 22 18:21:45 CST 2017
ndk.dir=D\:\\Programming\\Android\\android-sdk\\ndk-bundle
sdk.dir=D\:\\Programming\\Android\\android-sdk




配置完,我感觉最好重启一下。。。我擦,未知原因,我的头一次使用ndk-build命令时提示

不是内部或外部命令,也不是可运行的程序或批处理文件。





4、编写java以及相关的C/C++代码。


(1)新建JniLoader类。如:




package yue.excample.hello;

/**
 * Created by Yue on 2017/3/19.
 */

public class JniLoader {
    static {
        System.loadLibrary("firstndk");
    }
    public native String getHelloString();

}




(2)编写C/C++文件。


在“项目/app/src/main/jni”路径下(没有jni文件夹就创建)新建C++资源文件,名字随便写,尽量规范易懂。我取名为JniLoaderndk.cpp,内容如下:

#include <string.h>
#include <jni.h>
//#include "yue_excample_hello_JniLoader.h"
//按照C语言规则编译。jni依照C的规则查找函数,而不是C++,没有这一句运行时会崩溃报错:
// java.lang.UnsatisfiedLinkError: Native method not found:
extern "C"{

JNIEXPORT jstring JNICALL Java_yue_excample_hello_JniLoader_getHelloString
(JNIEnv *env, jobject _this)
{
//return (*env)->NewStringUTF(env, "Hello world from jni)");//C语言格式,文件名应为xxx.c
return env->NewStringUTF((char *)"Hello world from jni");//C++格式,文件名应为xxx.cpp
}
}



注意事项:如果编写的是C++代码,而不是C,应当注意使用extern “C”{};上述代码注释部分有说明。


比较固定的写法,其中Java层的native方法getHelloString()对应的名称为

Java_yue_simplendk_JniLoader_getHelloString,是一种全称的写法,Java代表java方法,接着是包名+方法名。包名的分隔符“.”号用“_”代替。



两个参数貌似乎是C/C++的指针和java对象。







如果需要编写C/C++的头文件,可以使用javah命令来快速生成。首先要找到.class文件所在,没有需要先Make Project 一下。



Android Studio和eclipse目录不同,Android Studio在“项目/app/build/intermediates/classes/debug”下。



使用terminal进入该目录下





执行“


javah -jni yue.simplendk.JniLoader


”命令。



JniLoader类要写全名,即带包名的方式,不能直接为“


javah -jni JniLoader


”,会提示找不到类文件。成功则在debug目录下直接生成了规范的头文件了,丢过去用即可。














(3)Activity中使用:




package yue.excample.hello;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class HelloActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_hello);
        JniLoader jniLoader = new JniLoader();
        TextView tv_mess = (TextView) findViewById(R.id.tv_message);
        tv_mess.setText(jniLoader.getHelloString());
    }
}






5、编译C/C++文件为.so库。




编写Android.mk和Application.mk文件。







Android.mk指定.so文件名称:






LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

# 要生成的.so库名称。java代码System.loadLibrary("firstndk");加载的就是它
LOCAL_MODULE := firstndk

# C++文件
LOCAL_SRC_FILES := JniLoaderndk.cpp

include $(BUILD_SHARED_LIBRARY)







Application.mk指定生成不同平台的.so库:








# 注释掉了,不写会生成全部支持的平台。目前支持:
# APP_ABI := armeabi arm64-v8a armeabi-v7a mips mips64 x86 x86_64








三种常用平台,不写则全部生成(

armeabi arm64-v8a armeabi-v7a mips mips64 x86 x86_64

)。架构不同有的不兼容。不写则全部生成。



在Terminal中使用cd 命令跳转到“jni”目录下,使用“ndk-build”命令编译C/C++文件。




生成结果如图:



题外话:



快速获取指定路径:Android Studio中选中文件(夹),Ctrl+Shift+C复制路径,相当于鼠标右击——Copy Path。


然后cd+粘贴路径即可。


另一个方法就直接跳转过去了:拖文件(夹)到Tirminal中。但是这么做的弊端是,清空了缓存的命令,也就是历史


命令不能调出来了(windows的CMD中不会,只会把路径字符串放过来)。





6、引入.so文件,调试、运行





首先,马上要

注意ndk-build命令生成的.so文件默认在src/main/libs目录下,此目录非Android Studio 默认的.so库目录,直接调试/运行会报错。要将.so文件全部移动到src/main/jniLibs中去。默认目录,可以修改。





打开app目录下的build.gralde,在android 节点下添加sourceSets部分,设置jni库的资源路径:

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"
    defaultConfig {
      ……
    }
    sourceSets {
        main {
            jniLibs.srcDirs = ['src/main/libs']//默认为jniLibs
        }
    }
    buildTypes {
        ……
    }
}







之后尝试调试/运行了,结构报错:




Error:Execution failed for task ‘:app:compileDebugNdk’.

> Error: Your project contains C++ files but it is not using a supported native build system.

Consider using CMake or ndk-build integration with the stable Android Gradle plugin:

https://developer.android.com/studio/projects/add-native-code.html

or use the experimental plugin:

https://developer.android.com/studio/build/experimental-plugin.html.










说的是项目包含C++文件,但是却没有使用能够支持的构建系统。考虑使用CMake,或者稳定的ndk集成插件进行构建。




看来,新版的有所问题。使用Cmake,额,这又是什么东东…好吧先不用它。。查资料,是这么解决的:在项目下的gradle.properties中增加一句:








android.useDeprecatedNdk=true





使用过时的NDK。











再次编译运行:


















——end




























版权声明:本文为Mingyueyixi原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。