NDK[1]ndk、ADT、AndroidStudio

【参考链接】

向您的项目添加 C 和 C++ 代码https://developer.android.com/studio/projects/add-native-code.html

AndroidStudio 2.2 CMAKE 高效NDK开发http://m.blog.csdn.net/l_215851356/article/details/74691147

AndroidStudio进行 JNI /NDK 开发http://blog.csdn.net/zeqiao/article/details/77893167

 

 

ndk其实提供了两样东西

1、.h头文件、.a静态库文件、.so动态库文件

2、编译工具,用于编译生成适用于android系统的动态库文件(.so)

 

以在Windows下开发为例

ndk

先去下载ndk,这里以android-ndk-r14b为例,解压,可以了解一下其中的目录结构

根目录下有ndk-build.cmd文件

platforms目录下有用于各个android版本的不同架构的.h、.a、.so文件,

以android-23版本的arm64架构为例,显然也有jni.h头文件

 

因为后续要使用到ndk-build,所以可以先把ndk根目录添加到环境变量PATH中,使用时直接输入ndk-build即可

 

ADT

为了更直观的理解开发过程,虽然已经不再使用ADT开发工具了,但是还是讲一下ADT下的开发步骤

新建一个Android工程,在MainActivity中定义一个native方法

 Java Code 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

package com.example.helloworldfromc;

import com.example.helloworldfromc.R;
import android.app.Activity;
import android.os.Bundle;

public class MainActivity extends Activity {
    
    
//定义一个native方法
    public native String getStringFromC();
    
    @Override
    
protected void onCreate(Bundle savedInstanceState) {
        
super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

 

先去生成需要的.h文件

在命令行中,定位到上面工程的src目录,执行javah –encoding utf-8 com.example.helloworldfromc.MainActivity,会在src目录下生成com_example_helloworldfromc_MainActivity.h文件

 

然后开始编写c/c++代码

在工程根目录下新建一个jni目录,c/c++相关的代码都会放到此目录下

将.h剪切到此目录下,在此目录下新建一个test.c文件,实现声明的方法,代码如下

(如果是test.cpp文件,执行的时候会出问题,所以这里用的test.c)

 C++ Code 

1
2
3
4
5
6
7
8
9
10

#include "com_example_helloworldfromc_MainActivity.h"
#include<stdio.h>

//public native String helloWorldFromC();
jstring Java_com_example_helloworldfromc_MainActivity_getStringFromC(JNIEnv* env, jobject obj){

    
//通过调用这个方法jstring     (*NewStringUTF)(JNIEnv*, const char*); 就可以返回一个java String类型的字符串
    return (*env)->NewStringUTF(env, "HelloWorldFromC");
}

 

编译还需要编译脚本,新建一个Android.mk文件,内容如下

LOCAL_PATH := $(call my-dir)

 

include$(CLEAR_VARS)

 

#设置打包生成的库文件的名称#会自动生成为libtest.so

LOCAL_MODULE    := test

#对应的C代码的文件

LOCAL_SRC_FILES := test.c

 

include$(BUILD_SHARED_LIBRARY)

 

新建一个Application.mk文件,内容如下,用于设置要生成哪些ABI的.so,这里只要求生成armeabi-v7a的

有关ABI的问题后续会讲

APP_ABI := armeabi, armeabi-v7a

 

此时工程的目录结构为

然后开始编译

将命令行定位到工程根目录,执行ndk-build,

会在工程下

生成obj目录,用于保存编译过程中生成的中间文件,可以删除

生成libs目录,里面有生成的.so,名称为libtest.so

 

接下来就可以在Java中进行调用了

 C++ Code 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

package com.example.helloworldfromc;

import com.example.helloworldfromc.R;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;


public class MainActivity extends Activity {
    
    
//定义一个native方法
    public native String getStringFromC();
    
    
static{
        System.loadLibrary(
"test");//会去Build Path下加载libtest.so
    }

    @Override
    
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    
    
public void onClick(View view){
        Toast.makeText(
this, getStringFromC(), Toast.LENGTH_LONG).show();
    }

}

 

编译生成的apk中就会包含lib文件夹,其中有.so

运行起来可以获取到C代码中返回的字符串

 

 

可以看出

在ADT中,需要自己先手动编译生成.so,然后再编译生成apk

这对于一名程序员来说显然是不能接受的

而在Android Studio中,可以一次编译,自动编译.so并添加到apk中

 

这里使用的Gradle版本为gradle-4.1,GradlePlugin版本为com.android.tools.build:gradle:3.0.0

Android Studio

AndroidStudio提供了两种方式来编译C/C++代码

一种依然是使用make脚本,一种是使用CMake脚本。

这两种方式都需要先在AndroidStudio中设置下ndk的路径,通过修改local.properties文件

 

make

使用make脚本的方式

依然是先在MainActivity中声明native方法,因为一会儿编译的时候会自动先编译so再加入到apk中,所以可以把调用也写了

package com.example.shadowfaxghh.demo;

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

public class
MainActivity extends Activity {

   
@Override
   
protected void onCreate(BundlesavedInstanceState) {
       
super.onCreate(savedInstanceState);
       
setContentView(R.layout.activity_main);

       
System.loadLibrary("test");
       
String str =getStringFromC();
       
Toast.makeText(this, str, Toast.LENGTH_SHORT).show();
   
}

   
public native String getStringFromC();
}

 

通过javah生成.h头文件

有说可以通过自动提示生成,不过我这里没

然后在src/main目录下新建一个目录jni,放入.h。新增一个test.c,实现代码

#include "com_example_shadowfaxghh_demo_MainActivity.h"

JNIEXPORT jstring JNICALL Java_com_example_shadowfaxghh_demo_MainActivity_getStringFromC
       
(JNIEnv *env, jobject jobj) {

   
return (*env)->NewStringUTF(env, "HelloWorld from C");
}

 

新增一个Android.mk文件,内容跟ADT中的相似(不过并不是完全一样,这个是我出错以后Gradle自动生成的,我把它拿出来使用了)

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

#
指定要生成的.so的文件名#libtest.so
LOCAL_MODULE := test

#
链接
LOCAL_LDFLAGS := -Wl,--build-id
LOCAL_LDLIBS := \
  
-llog \
   -lz \
   -lm \

#指定.c源码的路径
LOCAL_SRC_FILES := \
  
C:\Users\ShadowfaxGHH\Desktop\NdkAndroidStudioDemo1\app\src\main\jni\test.c\

LOCAL_C_INCLUDES +=C:\Users\ShadowfaxGHH\Desktop\NdkAndroidStudioDemo1\app\src\debug\jni
LOCAL_C_INCLUDES +=C:\Users\ShadowfaxGHH\Desktop\NdkAndroidStudioDemo1\app\src\main\jni

include $(BUILD_SHARED_LIBRARY)

 

新增一个Application.mk文件,内容跟ADT中的一致。不过这个其实没什么用了,后面Gradle中会再设置ABI,应该会覆盖这里的设置。但是这里如果不设置又会报错。

APP_ABI := armeabi

 

此时工程的目录结构如下


接下来修改module的build.gradle

在android下添加externalNativeBuild块,指定Android.mk文件的路径

externalNativeBuild {

   
ndkBuild {
        path "src/main/jni/Android.mk"
   
}
}

 

在defaultConfig下添加externalNativeBuild块

externalNativeBuild{
   
ndkBuild{

    }
}

 

在defaultConfig下添加ndk块,指定要生成的ABI

ndk{
   
abiFilters 'armeabi-v7a'
}

 

这样就配置完成了,编译assembleDebug, 完成以后,虽然工程中看不到.so,但是app-debug.apk中有lib文件夹,其中有.so

(Application.mk中配置的是armeabi,gradle中配置的是armeabi-v7a,从这里也可以看出gradle的覆盖了Application.mk的)

 

运行起来可以正常调用

 

CMake

现在Google推荐使用新出的编译工具CMake

要使用CMake,需要先在Android SDK中安装

 

然后跟上面一样,在src/main目录下新建一个jni目录,放入.h和test.c文件。不再赘述。

不过不需要.mk了,取而代之的是CMake脚本。

在module根目录下新建一个CMakeLists.txt文件,内容如下

cmake_minimum_required(VERSION 3.4.1)

#
TODO 添加自己的 C/C++源文件
# 指定生成的动态库的名称为testtt#libtesttt.so #源代码的路径为src/...
add_library( testtt
            
SHARED
             src/main/jni/test.c )

# TODO 添加依赖的NDK中的库
find_library( log-lib
             
log )

# TODO 链接
target_link_libraries( testtt
                      
${log-lib} )

 

此时工程的目录结构如下


 

接下来修改module的build.gradle

在android下添加externalNativeBuild块,指定CMakeLists.text文件的路径

externalNativeBuild {

   
cmake {
        path "CMakeLists.txt"
   
}
}

 

在defaultConfig下添加externalNativeBuild块

externalNativeBuild{
   
cmake{
        cFlags ""
       
cppFlags ""
   
}

}

 

在defaultConfig下添加ndk块,指定要生成的ABI (同上面一样)

ndk{
   
abiFilters 'armeabi-v7a'
}

 

这样就配置完成了,编译运行,跟上方效果一致,可以正常调用。

 

这两种方式的gradle的差异对比如下

 

此外,这里只是讲了一下CMake脚本的简单配置,更多问题可查阅参考链接。

 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值