Android Studio JNI开发入门教程

 

Android Studio JNI开发入门教程

 分类:
 

目录(?)[+]javascript

 

概述

在Andorid Studio不支持JNI开发以前你们通常都是使用Eclipse开发JNI,各类配置让人以为很蛋疼。从Andorid Studio支持JNI开发后,让咱们开发JNI变的如此简单。下面我就介绍一下Android Studio开发JNI的全过程,若有不对的地方你们批评指正。php

你将学习到什么

  • 什么是NDK和JNI
  • 为何要用JNI作开发
  • 如何使用Android Studio开发JNI项目

你须要准备什么

  • c/c++开发语言基础
  • java开发语言基础
  • 了解Android Studio开发环境

NDK 和 JNI介绍

JNI (Java Native Interface)是一套编程接口,用来实现Java代码和其余语言(c、C++或汇编)进行交互。这里须要注意的是JNI是JAVA语言本身的特性,也就是说JNI和Android没有关系。在Windows下面用JAVA作开发也常常会用到JNI,例如:读写系统注册表等。html

NDK(Native Development Kit)是Google提供的一套工具集,可让你其余语言(C、C++或汇编)开发 Android的 JNI。NDK能够编译多平台的so,开发人员只须要简单修改 mk 文件说明须要的平台,不须要改动任何代码,NDK就能够帮你编译出所需的so。java

用JNI作应用开发难度要比JAVA难不少,门槛也要高不少,若是你对C/C++把握的很差应用还会出现难以发现的Bug!因此
一般在对性能要求比较高才会使用。游戏引擎就是一个对性能要求极高的例子。另外就是若是你想把核心的一些算法或处理逻辑保护起来,选用JNI也是一个不错的方案。android

下载NDK

官网下载
网盘下载c++

NDK配置

将ndk解压到~/Library/Android/ndk下;
2016-03-24_15-10-10.png算法

建立Hello World工程

设置NDK路径

选择File>Project Structure>SDK Location(快捷键:Cmd+;),指定NDK的路径。
2016-03-24_16-11-28.png
2016-03-24_16-10-35.png
你也能够经过直接修改local.properties,在里面指定NDK的所在目录:编程

ndk.dir=/Users/open-open/Library/Android/ndk

2016-03-27_21-17-34.png

添加本地方法

在MainActivity中加入两个JNI方法数据结构

public static native String helloJni(); public static native int addCalc(int a, int b);

加载SO文件

static { System.loadLibrary("hello_jni"); // 注意没有前缀lib和后缀.so } 

利用javah命令生成JAVA所对应的JNI头文件,一、打开终端,二、将目录定位到java目录下,三、经过javah产生头文件。
2016-03-28_07-46-07.png
经过上图能够看到javah已经帮咱们产生了一个名为com_test_jcit_helloworld_MainActivity.h头文件,里面已经帮咱们产生好了2个函数原型定义,oracle

JNIEXPORT jstring JNICALL Java_com_test_jcit_helloworld_MainActivity_helloJni(JNIEnv *, jclass); JNIEXPORT jint JNICALL Java_com_test_jcit_helloworld_MainActivity_addCalc(JNIEnv *, jclass, jint, jint); 

javah产生的文件名和函数名不能直视,咱们只须要在须要的地方填入相应的代码。

建立JNI目录

2016-03-27_20-03-35.png
New Android Component中直接点击Finish按钮完成JNI目录建立
2016-03-27_20-34-45.png
结果以下:
2016-03-27_20-40-24.png
将前面产生的com_test_jcit_helloworld_MainActivity.h头文件移动到这个目录下.
2016-03-28_22-11-05.png
move窗口中点击ok按钮,肯定移动。
2016-03-28_22-14-02.png
com_test_jcit_helloworld_MainActivity.h拷贝一个将扩展名改成.c,在.c中完成业务逻辑处理相关代码:

#include <com_test_jcit_helloworld_MainActivity.h> JNIEXPORT jstring JNICALL Java_com_test_jcit_helloworld_MainActivity_helloJni   (JNIEnv *env, jclass jobj) {     return (*env)->NewStringUTF(env,"Hello JNI!"); } JNIEXPORT jint JNICALL Java_com_test_jcit_helloworld_MainActivity_addCalc   (JNIEnv *env, jclass jobj, jint ja, jint jb) {   return ja + jb; } 

2016-03-28_22-57-59.png

添加一个名为hello_jni的jni模块

修改Module中Build.gradle文件,在defaultConfig段落中加入ndk编译配置。  

defaultConfig {
    applicationId "com.test.jcit.helloworld"
    minSdkVersion 15
    targetSdkVersion 19
    versionCode 1
    versionName "1.0"
    ndk {
        moduleName "hello_jni"
    }
}

调用两个native方法

在MainActivity中OnCreate中调用helloJni和addCalc。

protected TextView mHelloWorldTV; protected TextView mCalcResultTV; @Override protected void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     setContentView(R.layout.activity_main);     mHelloWorldTV = (TextView) findViewById(R.id.HelloWorldTV);     mHelloWorldTV.setText(helloJni());     mCalcResultTV = (TextView) findViewById(R.id.CalcResultTV);     Integer addResult = new Integer(addCalc(12, 13));     mCalcResultTV.setText(addResult.toString()); }

编译错误排查

编译过程出现了Error:(13, 1) A problem occurred evaluating project ':app'.

Error: NDK integration is deprecated in the current plugin. Consider trying the new experimental plugin. For details, see http://tools.android.com/tech-docs/new-build-system/gradle-experimental. Set "android.useDeprecatedNdk=true" in gradle.properties to continue using the current NDK integration.

提示已经告诉咱们须要在gradle.properties设置android.useDeprecatedNdk=true,设置好后点击同步按钮。
2016-03-28_23-24-02.png

大功告成

2016-03-28_23-36-28.png

存在疑问

  • 之前咱们开发JNI的时候都须要一个Application.mk和Android.mk这两个文件的,没有这两个文件了怎么配置编译相关的参数呢?
    打开build.gradle,按下Cmd键并将鼠标移到moduleName,停留片刻能够看到NdkOptions,点击过去看看估计你就明白一个因此然了。
    2016-03-29_11-25-10.png
    public class NdkOptions implements CoreNdkOptions, Serializable {   private static final long serialVersionUID = 1L;   private String moduleName;   private String cFlags;   private List<String> ldLibs;   private Set<String> abiFilters;   private String stl;   private Integer jobs;
    看了这段代码,咱们就明白怎么配置ndk了,例如: 
    ndk {
      moduleName "hello_jni"
      stl "stlport_static"
      ldLibs =["log"]
    }

     

  • javah产生的函数名不能直视,能用自定义函数名吗?
    Java_com_test_jcit_helloworld_MainActivity_helloJni
    这样的命名看着真是很不爽,对于我这种有强迫症的人来讲绝对是不容许的,从代码规范的角度来讲也是不容许的。下面我就教你们一招怎么自定义函数名。
    当咱们调用System.loadLibrary("hello_jni")都会调用JNI_OnLoad函数通知咱们。
    jint JNI_OnLoad(JavaVM* vm, void* reserved) {   // **System.loadLibrary("hello_jni")**时调用     //  RegisterNatives注册本地方法 } 

    OK,接下来咱们改造一下hello_jni,首先须要定义一个本地方法和JAVA本地方法的对应关系表。

    static JNINativeMethod hello_jni_methods[] = {       {"helloJni", "()Ljava/lang/String;", (void*)helloJni},    // 第一个是JAVA里面的方法名,第二个是签名, 第三是函数指针       {"addCalc", "(II)I", (void*)addCalc} };

     签名看着也是怪怪的,能够经过官网了解其含义,或者先经过javah产生头文件,在每一个函数说明部分都有签名的定义。

    /* * Class:     com_test_jcit_helloworld_MainActivity * Method:    addCalc * Signature: (II)I */ JNIEXPORT jint JNICALL Java_com_test_jcit_helloworld_MainActivity_addCalc(JNIEnv *, jclass, jint, jint);

     

 

下面把几个关键部分完整的代码和配置贴上,但愿对你学习本教程有所帮助。
hellojni.c

#include "hellojni.h" #include <stdlib.h> #include <android/log.h> #define TAG "HELLO_JNI" #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR , TAG, __VA_ARGS__) #define METHOD_COUNT(x) ((int) (sizeof(x) / sizeof((x)[0]))) JNIEXPORT jstring JNICALL helloJni   (JNIEnv *env, jclass jobj) {     return (*env)->NewStringUTF(env,"Hello JNI!"); } JNIEXPORT jint JNICALL addCalc   (JNIEnv *env, jclass jobj, jint ja, jint jb) {   return ja + jb; } static JNINativeMethod hello_jni_methods[] = {   {"helloJni", "()Ljava/lang/String;", (void*)helloJni}   {"addCalc", "(II)I", (void*)addCalc} }; static const char *HELLO_JNI_CLASS_PATH_NAME = "com/test/jcit/helloworld/MainActivity"; jint JNI_OnLoad(JavaVM* vm, void* reserved) {     JNIEnv* env = NULL;     jclass clazz;      // 获取JNI环境对象     if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {         LOGE("ERROR: GetEnv failed\n");         return JNI_ERR;     }     // 寻找目标类     clazz = (*env)->FindClass(env, HELLO_JNI_CLASS_PATH_NAME);     if (clazz == NULL) {         LOGE("Native registration unable to find class '%s'", HELLO_JNI_CLASS_PATH_NAME);         return JNI_ERR;     }     // 注册本地native方法     if((*env)->RegisterNatives(env, clazz, hello_jni_methods, METHOD_COUNT(hello_jni_methods)) < 0) {         LOGE("ERROR: MediaPlayer native registration failed\n");         return JNI_ERR;     }     // 成功返回版本号     return JNI_VERSION_1_4; }

MainActivity.java 
 

package com.test.jcit.helloworld; import android.support.v7.app.ActionBarActivity; import android.os.Bundle; import android.widget.TextView; public class MainActivity extends ActionBarActivity {     public static native String helloJni();     public static native int addCalc(int a, int b);     static {         System.loadLibrary("hello_jni");     }     protected TextView mHelloWorldTV;     protected TextView mCalcResultTV;     @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_main);         mHelloWorldTV = (TextView) findViewById(R.id.HelloWorldTV);         mHelloWorldTV.setText(helloJni());         mCalcResultTV = (TextView) findViewById(R.id.CalcResultTV);         Integer addResult = new Integer(addCalc(12, 13));         mCalcResultTV.setText(addResult.toString());     } }

activity_main.xml 

<?xml version="1.0" encoding="utf-8"?> <LinearLayout     xmlns:android="http://schemas.android.com/apk/res/android"     xmlns:tools="http://schemas.android.com/tools"     android:layout_width="match_parent"     android:layout_height="match_parent"     android:paddingLeft="@dimen/activity_horizontal_margin"     android:paddingRight="@dimen/activity_horizontal_margin"     android:paddingTop="@dimen/activity_vertical_margin"     android:paddingBottom="@dimen/activity_vertical_margin"     tools:context="com.test.jcit.helloworld.MainActivity"     android:orientation="vertical">     <ImageView         android:src="@drawable/logo"         android:layout_width="wrap_content"         android:layout_height="wrap_content" />      <TextView         android:id="@+id/HelloWorldTV"         android:text=""         android:layout_width="wrap_content"         android:layout_height="wrap_content" />      <TextView         android:id="@+id/CalcResultTV"         android:text=""         android:layout_width="wrap_content"         android:layout_height="wrap_content" /> </LinearLayout>

/app/build.gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 19
    buildToolsVersion "23.0.3"
    defaultConfig {
        applicationId "com.test.jcit.helloworld"
        minSdkVersion 15
        targetSdkVersion 19
        versionCode 1
        versionName "1.0"
        ndk {
            moduleName "hello_jni"
            stl "stlport_static"
            ldLibs =["log"]
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    sourceSets {
        main {
            jni.srcDirs = ['src/main/jni']
        }
    }
}

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

gradle.properties

android.useDeprecatedNdk=true

local.properties

注:这个路径要用你本机的路径,贴出来只是告诉须要设置。

ndk.dir=/Users/open-open/Library/Android/ndk sdk.dir=/Users/open-open/Library/Android/sdk

 

本站原创,转载时保留如下信息:
本文转自:深度开源(open-open.com)
原文地址:http://www.open-open.com/lib/view/open1470560501408.html

相关文章
相关标签/搜索