ORB_SLAM2在Android上的移植过程

作者:Frank

转载请注明出处

一直没时间写博客,最近抽时间写了些关于在ORB_SLAM2在Android上的移植过程,也算是点经验吧。

写完后一个手贱点了个链接,瞬间1/3工作量没了,深夜弄完也是醉了。。。

正文开始



这篇博客讲述如何在Android平台上移植ORB_SLAM2,讲述过程包括基本的Android环境的搭建和NDK环境的配置,Android下移植的基本概念,ORB的具体移植步骤等。

Android平台搭建和NDK环境配置



系统:windows7 32bit

IDE:Eclipse Luna

环境工具: ADT24.0.2、Android SDK、NDK r10b

PS:不推荐使用集成了ADT环境的Eclipse版本,因为在NDK编译的时候可能会报各种莫名其妙的错。同时默认JDK环境已经装好。

组件下载地址:百度网盘(包括ADT、NDK。SDK太大,请自行下载)

步骤:

  1. 安装Eclipse,注意区分Eclipse是32位还是64位的,下载对应版本安装即可。下载地址在前面给出。
  2. 下载ADT,不需要解压。打开安装好的Eclipse,在菜单栏点击Help–>Install new software,弹出如下界面:
  3. 点击右上角Add,出现:

    第一栏随便填个名字,例如ADT,第二栏点击Archive,选择你下载的ADT压缩包,确定。在出现的列表中,全部选中,并取消勾选Contact all update sites….。点击next,则开始了ADT的安装,大概需要10分钟。可能还需要accept 协议什么的,这里略过不表。安装完成后提示重启Eclipse即安装成功。

  4. 下载Android SDK,并解压(路径不要有中文)。在Eclipse中选择Window–>preferences。在左边栏选中Android,在SDK Location中填入你解压文件夹的根目录,点击apply和OK,则Android环境配置基本完成。这是你的菜单栏应该有了这两项

  5. 更新SDK。点击Window–>Android SDK manager,弹出如下界面:

    可能由于墙的问题会弹出列表无法加载,需要用代理或镜像,当然如果你有梯子也可以。这里介绍些镜像吧:

    g.cn:80,在SDK Manager 中点击tools–>options:

    按照上图设置即可。其他的镜像源还有北京化工大学镜像站,请自行谷狗。设置好后点击Packages–>reload即可得到对应的镜像。有默认选中的一些选项,一般选中一个sdk tools,一份sdk build-platform-tools,一个sdk build tools,然后一个Android版本即可(一般4.0以上)。选中后点击Install就会从镜像中下载对应的SDK,这个过程有长有短,视网络而定。

    当上述步骤都完成后,Android开发环境就配置好了。

  6. NDK环境配置:从上面给的百度网盘中下载ndk,解压(规则同sdk),打开Eclipse,点击Window–>Preferences,选择Android–>NDK,在右边界面中填入NDK Location即可。

Android移植基础



NDK是集成的Android中调用C++代码的工具包,核心是JNI(Java Native Interface)技术,具体这里略过不表。只说说NDK开发的基本步骤:

1. 编写Java代码:在Java中定义一个类,比如说叫NDKHelper吧,里面定义几个java的方法,只需要声明,不需要实现,如下所示:

public class NDKHelper {
    //NDK示例方法1
    public static native void ndkOne(int a,long b);

    //NDK示例方法2
    public static native int ndkTwo(String a,String b);
}

native标识符表示该函数将会利用C++代码完成实现。

接下来在工程上右键,Android Tools–>Add native support,出现如下界面:

名字就是最后我们要生成的库的名字,随便填,可修改。点击确定就会给你的工程添加C++编译支持,菜单栏会多了个小锤子:

这个是用来编译C++的快捷键。在你的工程目录下会新建jni目录和obj目录,其中jni目录用来存放和C++代码有关的东西,obj则存放C++进行编译时产生的中间件,最后生成的library会写入到libs文件夹下。

在jni文件夹中生成了如下文件,一个.cpp,一个Android.mk,其中.cpp是自动生成的,是用来编写C++部分的,而Android.mk类似C++里面的CMakeList,用来指定需要编译的文件和编译生成的模块名,一个最简单的Android.mk文件如下所示:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE    := NDKTest
LOCAL_SRC_FILES := NDKTest.cpp
include $(BUILD_SHARED_LIBRARY)

LOCAL_PATH :=$(call my-dir)表示包含当前目录。

include $(CLEAR_VARS)表示清除全部非系统变量和部分系统变量;

LOCAL_MODULE := NDKTest 表示当前生成的模块名,最终会生成libNDKTest.so文件

LOCAL_SRC_FILES := NDKTest.cpp 表示当前需要编译的cpp文件;

include $(BUILD_SHARED_LIBRARY) 表示生成共享库,需要生成静态库请修改成BUILD_STATIC_LIBRARY。

其他基础命令:

LOCAL_C_INCLUDES:= 表示添加头文件进入编译环境

LOCAL_LDLIBS:= 表示添加系统静态库

LOCAL_SHARED_LIBRARIES:= 表示添加共享库

其他命令请自行查看API文档。

这里指定了进行编译时的各项条件,如果需要指定编译器版本和编译目标平台等信息,则需要在jni目录下新建Application.mk文件,基本语句如下:

APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
NDK_TOOLCHAIN_VERSION := 4.8
APP_ABI :=armeabi-v7a

APP_STL :=表示使用stl库,APP_CPPFLAGS表示一些CPP编译参数,NDK_TOOLCHAIN_VERSION 表示NDK使用的编译器版本,APP_ABI表示编译的目标平台,可以指定多个平台,平台之间用空格隔开,或者指定all则为全平台编译(armeabi,armeabi-v7a,mips,x86)。其他命令请自行查看API。

接下来编写对应的C++文件。

打开eclipse,点击Project–>build Project(若build automatically已勾选则会自动编译)打开命令行,cd到你的工程文件夹下的bin–>classes文件夹下,输入如下命令:

javah com.example.ndktest.NDKHelper

回车,则在你的classes文件夹下会生成对应的头文件。这里com.example.ndktest是你的package名字,NDKHelper是你的NDK函数的类名。

生成的头文件如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_ndktest_NDKHelper */

#ifndef _Included_com_example_ndktest_NDKHelper
#define _Included_com_example_ndktest_NDKHelper
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_ndktest_NDKHelper
 * Method:    ndkOne
 * Signature: (IJ)V
 */
JNIEXPORT void JNICALL Java_com_example_ndktest_NDKHelper_ndkOne
  (JNIEnv *, jclass, jint, jlong);

/*
 * Class:     com_example_ndktest_NDKHelper
 * Method:    ndkTwo
 * Signature: (Ljava/lang/String;Ljava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_com_example_ndktest_NDKHelper_ndkTwo
  (JNIEnv *, jclass, jstring, jstring);

#ifdef __cplusplus
}
#endif
#endif

其他不用管,我们关注中间的两个函数声明:

JNIEXPORT void JNICALL Java_com_example_ndktest_NDKHelper_ndkOne(JNIEnv *, jclass, jint, jlong);

这个函数就是NDKHelper类中ndkOne函数对应的C++版本,其中JNIEXPORT和JNICALL是固定字段,void是函数返回值,函数名由Java字段+包名+类名+函数名组成,参数则多了几个JNI的系统参数JNIEnv 和jclass,其他的就是NDKHelper类中的对应参数,ndk会对该函数进行解析和链接,实现java和C++的对接。

将生成的.h头文件复制到jni目录下,新建对应的cpp文件,将该头文件include进来并对对应函数进行实现,实现过程就视函数功能而定。

这些工作完成后需要修改你的Android.mk文件,将刚刚新建的cpp和h文件包括进来。

然后点击开始那个小锤子或者直接项目右键RunAs–>Android Application,则C++部分会开始编译,编译具体过程可以在Eclipse下方Console窗口看到(如果没有Console窗口则点击Window–>Show Views,选择Console确定即可)。

编译完成后会生成对应的库存放在libs目录下,则你可以开始在Java里面调用刚才定义的ndkOne和ndkTwo函数实现具体的功能。

NDK基础到此为止,更深入的学习可以下载Android官方给的ndk samples.

ORB_SLAM2的移植



不想知道移植过程的童鞋可以直接下载我的Github源码:https://github.com/FangGet/ORB_SLAM2_Android 直接按照步骤进行即可。

移植过程

先看目录:

分为ORB和ThirdParty,其中ThirdParty包括boost clapack DBow2 g2o eigen3。

clapack和eigen来自于一个github的开源库:https://github.com/simonlynen/android_libs 这里集成了一些经典的C++库的ndk版本,下载即可使用。g2o和DBoW2则来自于ORB_SLAM2原作者的github地址,Boost是自己编译的lib,这里只介绍clapack和opencv的库配置。

clapack配置

从前述的开源库中将clapack目录拷贝到Thirdparty的对应目录下,clapack中已经包含了对当前目录极其子目录的编译过程,我们在jni目录下的Android.mk文件中加入如下内容:

include $(CLEAR_VARS)
MAINDIR:= $(LOCAL_PATH)
include $(MAINDIR)/Thirdparty/clapack/Android.mk
LOCAL_PATH := $(MAINDIR)

include $(CLEAR_VARS)
MAINDIR:= $(LOCAL_PATH)
LOCAL_MODULE:= lapack
LOCAL_SHORT_COMMANDS := true
LOCAL_STATIC_LIBRARIES := tmglib clapack blas f2c

LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
LOCAL_PATH := $(MAINDIR)
include $(BUILD_SHARED_LIBRARY)

这里的基本命令之前都已经讲过了,只补充如下几点内容:

  • LOCAL_SHORT_COMMANDS是为了防止Windows对G++编译命令长度的限制而设置的参数,该参数会拖慢整个编译过程,因此请谨慎使用;
  • LOCAL_EXPORT_C_INCLUDES表示将当前库的头文件EXPORT给系统,让程序代码中能实现<>的调用过程,若不设置这一参数则在cpp文件中可能无法引用该库;
  • LOCAL_STATIC_LIBRARIES := tmglib clapack blas f2c是引用lapack子目录中编译好的一些依赖模块

    这里会编译出一个名为lapack的库工程,该工程就可以作为依赖项被ORB所引用。

    OpenCV的编译

    opencv4Android是opencv官网为了对Android的支持而推出的一个工具集,可以在opencv官网进行下载。其目录结构如下:

    其中sdk为核心部分,opencv4Android包含两个版本,一个是opencv为java做的本地化sdk,另一个是opencv利用ndk编译C++版本得到的库工程。我们将opencv4android解压后放置到ORB_SLAM2项目的同级目录下,如下所示:

    之后在jni目录下的Android.mk中需要引用到OpenCV的地方加入如下代码:

OPENCV_LIB_TYPE:=STATIC
ifeq ("$(wildcard $(OPENCV_MK_PATH))","")
#try to load OpenCV.mk from default install location
include E:/ORB_SLAM2/OpenCV-2.4.9-android-sdk/sdk/native/jni/OpenCV.mk
else
include $(OPENCV_MK_PATH)
endif 

这里opencv.mk我给的是绝对地址,其实相对地址也是可以的。上面这段引用会将opencv进行编译并引入到当前的工作模块上来,这里就完成了opencv库的基本调用。如果为了方便还可以将opencv自身单独编译成一个库工程并开放给其他模块引用。

其他libraries的编译过程和上述工程大同小异,其主要步骤可以概括如下:

  1. 将当前库复制到jni的特定目录下;
  2. 在Android.mk中新建一个模块并对模块进行命名;
  3. LOCAL_C_INCLUDE引入库的头文件,LOCAL_SRC_FILES引入库的cpp文件;
  4. LOCAL_LDLIBS/LOCAL_SHARED_LIBRARIES/LOCAL_STATIC_LIBRARIES引入依赖库;
  5. LOCAL_C_FLAGS设置编译参数;

ORB_SLAM2的编译

这里我们将ORB_SLAM2的源文件也编译为一个library以供调用,其编译过程和上面雷同,需要注意的是,由于pangolin编译有问题,我拆了源文件的pangolin部分并注释了对应的部分代码,同时引入了opengl es 来进行map和pose的绘制。同时,为了完成特征检测图像的回调,我改变了System.cc中TrackMonocular的返回值,将其返回值改成了Mat。

当上述过程完成后,我们的C++编译工作就基本完成了,最后也是最重要的一步是为Java中定义的native方法做C++的实现,在JAVA中,我定义了如下native函数:

    /**
     * jni中初始化SLAM系统
     * @param VOCPath
     * @param calibrationPath
     */
    public static native void initSystemWithParameters(String VOCPath,String calibrationPath);

    /**
     * Dataset模式中ORB系统的start函数
     * @param curTimeStamp
     * @param data
     * @param w
     * @param h
     * @return
     */
    public static native int[] startCurrentORB(double curTimeStamp,int[] data,int w,int h);
    /**
     * Camera模式中ORB系统的start
     * @param curTimeStamp
     * @param addr
     * @param w
     * @param h
     * @return
     */
    public native static int[] startCurrentORBForCamera(double curTimeStamp,long addr,int w,int h);
    /**
     * Opengl es 的初始化
     */
    public native static void glesInit();
    /**
     * opengl es绘制更新
     */
    public native static void glesRender();
    /**
     * 防止opengl es窗口resize带来的影响
     * @param width
     * @param height
     */
    public native static void glesResize(int width, int height);

其对应的C++代码为:

/*
 * Class:     orb_slam2_android_nativefunc_OrbNdkHelper
 * Method:    initSystemWithParameters
 * Signature: (Ljava/lang/String;Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_orb_slam2_android_nativefunc_OrbNdkHelper_initSystemWithParameters
(JNIEnv * env, jclass cls, jstring VOCPath, jstring calibrationPath) {
    const char *calChar = env->GetStringUTFChars(calibrationPath, JNI_FALSE);
    const char *vocChar = env->GetStringUTFChars(VOCPath, JNI_FALSE);
    // use your string
    std::string voc_string(vocChar);
    std::string cal_string(calChar);
    env->GetJavaVM(&jvm);
    jvm->AttachCurrentThread(&env, NULL);
    s=new ORB_SLAM2::System(voc_string,cal_string,ORB_SLAM2::System::MONOCULAR,true);
    env->ReleaseStringUTFChars(calibrationPath, calChar);
    env->ReleaseStringUTFChars(VOCPath, vocChar);
    init_end=true;
}

/*
 * Class:     orb_slam2_android_nativefunc_OrbNdkHelper
 * Method:    startCurrentORB
 * Signature: (DDD[I)[I
 */
JNIEXPORT jintArray JNICALL Java_orb_slam2_android_nativefunc_OrbNdkHelper_startCurrentORB(
        JNIEnv * env, jclass cls, jdouble curTimeStamp, jintArray buf, jint w,
        jint h) {
    jint *cbuf;
    cbuf = env->GetIntArrayElements(buf, false);
    if (cbuf == NULL) {
        return 0;
    }
    int size = w * h;
    cv::Mat myimg(h, w, CV_8UC4, (unsigned char*) cbuf);
    cv::Mat ima = s->TrackMonocular(myimg, curTimeStamp);
    jintArray resultArray = env->NewIntArray(ima.rows * ima.cols);
    jint *resultPtr;
    resultPtr = env->GetIntArrayElements(resultArray, false);
    for (int i = 0; i < ima.rows; i++)
        for (int j = 0; j < ima.cols; j++) {
            int R = ima.at < Vec3b > (i, j)[0];
            int G = ima.at < Vec3b > (i, j)[1];
            int B = ima.at < Vec3b > (i, j)[2];
            resultPtr[i * ima.cols + j] = 0xff000000 + (R << 16) + (G << 8) + B;
        }
    env->ReleaseIntArrayElements(resultArray, resultPtr, 0);
    env->ReleaseIntArrayElements(buf, cbuf, 0);
    return resultArray;
}
/*
 * Class:     orb_slam2_android_nativefunc_OrbNdkHelper
 * Method:    glesInit
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_orb_slam2_android_nativefunc_OrbNdkHelper_glesInit
(JNIEnv *env, jclass cls) {
    // 启用阴影平滑
    glShadeModel(GL_SMOOTH);
    // 黑色背景
    glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
    // 设置深度缓存
    glClearDepthf(1.0f);
    // 启用深度测试
    glEnable(GL_DEPTH_TEST);
    // 所作深度测试的类型
    glDepthFunc(GL_LEQUAL);
    // 告诉系统对透视进行修正
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
}

/*
 * Class:     orb_slam2_android_nativefunc_OrbNdkHelper
 * Method:    glesRender
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_orb_slam2_android_nativefunc_OrbNdkHelper_glesRender
(JNIEnv * env, jclass cls) {
    glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glMatrixMode (GL_MODELVIEW);
    glLoadIdentity ();
    if(init_end)
    s->drawGL();
}

/*
 * Class:     orb_slam2_android_nativefunc_OrbNdkHelper
 * Method:    glesResize
 * Signature: (II)V
 */
JNIEXPORT void JNICALL Java_orb_slam2_android_nativefunc_OrbNdkHelper_glesResize
(JNIEnv *env, jclass cls, jint width, jint height) {
    //图形最终显示到屏幕的区域的位置、长和宽
    glViewport (0,0,width,height);
    //指定矩阵
    glMatrixMode (GL_PROJECTION);
    //将当前的矩阵设置为glMatrixMode指定的矩阵
    glLoadIdentity ();
    glOrthof(-2, 2, -2, 2, -2, 2);
}

/*
 * Class:     orb_slam2_android_nativefunc_OrbNdkHelper
 * Method:    readShaderFile
 * Signature: (Landroid/content/res/AssetManager;)V
 */
JNIEXPORT jintArray JNICALL Java_orb_slam2_android_nativefunc_OrbNdkHelper_startCurrentORBForCamera
(JNIEnv *env, jclass cls,jdouble timestamp, jlong addr,jint w,jint h) {
    const cv::Mat *im = (cv::Mat *) addr;
    cv::Mat ima = s->TrackMonocular(*im, timestamp);
    jintArray resultArray = env->NewIntArray(ima.rows * ima.cols);
    jint *resultPtr;
    resultPtr = env->GetIntArrayElements(resultArray, false);
    for (int i = 0; i < ima.rows; i++)
    for (int j = 0; j < ima.cols; j++) {
        int R = ima.at < Vec3b > (i, j)[0];
        int G = ima.at < Vec3b > (i, j)[1];
        int B = ima.at < Vec3b > (i, j)[2];
        resultPtr[i * ima.cols + j] = 0xff000000 + (R << 16) + (G << 8) + B;
    }
    env->ReleaseIntArrayElements(resultArray, resultPtr, 0);
    return resultArray;
}

这里解释下Dataset和Camera模式下start方法的区别。其实就是图像参数传递的方式不一样。在DataSet模式中,我们是用ImageView显示图片,用Bitmap读取文件中的图片,而非基本类型的数据都是不能被jni接口所接受的因此我们需要利用Bitmap的getPixels方法将其转换成intp[型数据进行传递,在jni中int[]对应的数据类型为jintArray,我们可以在获取到数据后将jintArray转换成Mat进行后续处理;而在Camera模式中我们是利用opencv android sdk中的cvCameraView 来直接进行摄像头的调用和图像的显示。其onCameraFrame(CvCameraViewFrame inputFrame)中的inputfram可以通过rgba()方法转换成Mat类型数据,而Mat类型同样不被jni识别,因此需要利用Mat的getNativeObjAddr方法获取Mat数据的long型指针传递到jni中进行处理。

结尾



当上述步骤都完成后,我们会得到最终生成的sdk。Android部分的布局文件和对应activity文件在这里也略过不表。当得到最终生成的apk后,我们如果要测试Camera模式,需要先将opencv4Android中apk文件夹中对应类型的opencv manager安装到手机中并预先打开才能使用,否则会提示找不到opencv的支持库;若只需测试Dataset模式则无需上述步骤。

水平有限,如有错误请不吝指正,谢谢。

时间: 2024-10-19 15:30:43

ORB_SLAM2在Android上的移植过程的相关文章

拇指接龙游戏从WIN32向Android移植过程问题记录(2)

本文中,在前文(1)基础上,将尽可能详细记录拇指接龙游戏从WIN32向Android移植过程后期--UI运行级调试出现的问题及可能的解决办法. 问题1 正未运行,问题就来了.忽然发现,工程左上角挂着一个大大的感叹号!请看截图: 打开工程属性对话框,观察到如下现象: 我把鼠标停留在右边的横线处,后面出现一个关键单词missing.也就是说,我们的游戏工程依赖的cocos2d-x库包jar文件丢失了!? 再打开另一处观察,发现如下: 其实,有一个重要细节在上一篇中我没有交待.此前,我使用中家的版本在

ucos-ii在ti dsp 28377芯片上的运行过程和移植过程

1.移植过程 在将ucos移植到28377d平台上时主要遇见了下面几个问题, 1) 文件怎么组织,是通过修改micrim上提供的28335一直代码修改而成的,下载地址为:https://www.micrium.com/. 2)移植完成后发现创建任务完成后,任务无法跳转,移植在主函数中来回循环 3)当使用ostimedly()函数对任务延时,当延时时间已经完成,系统无法跳出空任务循环,移植在IdleTask中运行 4)任务切换过程中总是跳入到异常中断中. 移植思路: 开始移植过程时,下载了micr

在android上移植ios

Android与IOS是最受人们欢迎的两大移动平台,Android与IOS之争是一个永久的话题.接下来笔者就结合自己的知识和网上的一些文章,谈谈这两个系统的不同以及关于移植的话题,希望也是大家比较关心的话题. 一.关于Android与IOS的不同 应该会有很多机友都有类似疑问,感觉大多数安卓手机在流畅度上不如iPhone手机.先不说一些配置低的安卓机,即便是最新版安卓系统的旗舰手机,有时会感觉达不到iPhone手机的那种流畅度,因此有人以此判断安卓手机不好.其实,这都是二者系统机制不同导致的差异

拇指接龙游戏从WIN32向Android移植过程问题记录

本文将较细致地记录下最近开发课程中的示例游戏-拇指接龙游戏在从WIN32向Android移植过程中遇到的若干问题及相应解决办法. 目前极不完整,待进一步整理. 问题1 连接真机测试运行时,在SplashScreen运行时便出现如下错误提示(log.txt): 09-16 13:28:16.525: I/GLThread(25784): sending render notification tid=1009-16 13:28:18.530: D/cocos2d-x debug info(2578

Android 上实现非root的 Traceroute -- 非Root权限下移植可执行二进制文件 脚本文件

作者 : 万境绝尘 转载请著名出处 : http://blog.csdn.net/shulianghan/article/details/36438365 示例代码下载 : -- CSDN : http://download.csdn.net/detail/han1202012/7639253; -- GitHub : https://github.com/han1202012/TracerouteAndBusybox ; 1. 原理思路 文件权限修改无法实现 : 如果 没有 root 权限,

android bluetooth蓝牙移植

http://blog.csdn.net/zhengmeifu/article/details/7705172 前段时间移植神念系统需要使用到bluetooth功能,现将移植过程中碰到的问题简要列一下,备忘而已. 1.内核增加配置make menuconfig -> Networking support -> Bluetooth subsystem support -->进行选择BT选项,至少选择如下几项:CONFIG_BT=yCONFIG_BT_RFCOMM=y## Bluetooth

系列篇|编译可在Android上运行的依赖库(一):glib库

前言 这是系列文章,它们由<编译可在Android上运行的glib库>及其他4篇文章组成,这4篇文章在“编译依赖库”一节中列出.由于glib库依赖于其他第三方库,所以需要先将依赖的第三方库交叉编译到Android平台上才能成功的编译glib库,系列文章中除<编译可在Android上运行的glib库>外的其他交叉编译文章均是介绍如何对glib依赖库进行交叉编译.以上,所以叫系列文章,因为这些文章完整的介绍了如何编译可在Android上运行的glib库. 文章价值 这些文章的核心价值是

Android 上千实例源码分析以及开源分析

Android 上千实例源码分析以及开源分析(百度云分享) 要下载的直接翻到最后吧,项目实例有点多. 首先 介绍几本书籍(下载包中)吧. 01_Android系统概述 02_Android系统的开发综述 03_Android的Linux内核与驱动程序 04_Android的底层库和程序 05_Android的JAVA虚拟机和JAVA环境 06_Android的GUI系统 07_Android的Audio系统 08_Android的Video 输入输出系统 09_Android的多媒体系统 10_

Android上方便地开发的C程序

如果你基于没有一个专门的开发板练手,那你的Android手机也可以开发大多数C应用程序,安装好后编译C的编译器.本文只写一个Hello World的运行过程.优点是:不需要eclipse,不需要Android源码,不需要Android.mk,不需要NDK.一个C程序员就可以很好的利用Android了,需要一个编译器和一个adb要把程序放到Android系统中. 1.安装adb sudo apt-get install android-tools-adb 2.安装交叉工具链 sudo apt-ge