編輯:關於Android編程
Android Studio 進行jni的編程時有個實驗性的gradle-experimental,用起來還是比較方便,不需要編寫Android.mk文件和Application.mk文件,所有的配置的在app/build.gradle中完成,而且C++代碼也可以自動提示。本文以編譯使用OpenCV為示例,講解Android Studio NDK開放環境的配置。
使用的開放環境如下:
Android Studio 2.1.3 gradle版本為:2.14 gradle-experimental版本為:0.7.3 OpenCV的版本為:3.1.0在AS中新建一Android工程,名字隨意,我已OpenCV為工程名。
使用AS自帶的NDK,因此第一步需要下載NDK。打開File >Settings>Appearance & Behavior > System Settings > Android SDK。在界面左邊選擇SDK Tools 選擇NDK選項,如果沒安裝,點擊OK後,系統便會進行NDK的按照。如下圖所示:
按照完畢後打開File > Project Structrue > SDK Location,在右邊的界面中 Android NDK Location 一欄中會顯示安裝的NDK路徑,如下圖所示:
打開工程名/build.gradle,注意不是app/build.gradle。將默認的
classpath ‘com.android.tools.build:gradle:2.1.3’ 修改為:
classpath ‘com.android.tools.build:gradle-experimental:0.7.3’
整個文件內容如下:
修改完成後進行sync會出現錯誤提示,正是下一步需要解決的問題。<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxibG9ja3F1b3RlPg0KCTxwPtei0uKjusjnufvKudPDtcSyu8rHZ3JhZGxlLWV4cGVyaW1lbnRhbDowLjcuM6Os1PK7udDo0N64xGdyYWRsZbXEsOaxvqGjtPK/qkZpbGUtJmd0O1Byb2plY3QgU3RyY3V0cnVlLSZndDtQcm9qZWN0INTavefD5tPSsd/Q3rjER3JhZGxlIHZlcnNpb261xLDmsb6jrNLUysq6z2dyYWRsZS1leHBlcmltZW50YWy1xLDmsb6hozwvcD4NCjwvYmxvY2txdW90ZT4NCjxoMyBpZD0="修改appbuildgradle">修改app/build.gradle
因為使用的的gradle-experimental,原有的build.gradle就不適用了,需要進行修改。基本的就將
apply plugin: ‘com.android.application’ 修改為:
apply plugin: ‘com.android.model.application’。
在原有的android節點外添加model節點,使model節點包含android節點,並且原有的屬性配置需要添加等號。如下所示:
model { android { compileSdkVersion = 24 buildToolsVersion = "24.0.1" defaultConfig.with { applicationId = "com.leej.opencv" minSdkVersion.apiLevel = 14 targetSdkVersion.apiLevel = 24 versionCode = 1 versionName = "1.0" } } android.buildTypes { release { minifyEnabled = false proguardFiles.add(file('proguard-rules.pro')) } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:24.1.0' }
截至這裡基本的配置完成,下面就是ndk的配置和OpenCV的編譯使用了。
到這裡http://opencv.org/platforms/android.html下載OpenCV for Android 的合適版本。下載後解壓,拷貝..\OpenCV-android-sdk\sdk\native 文件夾到建立的Android項目工程目錄下。整個的工程目錄結構如下:
在app/src/main下創建jni文件夾,本地C++文件都存於次,創建的方法如下:
在gradle-experimental下,需在app/build.gradle中添加相應的配置,才能正確編譯本地方法。
在model節點下添加:
android.ndk { moduleName = "myOpenCV" cppFlags.add("-std=c++11") cppFlags.add("-frtti") cppFlags.add("-fexceptions") cppFlags.add("-I${file("../native/jni/include")}".toString()) cppFlags.add("-I${file("../native/jni/include/opencv")}".toString()) ldLibs.addAll(["android", "EGL", "GLESv2", "dl", "log", "z"]) //編譯需要 stl = 'gnustl_shared' //能夠實現C++代碼自動提示的關鍵 } android.productFlavors { create("arm") { ndk.with { abiFilters.add("armeabi") String libsDir = getProjectDir().getParent() + "\\native\\libs\\armeabi\\" String thirdPartyDir = getProjectDir().getParent() + "\\native\\3rdparty\\libs\\armeabi\\" ldFlags.add(libsDir + "libopencv_features2d.a") ldFlags.add(libsDir + "libopencv_flann.a") ldFlags.add(libsDir + "libopencv_imgproc.a") ldFlags.add(libsDir + "libopencv_core.a") ldFlags.add(libsDir + "libopencv_highgui.a") ldFlags.add(libsDir + "libopencv_calib3d.a")//注意添加的先後順序 ldFlags.add(thirdPartyDir + "libtbb.a") } } create("armv7") { ndk.with { abiFilters.add("armeabi-v7a") String libsDir = getProjectDir().getParent() + "\\native\\libs\\armeabi-v7a\\"; String thirdPartyDir = getProjectDir().getParent() + "\\native\\3rdparty\\libs\\armeabi-v7a\\"; ldFlags.add(libsDir + "libopencv_features2d.a") ldFlags.add(libsDir + "libopencv_flann.a") ldFlags.add(libsDir + "libopencv_imgproc.a") ldFlags.add(libsDir + "libopencv_core.a") ldFlags.add(libsDir + "libopencv_highgui.a") ldFlags.add(libsDir + "libopencv_calib3d.a") ldFlags.add(thirdPartyDir + "libtbb.a") } } create("x86") { ndk.with { abiFilters.add("x86") String libsDir = getProjectDir().getParent() + "\\native\\libs\\x86\\"; String thirdPartyDir = getProjectDir().getParent() + "\\native\\3rdparty\\libs\\x86\\"; ldFlags.add(libsDir + "libopencv_features2d.a") ldFlags.add(libsDir + "libopencv_flann.a") ldFlags.add(libsDir + "libopencv_imgproc.a") ldFlags.add(libsDir + "libopencv_core.a") ldFlags.add(libsDir + "libopencv_highgui.a") ldFlags.add(libsDir + "libopencv_calib3d.a") ldFlags.add(thirdPartyDir + "libtbb.a") } } }
完成之後整個app/build.gradle文件為:
model { android { compileSdkVersion = 24 buildToolsVersion = "24.0.1" defaultConfig.with { applicationId = "com.leej.opencv" minSdkVersion.apiLevel = 14 targetSdkVersion.apiLevel = 24 versionCode = 1 versionName = "1.0" } } android.buildTypes { release { minifyEnabled = false proguardFiles.add(file('proguard-rules.pro')) } } android.ndk { moduleName = "myOpenCV" cppFlags.add("-std=c++11") cppFlags.add("-frtti") cppFlags.add("-fexceptions") cppFlags.add("-I${file("../native/jni/include")}".toString()) cppFlags.add("-I${file("../native/jni/include/opencv")}".toString()) ldLibs.addAll(["android", "EGL", "GLESv2", "dl", "log", "z"]) //編譯需要 stl = 'gnustl_shared' //能夠實現C++代碼自動提示的關鍵 } android.productFlavors { create("arm") { ndk.with { abiFilters.add("armeabi") String libsDir = getProjectDir().getParent() + "\\native\\libs\\armeabi\\" String thirdPartyDir = getProjectDir().getParent() + "\\native\\3rdparty\\libs\\armeabi\\" ldFlags.add(libsDir + "libopencv_features2d.a") ldFlags.add(libsDir + "libopencv_flann.a") ldFlags.add(libsDir + "libopencv_imgproc.a") ldFlags.add(libsDir + "libopencv_core.a") ldFlags.add(libsDir + "libopencv_highgui.a") ldFlags.add(libsDir + "libopencv_calib3d.a")//注意添加的先後順序 ldFlags.add(thirdPartyDir + "libtbb.a") } } create("armv7") { ndk.with { abiFilters.add("armeabi-v7a") String libsDir = getProjectDir().getParent() + "\\native\\libs\\armeabi-v7a\\"; String thirdPartyDir = getProjectDir().getParent() + "\\native\\3rdparty\\libs\\armeabi-v7a\\"; ldFlags.add(libsDir + "libopencv_features2d.a") ldFlags.add(libsDir + "libopencv_flann.a") ldFlags.add(libsDir + "libopencv_imgproc.a") ldFlags.add(libsDir + "libopencv_core.a") ldFlags.add(libsDir + "libopencv_highgui.a") ldFlags.add(libsDir + "libopencv_calib3d.a") ldFlags.add(thirdPartyDir + "libtbb.a") } } create("x86") { ndk.with { abiFilters.add("x86") String libsDir = getProjectDir().getParent() + "\\native\\libs\\x86\\"; String thirdPartyDir = getProjectDir().getParent() + "\\native\\3rdparty\\libs\\x86\\"; ldFlags.add(libsDir + "libopencv_features2d.a") ldFlags.add(libsDir + "libopencv_flann.a") ldFlags.add(libsDir + "libopencv_imgproc.a") ldFlags.add(libsDir + "libopencv_core.a") ldFlags.add(libsDir + "libopencv_highgui.a") ldFlags.add(libsDir + "libopencv_calib3d.a") ldFlags.add(thirdPartyDir + "libtbb.a") } } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:24.1.0' }
創建一個新的類編寫兩個本方法,如下圖所示:
“myOpenCV”是生成的庫文件的名字,與app/build.gradle 中的moduleName名字相同。getStringFromNative方法測試由C++來創建字符串;gray方法測試OpenCV的邊緣檢測。從圖中可以看到標注為紅色的方法, 將光標定位到getStringFromNative方法,並按快捷鍵Alt + Enter,此時會彈出下圖的提示:
在彈出的框中點擊第一項,會在jni文件中創建一個.c的文件,並且會創建一個本地方法,如下圖所示:
注意:由於示例需要使用OpenCV,因此將.c文件改成了.cpp文件。
另外一個方法也采用同樣的方式生成。
完成後的.cpp文件的內容如下:
#include#include #include #include #include using namespace std; using namespace cv; IplImage* change4ChannelTo3InIplImage(IplImage * src); JNIEXPORT jstring JNICALL Java_com_leej_opencv_lib_OpenCV_getStringFromNative(JNIEnv *env, jclass type) { // TODO Mat mat(33, 22, 1); string s = "Cpp v1 - succ, row#:" + mat.rows; return env->NewStringUTF(s.data()); } JNIEXPORT jintArray JNICALL Java_com_leej_opencv_lib_OpenCV_gray(JNIEnv *env, jobject instance, jintArray buf_, jint w, jint h) { jint *buf = env->GetIntArrayElements(buf_, false); if (buf == NULL) return 0; Mat myImg(h, w, CV_8UC4, (unsigned char *)buf); IplImage image = IplImage(myImg); IplImage* image3Channel = change4ChannelTo3InIplImage(&image); IplImage* pCannyImage = cvCreateImage(cvGetSize(image3Channel), IPL_DEPTH_8U, 1); cvCanny(image3Channel, pCannyImage, 50, 150, 3); int* outImage = new int[w * h]; for (int i = 0; i < w * h; i ++) { outImage[i] = (int)pCannyImage->imageData[i]; } int size = w * h; jintArray result = env->NewIntArray(size); env->SetIntArrayRegion(result, 0, size, outImage); // TODO env->ReleaseIntArrayElements(buf_, buf, 0); return result; } IplImage* change4ChannelTo3InIplImage(IplImage * src) { if (src -> nChannels != 4) { return NULL; } IplImage* destImg = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 3); for (int row = 0; row < src->height; row ++) { for (int col = 0; col < src -> width; col ++) { CvScalar s = cvGet2D(src, row, col); cvSet2D(destImg, row, col, s); } } return destImg; }
點擊Build-> Make Project對工程進行編譯,在配置都正確的情況下,編譯是成功的。
至此ndk的配置和OpenCV的使用就到此結束。可以自行編寫程序測試這個方法是否成功,本人測試以通過。
1、 概述DialogFragment在android 3.0時被引入。是一種特殊的Fragment,用於在Activity的內容之上展示一個模態的對話框。典型的用於:
引子承接我的上一篇文章Android ClassyShark vs ApkTool,感謝讀者@weishu的提醒,jadx是我遺漏的一個非常好用的android反編譯gu
本文采用一個Demo來展示Android中ExpandableListView控件的使用,如如何在組/子ListView中綁定數據源。直接上代碼如下:程序結構圖:layo
從今天開始,把看書時候的知識點整理成博客, 這個比較簡單,估計有經驗的都用過,weight屬性 在做Android布局的時候,經常遇到需要幾個控件按比例分配空間的情況