近期由于要在移動(dòng)端優(yōu)化圖像的處理速度,經(jīng)過短暫調(diào)研發(fā)現(xiàn)用OpenCL或許可以達(dá)到優(yōu)化的效果,因此就開始在Android Studio上配置ocl的庫(kù)(使用平臺(tái)是Mac下的Android Studio)。遇到的坑不少,所以詳細(xì)記錄一下以備用。前提:<font color=#DC143C>具有一定的NDK編譯基礎(chǔ)!</font>
首先我們通過OpenCL的官方Demo來作為例子,該鏈接下有“OpenCL samples for Android”的分類下有兩個(gè)Sample Demo,隨便哪個(gè)都行。下載下來可以看到是使用Eclipse編譯的,而熟悉Android
Studio的朋友也知道,在AS下編譯使用NDK和Eclipse的差別還是很大的。將下載下來的Eclipse工程導(dǎo)入AS中(這個(gè)應(yīng)該都會(huì),就不說了),然后點(diǎn)擊運(yùn)行......用腳趾頭想當(dāng)然是跑不起來的啦,不急,繼續(xù)往下擼。
首先看一下工程的jni目錄下的文件,一共三個(gè)(以AndroidBasicOpenCL工程為例):Android.mk、Application.mk、step.cpp,make文件我們暫且不表,工程中使用的JNI代碼都是在step.cpp文件當(dāng)中(Sample的作者也是很懶,連.h文件都懶得寫(╯°Д°)╯︵ ┻━┻),點(diǎn)開該c++文件我們就發(fā)現(xiàn)一大堆頭文件找不到的錯(cuò)誤,一些是android NDK自帶的庫(kù)函數(shù),最重要的還是#include<CL/cl.h>頭文件找不到。本文章的主要問題就是解決該頭文件的引用問題。
順著思路往下走,既然找不到頭文件,就只好手動(dòng)引用進(jìn)去了,遍歷整個(gè)工程發(fā)現(xiàn)OCL的頭文件一個(gè)影子也沒見到,那就去Github上自己下吧!將列表里的所有頭文件都下載下來,并在工程里jni目錄下新建OpenCL文件夾,把所有的頭文件都Copy進(jìn)去,看下圖:

再編譯一下發(fā)現(xiàn)還是找不到,為啥?編過NDK的童鞋都知道是make文件沒有配置過。那就去Android.mk里面看看吧,原始導(dǎo)入到AS的make文件是長(zhǎng)這個(gè)樣子的:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# To allow building native part of application with OpenCL,
# OpenCL header files and statically linked library are required.
# They are used from Intel OpenCL SDK installation directories,
# that are determined below:
ifeq ($(TARGET_ARCH),x86)
OCL_POSTFIX := android32
else ifeq ($(TARGET_ARCH),x86_64)
OCL_POSTFIX := android64
else
# Unsupported target, do nothing
endif
ifneq ($(OS),Windows_NT)
ifndef INTELOCLSDKROOT
INTELOCLSDKROOT := /etc/alternatives/opencl-intel-tools
endif
# Setting LOCAL_CFLAGS with -I is not good in comparison to LOCAL_C_INCLUDES
# according to NDK documentation, but this only variant that works correctly
LOCAL_CFLAGS += -I$(INTELOCLSDKROOT)/include
LOCAL_MODULE := step
LOCAL_SRC_FILES := step.cpp
LOCAL_LDFLAGS += -llog -ljnigraphics -L$(INTELOCLSDKROOT)/lib64/$(OCL_POSTFIX) -lOpenCL
else
# Setting LOCAL_CFLAGS with -I is not good in comparison to LOCAL_C_INCLUDES
# according to NDK documentation, but this only variant that works correctly
LOCAL_CFLAGS += -I"$(INTELOCLSDKROOT)\include"
LOCAL_MODULE := step
LOCAL_SRC_FILES := step.cpp
LOCAL_LDFLAGS += -llog -ljnigraphics -L"$(INTELOCLSDKROOT)\lib\$(OCL_POSTFIX)" -lOpenCL
endif
LOCAL_ARM_MODE :=arm
include $(BUILD_SHARED_LIBRARY)
由于這里是Mac OS平臺(tái)下,所以直接看ifneq ($(OS),Windows_NT)下的邏輯,這里定義了一個(gè)常量INTELOCLSDKROOT下的路徑,推測(cè)是編寫該Sample的作者的PC系統(tǒng)路徑下的OpenCL函數(shù)庫(kù)所在,原生默認(rèn)的無論在MacOS下還是在Windows下都驗(yàn)證過該路徑并不存在,因此這里暫時(shí)忽略之。這個(gè)路徑既然沒什么卵用,那與之相關(guān)的代碼不妨也全部注釋掉自己寫,首先了解NDK編譯的童鞋應(yīng)該知道LOCAL_MODULE和LOCAL_SRC_FILES的含義,在這里我們也就用到這兩個(gè),其他的都可以用#注釋掉無視。我習(xí)慣在Gradle文件中去構(gòu)建make中的部分系統(tǒng)變量,這里也把Gradle的代碼貼出來:
import org.apache.tools.ant.taskdefs.condition.Os
apply plugin: 'com.android.application'
android {
compileSdkVersion 21
buildToolsVersion "23.0.3"
defaultConfig {
applicationId "com.intel.sample.androidbasicocl"
minSdkVersion 14
targetSdkVersion 19
ndk {
moduleName "step"
stl "stlport_static"
ldLibs "log","z","-I${file("src/main/libs")}".toString()
cFlags += "-I${file("src/main/jni/OpenCL")}".toString()
}
}
sourceSets.main {
jniLibs.srcDir 'src/main/libs'
jni.srcDirs = []
}
task ndkBuild(type: Exec) {
def ndkDir = android.ndkDirectory.getAbsolutePath()
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
commandLine ndkDir + '/ndk-build.cmd', '-C', file('src/main/jni').absolutePath
} else {
commandLine ndkDir + '/ndk-build', '-C', file('src/main/jni').absolutePath
}
}
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn ndkBuild
}
buildTypes {
release
{
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
}
}
重點(diǎn)看ndk編譯模塊下的代碼,NDK編譯的模塊名是step,stl支持c++的靜態(tài)庫(kù),這些沒啥好說的,關(guān)鍵是ldLibs以及cFlags兩個(gè)系統(tǒng)變量。ldLibs聲明鏈接時(shí)使用到的庫(kù)是android的log和z,后面一個(gè)庫(kù)很關(guān)鍵,是OpenCL的so庫(kù),后面再說。cFlags則是聲明編譯gcc時(shí)的flag,對(duì)應(yīng)前文加入的OpenCL頭文件。再下面的ndkBuild task可以忽略,因?yàn)楸緛硎窍朐贏S上直接編so的,后來發(fā)現(xiàn)不行,就用命令行來編了。
前面有說到需要使用OpenCL的so庫(kù)放入工程當(dāng)中,不然就算找到了頭文件也會(huì)在編譯的時(shí)候因?yàn)檎也坏絚l的相關(guān)引用而報(bào)錯(cuò)。那么這個(gè)so哪里來呢?可以從網(wǎng)上找,資源還是有的,就不推薦了,我是從手機(jī)系統(tǒng)里摳!出!來!的!沒錯(cuò),因?yàn)橛胁糠质謾C(jī)是不支持OCL的(看是否支持可以下載名為“OpenCL-Z”的應(yīng)用來測(cè)試),剛好我的測(cè)試機(jī)是支持的,所以我從手機(jī)根目錄/system/vendor/lib/libOpenCL.so的路徑下找到了這個(gè)庫(kù),并Copy出來放到了工程jniLibs/armeabi/目錄下,其他CPU架構(gòu)的類似,不表。好了,準(zhǔn)備工作就緒,下面就需要編譯成so庫(kù)了。
不知道為什么,我的Mac上把NDK的路徑放入.bash_profile里面還是不能引用,那就只能用絕對(duì)路徑玩了,先知道自己的NDK ndk-build編譯文件的路徑(Windows下應(yīng)該是ndk-build.cmd)的文件路徑,一般都是/Users/YourName/Library/Android/sdk/ndk-bundle,然后在bash命令行下進(jìn)入到工程的jni目錄下面,運(yùn)行/Users/YourName/Library/Android/sdk/ndk-bundle/ndk-build果然還是報(bào)錯(cuò)!(╯°Д°)╯︵ ┻━┻錯(cuò)誤是

直接寫解決方法:在Application.mk文件當(dāng)中添加以下代碼:
APP_BUILD_SCRIPT := Android.mk同樣在jni目錄下,用bash命令行寫:
/Users/YourName/Library/Android/sdk/ndk-bundle/ndk-build NDK_PROJECT_PATH=. NDK_APPLICATION_MK=Application.mk至此,編譯成功!

Log顯示編出來的so是放在了libs/armeabi/目錄下面,把庫(kù)移到j(luò)niLibs/armeabi/下面,并把生成的這個(gè)文件刪除,即可運(yùn)行成功。