Android Studio创建JNI项目(一)

最近博主在研究JNI,在Android Studio中开发JNI中遇到一些坑,记录下来,希望给需要的人提供一些解决方法.

JNI(Java Native Interface) Java本地接口.其实就是一种协议,只要实现这种协议,就可以实现Java,C代码的互相调用

提供了Java与其他的语言的进行交互的能力,增强Java的功能(适用场景):

  1.使用C语言的优秀开源框架 ffmpeg(视频opengl(图像)等.

  2.Java操作硬件效率问题.

  3.安全性,java代码反编译太过简单,可以直接看到Java代码(在C语言加密等,用户检验)等等.

但是:java语言不在跨平台,因为不同的平台遵循的c语言的标准是不同的,如果要使用的话,需要在不同的平台重新编译java本地方法,生成不同的so文件.armeabi.armeabi-v7a.x86.x86_64等等.

那么Android Studio如何创建一个JNI项目呢.

先看下最终的目录结构:


接下来:

  1.创建一个含本地的方法的Java类,不推荐在MainActivity中直接创建,一是javah生成头文件的时候会提示MainActivity的父类无法找到(因为MainActivity的父类的class文件并不再我们的项目中)二是增加代码的耦合性.

package com.example.administrator.yinhangapp;

/**
 * Created by Administrator Youngkaka on 2016/8/18.
 * 我的心愿是:世界和平
 */
public class NdkUtils {
    public native int getResYinHangC(int pass,int word);
}

  2.创建好类文件后,重新build这个工程(Build-->Rebuild Project),这里我们可以看到Rebuild后,在app/build/intermediates/classes/debug/生成NdkUtils的.class文件,我们进入Terminal模式使用javah生成.h的头文件(这里要注意的是Jdk1.8 生成.h文件的时候,需要指定classpath的位置)

目录结构

  

使用javah生成头文件,命令执行完毕后,会在debug的目录下,生成 包名_类名_本地方法名 的头文件,这时我们在/src/main目录下,新建一个jni目录下,将生成的.h文件拷贝该文件夹下.

生成的头文件内容:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_administrator_yinhangapp_NdkUtils */

#ifndef _Included_com_example_administrator_yinhangapp_NdkUtils
#define _Included_com_example_administrator_yinhangapp_NdkUtils
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_administrator_yinhangapp_NdkUtils
 * Method:    getResYinHangC
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_com_example_administrator_yinhangapp_NdkUtils_getResYinHangC
  (JNIEnv *, jobject, jint, jint);

#ifdef __cplusplus
}
#endif
#endif
我们今天先不过多讨论Jni协议里面的内容,现在只要清楚,我们生成的头文件中,有一个的本地C方法(参数,返回值同时也包括在内)

     3.在jni目录下,新建一个my.c 实现这个本地c方法.

// Created by Administrator on 2016/8/18.
//
#include "com_example_administrator_yinhangapp_NdkUtils.h"
int login(int num,int pass){
   if(num==1234 && pass==1234){
       return 1;
    }else{
       return 0;
    }
}
JNIEXPORT jint JNICALL Java_com_example_administrator_yinhangapp_NdkUtils_getResYinHangC
  (JNIEnv * env, jobject obj, jint num, jint pass){
   jint res=login(num,pass);  //调用本地的C方法.在C语言验证用户名和密码
   return res;
  }
4.接下来就是坑的开始,因为Android Studio使用Gradle编译,因此我们要配置Gradle,同时还要手动配置ndk_build的工作路径,我们先在/src/main/新建一个文件夹jniLibs

我的build.gradle内容如下:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 24
    buildToolsVersion "24.0.1"

    defaultConfig {
        applicationId "com.example.administrator.yinhangapp"
        minSdkVersion 15
        targetSdkVersion 24
        versionCode 1
        versionName "1.0"
        ndk {
            moduleName "JniLibName"         //生成的so名字
            abiFilters "armeabi", "x86", "armeabi-v7a" //输出指定三种abi体系结构下的so库。目前可有可无。
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    sourceSets { main {
            jni.srcDirs = []
            jniLibs.srcDirs=['src/main/jniLibs']
    } }

    tasks.withType(JavaCompile) {    //这里指定了ndkBuild的工作命令,以及拷贝so的命令
        compileTask -> compileTask.dependsOn 'ndkBuild', 'copyJniLibs'
    }
}

task ndkBuild(type: Exec) {//设置新的so的生成目录
    def ndkBuildingDir = project.plugins.findPlugin('com.android.application').sdkHandler.getNdkFolder().absolutePath
    commandLine ndkBuildingDir + "/ndk-build.cmd", '-C', 'src/main/jni',   
            "NDK_OUT=$buildDir/intermediates/ndk/obj",
            "NDK_APP_DST_DIR=$buildDir/intermediates/ndk/libs/\$(TARGET_ARCH_ABI)"
}

task copyJniLibs(type: Copy) {//将新生成的so拷贝到jniLibs目录
    from fileTree(dir: file(buildDir.absolutePath + '/intermediates/ndk/libs'), include: '**/*.so')
    into file('src/main/jniLibs')
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:24.1.1'
}

NDK-Build编译C文件生成.so文件需要Android.mk文件以及Application.mk文件,我在build.gradle文件中已经指定我的.mk在/src/main/jni文件下,因此我们要在jni目录下,新建

5.Android.mk文件,Application.mk文件内容如下:

Andorid.mk内容:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := JniLibName       //build.gradle指定的modulename
LOCAL_SRC_FILES := my.c      //我的C文件名称
include $(BUILD_SHARED_LIBRARY)
Application.mk内容:

APP_ABI		:= all
APP_PLATFORM:= android-19
APP_OPTIM	:= release

重新Rebuild后会在jniLibs文件夹中生成so文件,如何没有的话,从app/build/intermediates/ndk/libs拷贝一个.

 6.最后在我们的MainActivity中加入静态代码块

 static {
        System.loadLibrary("JniLibName");
    }
7   .运行



----------------------------------------------------------------------------------------------------------------------------华丽的分割线

可能会遇到的问题:


答:你的ndk版本不适合


答:删除你的检查的你的c源代码,以及Android.mk文件,C文件名称是否错误,检查无误后,重新编译


答:你的so文件并没有生成,或者没有存在在/src/main/jniLibs/目录下,拷贝并且检查你的build.gradle相关是否正确.



答:手动使用ndk-build时候,需要进入到jni目录下,并且设置Android.mk文件,然后使用ndk-build命令,或者使用配置gradle自动编译


答:jdk1.8的问题,手动设置classpath以及不能再MainActivity的中编写native方法.

我目前就遇到这些问题,欢迎互相交流.

反向编译后,只能看到native的声明调用,但是看不到本地方法具体实现.



  • 10
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值