android studio中使用ndk编译.so文件,调用C/C++代码(jni编程)

一.前言
什么是NDK?
NDK全称是Native Development Kit,NDK提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,并能自动将so和Java应用一起打包成apk。NDK集成了交叉编译器(交叉编译器需要UNIX或Linux系统环境),并提供了相应的mk文件隔离CPU、平台、ABI等差异,开发人员只需要简单修改mk文件(指出“哪些文件需要编译”、“编译特性要求”等),就可以创建出so。
为什么使用NDK?
1、代码的保护。由于apk的java层代码很容易被反编译,而C/C++库反汇难度较大。
2、可以方便地使用现存的开源库。大部分现存的开源库都是用C/C++代码编写的。
3、提高程序的执行效率。将要求高性能的应用逻辑使用C开发,从而提高应用程序的执行效率。
4、便于移植。用C/C++写得库可以方便在其他的嵌入式平台上再次使用。
什么是JNI?
JNI的全称是Java Native Interface,它提供了若干的API实现了Java和其他语言的通信(主要是C和C++)。
为什么使用JNI?
JNI的目的是使java方法能够调用c实现的一些函数。
安卓中的so文件是什么?
Android中用到的so文件是一个c++的函数库。在android的JNI中,要先将相应的C语言打包成so库,然后导入到lib文件夹中供java调用。
Android Studio NDK及so文件开发

在实际项目开发中,针对项目各异,我们一般都会调用底层的c/c++代码,或者一些开源项目,会用到它们的.so文件,但是我们根据项目不需要全部的功能,这时我们也会对它们的源码进行编译,生成自己想要的.so文件,这样也能有效缩减项目的体积.

二:安装NDK
1.打开android studio上的Tools选项,选择android选项中的sdk Manager,然后选择SDK Toos勾上LLDB和NDK,然后等待下载即可!
这里的LLDB是一个高效的c/c++的调试器,是与LLVM编译器一起使用,提供了丰富的流程控制和数据检测,有效的帮忙我们调试程序。

这里写图片描述

下载完后我们的NDK文件默认在sdk目录下的文件名为 ndk-bundle

这里写图片描述

2.配置NDK环境变量
在系统环境变量中新建变量名为NDK_ROOT,变量值为C:\Users\Administrator\AppData\Local\Android\sdk\ndk-bundle ,变量值为你的ndk路径
然后把你新建的NDK_ROOT系统变量配置到系统变量Path里,编辑Path,在后面加上 %NDK_ROOT%
这里写图片描述
这里写图片描述

这样NDK的环境变量就配置完成了

测试一下,打开cmd,输入ndk-build
这里写图片描述

这样我们的NDK都配置完毕了!

三.jni编程开发(生成.so库)
1.新建android项目,在MainActivity中新建一个getStrFromJNI()方法

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    public native String getStrFromJNI();
}

getStrFromJNI()方法,通过native关键字,表示这个方法是通过本地代码(c/c++)实现的.

注意点 :
a. 在local.properties文件中配置ndk的路径
这里写图片描述

b.配置app的build.gradle
在build.gradle文件中的android中增加sourceSets节点,如下:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"
    defaultConfig {
        applicationId "com.example.administrator.ndkproject"
        minSdkVersion 15
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
sourceSets{
    main{
        jniLibs.srcDirs = ['libs']
    }
}

}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:25.2.0'
    compile 'com.android.support.constraint:constraint-layout:1.0.2'
    testCompile 'junit:junit:4.12'
}

2.编译得到.h文件
在android studio底部栏切换到Terminal,找到你项目的java文件目录下输入javah -jni com.example.administrator.ndkproject.MainActivity, 其中com.example.administrator.ndkproject.MainActivity 为你包名下的在写了通过本地代码实现的方法所在的类
这里写图片描述

然后它会生成一个.h文件

3.编写.C文件
在我们项目的App目录下新建jni文件夹,在jni文件夹里新建demo.c文件,
编写测试代码,如下:

#include <jni.h>
jstring
Java_com_example_administrator_ndkproject_MainActivity_getStrFromJNI(JNIEnv *env,jobject thiz) {
return (*env)->NewStringUTF(env, "I`m Str from jni libs!");
}

这里写图片描述

4.编写Android.mk文件
在jni目录下新建Android.mk文件,mk文件内容如下:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := demo
LOCAL_SRC_FILES := demo.c
include $(BUILD_SHARED_LIBRARY)

其中LOCAL_MODULE := demo //要生成的so库的名称,但实际为libdemo.so
LOCAL_SRC_FILES := demo.c //要使用的文件,刚才编写的demo.c文件

这里写图片描述

5.编译生成.so库
打开cmd,找到你项目的app目录下,输入ndk-build
这里写图片描述
这里写图片描述

这样在libs目录就生成了各个版本的.so库文件

6.使用.so库
在MainActivity的onCreate中添加一个Log打印日志,并在类添加一个加载.so库文件方法并调用,代码如下:

package com.example.administrator.ndkproject;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends AppCompatActivity {

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

        Log.e("我的so文件", getStrFromJNI());
    }

    public native String getStrFromJNI();

    static {
        System.loadLibrary("demo");
    }
}

运行程序,可以看到Android Monitor栏中打印出来的日志如下:
这里写图片描述

getStrFromJNI方法的内容为I’m Str form jni libs,也就是我们在一开始编写的demo.c文件自定义的字段

这样我们使用ndk编译.so文件,调用c/c++代码(jni编程)就大工告成了,但是在实际开发中,我们还有很多要做的,加油!

Demo下载

Logo

更多推荐