首页 > 其他分享 >android ndk之hello world

android ndk之hello world

时间:2022-12-14 20:01:35浏览次数:53  
标签:NDK ndk C++ 编译 world android Android


前言:Android NDK r7及以上的版本已经集成了Cygwin编译环境,也就是说,我们完全可以抛弃庞大的Cygwin了。


r6及以下版本,也可以抛弃几个G的完整版,使用精简过的Mini-Cygwin来编译,解压后大小仅9M,但短小巧精悍,完全可以满足Android NDK的开发。




下面进入正题,r7及以上版本,跟着我的这篇帖子,完成环境的升级吧!!!

参考官网:​​http://tools.android.com/recent/usingthendkplugin​

1、首先确认自己的ADT版本,NDK plugin的支持是在ADT 20及以后的版本。

2、安装Android Native Development Tools(该组件集成C/C++开发环境),既在安装ADT工具的时候勾选NDK组件,一路next后重启Eclipse使插件生效。

android ndk之hello world_so库

3、下载NDK工具​​http://developer.android.com/tools/sdk/ndk/index.html​​,我使用的是最新的android-ndk-r8e-windows-x86.zip,下载完后解压缩。

4、Eclipse -> Window -> Preferences -> Android -> NDK,设置NDK为刚刚解压缩的工具包路径。

android ndk之hello world_Android_02

侯注:做到这里时,控制台报出了一个错误:“Unable to launch cygpath. Is Cygwin on the path”,因为我的ndk是之前安装的,并没有专门设置环境变量。按照​​这个链接​​中的引导解决:

  1. Head to the project's properties. If you're using Windows, the shortcut is ​​Alt + Enter​​; or simply right-click the project name to find its properties.
  2. Go to the ​​C/C++ Build​​ section; under ​​Builder Settings​​ tab in ​​Build command:​​ text box you're likely to find something similar to that below, if it's empty then type in the similar text - namely:​​${NDKROOT}/ndk-build.cmd​​ where ​​NDKROOT​​, as its name implies, refers to the path where your NDK root folder exists. 
  3. Now you must inform eclipse what ​​NDKROOT​​ equates to; as in, where is the NDK root path. You can do this by heading to (in your project's properties) ​​C/C++ Build​​ > ​​Environment​​ > press ​​Add…​
  4. Now add your environment variable named NDKROOT (the ​​Name​​) with the relevant path (​​Value​​). Note that you're to repeat this per NDK project. You would get a window similar to that below. 
  5. Press ​​OK​​ to the New variable input window and then ​​OK​


5、NDK环境基本上已经搭建好,新建一个普通Android项目测试NDK支持。项目右键->Android Tools->Add Native Support...,输入.so库名字后点击Finish

(注意:若项目已经是一个Jni项目,例如NDK example里面的HelloJni,这一步中.so库名字不用填)

android ndk之hello world_android_03

6、现在已经可以Build我们的Jni项目了,选择项目,Project->Build Project,编译我们的c/c++代码,此时项目结构如下,NDK plugin已经为我们添加好了include,已经为我们生成了相应的Android.mk以及

android ndk之hello world_android_04

7、这时,Android NDK环境已经完美搭建好,我们可以在cpp文件中流畅地书写我们的c/c++代码。

(而且当你Alt+/时,已经可以使用自动提示,各种爽歪歪有木有。若你不是用NDK plugin来构建JNI项目,而是选择手动方式,Alt+/是不会提示的)

android ndk之hello world_android_05

8、关于编译,默认情况下:选择项目,Project->Build Project,来重新编译我们的代码。而且每次run项目的时候,也会自动编译.so库。

android ndk之hello world_android_06



一些问题与解决方法:

问题一:Android NDK: WARNING: APP_PLATFORM android-14 is larger than android:minSdkVersion 8 in ./AndroidManifest.xml    (这个是NDK工具的一个BUG,若build Target大于minSdkVersion,则会报这个错误,导致无法运行)
解决方法:
android-ndk-r8e/build/core/add-application.mk第128行把__ndk_warning改为__ndk_info;然后重新build一次项目即可消除错误。
原文:
this problem may be safely fixed by changing this line in add-application.mk from __ndk_warning to __ndk_info链接:
​​​https://code.google.com/p/android/issues/detail?id=39752​

问题二:使用c++来编写本地库,会有一些兼容问题。

(1)直接黏贴HelloJni的stringFromJNI函数过来测试,提示Method 'NewStringUTF' could not be resolved解决方法
改为:将(*env)->NewStringUTF(env, "Hello from JNI !")改为return env->NewStringUTF("Hello from JNI !")即可

原因是:

NDK plugin默认为我们生成的是cpp文件,而C与C++调用函数的参数不一致,因此找不到函数,具体参考jni.h中的定义。cpp文件中形如(*env)->Method(env, XXX)改成env->Method(XXX)即可。



(2)运行c++生成的.so库,若报以下错误:(既找不到函数)

No implementation found for native Lcom/dgut/android/MainActivity;.stringFromJNI ()Ljava/lang/String;

java.lang.UnsatisfiedLinkError: stringFromJNI

at com.dgut.android.MainActivity.stringFromJNI(Native Method)

解决方法:

为供Java调用的c++函数前加入extern "C" 修饰,如:(NDK example里面的cpp文件也是这么声明的,参考hello-gl2)

[java]  ​​view plain​​ ​​copy​​


  1. extern "C" {  
  2.     JNIEXPORT jstring JNICALL Java_com_dgut_android_MainActivity_stringFromJNI( JNIEnv* env, jobject thiz );  
  3. }  
  4.   
  5. JNIEXPORT jstring JNICALL Java_com_dgut_android_MainActivity_stringFromJNI( JNIEnv* env, jobject thiz )  
  6. {  
  7. return env->NewStringUTF("Hello from JNI bear c++");  
  8. }  

原因是:


        首先看看C++中对类似C的函数是怎样编译的:作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。函数被C++编译后在符号库中的名字与C语言的不同。例如,假设某个函数的原型为:void foo( int x, int y );该函数被C编译器编译后在符号库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字(不同的编译器可能生成的名字不同,但是都采用了相同的机制,生成的新名字称为“mangled name”)。_foo_int_int这样的名字包含了函数名、函数参数数量及类型信息,C++就是靠这种机制来实现函数重载的。例如,在C++中,函数voidfoo( int x, int y )与void foo( int x, float y )编译生成的符号是不相同的,后者为_foo_int_float。
        同样地,C++中的变量除支持局部变量外,还支持类成员变量和全局变量。用户所编写程序的类成员变量可能与全局变量同名,我们以"."来区分。而本质上,编译器在进行编译时,与函数的处理相似,也为类中的变量取了一个独一无二的名字,这个名字与用户程序中同名的全局变量名字不同。


        首先上面的部分是2014年的时候写的,也比较老了,还在用cywin等。

Android studio的ndk的正确姿势

1,环境配置

首先我们要确认我们的sdk目录下包含ndk的环境,安装好的NDk一般位于你的sdk文件夹下的ndk-bundle。如若没有的直接更新下就好了。我们需要对ndk的路径进行环境配置


android ndk之hello world_android_07


在系统环境变量里面创建NDK_HOME,然后将将NDK_ROOT追加到Path环境变量下-->;%NDK_HOME%。

使用ndk-build命令,如果出现下面的画面说明环境变量配置成功。


android ndk之hello world_Android_08


接下来我们开始真正的写代码的了,很简单。

1,新建项目,修改build.gradle脚本文件(加入ndk的支持)

defaultConfig {
applicationId "com.xzh.ndkdemo"
minSdkVersion 10
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

ndk{
moduleName "NDKDEMO" //生成的so名字
ldLibs "log", "z", "m" //链接时使用到的库
abiFilters "armeabi", "armeabi-v7a", "x86" //输出指定三种abi体系结构下的so库。目前可有可无
}
}

同时需要修改下gradle.properties的配置,加入如下代码

android.useDeprecatedNdk=true

2,新建MainActivity,新增本地Native方法(为了简单,我们只做一个hello word的调用,复杂情况就不在这里讲解)


public native String getStringFromNative();


android ndk之hello world_android_09

3,新建jni文件


android ndk之hello world_Android_10


3.编译该类得到对应的.h文件,打开studio自带的Terminal面板,javah -jni 具体的类名路径,如我这里的就是

由于我这里编译不成功,所以我就拷贝了之前的。

其实编译成功后,刷新下工程可以看到编译出的.h文件,该文件只是为了辅助我们写出相应的.c文件,使用完了即可删除。如果你对C比较了解,可以自己手写,或者找下会c的同事。


标签:NDK,ndk,C++,编译,world,android,Android
From: https://blog.51cto.com/u_13657808/5938303

相关文章

  • SpringMVC中HelloWorld实现(三)
    我机器的开发环境为:开发工具:EclipseForJavaEE;数据库:MySql5.5.35;运行环境:TomCatV7.0;JDK:JDK1.7.0_45;项目工程为:DynamicWebProject; 一、项目依赖的jar包:[ht......
  • SpringBoot入门:Hello World(一)
       跟随SpringBoot的文档(http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-documentation)学习,前后几章关联才调通代码,煞是痛苦,在......
  • HelloWorld编写与编译
    HelloWorld1.随便新建一个文件夹,存放代码2.新建一个Java文件文件后缀名为.javaHello.java【注意】系统可能没有显示文件后缀名,我们需要手动打开3.编写代码p......
  • Android Studio Gradle编译输出信息乱码
    有时候AndroidStudioGradle编译时输出的信息是乱码,这是因为输出信息和信息的窗口不统一。首先,我们先修改设置里的文件编码Settings->Editor->FileEncodings打开都改......
  • Android 10 开启WiFi后出现弹窗改为出现一次,后面不关闭WiFi,不要弹出提示
    \frameworks\opt\net\wifi\service\java\com\android\server\wifi\AvailableNetworkNotifier.javapackagecom.android.server.wifi;importstaticcom.android.server.......
  • Android11 实现侧键打印走纸
    /frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.javaelseif(keyCode==KeyEvent.KEYCODE_F12){      //XCSWw......
  • Android11 双击亮屏 上层修改
    packagecom.android.systemui.qs.tiles;importandroid.content.BroadcastReceiver;importandroid.content.ComponentName;importandroid.content.Context;import......
  • Android 移动应用性能优化 之 友盟
    移动应用性能优化之友盟​​前言​​​​正文​​​​一、问题​​​​1.ANR​​​​2.NullPointerException​​​​二、友盟使用​​​​1.创建平台应用​​​​2.使......
  • Android 天气APP(七)城市切换 之 城市数据源
    9.城市选择既然是城市切换,那么首先得有城市的数据,数据来源有两种,本地和网络,但是网络数据对手机的网络要求比较高,看起来会延迟很大,所以这里我用本地的数据。也是从网络上找......
  • Linux(fedora 10)Hello World模块
     一个内核模块的例子: #include<linux/init.h>#include<linux/module.h>MODULE_LICENSE("GPL");staticinthello_init(void){printk(KERN_ALERT"hello,world!/n");......