IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    [原]Android实战技巧之四十五:复用原有C代码的方案

    lincyang发表于 2015-11-03 16:57:23
    love 0

    任务描述

    有一套C写的代号为“Shooter”的核心算法库可以解决我们面临的一些问题,只是这个库一直用在其他平台。
    我们现在的任务是将其复用到Android平台上。

    复用方案描述

    示意图如下
    这里写图片描述

    编译Android下的Shooter.so

    共享库是不能跨平台的,我们首要的工作就是用交叉编译器将原Shooter库编译成可在Android平台下使用的so。Android下常用cpu架构是ARM,现在X86也多了起来,为了兼容多平台,我们要应对几款不同的cpu架构。下面就先来演示ARM下的交叉编译。
    1、安装交叉编译工具

    $ /opt/ndk/android-ndk-r10b/build/tools/make-standalone-toolchain.sh 
    Auto-config: --toolchain=arm-linux-androideabi-4.6
    Copying prebuilt binaries...
    Copying sysroot headers and libraries...
    Copying c++ runtime headers and libraries...
    Creating package file: /tmp/ndk-linc/arm-linux-androideabi-4.6.tar.bz2
    Cleaning up...
    Done.
    

    将其解压到你喜欢的位置(我放到了~/bin/build-tools/arm-linux-androideabi-4.6/),这样就可以工作啦。

    2、Shooter源码重编译
    请参考《做一个动态链接库》
    gcc的编译指令。
    Shooter库在Android下的调式请参考《Android实战技巧之四十四:Hello,Native!》

    下面将shooter源码编译:

    $ ~/bin/build-tools/arm-linux-androideabi-4.6/bin/arm-linux-androideabi-gcc -c -fPIC -o ./arm/shooter.o shooter.c
    $ cd arm/
    $ ~/bin/build-tools/arm-linux-androideabi-4.6/bin/arm-linux-androideabi-gcc -shared -o libshooter.so shooter.o 
    $ ll
    total 20
    drwxrwxr-x 2 linc linc 4096 11月  1 13:49 ./
    drwx------ 3 linc linc 4096 11月  1 13:47 ../
    -rwxrwxr-x 1 linc linc 5932 11月  1 13:49 libshooter.so*
    -rw-rw-r-- 1 linc linc 1764 11月  1 13:48 shooter.o
    

    tips:
    将此工具路径加到环境变量中吧。

    shooterProxy

    JNI技术是联系Java和Native世界的桥梁,它由Java端和Native端构成。

    1、ShooterProxy.java

    package com.linc.lib;
    
    public class ShooterProxy {
    /*
        static {
            System.loadLibrary(ShooterProxy);
        }   
    */
        public static native int A(int a);
    
    }

    编译后再用javah工具生成c头文件,参考:
    第一个NDK程序
    http://blog.csdn.net/lincyang/article/details/6705143
    2、ShooterProxy.c
    根据com_linc_lib_ShooterProxy.h,编写c源文件。

    #include "com_linc_lib_ShooterProxy.h"
    //#include <stdio.h>
    #include "shooter.h"
    #include <android/log.h>
    
    #define  LOG_TAG    "ShooterProxy:"
    //#define  LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
    #define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
    
    
    JNIEXPORT jint JNICALL Java_com_linc_lib_ShooterProxy_A
      (JNIEnv *env, jclass obj, jint i) {
        //call shooter lib
        LOGD("method A() called! i: %d",i);
        if(i>0) 
            return 54321;
        else {
            int result = A(0);
            LOGD("call shooter A method.result:%d",result);
            return result;
        }
    }

    注意Android.mk的写法:

    LOCAL_PATH := $(call my-dir)
    
    include $(CLEAR_VARS)
    
    LOCAL_MODULE    := ShooterProxy
    LOCAL_SRC_FILES := ShooterProxy.c
    LOCAL_LDLIBS := ./armeabi-v7a/libshooter.so
    #LOCAL_LDLIBS    := -llog -landroid -lEGL -lGLESv1_CM
    #LOCAL_STATIC_LIBRARIES := android_native_app_glue
    LOCAL_LDLIBS     += -llog -ldl
    
    include $(BUILD_SHARED_LIBRARY)

    编译:

    $ ndk-build
    Android NDK: WARNING:/home/linc/workspace/csdn_code/reusec/reuse-C/jni/Android.mk:ShooterProxy: non-system libraries in linker flags: ./armeabi-v7a/libshooter.so    
    Android NDK:     This is likely to result in incorrect builds. Try using LOCAL_STATIC_LIBRARIES    
    Android NDK:     or LOCAL_SHARED_LIBRARIES instead to list the library dependencies of the    
    Android NDK:     current module    
    [armeabi-v7a] Compile thumb  : ShooterProxy <= ShooterProxy.c
    [armeabi-v7a] SharedLibrary  : libShooterProxy.so
    [armeabi-v7a] Install        : libShooterProxy.so => libs/armeabi-v7a/libShooterProxy.so
    

    此时我们有了两个so文件,一个是libshooter.so,一个是libShooterProxy.so。接下来我们在App中动态加载这两个动态库。

    应用中调用ShooterProxy

    就用一个最最简单的例子来做此事好了,一个TextView在Activity启动时调用A方法。

    11-03 16:05:29.240 11203-11203/com.linc.shooterdemo D/ShooterProxy:: method A() called! i: 0
    11-03 16:05:29.240 11203-11203/com.linc.shooterdemo D/ShooterProxy:: call shooter A method.result:4

    比较遗憾的是,我的设备虽然root,但是stdout重定向后也没能在logcat中看到输出。重定向方法如下:

    $ adb root
    adbd is already running as root
    $ adb shell stop
    $ adb shell setprop log.redirect-stdio true
    $ adb shell start

    尾声

    这篇文章早在今年6月份就开始起草,但事情一多就给耽搁了。
    后来的经历也让我意识到,使用第三方so并没有那么简单,我们需要在ShooterProxy这一层做很多事情。
    这里面要遇到很多Java与C的数据类型转换,C中要使用Java中的类等等事情,扎实的JNI功底才是本项目成功的关键。
    所以,Java与C/C++复合型人才很适合这个领域。

    源码在这里!

    参考:
    http://blog.csdn.net/jiangxuchen/article/details/22883575
    http://blog.csdn.net/lincyang/article/details/6705143
    http://blog.csdn.net/lincyang/article/details/44725529



沪ICP备19023445号-2号
友情链接