Android Studio NDK开发——三步实现HelloWorld一篇就够了



前言: 今日付出的汗水,只为明日不一样的自己。唯有坚持不懈的努力,才能过上自己想要的生活

转载请标明出处:
http://blog.csdn.net/w690333243/article/details/78184056
更多内容请访问【-小沫-专栏】


引言

        之前工作做MTK平台那会,一直用的C语言,现在改做高通平台Android了,C语言也用的少了,这里就借助NDK再锻炼下C语言的编程能力。
相信和大多数人一样,在用一样新东西时,肯定会在网上一阵乱搜,搜索自己需要的相关的信息。
        小编在网上搜索了很多关于NDK开发的(这里关于C语言的基础知识不在单独介绍),开篇HelloWorld,但是照着网上说的敲代码,还是遇到了很多问题,或者使用的软件环境配置不一样,其他人写的时候可能不会有问题,你自己写的时候会出问题,这就需要自己根据错误提示来一步一步解决了。而且其他的介绍真的非常非常复杂,真的有那么难吗,就一个NDK helloworld的demo,不试不知道
NDK HelloWorld真的非常非常简单:三步

一、AS中下载NDK即可
二、添加代码块
static{
System.loadLibrary("hello");
}
public native String getStringFromC();
三、自动生成C文件
四、build项目,运行
这里不需要javah 生成头文件,不需要copy so库文件,真的就三步,试试才知道。

一、环境配置

Android Studio 开发NDK,环境配置真的非常非常简单
就像大多数人一样,在做NDK开发时(自己写demo也好,项目需要也好),可能第一步就是NDK环境配置,小编使用的是Android Studio,故这里讲述的是AS关于NDK开发的环境配置。
网上有的说还要自己单独下载NDK android-ndk-r11c类似这个,完全没必要,在AS中下载即可

1、AS->File->Settings,如图

这里写图片描述
前面带 - 的是可以升级的项,可以不用管,如果前面没有勾选的,勾选,然后下载。NDK这项是必须要下载的,看其他博文,有让下载CMake,LLDB的,小编还没用到这两样,这里一并下载,以防以后出现问题。
下载过的可以略过此步骤。

3、AS->File->Project Structure

如果AS中NDK下载好后,新建项目,你会发现NDK的路径已经配置好了,如果路径没有配置好,自行点击路径栏后面的更多按钮,找到NDK下载到的路径,确定即可
这里写图片描述

2、新建Project

这里新建Project时,不需要勾选C++ support,记住,不用勾选此项(你可以勾选,小编就感觉勾选后再自己配置NDK挺别扭的,因为勾选后,相当于AS很多东西都给你写好了,不需要你手动来添加文件之类的了,再学习配置也就失去了本身的意义,你可以在对NDK的配置及需要写的东西熟练后,再默认勾选。记得当上大学那会,刚学Html时,我们用的是记事本编写的heml,没有用MyEclipse,或者其他的自动化编写软件,一个道理,对里面的结构,内容熟练后可以用自动生成,前期为了锻炼,熟练后是为了省时,高效)。

这里写图片描述

二、代码部分

环境配置就上面那么多,有没有发现真的特别简单,其实环境配置说白了就只需要下载下NDK就可以了(小编不确认需要不需要配置环境变量,还是说小编之前已经配置过环境变量了,这里没有特殊的要求,没有再配置环境变量)
到了撸代码的时候了,有没有很兴奋。

1、so库文件的生成

(1)、
在project的gradle.properties中添加如下代码,表示支持旧版本NDK

android.useDeprecatedNdk=true

这里写图片描述

(2)、

1>在defaultConfig{}中添加如下代码,

ndk {
            moduleName "hello" //模块名,需要和System.loadLibrary("xxx");中的xxx保持一致,必现一致
            abiFilters "armeabi", "armeabi-v7a", "x86"//平台支持,java是跨平台语言,C语言不是,这里添加的是支持的平台,具体含义百度
}

2>在module的build.gradle中添加gradle-experimental插件支持,此插件只是为了便于C文件代码的自动生成,在C代码生成后,需要注释掉此插件,否则项目运行时会报错。记住,在添加此插件后,clean或者rebuild时有可能会报错,不用管,待C文件自动生成(见步骤<3>)后注释掉。

compile 'com.android.tools.build:gradle-experimental:0.7.0'
//仅仅在我们生成jni方法框架时添加,
// 当我们全部添加完JNI方法框架之后,必须注释或者删除掉,否则run的时候就绝对报错

(3)、
在自己的包名下新建java文件,HelloWorld.java

package com.ndk.demo;

/**
 * Created by wangqixu on 2017/10/9.
 */

public class HelloWorld {
    //这里先把static代码块去掉,static代码块是为了调用so库中的方法,和生成so库没有关系。
    //另外,小编在生成.h头文件时,带上static代码块,报错了。
    /*static{
        System.loadLibrary("hello");
    }*/
    public static native String getStringFromC();//如果第(2) 2>步你做了的话,
    你会发现当你写完这行代码时,AS会提示你创建C函数(解释下函数概念,C语言中的函数即相当于java中的方法,只是的称呼不一样),
    点击alter+enter(自动完成快捷键,视你设置的快捷键而定),将会在jni目录下自动创建C文件,如果没有添加gradle-experimental插件支持,是不会提示自动穿件C文件的
}

有没有感觉到,特别的简单。如果(2) 2>步骤完成后,(3)中写完native方法声明后,c函数的实现将可以自动创建完成。
自动生成的hello.c文件(c文件名和java文件名保持一致,这个小编不确定,文件名任意也没有影响)如下

这里写图片描述

return (*env)->NewStringUTF(env, "来自C语言的返回值 哈哈哈哈");//修改其中的return语句
JNIEXPORT jstring JNICALL//注意到没,自动生成的代码jstring和JNICALL之间少了空格,添加上空格(在代码编写过程中不同的人可能会遇到不同的问题,需要你自己看log,错误提示,来解决,有可能其他人使用的AS版本自动生成的是正常的,而你用的AS版本或者插件版本却是有错误的,类似这样的问题自己调试)

c文件编写完成无误后,注释掉(2)中的gradle-experimental插件,clean,rebuild 项目,会在如下图,生成相应的so文件。
这里写图片描述

三、加载so库文件

1、MainActivity.java文件
代码真的特别特别的少

这里写图片描述

只需要这两句即可

package com.ndk.demo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

import org.w3c.dom.Text;

public class MainActivity extends AppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView textView = (TextView)findViewById(R.id.tv);
        textView.setText(HelloWorld.getStringFromC());//关键的就这一句代码,少不少
    }
}

HelloWorld.java

package com.ndk.demo;

/**
 * Created by wangqixu on 2017/10/9.
 */

public class HelloWorld {
    static{
        System.loadLibrary("hello");//加载库文件
    }
    public static native String getStringFromC();
}



运行项目,你会发现效果已经出来了
这里写图片描述

四、疑惑

不知道你会不会有疑问,怎么这么奇怪,网上说的还有生成.h文件 javah ,还要copy so文件呢,怎么这里都没做,运行项目效果就出来了,难道就这么简单吗,我的天,完全颠覆了之前看的NDK的博客,什么情况啊这是??有没有一万个草泥马飞过。对,就是这么简单,因为这只是个demo,本来就特别简单。
1、为何没有生成.h文件:因为不需要生成.h文件,c语言中只有在互相调用函数时(不再解释了,.h文件的作用,自行百度)
有人说了,我就要生成.h文件,怎么办,好办,也简单

(1)、生成.h文件

在AS Terminal中执行

javah -d ../jni -jni com.ndk.demo.HelloWorld

即可在main目录下的jni目录下生成com_ndk_demo_HelloWorld.h文件

E:\Users\NDK\app\src\main\java>javah -d ../jni -jni com.ndk.demo.HelloWorld

解释:此命令相当于在java目录下执行的,-d指定生成生成文件所在的目录 ../jni 表示父目录下的jni文件夹,即main下的jni目录,如果没有此目录,则生成jni文件夹
-jni,意思是生成jni标识的头文件,其实这个参数完全可以不加,暂时没有发现什么意义。
com.ndk.demo.HelloWorld 需要带上包名
注:小编在使用此命令的时候,仅仅是把HelloWorld中的 static代码块注释掉,但是依然报错,索性直接去掉了,没有再报错。
这里写图片描述


如果你的快捷栏没有Terminal,可以
这里写图片描述


或者鼠标右键电脑形状的图标或点击此图标
这里写图片描述
调出Terminal

(2)、手动编写c文件
        copy .h文件中的函数声明部分到c文件,然后编辑此函数(之所以copy,是为了防止书写错误)

        hello.c文件,(文件名可以随意定),如果c文件是自动生成的则文件名应该是和java文件名保持一致的(小编的是这样的)

#include <jni.h>
JNIEXPORT jstring JNICALL Java_com_ndk_demo_HelloWorld_getStringFromC(JNIEnv *env, jclass type){
    return (*env)->NewStringUTF(env, "来自C语言的返回值 哈哈哈哈");
}

        有人说了,那这样的话生成的.h文件不是还是没有用到吗,是的,没有用到,因为helloworld这个demo确实不需要用.h文件,当然你也可以用

#include <com_ndk_demo_HelloWorld.h>
//它的作用其实就是在里面包含了jni.h,c文件中如果加入#include <com_ndk_demo_HelloWorld.h>
//则可以不用写#include <jni.h>,效果是一样的
小编在使用#include <com_ndk_demo_HelloWorld.h>时,
代码编译过程中报错了,故没有使用头文件,另外c文件如果报错,试着将AS新建文件时自动添加的Created by xxx  2017 xxx,类似的这些没用的都去掉,再编译试试。

2、为何没有copy 库文件到libs目录
        之所以没copy:因为不需要copy,项目中有源码C文件,so是c文件生成的,故即使不copy so库文件到jni目录,也可以正常运行项目。之所以说需要copy,是因为你在使用别人的so文件时,一般是没有c代码的(为了代码的安全,比如别人写的一个很牛逼的图像处理算法,做相机美颜时使用的,别人肯定只会给你so文件,不会给你c代码。所以你需要将so库文件copy到 libs目录下),c代码生成的so文件很难反编译,另外c可以操作硬件,运行效率要比java快(不一定是绝对的)

        copy so文件到libs目录下后,你可以删除jni目录,即删除c代码,此时仍然可以运行。如果你不copy so库文件到libs,删除c代码后项目肯定是不能运行的。

五、自动生成so库文件并copy到指定目录的方法

前面说到在build.gradle中添加

ndk {
            moduleName "hello"
            abiFilters "armeabi", "armeabi-v7a", "x86"
    }

        则最后生成的so库目录分别是 armeabi,armeabi-v7a,x86,那如果有很多平台,我是不是要一个一个都写上呢,如果我不知道其他是什么平台怎么办呢?有没有办法解决呢,有,直接在jni目录上鼠标右键,执行ndk-build即可生成so文件,并自动copy到jnilibs目录下。你可能会跃跃欲试,但是最后却发现自己的右键中没有NDK这个命令,奇了怪,又一万个草泥马飞过吧,这都不是事,见下面配置。
鼠标右键,直接执行ndK-build命令
这里写图片描述



执行命令后生成的so库文件,是不是很全,而且build.gradle中也不用添加 ndk{xxx},不用添加
这里写图片描述

1、ndk命令配置,AS-File-Settings,见下图,
这里写图片描述

javah:

Projram              $JDKPath$/bin/javah
Parameters:          -encoding UTF-8 -d ../jni -jni $FileClass$
Working directory:   $SourcepathEntry$\..\java

ndk-build:

Projram              D:\adt\sdk\ndk-bundle\build\ndk-build.cmd //换成你自己的ndk-bundle目录即可
Parameters:          NDK_LIBS_OUT=$ModuleFileDir$/src/main/jniLibs
Working directory:   $ModuleFileDir$\src\main\

        除了ndk-build中 projram换成你自己的ndk-bundle路径外(一般都在sdk目录下xxx\xxx),其他基本一致,关于勾选框的地方,勾选不勾选没有特殊要求,默认就可以

效果图
javah

这里写图片描述

ndk-build
这里写图片描述

别忘确定保存。

2、编写mk文件,文件必须是Android.mk

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello   //模块名,和System.loadLibrary("xxx");xxx必须保持一致
LOCAL_SRC_FILES := helloworld.c  //和你的c文件名对应
include $(BUILD_SHARED_LIBRARY)

helloworld.c 文件

#include <jni.h>
JNIEXPORT jstring JNICALL Java_com_ndk_demo_HelloWorld_getStringFromC(JNIEnv *env, jclass type){
// TODO
return (*env)->NewStringUTF(env, "来自自C语言的返回值  哈哈哈哈");
}

        再说一遍,helloWorld这个demo,真的不需要生成com_ndk_demo_HelloWorld.h头文件,不需要引用com_ndk_demo_HelloWorld.h文件

        当然关于HelloWorld.java这个文件,你也可以不用单独写,里面的代码完全可以放到Activity中,这里只是为了和Activity独立出来,其他地方便于调用,要灵活使用。
        ok,本篇讲完。有疑问可以留言。

参考文档
http://blog.csdn.net/crazymo_/article/details/52804896
http://blog.csdn.net/clovelegent/article/details/52612454
http://blog.csdn.net/tongseng/article/details/53005123
http://blog.csdn.net/u010030505/article/details/51942157
http://www.cnblogs.com/Free-Thinker/p/6169590.html

  • 7
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值