android ndk关于.so文件的制作封装与跨平台引用

结合网上众多jni实现流程整理了一套完整且可行的jni教程,有不足的地方欢迎指出与探讨!!
废话不多说直接上实现流程:看网上大多都是用命令生成.c .cpp文件,每次都得编译命令去生成有点小麻烦,本文介绍直接用studio配置命令一键生成.so文件,一劳永逸。
直接在project下开始:
android ndk关于.so文件的制作封装与跨平台引用_第1张图片




1、打开File | Settings | Tools | External Tools  

          android ndk关于.so文件的制作封装与跨平台引用_第2张图片


    第一步:
     通过配置javah -jni命令的配置 一键生成.h文件
     
      android ndk关于.so文件的制作封装与跨平台引用_第3张图片


  

            1.Program:$JDKPath$\bin\javah.exe(javah路径)

            2.Parametes: -classpath . -jni -d $ModuleFileDir$/src/main/jni $FileClass$

            3.Working:$ModuleFileDir$\src\main\java    



    第二步:
       通过配置ndk-build命令的配置 一键生成.so库文件

      android ndk关于.so文件的制作封装与跨平台引用_第4张图片

            

            1.Program:G:\Users\Administrator\AppData\Local\Android\sdk\ndk-bundle\ndk-build.cmd(这个换成你自己的)

            2.

            3.Working:$ModuleFileDir$\src\main\

  

    第三步:

          通过配置   javap-s(此命令用于清除java方法时方法的签名)
      
          android ndk关于.so文件的制作封装与跨平台引用_第5张图片


            1.Program:$JDKPath$\bin\javap.exe

            2.Parametes: -classpath ModuleFileDir/build/intermediates/classes/debug -s $FileClass$

            3.Working:$ModuleFileDir$




     模块build.gradle完整配置如下:


      android ndk关于.so文件的制作封装与跨平台引用_第6张图片


android ndk关于.so文件的制作封装与跨平台引用_第7张图片


wanzhe

  apply plugin: 'com.android.application'

    android {
    compileSdkVersion 26
    buildToolsVersion "27.0.1"
    defaultConfig {
        applicationId "com.mj.jnitest"
        minSdkVersion 15
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

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


        jackOptions {
            enabled true
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
   }

   dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    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:26.+'
    compile 'com.android.support.constraint:constraint-layout:1.0.2'
    testCompile 'junit:junit:4.12'
   


 gradle.properties配置:


android ndk关于.so文件的制作封装与跨平台引用_第8张图片



 
    
 不要忘记在gradle.properties中加入下面这句话

android.useDeprecatedNdk= true


     命令到此完毕,看具体使用流程,新建JNIutils.java 右键选择 javah -jni 生成.h文件

     package com.mj.jnitest;

     /**
     * Created by liuguodong on 2017/12/1.
     */

     public class JNIutils {
    /**
     * @author liuguodong
     * 编写本地java层调用方法
     * @Time 2017/12/1 14:38
     */
    public static native String getString();

     /**
      * @author liuguodong
      * 加载本地文件so文件
      * @Time 2017/12/1 14:40
      * @ JNI_HELLO_WORLD这个是Module build.gradle中配置的 moduleName
      */
     static  {
        System.loadLibrary("JNI_HELLO_WORLD");
     }
   }


  

android ndk关于.so文件的制作封装与跨平台引用_第9张图片
         
 

    编译效果如下:

             android ndk关于.so文件的制作封装与跨平台引用_第10张图片
 

  

 成功的生成了.h文件 文件内容如下:


         android ndk关于.so文件的制作封装与跨平台引用_第11张图片



  
    根据刚才生成的.h文件编写.cpp文件,声明getString()函数具体要做的事情
       
    
        android ndk关于.so文件的制作封装与跨平台引用_第12张图片





        JNI_GetString.cpp  内容:

//
// Created by liuguodong on 2017/12/1.
//

#include 
#include 
#include 
#include 
 //com_mj_jnitest_JNIutils 一点要与刚才生成的.h文件名一致
JNIEXPORT jstring JNICALL Java_com_mj_jnitest_JNIUtils_getString(JNIEnv * env, jclass jclass){
    return env -> NewStringUTF(" THIS   JNI");
}
//JNIEXPORT      在Jni编程中所有本地语言实现Jni接口的方法前面都有一个"JNIEXPORT",这个可以看做是Jni的一个标志,至今为止没发现它有什么特殊的用处。
//jstring        这个学过编程的人都知道,当然是方法的返回值了。
//JNICALL        这个可以理解为Jni 和Call两个部分,和起来的意思就是 Jni调用XXX(后面的XXX就是JAVA的方法名)
//Java_com_mj_jnitest_JNIUtils_getString    这个就是被上一步中被调用的部分,也就是Java中的native 方法名,这里起名字的方式比较特别,是:包名+类名+方法名。
//JNIEnv * env: 这个env可以看做是Jni接口本身的一个对象,在上一篇中提到的jni.h头文件中存在着大量被封装好的函数,这些函数也是Jni编程中经常被使用到的,要想调用这些函数就需要使用JNIEnv这个对象。例如:env->GetObjectClass()。
//jclass jclass    与jobject类似  都可以看做java类的本身
//NewStringUTF(" THIS   JNI")    返回getString()函数的返回值
//



  新建Application.mk与Android.mk文件 


android ndk关于.so文件的制作封装与跨平台引用_第13张图片



    Android.mk文件如下:


LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := JNI_HELLOWORD
LOCAL_SRC_FILES =: JNI_GetString.cpp
include $(BUILD_SHARED_LIBRARY)


LOCAL_PATH := $(call my-dir) 

每个Android.mk文件必须以定义LOCAL_PATH为开始。它用于在开发tree中查找源文件。

my-dir 则由Build System提供。返回包含Android.mk的目录路径。


include $(CLEAR_VARS) 

CLEAR_VARS 变量由Build System提供。并指向一个指定的GNU Makefile,由它负责清理很多LOCAL_xxx.

例如:LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES等等。但不清理LOCAL_PATH.

这个清理动作是必须的,因为所有的编译控制文件由同一个GNU Make解析和执行,其变量是全局的。所以清理后才能避免相互影响。


LOCAL_MODULE    :=JNI_HELLO_WORLD

LOCAL_MODULE模块必须定义,以表示Android.mk中的每一个模块。名字必须唯一且不包含空格。

Build System会自动添加适当的前缀和后缀。例如,foo,要产生动态库,则生成libfoo.so. 但请注意:如果模块名被定为:libfoo.则生成libfoo.so. 不再加前缀。


LOCAL_SRC_FILES :=JNI_GetString.cpp
 

LOCAL_SRC_FILES变量必须包含将要打包如模块的C/C++ 源码。

不必列出头文件,build System 会自动帮我们找出依赖文件。

缺省的C++源码的扩展名为.cpp. 也可以修改,通过LOCAL_CPP_EXTENSION。


include $(BUILD_SHARED_LIBRARY) 

BUILD_SHARED_LIBRARY:是Build System提供的一个变量,指向一个GNU Makefile Script。

它负责收集自从上次调用 include $(CLEAR_VARS)  后的所有LOCAL_XXX信息。并决定编译为什么。


Application.mk文件如下:

APP_MODULES := JNI_HELLO_WORLD
APP_ABI := all

整体结构如下:

android ndk关于.so文件的制作封装与跨平台引用_第14张图片




    现在执行ndk-build 命令一键生成.so文件


android ndk关于.so文件的制作封装与跨平台引用_第15张图片


android ndk关于.so文件的制作封装与跨平台引用_第16张图片


        这里对不同cpu生成对应的库做一下说明:
             armeabiv-v8a:第8代及以上的 ARM 处理器
        armeabiv-v7a: 第7代及以上的 ARM 处理器。2011年15月以后的生产的大部分Android设备都使用它.
        arm64-v8a: 第8代、64位ARM处理器,很少设备,三星 Galaxy S6是其中之一。
        armeabi: 第5代、第6代的ARM处理器,早期的手机用的比较多。
        x86: 平板、模拟器用得比较多。
        x86_64: 64位的平板。
        mip:...


      到此so已经生成完毕,下面来看具体调用步骤(关于so调用出错的很多坑以后再慢慢补充说明)
     在main文件下新建JniLibs文件夹,将新生成的so文件拷贝过去
      
      android ndk关于.so文件的制作封装与跨平台引用_第17张图片
      


  运行 :   因为是直接在生成的工程里运行的所以直接运行成功了,如果要把so文件拿到其他工程使用,就必须要知道so文件里面                   的包名与方法名,对于知道的还好说,不知道的就嗝屁了,所以一般so都有配套的jar文件来完成调用


android ndk关于.so文件的制作封装与跨平台引用_第18张图片
     android ndk关于.so文件的制作封装与跨平台引用_第19张图片
   
   


使用exlipse来生成成指定报名的jar文件来完成调用

    
    android ndk关于.so文件的制作封装与跨平台引用_第20张图片

android ndk关于.so文件的制作封装与跨平台引用_第21张图片

导出jar文件:一直next就OK了


android ndk关于.so文件的制作封装与跨平台引用_第22张图片



   直接引用:

android ndk关于.so文件的制作封装与跨平台引用_第23张图片

android ndk关于.so文件的制作封装与跨平台引用_第24张图片



调用的时候出现类似错误:
java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.mj.jnitest-2/base.apk"],nativeLibraryDirectories=[/data/app/com.mj.jnitest-2/lib/arm64, /data/app/com.mj.jnitest-2/base.apk!/lib/arm64-v8a, /system/lib64, /vendor/lib64, /system/vendor/lib64, /product/lib64]]] couldn't find "libJNI_HELLO_WORLD.so"
                                                                    at java.lang.Runtime.loadLibrary0(Runtime.java:989)
                                                                    at java.lang.System.loadLibrary(System.java:1533)
                                                                    at com.mj.jnitest.JNIutils.(JNIutils.java:22)
                                                                    at com.mj.jnitest.JNIutils.getString(Native Method)

 


多clear几遍工程就行了了 或者直接把第一个build直接删除掉就行

完毕!欢迎有问题随时指出
源码地址:http://download.csdn.net/download/liu3364575/10141094

你可能感兴趣的:(android ndk关于.so文件的制作封装与跨平台引用)