注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

还东国的博客

行之苟有恒,久久自芬芳

 
 
 

日志

 
 

Android开发之四十AndroidStudio中处理第三方库的Prebuilt遇到的问题及解决方法之一  

2015-08-26 21:16:56|  分类: Android |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

Android开发之四十AndroidStudio中处理第三方库的Prebuilt遇到的问题及解决方法之一

在前面的“Android开发之三十七AndroidStudio使用NDK开发SO库并调用第三方库”对Prebuilt技术做了详细的说明。

最近在网上也发现了一些博文来介绍如何在SO中调用第三方静态和动态的库,先把这个总结一下:

第一种方式:类似c/c++直接指定的编译方式

http://blog.csdn.net/lizhiguo0532/article/details/7219349

     linux下编程时,当使用到了标准或者特定的库时我们大多使用如下的形式来指定名字或者目录:

       gcc I<特定头文件路径> -L<特定库文件路径> -l<特定库> -l<标准库> xxx.c o xxx

 

       如:gcc I./include L./lib lHWrecog lm lc {static} test.c o test

 

gcc命令的常用选项见GGC手册

       那么在android的编译过程中,也可以使用类似于这种方式来指定参数,不过在这之前,我们需要了解以下一些编译变量:

LOCAL_C_INCLUDES

LOCAL_CC

LOCAL_CFLAGS

 

LOCAL_CPP_EXTENSION

LOCAL_CPPFLAGS

LOCAL_CXX

 

LOCAL_LDLIBS

LOCAL_LDFLAGS

LOCAL_FORCE_STATIC_EXECUTABLE

这些LOCAL_开头的变量都是模块编译内的局部变量,因为通常在Android.mk开头都要包含:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

这两句,特别是include $(CLEAR_VARS)这个会清除上一个模块编译时候留下的所有LOCAL_变量,以准备给当前模块使用。

LOCAL_C_INCLUDES:额外的C/C++编译头文件路径,用LOCAL_PATH表示本文件所在目录,像gcc-I参数。如:LOCAL_C_INCLUDES += $(LOCAL_PATH)/include

 

LOCAL_CC:另外指定c编译器,不使用默认的。

 

LOCAL_CFLAGS:为C编译器传递额外的参数(如宏定义),举例:LOCAL_CFLAGS += -DLIBUTILS_NATIVE=1

 

LOCAL_CPP_EXTENSION:如果你的C++文件不是以cpp为文件后缀,你可以通过LOCAL_CPP_EXTENSION指定C++文件后缀名

       如:LOCAL_CPP_EXTENSION := .cc注意统一模块中C++文件后缀必须保持一致。

LOCAL_CPPFLAGS:传递额外的参数给C++编译器,如:LOCAL_CPPFLAGS += -ffriend-injection -DLIBUTILS_NATIVE

 

LOCAL_CXX:指定C++编译器

下面的三个编译变量都是和ld有关的,所以比较重要:

LOCAL_LDLIBS:故名思议,ldlibs,就是指定那些存在于系统目录下本模块需要连接的库。如果某一个库既有动态库又有静态库,那么在默认情况下是链接的动态库而非静态库。

如:LOCAL_LDLIBS += -lm lz lc -lcutils lutils llog

如果你的Android.mk文件中只有这么一行,那么将会采用动态链接。这个类似于上面用gcc编译时直接指定库是一样的道理。

LOCAL_LDFLAGS:这个编译变量传递给链接器一个一些额外的参数,比如想传递而外的库和库路径给ld,或者传递给ld linker的一些链接参数,-On-EL{B}(大小端字节序),那么就要加到这个上面,如:

LOCAL_LDFLAGS += -L$(LOCAL_PATH)/lib/ -lHWrecog –EB{EL} –O{n} …

或者直接加上绝对路径库的全名:

LOCAL_LDFLAGS += $(LOCAL_PATH)/lib/libHWrecog.a –EB{EL} –O{n}

LOCAL_FORCE_STATIC_EXECUTABLE:如果编译时候需要链接的动态库库存在静态库形式,那么在这个编译变量等于true的情况下,将会链接到对应的静态库而不是动态库。比如上面列出的libmlibzlibclibcutilslibutilsliblog等动静态库都存在,那么在该变量被置true的时候,将会链接对应的静态库。当然对于本来就是静态库的libHWrecog.a来说,该变量值不会影响它是被静态链接的。所以可以想到这个参数的设置是和前面用gcc编译时候指定-static参数一样的效果, 推荐只是编译特殊ELF文件才用。

 

       通常这种情况只会在编译root/sbin目录下的应用才会用到,应为通常他们执行的时间比较早,文件系统的其他部分都没加载,所以动态库就会链接不上,这个时候静态链接是最好不过了。不过在android的系统中好像没有怎么用到,因为linux一起来就是执行的init进程,就开始引导android系统了。

 

第二种方法:androidprebuilt方式

这里重点是介绍这个。

方法如前面所讲,设置

MODULEgradle:

apply from: "ndk_build_script.gradle"

 

    sourceSets {

        main {

            jni.srcDirs = []

            jniLibs.srcDirs 'src/main/libs'

        }

    }

    tasks['preBuild'].dependsOn 'ndkBuild'

注意后面这个红色的部分,没有这个,是不会启动编译的,同样为了使用要引用一上ndk_build_script.gradle,注意这个的内容与下面的两个在stackoverflow上大家提出的解决问题的方法的思路是一样,即获得ndk-build.cmd路径,然后加入参数调用进行编译。

注意上面加红的部分,不要搞错了文件,否则报这个文件的命令有错,就停止编译了。

######### ndk_build_script.gradle

import org.apache.tools.ant.taskdefs.condition.Os

 

ext{

    ndkBuildCmdRun = this.&ndkBuildCmdRun

}

 

task ndkBuild << {

    ndkBuildCmdRun(project.file('src/main').absolutePath)

}

 

def ndkBuildCmdRun(String mainDirPath){

    exec{

        executable getNdkBuildCmd()

        args '-C', mainDirPath

    }

}

''

def getNdkDir() {

    if (System.env.ANDROID_NDK_ROOT != null)

        return System.env.ANDROID_NDK_ROOT

 

    Properties properties = new Properties()

    properties.load(rootProject.file('local.properties').newDataInputStream())

    def ndkdir = properties.getProperty('ndk.dir', null)

    if (ndkdir == null)

        throw new GradleException("NDK location not found. Define location with ndk.dir in the local.properties file or with an ANDROID_NDK_ROOT environment variable.")

 

    return ndkdir

}

 

def getNdkBuildCmd() {

    def ndkbuild = getNdkDir() + "/ndk-build"

    if (Os.isFamily(Os.FAMILY_WINDOWS)) {

        ndkbuild += ".cmd"

    }

    return ndkbuild

}

###############################################################

下面是相应的Android.mk的内容:

LOCAL_SHORT_COMMANDS := true

LOCAL_PATH := $(call my-dir)

$(warning "local dir $(LOCAL_PATH)")

#################################### Prebuilt libTestNDKOption.so

include $(CLEAR_VARS)

 

LOCAL_MODULE := TestNDKPtion

LOCAL_SRC_FILES := ../apilib/libTestNDKPtion.so

 

#include $(PREBUILT_SHARED_LIBRARY)

####################################  Build libleftapi.so

#include $(CLEAR_VARS)

 

LOCAL_MODULE    := leftapi

LOCAL_LDLIBS    := -lm -llog

LOCAL_LDFLAGS   += -s    #忽略符号表信息,可在LD的手册中查找

 

LOCAL_SRC_FILES := IrisMathcer.c TeeMatcherBuilder.c TeeMatcherParse.c

LOCAL_SHARED_LIBRARIES := TestNDKOption  #注意下面的红色说明

include $(BUILD_SHARED_LIBRARY)

 

 

LOCAL_SHARED_LIBRARIESLOCAL_SHARED_LIBRARY:注意前者是复数S形式,用于Link多个库(只有一个也可以用),后者只能添加一个链接库,可恶的文档关于Prebuilts的介绍里面给出的例子是LOCAL_SHARED_LIBRARY,使用两个库的时候第二个库死也link不上。

一些细节还是非常要,比如带不带复数,本来帮助要是全的话这个都不是事儿,可天朝的事儿大家也都知道。

 

上面的程序是基本跑通的,但是在一开始时,用得是下面的方法,这两个方法,有几个不同的地方,一个是获得NDKNDK编译脚本路径的方法不同,一个是获得脚本的名字不同,通过前面的证明(并且看了Eclipse中的原有的NDK的编译方法),必须得有.cmd

更加详细的这里就不再细说了,看下面的文章和代码。

http://stackoverflow.com/questions/21096819/jni-and-gradle-in-android-studio

应用的下面的,

apply plugin: 'com.android.application'

 

android {

    compileSdkVersion 14

    buildToolsVersion "20.0.0"

 

    defaultConfig {

        applicationId "com.example.application"

        minSdkVersion 14

        targetSdkVersion 14

 

        ndk {

            moduleName "YourModuleName"

        }

    }

 

    sourceSets.main {

        jni.srcDirs = [] // This prevents the auto generation of Android.mk

        jniLibs.srcDir 'src/main/libs' // This is not necessary unless you have precompiled libraries in your project.

    }

 

    task buildNative(type: Exec, description: 'Compile JNI source via NDK') {

        def ndkDir = android.ndkDirectory

        commandLine "$ndkDir/ndk-build",

                '-C', file('src/main/jni').absolutePath, // Change src/main/jni the relative path to your jni source

                '-j', Runtime.runtime.availableProcessors(),

                'all',

                'NDK_DEBUG=1'

    }

 

    task cleanNative(type: Exec, description: 'Clean JNI object files') {

        def ndkDir = android.ndkDirectory

        commandLine "$ndkDir/ndk-build",

                '-C', file('src/main/jni').absolutePath, // Change src/main/jni the relative path to your jni source

                'clean'

    }

 

    clean.dependsOn 'cleanNative'

 

    tasks.withType(JavaCompile) {

        compileTask -> compileTask.dependsOn buildNative

    }

}

 

dependencies {

    compile 'com.android.support:support-v4:20.0.0'

}

最后还有一个(带.cmd的):

apply plugin: 'com.android.application'

 

android {

    compileSdkVersion 21

    buildToolsVersion "21.1.2"

 

    defaultConfig {

        applicationId "com.example.hellojni"

        minSdkVersion 4

        targetSdkVersion 4

 

        ndk {

            moduleName "hello-jni"

        }

 

        testApplicationId "com.example.hellojni.tests"

        testInstrumentationRunner "android.test.InstrumentationTestRunner"

    }

    sourceSets.main {

        jni.srcDirs = [] // This prevents the auto generation of Android.mk

//        sourceSets.main.jni.srcDirs = []

        jniLibs.srcDir 'src/main/libs' // This is not necessary unless you have precompiled libraries in your project.

    }

 

    task buildNative(type: Exec, description: 'Compile JNI source via NDK') {

        def ndkDir = android.plugin.ndkFolder

        commandLine "$ndkDir/ndk-build.cmd",

                '-C', file('src/main/jni').absolutePath, // Change src/main/jni the relative path to your jni source

                '-j', Runtime.runtime.availableProcessors(),

                'all',

                'NDK_DEBUG=1'

    }

 

    task cleanNative(type: Exec, description: 'Clean JNI object files') {

        def ndkDir = android.plugin.ndkFolder

        commandLine "$ndkDir/ndk-build.cmd",

                '-C', file('src/main/jni').absolutePath, // Change src/main/jni the relative path to your jni source

                'clean'

    }

 

    clean.dependsOn 'cleanNative'

 

    tasks.withType(JavaCompile) {

        compileTask -> compileTask.dependsOn buildNative

    }

 

}

 

 

dependencies {

    compile 'com.android.support:support-v4:21.0.3'

}

看红色的区别,去虚拟机看了原来Eclipse中的NDK配置才发现得用后面的啊,不过后面的那个NDK路径的获得还有前面的都一样,是老的,已经有新的来代替了,AndroidStudio的升级还是非常快的。

 

在这个网页介绍了一种方法,也可以增加预处理库:

http://www.tuicool.com/articles/yuAjAz

sourceSets {

        main {

            jniLibs.srcDirs = ['libs']

        }

    }

这个没有把默认的编译,又增加了一个新的库,不知道这样是不是可以,没有试。

预编译库的问题,放到下一篇博文来详述,这篇太多了些。

  评论这张
 
阅读(377)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017