零基础Android Studio 入门爬坑记

[原创不易,转载请标明出处] https://blog.csdn.net/u013945158/article/details/80079775

任务: 将现有C代码编译成so库,开发APP时使用该库
注:开发环境 Ubuntu14.04LTS

准备工作:
1. 下载Android Studio (3.0.1)
2. 在Android Studio中下载SDK (啊,慢呀,零基础时候还不懂使用国内镜像,具体方法自行查找)
3. 在SDK manager中勾选下载NDK (因为要做native开发,这个是必须的)
4. 构建一个模拟器呗 (what?不支持VT-x,骚年放弃吧,请使用真机,幸好家里有两部闲置的Android机.期间没有放弃在不支持vt-x的机器上安装模拟器的努力,均没有成功,比如尝试arm内核image,不行;使用genumotion+virtualbox,失败;....放弃了,有条件的请换电脑吧(相信现在使用不支持vt-x机器的开发者已经很少了) )


顺利入坑-----------~~~~~~--->>>>>

====> 坑:如何通过JNI/NDK将C/C++代码添加到Android项目中?
使用Android Studio新建NDK项目(初始时勾选支持C++),即可以看到一个支持C++的Android工程. 但是一开始接到的任务是以第三方库的形式将C++/C代码导入工程,所以一开始考虑的是如何导入so库文件.
可以看到native-lib即以shared lib的形式在工程中.但是问题来了,如何将现有c源码以shared lib的形式导入Android项目并能够正确识别接口,就像native-lib一样...直观上想一下好像有好远的路子哦,且现有code中没有JNI接口.做思考状: 这样应该不可以,所以一定是要再包一层,接口层一定要的.

本就是零基础,想到的快速掌握这个技术的途径就是下载一些NDK项目学习一下咯.在Git上下载好多NDK开源项目,终于找到一个法宝:android-ndk-master (请至Git下载,这个好像是NDK官方示例)

其中有个hello-libs示例,完美.
可以学到第三方库是如何被引入Android项目中,如何配置;接口如何写等.
所以,总结一下: Android APP--接口层--native code(或so库)

====> 坑:如何生成第三方库?
刚开始天真的下载的gcc,使用命令: gcc test.c -o libtest.so
好了,有了. 库有了就去忙别的呗,先看看Android Studio上出的问题.
可是最后摔了大跟头,从来不懂abi的概念,也从没关心过处理器架构.

依然从示例下手.hello-libs.

在该项目中 settings.gradle中:

// To generate libs used in this sample:
//   1) enable the gen-libs at end of this file
//   2) enable module build dependency in app/build.gradle
//   3) build the app's APK in Android Studio or on command line
//   4) undo step 1) and 2) above
 include ':gen-libs'
所以,去掉最后一行(// include ':gen-libs')的注释,并在 app/build.gradle中打开dependency(最后,去除/* */)语句,sync gradle.
可以看到生成hello-libs中的两个library的源码和配置.
这里可以获益匪浅.


====> 坑 :第三方库如何添加到项目中?
以动态库为例:
包含第三方lib的方法:需要工程建立一个接口类,并生成对接的C/C++文件,该文件即为NDK开发的lib,注意配置.引入第三方库的时候,设置NDK lib依赖第三方库即可.依赖的配置方法.
_________配置方法___________
1.将第三方库放置在distribution目录下,在配置文档CMakeLists.txt中设置常用目录,命令如下
    
set(distribution_DIR ${CMAKE_SOURCE_DIR}/../../../../distribution)
2.增加引用第三方库,注意add_library的参数(lib名,导入方式{shared static},文档路径或者导入),为第三方库增加路径
add_library(lib_gperf SHARED IMPORTED)    //shared library and imported from 3rd part
set_target_properties(lib_gperf PROPERTIES IMPORTED_LOCATION
    ${distribution_DIR}/gperf/lib/${ANDROID_ABI}/libgperf.so)    //the path of so lib
3. 为引用库添加第三方库的头文件目录
target_include_directories(hello-libs PRIVATE
                           ${distribution_DIR}/gmath/include
                           ${distribution_DIR}/gperf/include)    //the header files
4.设置接口库对第三方库的链接,其中第一个参数为接口库,后面参数为第三方库
target_link_libraries(hello-libs
                      android
                      lib_gmath
                      lib_gperf    //third part so
                      log)
5. 在app的build.gradle中添加jniLibs路径,该命令决定了在apk打包时候会将该库打包进apk
    sourceSets {
        main {
            // let gradle pack the shared library into apk
            jniLibs.srcDirs = ['../distribution/gperf/lib']    //package the so libs into apk
            jni.srcDirs = ['src/main/jni', 'src/main/jni/']
        }
    }
___________________

====> 坑:APP闪退
这里列出一个tips,添加下述语句表示将对应文件夹下的lib添加到工程的jniLibs中,在进行软件打包时候会将对应文件夹下的库文件打包进APK中.如若没有该语句,编译正常,但是在APK运行时候发生闪退现象,因为没有找到可链接的库文件.可惜的时候这个时候还不懂的去抓log.
//下述语句应该包含在android { ... }中
    sourceSets {
        main {
            // let gradle pack the shared library into apk
            jniLibs.srcDirs = ['../distribution/crossadd/lib']    //package the *.so libs in the folder into the APK
            jni.srcDirs = ['src/main/jni', 'src/main/jni/']
        }
    }
====> 坑: 如何构建交叉编译工具链?
但是源码若是不方便导出,不能在Android Studio中建立gen-libs项目生成so,那么可以考虑交叉编译工具链咯...
上述准备工作中已经下载SDK和NDK了,那么可以找到一个目录  xxx/Sdk/ndk-bundle
这就是Sdk中的NDK了;或者直接下载某版本的NDK 如android-ndk-r14b
将NDK中的交叉编译工具链拉出变成Stand-alone交叉编译工具链的方法,网上有很多资源,也很简单:
新建一个文件make_toolchain.sh,添加下述内容,之后,增加可执行权限 chmod +x make_toolchain.sh
export NDK_HOME=/home/xxx/Android/Sdk/ndk-bundle    #your ndk path


platform=android-19    #the platform you want to use
#platform>19 support the blow 6 kinds of archs, otherwise support only 3 kinds of archs, when check the path ${NDK_HOME}/platforms/android-x, you will find the difference.


shmake=$NDK_HOME/build/tools/make-standalone-toolchain.sh
archs=(       #here list 6 kinds of arch
    'arm'
    'arm64'
    'x86'
    'x86_64'
    'mips'
    'mips64'
)


toolchains=(    #here list 6 kinds of toolchains
    'arm-linux-androideabi-4.9'
    'aarch64-linux-android-4.9'
    'x86-4.9'
    'x86_64-4.9'
    'mipsel-linux-android-4.9'
    'mips64el-linux-android-4.9'
)


echo $NDK_HOME


num=${#archs[@]}
for((i=0;i<$num;i++))
do
    $shmake --arch=${archs[i]} --platform=$platform --install-dir=$HOME/Android/android-toolchains/${archs[i]} --toolchain=${toolchains[i]}
done
# --install-dir, the direction of the toolchains to be placed


增加可执行权限之后,执行 ./make_toolchains.sh
可以在$HOME/Android/android-toolchains构建不同架构的交叉编译工具链


=====> 坑: x86_64/arm64/mips64的交叉编译工具链构建失败
请注意上述make_toolchains.sh中
platform=android-19    #the platform you want to use
#platform>19 support the blow 6 kinds of archs, otherwise support only 3 kinds of archs, when check the path ${NDK_HOME}/platforms/android-x, you will find the difference.
在platform设置版比较低时候,arch不支持64位的platform.
比较好理解,随着科技的发展,Android支持的架构越来越多.因此在低版本时候,并没有对64位的架构进行支持.在NDK目录下的platforms/android-x文件夹中可以看到android版本x支持的架构.
在Android-19以上的版本才支持上述列出的6种架构.
所以,当设置platform比较低时候,出现交叉编译工具链构建失败不是问题.

====> 坑:如何使用交叉编译工具链编译期望的so文件?
以前接触过一点点交叉编译的知识.就直接列出交叉编译的文件目录了.以crossadd工程为例,写一个mycrossadd()函数.
crossadd------
        |--c/
        |  |--CmakeLists.txt
        |  |--crossadd.c
        |  |--crossadd.h
        |--lib/
        |--build.sh
请注意,重点是build.sh和CmakeLists.txt.
下述为build.sh的内容
#!/bin/bash


cd ./build/


export CC=/home/mechmqx/mechmqx/arm-linux-androideabi-gcc/arm-android-16/bin/arm-linux-androideabi-gcc
export CXX=/home/mechmqx/mechmqx/arm-linux-androideabi-gcc/arm-android-16/bin/arm-linux-androideabi-g++


# cmake command
cmake ../c/


#do make
make


#clear the build file, all are generated in the process of compile; optional
rm -rf ./*


#external command. to copy the head file and lib file to the project; user command
sudo cp ../lib/* /home/mechmqx/AndroidStudioProjects/testlib/distribution/crossadd/lib/armeabi-v7a
sudo cp ../c/crossadd.h /home/mechmqx/AndroidStudioProjects/testlib/distribution/crossadd/include
CmakeList.txt内容如下:
project(crossadd)


cmake_minimum_required(VERSION 2.4.1)


set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99")


#Android 5.0 以上需要在此设置PIE
#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIE")
#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fPIE -pie")


#配置使用NDK Standalone Toolchain 编译
set(NDK_STANDALONE_TOOLCHAIN /home/mechmqx/mechmqx/arm-linux-androideabi-gcc/arm-android-16)
set(CMAKE_SYSTEM_NAME Android)
set(CMAKE_SYSTEM_VERSION 16)
#set(CMAKE_C_COMPILER ${NDK_STANDALONE_TOOLCHAIN}/bin/arm-linux-androideabi-clang)  #it seems no work if set compiler here
#set(CMAKE_CXX_COMPILER ${NDK_STANDALONE_TOOLCHAIN}/bin/arm-linux-androideabi-clang++)
#set(CMAKE_C_COMPILER ${NDK_STANDALONE_TOOLCHAIN}/bin/arm-linux-androideabi-gcc)
#set(CMAKE_CXX_COMPILER ${NDK_STANDALONE_TOOLCHAIN}/bin/arm-linux-androideabi-g++) 
set(CMAKE_FIND_ROOT_PATH ${NDK_STANDALONE_TOOLCHAIN})


#使用NDK提供的头文件
add_definitions("--sysroot=${NDK_STANDALONE_TOOLCHAIN}/sysroot")


add_library(crossadd SHARED crossadd.c)


set_target_properties(crossadd 
                      PROPERTIES 
                      LIBRARY_OUTPUT_DIRECTORY 
                      ${CMAKE_CURRENT_SOURCE_DIR}/../lib
                     )
请注意,实际应用中,在CMakeLists.txt中设置编译器(set(CMAKE_C_COMPILER xxx))语句貌似无用,编译时候仍然是默认的编译器(GNU)
编译log如下:
mechmqx@mechmqx-R467-R464-P467:~/mechmqx/testgcc/crosscompile$ ./build_for_test_crossadd.sh 
-- The C compiler identification is GNU 4.8.4
-- The CXX compiler identification is GNU 4.8.4
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/mechmqx/mechmqx/testgcc/crosscompile/build
Scanning dependencies of target crossadd
[100%] Building C object CMakeFiles/crossadd.dir/crossadd.o
Linking C shared library /home/mechmqx/mechmqx/testgcc/crosscompile/lib/libcrossadd.so
[100%] Built target crossadd
后来修改:注释设置编译器的语句,改用在shell文件中export编译器,后续有提到.(使用错误的编译器编译出的libxxx.so不能够被正常编译,incompatible target)

请注意:前面使用make_toolchains.sh得到的交叉编译工具链有多个.这里在编译so时候,请注意工具链的选取,可以在CMakeLists.txt中设置,设置时候请注意需要的平台的架构,目标机的Android版本.

=====> 坑: incompatible target

Error:error: ../../../../../distribution/crossadd/lib/ armeabi-v7a/libcrossadd.so: incompatible target


在编译时候配置正确的编译器:
#下述语句添加在自己构建的build.sh文件中
export CC=/home/mechmqx/mechmqx/arm-linux-androideabi-gcc/arm-android-16/bin/arm-linux-androideabi-gcc
export CXX=/home/mechmqx/mechmqx/arm-linux-androideabi-gcc/arm-android-16/bin/arm-linux-androideabi-g++
正确配置编译器后的编译log:
mechmqx@mechmqx-R467-R464-P467:~/mechmqx/testgcc/crosscompile$ ./build_for_test_crossadd.sh 
-- The C compiler identification is GNU 4.9.0
-- The CXX compiler identification is GNU 4.9.0
-- Check for working C compiler: /home/mechmqx/mechmqx/arm-linux-androideabi-gcc/arm-android-16/bin/arm-linux-androideabi-gcc
-- Check for working C compiler: /home/mechmqx/mechmqx/arm-linux-androideabi-gcc/arm-android-16/bin/arm-linux-androideabi-gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /home/mechmqx/mechmqx/arm-linux-androideabi-gcc/arm-android-16/bin/arm-linux-androideabi-g++
-- Check for working CXX compiler: /home/mechmqx/mechmqx/arm-linux-androideabi-gcc/arm-android-16/bin/arm-linux-androideabi-g++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/mechmqx/mechmqx/testgcc/crosscompile/build
Scanning dependencies of target crossadd
[100%] Building C object CMakeFiles/crossadd.dir/crossadd.o
Linking C shared library /home/mechmqx/mechmqx/testgcc/crosscompile/lib/libcrossadd.so
[100%] Built target crossadd

=====> 继续坑:(两个坑长得很像,报错均为 incompatible target,仔细看其路径不一样,所以很具迷惑性,很容易在得到前一个正确解决方案的时候没有意识到,同样也难以发觉新问题)
Error:error: ../../../../../distribution/crossadd/lib/ x86/libcrossadd.so: incompatible target
Error:error: ../../../../../distribution/crossadd/lib/ x86_64/libcrossadd.so: incompatible target

在没有配置abi Filter时候,Android Studio默认会编译各种架构的so(本例是将第三方so文件给native-lib库使用,添加abi filter之前会编译各个架构的native-lib),此时会逐个引用各个架构目录下的第三方库,若某个so不满足架构需求(即so可能不是按照特定架构的编译器编译得到),即会继续报incompatible target错误.
修改方案:
1.为各种架构编译对应的so
2.现有so架构种类有限,可使用abiFilter语句加以限制.(注意,这样得到的APK只能在有限的平台下运行)
在app:build.gradle中添加: 
//下述语句应该包含在android {defaultConfig{ ...} }中
        ndk{
            abiFilters "armeabi-v7a","x86"  //filter the abi to be complied
        }

=====> 坑:运行时link error

解决java.lang.UnsatisfiedLinkError问题:
 Caused by: java.lang. UnsatisfiedLinkError: Cannot load library: link_image[1892]:  1910 could not load needed library 'libcrossadd.so' for 'libnative-lib.so' (load_library[1094]: Library 'libcrossadd.so' not found)
解决方法:该错误属于运行时so链接失败,修改如下,
在系统加载so库时候,事先加载被依赖so库,如下备注.请注意一定要将被依赖so库放在调用so库前面.
    static {
        System.loadLibrary("crossadd");      //add this line to solve link error
        System.loadLibrary("native-lib");
    }
注:在使用Android-19真机调试时候,android-ndk-master实例(Git上可以下载)中的hello-libs可以直接运行,但是在Android-16的真机上调试就出现上述linkError.目前不清楚原因,如有大神,还请帮忙解释.(不要问我为什么换成Android-16的,酷派大神ANdroid-19的已经被我玩成砖了...)

=======>至此为止,交叉编译的第三方so库,已经能够被正常的APP使用并打包进APK中.


Android不止,坑无止境,唯心不畏,勇往直前.
--------------------------------------------------------
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值