Makefile 实现一个Android JNI的开发

Android NDK开发

概述

使用Makefile构建工具完成jni的开发一般会比较繁琐,其实现的基本步骤如下:

  • 编写实现jni的java文件
  • 使用javac 命令编译java文件为class文件(javac Mp3Encoder.java)
  • 使用javah命令编译class文件生成一个jni的头文件(javah -jni cn.enjoytoday.mp3encoder.Mp3Encoder)
  • 将jni头文件复制到jni目录,并建立一个cpp的实现文件实现该JNI头文件的函数
  • 编写Android.mk文件,加入上步的本地代码,利用ndk-build生成动态链接库
  • java代码中加载生成好的动态链接库,并调用native方法

实施过程

本案例结合一个通过JNI来打印“Hello,Mp3 encoder!”的基础工程实现。

  1. 创建工程,编写实现jni的java文件Mp3Encoder,代码如下:
    public class Mp3Encoder {
       public native void encode();
    }
    
    
  2. 完成工程编译生成对应的class文件,或直接通过javac命令生成class文件,这里使用javac命令生成class文件,如下:
    hfcai@hfcai-Lenovo-XiaoXin-V4000:~/Mp3Encoder/app/src/main/java$ javac cn/enjoytoday/mp3encoder/Mp3Encoder.java
    hfcai@hfcai-Lenovo-XiaoXin-V4000:~/Mp3Encoder/app/src/main/java$ cd cn/enjoytoday/mp3encoder/
    hfcai@hfcai-Lenovo-XiaoXin-V4000:~/Mp3Encoder/app/src/main/java/cn/enjoytoday/mp3encoder$ ll
    总用量 20
    drwxrwxr-x 2 hfcai hfcai 4096 8月  10 06:35 ./
    drwxrwxr-x 3 hfcai hfcai 4096 8月   8 20:48 ../
    -rw-rw-r-- 1 hfcai hfcai  880 8月   9 08:18 MainActivity.java
    -rw-r--r-- 1 hfcai hfcai  236 8月  10 06:35 Mp3Encoder.class
    -rw-rw-r-- 1 hfcai hfcai   98 8月   9 22:10 Mp3Encoder.java
    hfcai@hfcai-Lenovo-XiaoXin-V4000:~/Mp3Encoder/app/src/main/java/cn/enjoytoday/mp3encoder$
    
  3. 使用javah命令编译class文件生成一个jni的头文件,Android Studio和Eclipse也可以通过编译的方式生成该头文件,但这里为了更加清楚的了解整个实现过程,采用命令的方式实现.如下:
    hfcai@hfcai-Lenovo-XiaoXin-V4000:~/Mp3Encoder/app/build/intermediates/classes/debug$ javah -jni cn.enjoytoday.mp3encoder.Mp3Encoder
    hfcai@hfcai-Lenovo-XiaoXin-V4000:~/Mp3Encoder/app/build/intermediates/classes/debug$ ll
    总用量 24
    drwxrwxr-x 5 hfcai hfcai 4096 8月   9 22:14 ./
    drwxrwxr-x 3 hfcai hfcai 4096 8月   9 22:13 ../
    drwxrwxr-x 4 hfcai hfcai 4096 8月   9 22:13 android/
    drwxrwxr-x 3 hfcai hfcai 4096 8月   9 22:13 androidx/
    drwxrwxr-x 3 hfcai hfcai 4096 8月   9 22:13 cn/
    -rw-rw-r-- 1 hfcai hfcai  504 8月   9 22:14 cn_enjoytoday_mp3encoder_Mp3Encoder.h
    

    如上,cn_enjoytoday_mp3encoder_Mp3Encoder.h为生成的jni头文件,文件名定义格式为包名+类名.h。如下为生成的内容:

    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include <jni.h>
    /* Header for class cn_enjoytoday_mp3encoder_Mp3Encoder */
    
    #ifndef _Included_cn_enjoytoday_mp3encoder_Mp3Encoder
    #define _Included_cn_enjoytoday_mp3encoder_Mp3Encoder
    #ifdef __cplusplus
    extern "C" {
    #endif
    /*
    * Class:     cn_enjoytoday_mp3encoder_Mp3Encoder
    * Method:    encode
    * Signature: ()V
    */
    JNIEXPORT void JNICALL Java_cn_enjoytoday_mp3encoder_Mp3Encoder_encode
     (JNIEnv *, jobject);
    
    #ifdef __cplusplus
    }
    #endif
    #endif
    
    

    该文件不可修改,这里定义了一个方法Java_cn_enjoytoday_mp3encoder_Mp3Encoder_encode,需要在对应的c文件中实现它。

  4. 将jni头文件复制到jni目录,并建立一个cpp的实现文件实现该JNI头文件的函数,创建Mp3Encoder.cpp文件如下:

    #include <jni.h>
    #include <android/log.h>
    #include <string.h>
    #include  "cn_enjoytoday_mp3encoder_Mp3Encoder.h"
    #define LOG_TAG "mp3encoder"
    #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
    JNIEXPORT void JNICALL Java_cn_enjoytoday_mp3encoder_Mp3Encoder_encode
    (JNIEnv *, jobject){
       LOGI("encoder encode!");
    }
    

    在jni下添加Android.mk文件:

    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    LOCAL_LDLIBS    := -lm -llog
    LOCAL_MODULE := audioencoder
    LOCAL_LDFLAGS := -Wl,--build-id
    LOCAL_SRC_FILES := \
     /home/hfcai/Mp3Encoder/app/src/main/jni/Android.mk \
     /home/hfcai/Mp3Encoder/app/src/main/jni/Mp3encoder.cpp \
    LOCAL_C_INCLUDES += /home/hfcai/Mp3Encoder/app/src/main/jni
    LOCAL_C_INCLUDES += /home/hfcai/Mp3Encoder/app/src/debug/jni
    include $(BUILD_SHARED_LIBRARY)
    

    build.gradle设置ndk编译:

    android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "cn.enjoytoday.mp3encoder"
        minSdkVersion 19
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    
        //ndk-build
        ndk{
            moduleName "audioencoder"
        }
    
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    //ndkBuild
    externalNativeBuild {
        ndkBuild {
            path file("src/main/jni/Android.mk")
        }
     }
    }
    
    

    需要在项目的根目录gradle.properties中添加

    android.deprecatedNdkCompileLease=true
    

    注意:Android studio 从3.1.4之后将不再支持make方式使用ndk.

  5. 加载调用native方法

    Mp3Encoder.java
    package cn.enjoytoday.mp3encoder;
    
    public class Mp3Encoder {
    static {
        System.loadLibrary("audioencoder");
    }
    public native void encode();
    }
    //调用
    
    new Mp3Encoder().encode();
    

如上,实测可以实现log打印.

说点什么

avatar
  订阅  
提醒