用Android Studio进行NDK编程入门实例

参考了网上各种教程,跌跌撞撞最终才把流程走通,特此记录一下:

有必要先交代下开发环境:

操作系统:Win7

Android Studio 3.0.1

gradle 3.0.1


首先,新建一个Android项目,然后在MainActivity的布局文件activity_main.xml文件中新增一个测试按钮,按钮id是btnTest
,效果如下所示:

用Android Studio进行NDK编程入门实例_第1张图片


然后在新增一个java类,包名是:package com.ctrip.ndktest(具体的名称以你的为主),类名是Java2CJNI,并在类中新增一个实例方法Java2C,代码如下:

package com.ctrip.ndktest;

/**
 * Created by yuanlongzhang on 2018/4/17.
 */

public class Java2CJNI {   
    //这个就是我们将来需要调用的方法
    public native String java2C();
}

文件目录图如下:

用Android Studio进行NDK编程入门实例_第2张图片


生成.h文件

然后使用菜单【Build】-【Rebuild Project】将Java2CJNI.java文件变成Java2CJNI.class文件。这些文件一般位于:你的项目目录\app\build\intermediates\classes\debug\下,打开cmd窗口或者在Android Studio中的Terminal窗口(如果该窗口不可见,可以通过菜单【View】-【Tool Windows】-【Terminal】打开),进入该目录使用javah命令为Java2CJNI.class生成对应的头文件,该命令的帮助如下:

用法:
  javah [options]
其中, [options] 包括:
  -o                 输出文件 (只能使用 -d 或 -o 之一)
  -d

                 输出目录
  -v  -verbose             启用详细输出
  -h  --help  -?           输出此消息
  -version                 输出版本信息
  -jni                     生成 JNI 样式的标头文件 (默认值)
  -force                   始终写入输出文件
  -classpath         从中加载类的路径
  -bootclasspath     从中加载引导类的路径
是使用其全限定名称指定的

(例如, java.lang.Object)。


我们使用如下指令生成对应的.h文件:

javah -classpath D:\androidstudy\app\build\intermediates\classes\debug -jni com.ctrip.ndktest.Java2CJNI 

其中D:\androidstudy\app\build\intermediates\classes\debug是我机器android studio生成的java .class文件所在的目录(根据情况改成你的),com.ctrip.ndktest.Java2CJNI 是需要生成对应头文件所在的包名+类名,生成成功后,会在D:\androidstudy\app\build\intermediates\classes\debug位置生成一个com_ctrip_ndktest_Java2CJNI.h文件,我们将这个文件放到 安卓项目路径\app\src\main\jni\文件夹下,如果main目录下没有jni目录,可以新建一个,注意一定是在main目录下的jni,这个时候的com_ctrip_ndktest_Java2CJNI.h的内容如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include 
/* Header for class com_ctrip_ndktest_Java2CJNI */

#ifndef _Included_com_ctrip_ndktest_Java2CJNI
#define _Included_com_ctrip_ndktest_Java2CJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_ctrip_ndktest_Java2CJNI
 * Method:    java2C
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_ctrip_ndktest_Java2CJNI_java2C
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

然后我们在该jni目录下再新建一个.c文件Java2C.c,并将com_ctrip_ndktest_Java2CJNI.h中的Java_com_ctrip_ndktest_Java2CJNI_java2C函数前面拷贝过去并实现,不知道你有没有注意到,这个函数在com_ctrip_ndktest_Java2CJNI.h中的申明时,函数参数只有类型没有参数名,我们在Java2C.c需要将其补全,并在Java2C.c中包含头文件com_ctrip_ndktest_Java2CJNI.h,Java2C.c内容如下:

//
// Created by yuanlongzhang on 2018/4/17.
//

#include "com_ctrip_ndktest_Java2CJNI.h"

JNIEXPORT jstring JNICALL Java_com_ctrip_ndktest_Java2CJNI_java2C(JNIEnv* env, jobject instance)
{
    return (*env)->NewStringUTF(env, "I am From Native C");
}

此时项目目录层次如下:

用Android Studio进行NDK编程入门实例_第3张图片


这些C代码最终会生成*.so文件,为了说明生成的*.so文件的类型和名称,我们需要修改app目录下的build.gradle,注意是app目录下的,不是项目目录下的build.gradle(图中蓝色箭头指向的文件):

用Android Studio进行NDK编程入门实例_第4张图片

这个build.gradle内容如下:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 26
    defaultConfig {
        applicationId "com.ctrip.ndktest"
        minSdkVersion 15
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        /*
            Error:Execution failed for task ':libtermexec:compileReleaseNdk'.
            > 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.
         */

        ndk {
            moduleName "Java2C"                 //生成的库名
            abiFilters  "armeabi-v7a", "x86"    //指定的平台类型,如果不写,则默认会生成全平台的
        }
    }

    /*
    
sourceSets {
        main {
            jni.srcDirs = []
        }
    }
    */
buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } }}dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:26.1.0' implementation 'com.android.support.constraint:constraint-layout:1.1.0' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.1' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'}

网上很多文章说要加如下节点:

sourceSets {
        main {
            jni.srcDirs = []
        }
    }

在新版的android studio中是不需要添加的,如果添加了可能会导致无法生成*.so文件。另外如果不指定平台编译时会有如下的错误提示:

Information:Gradle tasks [clean, :app:assembleDebug]
Warning:Deprecated NDK integration enabled by android.deprecatedNdkCompileLease flag in gradle.properties will be removed from Android Gradle plugin in the next version.
D:\Users\zxf\AppData\Local\Android\Sdk\ndk-bundle\build\core\setup-app.mk
Error:(81) Android NDK: Application targets deprecated ABI(s): armeabi    
Error:(82) Android NDK: Support for these ABIs will be removed in a future NDK release.    
Information:BUILD SUCCESSFUL in 8s
Information:2 errors
Information:1 warning

Information:See complete output in console

这是因为armeabi这个平台,已经被废弃了,所以我们需要将它去掉,所以只好显式地指定生成的平台类型,在上文说的build.gradle文件中将armeabi去掉,由

ndk {
            moduleName "Java2C"                 //生成的库名
            abiFilters  "armeabi", "armeabi-v7a", "x86"    //指定的平台类型,如果不写,则默认会生成全平台的
        }

变成:


ndk {
            moduleName "Java2C"                 //生成的库名
            abiFilters  "armeabi-v7a", "x86"    //指定的平台类型,如果不写,则默认会生成全平台的
        }

另外,为了支持NDK,我们还需要在项目目录的gradle.properties文件中增加如下一行android.useDeprecatedNdk=true,如下图所示:

用Android Studio进行NDK编程入门实例_第5张图片

这在早些版本的android studio中确实可以,但是新版本的android studio也不行了,再次Rebuild Project时会提示如下错误:

Information:Gradle tasks [clean, :app:assembleDebug]
Error:Execution failed for task ':app:compileDebugNdk'.
> Error: Flag android.useDeprecatedNdk is no longer supported and will be removed in the next version of Android Studio.  Please switch to a supported build system.
  Consider using CMake or ndk-build integration. For more information, go to:
   https://d.android.com/r/studio-ui/add-native-code.html#ndkCompile
   To get started, you can use the sample ndk-build script the Android
   plugin generated for you at:
   D:\androidstudy\app\build\intermediates\ndk\debug\Android.mk
  Alternatively, you can use the experimental plugin:
   https://developer.android.com/r/tools/experimental-plugin.html
  To continue using the deprecated NDK compile for another 60 days, set 
  android.deprecatedNdkCompileLease=1523965196208 in gradle.properties
Information:BUILD FAILED in 5s
Information:1 error
Information:0 warnings
Information:See complete output in console

按照错误提示,我们去掉android.useDeprecatedNdk=true,用另外一行代替android.deprecatedNdkCompileLease=1523965196208,如下图所示:

用Android Studio进行NDK编程入门实例_第6张图片

接下来我们再次Rebuild Project,就可以生成对应的*.so文件了,这些so文件的位置在:项目路径\app\build\intermediates\ndk\debug\目录下,每个平台对应一个文件夹,而这些so的名字就是我们在上面配置的名称Java2C,只不过linux系统(Android是一种linux系统)对这种库文件的格式命名按如下规则来:

lib+库名.so

这样Java2C就变成了libJava2C.so文件。如下图所示:

用Android Studio进行NDK编程入门实例_第7张图片


如何使用这些库呢?我们需要在上文中说的Java2CJNI.java文件中加载这些库,代码如下:

package com.ctrip.ndktest;

/**
 * Created by yuanlongzhang on 2018/4/17.
 */

public class Java2CJNI {
    static {
        //System的S是大写
        System.loadLibrary("Java2C");
    }

    public native String java2C();
}
其中
static {
        //System的S是大写,loadLibrary的L也是大写!
        System.loadLibrary("Java2C");
    }

就是我们新增的代码。

最后我们在MainActivity中使用这个Java2CJNI类和它的Java2C()方法:

package com.ctrip.ndktest;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

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

        ((Button)findViewById(R.id.btnTest)).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String result = new Java2CJNI().java2C();

                Toast.makeText(MainActivity.this, "click test button", Toast.LENGTH_SHORT).show();
            }
        });
    }
}

String result = new Java2CJNI().java2C();这一行就是我们添加的,这样一来,我们就可以使用了。程序运行效果如下:

用Android Studio进行NDK编程入门实例_第8张图片

点击TEST按钮就会有提示了。注意第一次调试这个apk可能需要加载很多其他文件,会比较慢,需要耐心等待一下。另外,检测一个NDK程序是否正常,你不仅需要看下对应的so文件是否生成了,你可以将生成的apk文件解压,看下是否在根目录下的lib目录下有对应的so文件,如果没有lib目录或者无对应的so文件,你的生成的apk文件也会有问题。无法运行,运行的时会提示如下错误:

用Android Studio进行NDK编程入门实例_第9张图片

如果正确的话,apk解压后会有个lib目录,目录中有各个平台的so文件:

用Android Studio进行NDK编程入门实例_第10张图片

用Android Studio进行NDK编程入门实例_第11张图片

用Android Studio进行NDK编程入门实例_第12张图片


文章配套源码下载地址:https://download.csdn.net/download/analogous_love/10355768

你可能感兴趣的:(Android)