Android开发手记一

---- NDK编程实例

Android上,应用程序的开发,大部分基于Java语言来实现。要使用c或是c++的程序或库,就需要使用NDK来实现。NDKNative Development Kit的简称。它是一个工具集,集成了Android的交叉编译环境,并提供了一套比较方便的Makefile,可以帮助开发者快速开发C或是C++的动态库,并自动的将sojava程序打包成apk,在Android上运行。

好,闲话少说,我们以一个简单的实例,来讲解NDK的应用。

开发环境的搭建

这一步虽然没什么技术含量,但是对于初学者,有一个很好的入门指导,还是很有帮助的。

1.1Android SDK的搭建

首先,要进行Android程序的开发,AndroidSDK是必须要安装的。当然,Java环境也必不可少。我们先要安装JDKEclipse,这个可以选比较新的版本,因为Android新的SDK已经不支持旧版本了。

1.1.1 JDK可以用V5V6版本,下载地址http://java.sun.com/javase/downloads/index.jsp

1.1.2 Eclipse可以用版本version 3.4 or 3.5,下载地址http://www.eclipse.org/downloads/.当然,若你需要其他的Java开发环境,可以不用Eclipse,不过这样也就用不了ADT(Android Development Tools)插件了。推荐还是用Eclipse来进行开发比较好,毕竟比较权威和方便么。

1.1.3安装SDK

Android SDK下载地址为http://androidappdocs.appspot.com/sdk/tools-notes.html

1.1.4Eclips安装插件ADT。在Eclipse中,填加更新站点https://dl-ssl.google.com/android/eclipse/然后选择安装ADT.

1.1.5接下来,我们选择Android平台和组件。若是在window系统下,运行SDK Setup.exe;若是在Linux系统下,运行tools目录下的android程序,就可以选择需要的Android Platform和组件。

完成以上工作后,就可以进行Android应用程序的开发了。可以用Eclipse创建一个Android工程,比较简单的Hello Android,然后在模拟器下运行。具体的操作可以参看Android开发网站的说明,上面有详细的步骤。

1.2 Android NDK的搭建

上面我们搭建好了SDK的环境,可以开发Java应用程序了。要开发C的程序,还得搭建NDK环境。

NDK给我们提供了以下内容:

libc (C library) headers

libm (math library) headers

JNI interface headers

bz (Zlib compression) headers

blog (Android logging) header

A Minimal set of headers for C++ support

1.2.1 NDK的安装

下载NDK安装包,下载地址http://androidappdocs.appspot.com/sdk/ndk/index.html,下载后解压即可使用。

1.2.2若在Linux开发环境下那么,这样就可以使用了。若是在window环境下,还需要安装cygwincygwin下载地址:http://www.cygwin.com/

这样,NDK的环境也搭建好了。下面我们来进行实战演习。

NDK开发实例

关于NDK的使用,首先需要了解一个概念:JNI。什么是JNI

2.1 Hello-jni

这个是NDK自带的例子程序,安装官方网站的说明,一步步来,应该没有什么问题,这里就不细说了。

2.2 My God I did it

学习的第一步,就是模仿。我们依照上面Hello-jni的例子,在创建自己的NDK程序。在此过程中,对相关的内容和概念进行分析和说明。

首先,创建自己的NDK工程。我们在ndksample目录下创建自己的工程myjni,然后在这个文件夹子下,创建两个目录jnisrcjni用来放我们的c文件,src是调用的cjava接口文件。创建好目录,接着创建文件jni/myjni.c,该文件比较简单,就是输出一个字符串,内容如下

#include <string.h>

#include <stdio.h>

#include <jni.h>

#include <android/log.h>

#define LOG_TAG "MYJNI"

#define LOGI(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)

static char s_string[] = "My god, I did it!";

jstring

Java_com_jpf_myjni_MyJNI_stringFromJNI( JNIEnv* env,

jobject thiz )

{

LOGI("MyJNI is called!");

return (*env)->NewStringUTF(env, s_string);

}

这个程序,唯一和hello-jni不同的就是引用了<android/log.h>这个头文件。在该头文件中,声明了函数__android_log_print(),可以根据不同的log级别,输出log,方便代码的调试。在NDK中,printf()没法输出,所以我们需要借助log库来将我们c代码库中需要输出的内容,通过java控制台输出。调用函数__android_log_print(),就可以在Eclipse中,查看LogCat来查看相关的输出信息了。

注意:

c文件中,函数名这样定义:Java_com_jpf_myjni_MyJNI_stringFromJNI,有什么讲究么?这个是JNI的标准,定义需要按照如下格式:

Java_packagename_classname_methodname,

例如:Java_com_jpf_myjni_MyJNI_stringFromJNI

接着创建文件jni/Android.mk.这个文件是我们本地c代码的Makefile。文件内容如下:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := myjni

LOCAL_SRC_FILES := myjni.c

LOCAL_LDLIBS += -llog

include $(BUILD_SHARED_LIBRARY)

分别对上述Makefile的语句进行说明。

LOCAL_PATH := $(call my-dir)这句用来指定编译的路径。通过调用宏my-dir,获取到当前工作的路径。

include $(CLEAR_VARS) CLEAR_VARS这个变量是编译系统提供的,用来指明一个GNU makefile文件,添加这句,主要的目的是清理所有的LOCAL_XXX.,比如LOCAL_MODULELOCAL_LDLIBS。在每个新模块的开始处,需要添加这句。

LOCAL_MODULE := myjni这句定义了模块名称,将来编译的库就以此命名。若果编译的是动态库,那么库名就是libmyjni.so.需要注意的是,如果你定义modulelibmyjni,那么系统在生成动态库的时候,就不要再为你添加lib的前缀了,生成德动态库名字还是libmyjni.so.

LOCAL_LDLIBS += -llog这句指定了需要另外链接的库。我们在代码中,用到了log库,所以这里加上这句。

include $(BUILD_SHARED_LIBRARY)这句说明将来生产的库是共享库,及动态链接库。若需要生产静态库,可以这样写:include $(BUILD_STATIC_LIBRARY)

写完了c文件和Makefile文件,是否可以编译了呢?我们试一下。在cygwin中,进入工程目录,运行ndk-build,得到下面的结果:

Administrator@lenovo-0e47e162 /android/android-ndk-r4/samples/myndk

$ ndk-build

Android NDK: Could not find application's manifest from current directory.

Android NDK: Please ensure that you are inside the project's directory !

/android/android-ndk-r4/build/core/build-local.mk:74: *** Android NDK: Aborting

. Stop.

看到这个错误的意思是,缺少manifest文件。老版本的NDk,工程中有一个apps,里面包含了应用的程序文件和Application.mk。现在的版本,不需要我们自己编写Application.mk,,不过仍需要工程相关的配置信息。那么如何做到呢?需要手工去写manifest文件么?不需要。我们只需要在Eclipse中,创建工程就可以了,这些配置文件会自动生成。

前面讲过,在工程的src夹子下用来放置Java文件。我们打开Eclipse,然后新建一个Android工程,工程名就叫MyJNI,工程路径选择我们创建的NDK的路径。这里需要注意的是,工程名,包名等,需要和上面的c文件中的保持一致。

(Java_com_jpf_myjni_MyJNI_stringFromJNI)



工程建立好后,编辑src/com/jpf/myjni/MyJNI.java文件,内容如下:

packagecom.jpf.myjni;

importandroid.app.Activity;

importandroid.widget.TextView;

importandroid.os.Bundle;

publicclassMyJNIextendsActivity {

/** Called when the activity is first created. */

@Override

publicvoidonCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

TextView tv =newTextView(this);

tv.setText( stringFromJNI() );

System.out.println("Here we go ...");

setContentView(tv);

System.out.println("Done!");

}

publicnativeString stringFromJNI();

static{

System.loadLibrary("myjni");

}

}

需要说明的几点:

public native String stringFromJNI();这句申明,带有native关键字,说明该方法是本地方法。

System.loadLibrary("myjni");这句就是用来加载我们的c动态库的。上面声明的方法,具体实现,就在我们加载的库中。

建立好工程,再次编译,在cygwin中运行ndk-build,结果OK

Administrator@lenovo-0e47e162 /android/android-ndk-r4/samples/myndk

$ ndk-build

Compile thumb : myjni <= /android/android-ndk-r4/samples/myndk/jni/myjni.c

SharedLibrary : libmyjni.so

Install : libmyjni.so => /android/android-ndk-r4/samples/myndk/libs/armea

bi

我们看到,需要的共享库已经生成,并且安装好了。下面就可以生成apk了。

Cygwin中进行工程的build,编译后,在工程的bin目录下,会看到我们的apk包。



好,我们试试看,能否正常运行。在Eclipse选择执行方式为Android Application,点击run,以下console的输出:

[2010-07-07 14:26:18 - MyJNI] ------------------------------

[2010-07-07 14:26:18 - MyJNI] Android Launch!

[2010-07-07 14:26:18 - MyJNI] adb is running normally.

[2010-07-07 14:26:18 - MyJNI] Performing com.jpf.myjni.MyJNI activity launch

[2010-07-07 14:26:18 - MyJNI] Automatic Target Mode: using existing emulator 'emulator-5554' running compatible AVD 'android21'

[2010-07-07 14:26:18 - MyJNI] WARNING: Application does not specify an API level requirement!

[2010-07-07 14:26:18 - MyJNI] Device API version is 7 (Android 2.1-update1)

[2010-07-07 14:26:18 - MyJNI] Uploading MyJNI.apk onto device 'emulator-5554'

[2010-07-07 14:26:18 - MyJNI] Installing MyJNI.apk...

[2010-07-07 14:26:24 - MyJNI] Success!

[2010-07-07 14:26:25 - MyJNI] Starting activity com.jpf.myjni.MyJNI on device

[2010-07-07 14:26:29 - MyJNI] ActivityManager: Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.jpf.myjni/.MyJNI }

上面的warning,是我们没有指定API的版本号。如下指定一下就没有这个warning了。


下图为执行的效果:

下图是我们查看LogCat的输出:

可以看到我们的输出MYJNIMyJNI is called

2.3 Study Hard

有了上面的基础,我们就可以用NDK来进行项目开发了。

我们经常会遇到这样的问题,就是将一些现有的,成熟的C库移植到Android平台上。通过上面我们的介绍,我们已经知道,我们需要用JNI来对现有的C库包装一下,然后提供Java接口,供上层调用。

首先的问题,就是C库的编译和测试。其实Android底层用的是Linux的内核,所以,和其他Linux程序开发一样,无法使进行交叉编译。不过,Android有些特殊的地方,我们需要注意。下面就以一个很简单的例子,讲讲如何应用NDK,做一个C的应用终端测试程序。

首先,创建study-hadr/study-hard.c文件,程序非常简单,就是Hello Worldc程序。

#include <string.h>

#include <stdio.h>

static char s_string[] = "Study hard!";

int main()

{

printf("%s/n", s_string);

return 0;

}

别看程序很简单,不过这个程序的编译可不简单。

若是在Linux下,只需要执行:

gcc –o study-hard study-hard.c就可以生成应用程序study-hard了。

Android下就不是这么简单了。在Window环境开发环境下,用到的交叉工具链,目录是/android-ndk-r4/build/prebuilt/windows/arm-eabi-4.4.0在这个目录的bin路径下,你会看到arm-eabi为前缀的诸多工具,这些就是Android用的编译工具。那么c库和c头文件又在哪里呢?对于Android,不同的Platform,有不同的库和头文件,需要我们自己选择。比如,现在我们要用Platform5,那么

C头文件的路径为:

/android-ndk-r4/build/platforms/android-5/arch-arm/usr/include

C库的路径为:

/android-ndk-r4/build/platforms/android-5/arch-arm/usr/lib

好了,我们知道了C的编译工具链,知道了C库路径和C头文件路径,应该可以编译了。写个简单的Makefile,试一下,结果出错了。crt0.o没有找到。


这个错误很糟糕,指出在链接的时候,找不到crt0.o。我们在Makefile中添加如下几句:

LDFLAGS += -nostdlib

-nostdlib表示不连接系统标准启动文件和标准库文件.只把指定的文件传递给连接器。

此时编译,结果为:


错误指出,在链接的时候,找不到puts,这个函数是c库中的,我们添加如下语句再次尝试:

LDFLAGS += -lc

我们修改链接选项,增加对dl库的链接,再次尝试:

LDFLAGS += -lc –ldl

这次生成了可执行文件,不过还是有warning,在生成的可执行文件中,没有找到入口_start。这个问题也比较奇怪。我们查看下生成的可执行文件:

readelf –a study-hard

发现生成的可执行文件,真的没有入口函数。这是为什么呢?

Linux下,用-v选项跟踪下gcc编译hello world程序的过程。会发现,在链接的过程中,除了hello.o,还会链接crt1.o, crtn.o等文件,正是这些文件,在生成可执行程序的过程中,组成了elf文件中程序入口和程序退出等相关的处理部分。

查看我们指定的C库:

会发现,C库下有crt打头的三个.o文件。我们修改Makefile,链接crtbegincrtend文件:

EXTRA_OBJS := $(PATH_PREFIX)/lib/crtbegin_dynamic.o $(PATH_PREFIX)/lib/crtend_android.o

… …

$(CC) $(CFLAGS) -o $(TARGET) $(OBJS)$(EXTRA_OBJS)$(LDFLAGS)

再次编译,结果如下,此次终于编译成功了。

我们将编译好的程序放到Android上运行下看看效果。

显示程序没有找到。怎么回事呢?继续研究下AndroidNDK相关文档。我们还需要修改Makefile的一个地方:

LDFALGS += -Bdynamic -Wl,-dynamic-linker,/system/bin/linker

指定链接动态库,动态连接器为/system/bin/linker

编译后,再次运行,终于看到了Study hard

更多相关文章

  1. Android NDK工程的编译和链接调试
  2. 用git下载Android的某个应用程序
  3. Android应用程序启动过程源代码分析
  4. Android 4.1源码编译找不到资源文件解决办法
  5. Android应用程序与SurfaceFlinger服务之间的共享UI元数据(SharedC
  6. Android 根文件系统分析
  7. Android应用程序与SurfaceFlinger服务的连接过程分析

随机推荐

  1. vlc for android demo
  2. 三星S3C6410(ARM1176)平台Android资源获
  3. firefox 对WebRTC支持
  4. Android中解析XML的方法
  5. Android之FileOutputStream与openFileOut
  6. 2.1 五种数据存储与共享方式的比较、
  7. Android7.0 禁止锁屏状态的下拉状态栏
  8. 73、ImageView 让图片填充满
  9. 学习Android studio 日常总结
  10. Android的屏幕切换动画—左右滑动切换