編輯:關於Android編程
今天我們來簡單說一下Android NDK的使用方法。眾所周知,so文件在Android的開發過程中起到了很重要的作用,無論與底層設備打交道還是在Android安全領域。so文件都格外受人青睐。NDK就是Android發布的用於編譯so文件的一套工具,
引用自百度百科的一段解釋
Android NDK 是在SDK前面又加上了“原生”二字,即Native Development Kit,因此又被Google稱為“NDK”。
眾所周知,Android程序運行在Dalvik虛擬機中,NDK允許用戶使用類似C / C++之類的原生代碼語言執行部分程序。
從C / C++生成原生代碼庫所需要的工具和build files。 將一致的原生庫嵌入可以在Android設備上部署的應用程序包文件(application packages files ,即.apk文件)中。 支持所有未來Android平台的一系列原生系統頭文件和庫
NDK包括了:為何要用到NDK?
代碼的保護,由於apk的java層代碼很容易被反編譯,而C/C++庫反匯難度較大。 在NDK中調用第三方C/C++庫,因為大部分的開源庫都是用C/C++代碼編寫的。 便於移植,用C/C++寫的庫可以方便在其他的嵌入式平台上再次使用。
概括來說主要分為以下幾種情況:
本文從以下三個方面講解NDK的使用
直接在命令行中用NDK進行編譯 Android Studio2.2以前對NDK的支持 Android Studio2.2及以後對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+類名命名文件,如下:
感覺現在應該很簡單了,只需要關注方法實現就可以了,其他基本都不要開發者關心了。
目的 因為畢設要到公司去做公司給的題目是:基於Android平台的電梯廣告機。Android平台和嵌入式系統的通信是通過CAN總線進行的。具體的硬件是Ma
Fragment一般是宿主Activity UI的一部分或一種行為,作為Activity的整個V
關於IPC應該不用多介紹了,Android系統中的進程之間不能共享內存,那麼如果兩個不同的應用程序之間需要通訊怎麼辦呢?比如公司的一個項目要更新,產品的需求是依附於當前項
源碼地址:點擊打開鏈接 Android-Universal-Image-Loader的廣泛使用,我們有必要好好研究下他,對於我們使用,和進步都有很多的幫助, 編程的,先