Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> AndroidNDK使用簡介

AndroidNDK使用簡介

編輯:關於Android編程

今天我們來簡單說一下Android NDK的使用方法。眾所周知,so文件在Android的開發過程中起到了很重要的作用,無論與底層設備打交道還是在Android安全領域。so文件都格外受人青睐。NDK就是Android發布的用於編譯so文件的一套工具,

引用自百度百科的一段解釋

Android NDK 是在SDK前面又加上了“原生”二字,即Native Development Kit,因此又被Google稱為“NDK”。

眾所周知,Android程序運行在Dalvik虛擬機中,NDK允許用戶使用類似C / C++之類的原生代碼語言執行部分程序。
NDK包括了:

從C / C++生成原生代碼庫所需要的工具和build files。 將一致的原生庫嵌入可以在Android設備上部署的應用程序包文件(application packages files ,即.apk文件)中。 支持所有未來Android平台的一系列原生系統頭文件和庫

為何要用到NDK?
概括來說主要分為以下幾種情況:

代碼的保護,由於apk的java層代碼很容易被反編譯,而C/C++庫反匯難度較大。 在NDK中調用第三方C/C++庫,因為大部分的開源庫都是用C/C++代碼編寫的。 便於移植,用C/C++寫的庫可以方便在其他的嵌入式平台上再次使用。

本文從以下三個方面講解NDK的使用

直接在命令行中用NDK進行編譯 Android Studio2.2以前對NDK的支持 Android Studio2.2及以後對NDK的支持

直接在命令行使用NDK

NDK本來就是一套編譯工具,自然是在命令行中執行,其實後面兩種方法都是對這種方法的自動化處理,萬變不離其宗, 要理解後面兩種方法,還是應該熟悉一下不借助任何工具時的操作。

SDK默認是不帶NDK的,所以NDK需要額外下載,下載後還需要配置環境變量。具體方法可以查看百度,配置環境變量很簡單,只需要把NDK根目錄,也就是ndk-build所在的目錄加入環境變量即可。

用NDK-BUILD構建一個NDK程序,我們知道就是將C文件編譯成so文件,其實原理很簡單,用gcc進行編譯。哦,因為我是mac環境,所以自帶GCC編譯環境,如果是windows下的話,還需要安裝Cygwin環境來模擬linux,不過聽說最新的NDK自帶Cygwin,所以不再需要額外安裝,Windows的同學可以試一下,有問題可以在評論區提問, 有機會我會補充Win下的使用方法。

編譯c程序需要makefile,其實簡單說就是告訴GCC怎麼編譯,先編什麼在編什麼,需要哪些包等等。這個熟悉c的同學應該知道的。一個簡單的so項目包含以下四個文件。

這裡寫圖片描述

除了.h和.c文件,還有兩個makefile,Application.mk是項目makefile,它會指定調用哪個子makefile,然後Android.mk是具體執行操作的makefile。Application.mk的名字不能變,因為NDK會默認去找這個文件,後面也會講到,Android.mk的名字可以變,是配置在Application.mk中的。

然後NDK還有一些規定,看.h文件的名字,c文件中的方法與java中某個方法是一一對應的,出於安全考量,NDK要求C中的方法名應該以對應java文件的包名+類名+方法名來命名。

頭文件

/* DO NOT EDIT THIS FILE - it is machine generated */
#include 
/* Header for class com_example_zzw_helloworld_JniUtils */

#ifndef _Included_com_example_zzw_helloworld_JniUtils
#define _Included_com_example_zzw_helloworld_JniUtils
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_zzw_helloworld_JniUtils
 * Method:    stringFromJNI
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_zzw_helloworld_JniUtils_stringFromJNI
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

這個頭文件很簡單,就聲明了一個方法,這兩個參數是固定的,這個方法在java中的表現形式為stringFromJNI() 返回類型對應的是java中的string。

然後創建C文件,實現該方法。

//
// Created by zzw on 16/10/11.
//
#include "com_example_zzw_helloworld_JniUtils.h"

JNIEXPORT jstring JNICALL Java_com_example_zzw_helloworld_JniUtils_stringFromJNI
        (JNIEnv *env, jobject instance) {
    return (*env)->NewStringUTF(env, "Hello from JNI !");
}

然後我們還需要兩個make文件,一個是Application.mk另一個是Android.mk
Application.mk內容如下:

APP_BUILD_SCRIPT := /Users/zzw/Desktop/jni/Android.mk

其實就是聲明Android.mk的位置
Android.mk如下:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := hello-jni
LOCAL_SRC_FILES := hello-jni.c

include $(BUILD_SHARED_LIBRARY)

然後運行

#include "com_example_zzw_helloworld_JniUtils.h"

JNIEXPORT jstring JNICALL Java_com_example_zzw_helloworld_JniUtils_stringFromJNI
        (JNIEnv *env, jobject instance) {
    return (*env)->NewStringUTF(env, "Hello from JNI !");
}

build.gradle中需要配置ndk
這裡寫圖片描述

然後點擊運行。

遇到的問題,
報錯
<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;"> Error:Execution failed for task ':app:compileDebugNdk'. > 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 "$USE_DEPRECATED_NDK=true" in gradle.properties to continue using the current NDK integration.

這是因為gradle插件版本太高,已經不支持這個方法了,它提示我們在gradle.properties裡面加一句話,但經過我測試,那句話是不對的,應該加如下一句:

android.useDeprecatedNdk=true

然後運行,正常。

Android Studio2.2之後使用NDK

前面我們看到其實比起第一種方法也沒有簡化多少,所以在Studio2.2的時候google又嘗試簡化了做法,Android Studio 2.2開始支持用內建的方法來執行復雜的NDK編譯,這意味著開發者只需要寫好c文件,其他所有的編譯,鏈接都可以交給系統去做。

注意:gradle版本需要2.2及以上

這個特性的實現要依賴於一個build標簽,叫externalNativeBuild。標簽配置如下

defaultConfig {
    externalNativeBuild {
        ndkBuild {
            arguments "NDK_LIBS_OUT=$jniLibsDir", "-j$numProcs", "all"
            abiFilters "armeabi-v7a"
        }
    }
}
externalNativeBuild{
    ndkBuild{
        path "src/main/jni/Android.mk"
    }
}

arguments指定編譯的參數,abiFilters指定編譯的平台,這些參數都可以省略以使用默認參數。下面path指定make文件的位置。

之後,NDK執行的task配置如下

task ndkBuild(type: Exec) {
    commandLine getNdkBuildCmd(),
            '-C', file('src/main/jni').absolutePath,
            '-j', Runtime.runtime.availableProcessors(),
            "NDK_LIBS_OUT=$jniLibsDir",
            'all',
            'NDK_DEBUG=1'

    dependsOn 'generateLuaBytecodes'

    doFirst {
        println '== ndkBuild =='
    }
}

接下來讓我們來看一下,具體的實現步驟是什麼樣的,不需要像以前一樣自己寫頭文件,然後再編譯,現在只需要關注c文件即可。以hello world為例,我們寫一個C文件如下:

#include 
#include 


JNIEXPORT jstring JNICALL
Java_com_example_zzw_helloworld_MainActivity_stringFromJNI(JNIEnv *env, jobject instance) {
    return (*env)->NewStringUTF(env, "Hello from JNI !");
}

注意方法名要以調用它的JAVA文件的包名+類名+方法名命名。
這樣寫完之後,我們就可以在相應的JAVA文件中調用了,代碼如下:

package com.example.zzw.helloworld;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {



    static {
        System.loadLibrary("hello-jni");
    }

    public native String stringFromJNI();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        System.out.println(stringFromJNI());

    }
}

先用loadLibrary引入so文件,然後用native聲明底層方法,然後我們就可以在程序中調用方法了。
當然前面提到了make文件,我們要創建一個Android.mk文件在externalNativeBuild中聲明的位置,如下:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := hello-jni
LOCAL_SRC_FILES := hello-jni.c

include $(BUILD_SHARED_LIBRARY)

之後,make project,運行程序應該就能看的效果了,通過這種方法生成的so文件放在app/.externalNativeBuild/debug/obj/local/下,並以lib+類名命名文件,如下:

這裡寫圖片描述

感覺現在應該很簡單了,只需要關注方法實現就可以了,其他基本都不要開發者關心了。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved