小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

Android Studio JNI開(kāi)發(fā)入門教程

 流浪的星星318 2017-05-19

概述

在Andorid Studio不支持JNI開(kāi)發(fā)之前大家一般都是使用Eclipse開(kāi)發(fā)JNI,各種配置讓人覺(jué)得很蛋疼。從Andorid Studio支持JNI開(kāi)發(fā)后,讓我們開(kāi)發(fā)JNI變的如此簡(jiǎn)單。下面我就介紹一下Android Studio開(kāi)發(fā)JNI的全過(guò)程,如有不對(duì)的地方大家批評(píng)指正。

你將學(xué)習(xí)到什么

  • 什么是NDK和JNI
  • 為什么要用JNI做開(kāi)發(fā)
  • 如何使用Android Studio開(kāi)發(fā)JNI項(xiàng)目

你需要準(zhǔn)備什么

  • c/c++開(kāi)發(fā)語(yǔ)言基礎(chǔ)
  • java開(kāi)發(fā)語(yǔ)言基礎(chǔ)
  • 了解Android Studio開(kāi)發(fā)環(huán)境

NDK 和 JNI介紹

JNI (Java Native Interface)是一套編程接口,用來(lái)實(shí)現(xiàn)java代碼和其他語(yǔ)言(c、C++或匯編)進(jìn)行交互。這里需要注意的是JNI是JAVA語(yǔ)言自己的特性,也就是說(shuō)JNI和Android沒(méi)有關(guān)系。在Windows下面用JAVA做開(kāi)發(fā)也經(jīng)常會(huì)用到JNI,例如:讀寫(xiě)系統(tǒng)注冊(cè)表等。

NDK(Native Development Kit)是Google提供的一套工具集,可以讓你其他語(yǔ)言(C、C++或匯編)開(kāi)發(fā) Android的 JNI。NDK可以編譯多平臺(tái)的so,開(kāi)發(fā)人員只需要簡(jiǎn)單修改 mk 文件說(shuō)明需要的平臺(tái),不需要改動(dòng)任何代碼,NDK就可以幫你編譯出所需的so。

用JNI做應(yīng)用開(kāi)發(fā)難度要比JAVA難很多,門檻也要高很多,如果你對(duì)C/C++把握的不好應(yīng)用還會(huì)出現(xiàn)難以發(fā)現(xiàn)的Bug!所以
通常在對(duì)性能要求比較高才會(huì)使用。游戲引擎就是一個(gè)對(duì)性能要求極高的例子。另外就是如果你想把核心的一些算法或處理邏輯保護(hù)起來(lái),選用JNI也是一個(gè)不錯(cuò)的方案。

下載NDK

官網(wǎng)下載
網(wǎng)盤下載

NDK配置

將ndk解壓到~/Library/Android/ndk下;
2016-03-24_15-10-10.png

創(chuàng)建Hello World工程

設(shè)置NDK路徑

選擇File>Project Structure>SDK Location(快捷鍵:Cmd+;),指定NDK的路徑。
2016-03-24_16-11-28.png
2016-03-24_16-10-35.png
你也可以通過(guò)直接修改local.properties,在里面指定NDK的所在目錄:

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

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

添加本地方法

在MainActivity中加入兩個(gè)JNI方法

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

加載SO文件

static {
    System.loadLibrary("hello_jni"); // 注意沒(méi)有前綴lib和后綴.so
}

利用javah命令生成JAVA所對(duì)應(yīng)的JNI頭文件,1、打開(kāi)終端,2、將目錄定位到j(luò)ava目錄下,3、通過(guò)javah產(chǎn)生頭文件。
2016-03-28_07-46-07.png
通過(guò)上圖可以看到j(luò)avah已經(jīng)幫我們產(chǎn)生了一個(gè)名為com_test_jcit_helloworld_MainActivity.h頭文件,里面已經(jīng)幫我們產(chǎn)生好了2個(gè)函數(shù)原型定義,

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產(chǎn)生的文件名和函數(shù)名不能直視,我們只需要在需要的地方填入相應(yīng)的代碼。

創(chuàng)建JNI目錄

2016-03-27_20-03-35.png
New Android Component中直接點(diǎn)擊Finish按鈕完成JNI目錄創(chuàng)建
2016-03-27_20-34-45.png
結(jié)果如下:
2016-03-27_20-40-24.png
將前面產(chǎn)生的com_test_jcit_helloworld_MainActivity.h頭文件移動(dòng)到這個(gè)目錄下.
2016-03-28_22-11-05.png
move窗口中點(diǎn)擊ok按鈕,確定移動(dòng)。
2016-03-28_22-14-02.png
com_test_jcit_helloworld_MainActivity.h拷貝一個(gè)將擴(kuò)展名改為.c,在.c中完成業(yè)務(wù)邏輯處理相關(guān)代碼:

#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

添加一個(gè)名為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"
    }
}

調(diào)用兩個(gè)native方法

在MainActivity中OnCreate中調(diào)用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());
}

編譯錯(cuò)誤排查

編譯過(guò)程出現(xiàn)了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./tech-docs/new-build-system/gradle-experimental. Set "android.useDeprecatedNdk=true" in gradle.properties to continue using the current NDK integration.

提示已經(jīng)告訴我們需要在gradle.properties設(shè)置android.useDeprecatedNdk=true,設(shè)置好后點(diǎn)擊同步按鈕。
2016-03-28_23-24-02.png

大功告成

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

存在疑問(wèn)

  • 以前我們開(kāi)發(fā)JNI的時(shí)候都需要一個(gè)Application.mk和Android.mk這兩個(gè)文件的,沒(méi)有這兩個(gè)文件了怎么配置編譯相關(guān)的參數(shù)呢?
    打開(kāi)build.gradle,按下Cmd鍵并將鼠標(biāo)移到moduleName,停留片刻可以看到NdkOptions,點(diǎn)擊過(guò)去看看估計(jì)你就明白一個(gè)所以然了。
    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產(chǎn)生的函數(shù)名不能直視,能用自定義函數(shù)名嗎?
    Java_com_test_jcit_helloworld_MainActivity_helloJni這樣的命名看著真是很不爽,對(duì)于我這種有強(qiáng)迫癥的人來(lái)說(shuō)絕對(duì)是不允許的,從代碼規(guī)范的角度來(lái)說(shuō)也是不允許的。下面我就教大家一招怎么自定義函數(shù)名。
    當(dāng)我們調(diào)用System.loadLibrary("hello_jni")都會(huì)調(diào)用JNI_OnLoad函數(shù)通知我們。
    jint JNI_OnLoad(JavaVM* vm, void* reserved) {   // **System.loadLibrary("hello_jni")**時(shí)調(diào)用
        //  RegisterNatives注冊(cè)本地方法
    }
    
    

    OK,接下來(lái)我們改造一下hello_jni,首先需要定義一個(gè)本地方法和JAVA本地方法的對(duì)應(yīng)關(guān)系表。

    static JNINativeMethod hello_jni_methods[] = {
          {"helloJni", "()Ljava/lang/String;", (void*)helloJni},    // 第一個(gè)是JAVA里面的方法名,第二個(gè)是簽名, 第三是函數(shù)指針
          {"addCalc", "(II)I", (void*)addCalc}
    };

     簽名看著也是怪怪的,可以通過(guò)官網(wǎng)了解其含義,或者先通過(guò)javah產(chǎn)生頭文件,在每個(gè)函數(shù)說(shuō)明部分都有簽名的定義。

    /*
    * 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);

     

 

下面把幾個(gè)關(guān)鍵部分完整的代碼和配置貼上,希望對(duì)你學(xué)習(xí)本教程有所幫助。
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環(huán)境對(duì)象
    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        LOGE("ERROR: GetEnv failed\n");
        return JNI_ERR;
    }

    // 尋找目標(biāo)類
    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;
    }

    // 注冊(cè)本地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;
    }

    // 成功返回版本號(hào)
    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./apk/res/android"
    xmlns:tools="http://schemas./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

注:這個(gè)路徑要用你本機(jī)的路徑,貼出來(lái)只是告訴需要設(shè)置。

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

 

本站原創(chuàng),轉(zhuǎn)載時(shí)保留以下信息:
本文轉(zhuǎn)自:深度開(kāi)源()
原文地址:http://www./lib/view/open1470560501408.html

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多