android在ndk r5版本中,提供了使用NDK来编写android应用。也算是C,C++的福音,其实android源码中,很多设计本质上都是c和C++实现的,然后使用jni调用来使JAVA程序员能够参与开发。但是在开发复杂应用的过程中,暴露出了大量的问题,由于java语言的局限性,使得很多问题很难从根本上解决。比如java内存管理的自动化,使得项目中经常出现空指针问题,但因为java并不提供指针的直接支持,比如浏览大量图片时,产生的内存异常,这一切官方似乎也提供不了有效的解决办法。自从有了ndk,使得大家可以不再满足于知其然而不知其之所以然。
先设置环境变量,下面是ndk,adk的设置,如果你没有装jdk,也需要安装,并像这样配置好。路径配置如下。使用vim编辑~/.profile在其中加入以下内容。
#---- NDK ----
NDK_ROOT=~/NDK/android-ndk-r8d
PATH=$PATH:$NDK_ROOT
export NDK_ROOT
#---- android-SDK ----
ANDROID_SDK_ROOT=~/SDK/adt-bundle-linux-x86_64/sdk
PATH=$PATH:$ANDROID_SDK_ROOT
export ANDROID_SDK_ROOT
#---- adb ----
ADB_PATH=~/SDK/adt-bundle-linux-x86_64/sdk/platform-tools
PATH=$PATH:$ADB_PATH
#---- tools/android ----
PATH=$PATH:~/SDK/adt-bundle-linux-x86_64/sdk/tools
exportPATH
借助anroid提供的丰富的命令行工具,在没用eclipse等开发工具的情况下,使用VIM和LINUX 的shell可以完成几乎所有的操作。
Android是一个强大的命令行工具。
1。查看帮助信息:
android-h
2。查看android的sdk
androidsdk
3。已经安装的android版本包
androidlist targets
4。查看模拟器
androidlist avd
5。运行模拟器
Gingerbread
6。创建android
工程
android create project -n TestAndroidProj -t 'android-15' -p ./android_proj -k com.magcomm.test -a MainActivity
-n
:项目名
(
TestAndroidProj);
-t
:
androidSDK
版本号
(android-15);
-p
:
Android
项目的路径
;
-k
:
Java
的包名
;
-a
:初始的
Activity
。
运行上面的命令后,一个Android项目就创建完成了。名字为TestAndroidProj
,api版本为15,路径为当前目录下android_proj
,包名为com.magcomm.test
,主activity为MainActivity
7。创建java工程
ProjectCreate./myproject -n java
8。创建模拟器
Androidcreate avd -n forwind.cn
9。
ProjectList
ProjectTreemyproject
10。编辑完成,进入jni编绎c,c++
编译so
文件
ndk-build
ndk-build NDK_LOG=1
创建build文件
11。生成用于创建apk的build.xml文件
androidupdate project --target 3 -p . -s
12。生成apk,分别对应于release和debug版本
antdebug
antclean release
13
。生成证书
keytool-genkey -v -keystore app_signing.keystore -alias release -keyalg RSA-keysize 2048 -validity 10000
JDK
的
keytool
用于创建私钥。
-keystore
:输出私钥文件的名称;
-alias
:私钥别名,可存储多个键在密钥库用来后面使用;
设置
RSA
加密算法,密钥长度
2048
位和
10000
天的有效期。
确保生成的
keystore
文件非常安全,因为这谷歌市场对你的唯一标识。
14
。签名证书
jarsigner-keystore app_signing.keystore -digestalg SHA1 -sigalg MD5withRSAbin/TestAndroidProj-release-unsigned.apk release
15
去掉签名
jarsigner -verify bin/TestAndroidProj-release-unsigned.apk
15
发布,这个未用过,需要google支持,应该不能正常使用
zipalign-v 4 bin/TestAndroidProj-release-unsigned.apk bin/TestAndroidProj.apk
16
。
antclean debug install
17.
如果是使用eclipse
创建的项目,只需要这三步,就可以编辑成apk
应用了,而且其实最后一步,eclipse
也会帮我们做
a
。编译so
文件
ndk-build
ndk-build NDK_LOG=1
b。创建build文件
androidupdate project --target 3 -p . -s
c。打包生成apk
antdebug
antrelease
d。漏了个步骤,在创建jni文件时,可以先在eclipse里引入相应的so库名和相应的导出函数,然后使用命令
javah -classpath bin -d jni com.example.hellojni.MainActivity 生成相应头文件
-classpath bin:表示类的路劲
-d jni: 表示生成的头文件存放的目录
com.example.hellojni.HelloJni 则是完整类名
这一步的成功要建立在已经在 bin/com/example/hellojni/ 目录下生成了 HelloJni.class的基础之上,而且这个bin的路径在不同版本时好像略有差异,com.example.hellojni.HelloJni
.MainActivity
要换成自己的相应包名和引用jni的类名,然后可以完成这些生成包含这些函数的类名。
18
。如果对这么多命令行不感冒,希望借助
eclipse
来完成的话,下面有网友提供的
eclipse
配置NDK的方法,不保证能运行,试试吧
一
.
新建一个
Android
工程,工程名为
jnitest
二.右击jnitest工程-->new-->other;选择C/C++-->Convertto a C/C++ Project(Adds C/C++Nature)
三.右击jnitest工程-->properties;在左边找到C/C++Build, 在右边的 BuilderSettings 将
Buildcommand: 添加ndk-build-j4
Builddirectore: ${workspace_loc:/jnitest}
四.打开C/C++Build找到Environment,在右边Add一变量
Variable:NDK
Value:/home/<user-name>/AndroidNDK
确保AndroidNDK下面有ndk-build,这个自己下载。
五然后打开左边的C/C++General-->Path and Symbols
进入右边的includes-->GNUC添加
${NDK}/platforms/android-9/arch-x86/usr/include
${NDK}/platforms/android-9/arch-arm/usr/include
GNUC++添加
${NDK}/sources/cxx-stl/stlport/stlport
${NDK}/platforms/android-9/arch-x86/usr/include
${NDK}/platforms/android-9/arch-arm/usr/include
六.建立jni文件夹,并编写.mk文件
19
。提供一个全部使用C编写的
activity
:
1。首先是
AndroidManifest.xml
文件配置:
<?xmlversion="1.0" encoding="utf-8"?>
<!--BEGIN_INCLUDE(manifest) -->
<manifestxmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.native_activity"
android:versionCode="1"
android:versionName="1.0">
<!--This is the platform API where NativeActivity was introduced. -->
<uses-sdkandroid:minSdkVersion="9" />
<!--This .apk has no Java code itself, so set hasCode to false. -->
<applicationandroid:label="@string/app_name" android:hasCode="false">
<!--Our activity is the built-in NativeActivity framework class.
Thiswill take care of integrating with our NDK code. -->
<activityandroid:name="android.app.NativeActivity"
android:label="@string/app_name"
android:configChanges="orientation|keyboardHidden">
<!--Tell NativeActivity the name of or .so -->
<meta-dataandroid:name="android.app.lib_name"
android:value="native-activity"/>
<intent-filter>
<actionandroid:name="android.intent.action.MAIN" />
<categoryandroid:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
<!--END_INCLUDE(manifest) →
这个文件和
java
版本的
activity
是差不多的,区别在于
activity
的
name
,名字必须是
android.app.NativeActivity
,并且
meta-data
也是必须的
2。
Android.mk
的内容如下:
LOCAL_PATH:= $(call my-dir)
include$(CLEAR_VARS)
LOCAL_MODULE := native-activity
LOCAL_SRC_FILES:= main.c
LOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv1_CM
LOCAL_STATIC_LIBRARIES:= android_native_app_glue
include$(BUILD_SHARED_LIBRARY)
$(callimport-module,android/native_app_glue)
需要注意的是
LOCAL_MODULE
的名字对应于
AndroidManifest.xml
中
activity
的名字,这是必须的这样命名的,一是两人处必须一样,二是这个名字是固定格式,定义的activity名字必须为NativeActivity。这个文件的很东西定义和java的mk文件不同,可以看到
LOCAL_LDLIBS
前面都带了l,如LOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv1_CM,同时和jni相比也多出了静态库
LOCAL_STATIC_LIBRARIES:= android_native_app_glue
,多出了$(call import-module,android/native_app_glue)。
普通JNI如下
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := com_example_ndk_MainActivity_jni
LOCAL_SRC_FILES := com_example_ndk_MainActivity.cpp
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)
而一个普通java项目的mk有可能是如此的:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_JAVA_LIBRARIES := bouncycastle \
framework \
mediatek-framework
LOCAL_CERTIFICATE := platform
LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-v4
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := Scr
#LOCAL_SDK_VERSION := current
include $(BUILD_PACKAGE)
# Use the following include to make our test apk.
include $(call all-makefiles-under,$(LOCAL_PATH))
3。另有一个
Application.mk
文件指明了
android
的版本
APP_PLATFORM:= android-9
4。最后是
main.c
文件的内容
.
#include<jni.h>
#include<errno.h>
#include<EGL/egl.h>
#include<GLES/gl.h>
#include<android/sensor.h>
#include<android/log.h>
#include<android_native_app_glue.h>
#defineLOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO,"native-activity", __VA_ARGS__))
#defineLOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN,"native-activity", __VA_ARGS__))
/**
*Our saved state data.
*/
structsaved_state {
floatangle;
int32_tx;
int32_ty;
};
/**
*Shared state for our app.
*/
structengine {
structandroid_app* app;
ASensorManager*sensorManager;
constASensor* accelerometerSensor;
ASensorEventQueue*sensorEventQueue;
intanimating;
EGLDisplaydisplay;
EGLSurfacesurface;
EGLContextcontext;
int32_twidth;
int32_theight;
structsaved_state state;
};
/**
*Initialize an EGL context for the current display.
*/
staticint engine_init_display(struct engine* engine) {
//initialize OpenGL ES and EGL
/*
*Here specify the attributes of the desired configuration.
*Below, we select an EGLConfig with at least 8 bits per color
*component compatible with on-screen windows
*/
constEGLint attribs[] = {
EGL_SURFACE_TYPE,EGL_WINDOW_BIT,
EGL_BLUE_SIZE,8,
EGL_GREEN_SIZE,8,
EGL_RED_SIZE,8,
EGL_NONE
};
EGLintw, h, dummy, format;
EGLintnumConfigs;
EGLConfigconfig;
EGLSurfacesurface;
EGLContextcontext;
EGLDisplaydisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(display,0, 0);
/*Here, the application chooses the configuration it desires. In this
*sample, we have a very simplified selection process, where we pick
*the first EGLConfig that matches our criteria */
eglChooseConfig(display,attribs, &config, 1, &numConfigs);
/*EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is
*guaranteed to be accepted by ANativeWindow_setBuffersGeometry().
*As soon as we picked a EGLConfig, we can safely reconfigure the
*ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID. */
eglGetConfigAttrib(display,config, EGL_NATIVE_VISUAL_ID, &format);
ANativeWindow_setBuffersGeometry(engine->app->window,0, 0, format);
surface= eglCreateWindowSurface(display, config, engine->app->window,NULL);
context= eglCreateContext(display, config, NULL, NULL);
if(eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) {
LOGW("Unableto eglMakeCurrent");
return-1;
}
eglQuerySurface(display,surface, EGL_WIDTH, &w);
eglQuerySurface(display,surface, EGL_HEIGHT, &h);
engine->display= display;
engine->context= context;
engine->surface= surface;
engine->width= w;
engine->height= h;
engine->state.angle= 0;
//Initialize GL state.
glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_FASTEST);
glEnable(GL_CULL_FACE);
glShadeModel(GL_SMOOTH);
glDisable(GL_DEPTH_TEST);
return0;
}
/**
*Just the current frame in the display.
*/
staticvoid engine_draw_frame(struct engine* engine) {
if(engine->display == NULL) {
//No display.
return;
}
//Just fill the screen with a color.
glClearColor(((float)engine->state.x)/engine->width,engine->state.angle,
((float)engine->state.y)/engine->height,1);
glClear(GL_COLOR_BUFFER_BIT);
eglSwapBuffers(engine->display,engine->surface);
}
/**
*Tear down the EGL context currently associated with the display.
*/
staticvoid engine_term_display(struct engine* engine) {
if(engine->display != EGL_NO_DISPLAY) {
eglMakeCurrent(engine->display,EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if(engine->context != EGL_NO_CONTEXT) {
eglDestroyContext(engine->display,engine->context);
}
if(engine->surface != EGL_NO_SURFACE) {
eglDestroySurface(engine->display,engine->surface);
}
eglTerminate(engine->display);
}
engine->animating= 0;
engine->display= EGL_NO_DISPLAY;
engine->context= EGL_NO_CONTEXT;
engine->surface= EGL_NO_SURFACE;
}
/**
*Process the next input event.
*/
staticint32_t engine_handle_input(struct android_app* app, AInputEvent*event) {
structengine* engine = (struct engine*)app->userData;
if(AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) {
engine->animating= 1;
engine->state.x= AMotionEvent_getX(event, 0);
engine->state.y= AMotionEvent_getY(event, 0);
return1;
}
return0;
}
/**
*Process the next main command.
*/
staticvoid engine_handle_cmd(struct android_app* app, int32_t cmd) {
structengine* engine = (struct engine*)app->userData;
switch(cmd) {
caseAPP_CMD_SAVE_STATE:
//The system has asked us to save our current state. Do so.
engine->app->savedState= malloc(sizeof(struct saved_state));
*((structsaved_state*)engine->app->savedState) = engine->state;
engine->app->savedStateSize= sizeof(struct saved_state);
break;
caseAPP_CMD_INIT_WINDOW:
//The window is being shown, get it ready.
if(engine->app->window != NULL) {
engine_init_display(engine);
engine_draw_frame(engine);
}
break;
caseAPP_CMD_TERM_WINDOW:
//The window is being hidden or closed, clean it up.
engine_term_display(engine);
break;
caseAPP_CMD_GAINED_FOCUS:
//When our app gains focus, we start monitoring the accelerometer.
if(engine->accelerometerSensor != NULL) {
ASensorEventQueue_enableSensor(engine->sensorEventQueue,
engine->accelerometerSensor);
//We'd like to get 60 events per second (in us).
ASensorEventQueue_setEventRate(engine->sensorEventQueue,
engine->accelerometerSensor,(1000L/60)*1000);
}
break;
caseAPP_CMD_LOST_FOCUS:
//When our app loses focus, we stop monitoring the accelerometer.
//This is to avoid consuming battery while not being used.
if(engine->accelerometerSensor != NULL) {
ASensorEventQueue_disableSensor(engine->sensorEventQueue,
engine->accelerometerSensor);
}
//Also stop animating.
engine->animating= 0;
engine_draw_frame(engine);
break;
}
}
/**
*This is the main entry point of a native application that is using
*android_native_app_glue. It runs in its own thread, with its own
*event loop for receiving input events and doing other things.
*/
voidandroid_main(struct android_app* state) {
structengine engine;
//Make sure glue isn't stripped.
app_dummy();
memset(&engine,0, sizeof(engine));
state->userData= &engine;
state->onAppCmd= engine_handle_cmd;
state->onInputEvent= engine_handle_input;
engine.app= state;
//Prepare to monitor accelerometer
engine.sensorManager= ASensorManager_getInstance();
engine.accelerometerSensor= ASensorManager_getDefaultSensor(engine.sensorManager,
ASENSOR_TYPE_ACCELEROMETER);
engine.sensorEventQueue= ASensorManager_createEventQueue(engine.sensorManager,
state->looper,LOOPER_ID_USER, NULL, NULL);
if(state->savedState != NULL) {
//We are starting with a previous saved state; restore from it.
engine.state= *(struct saved_state*)state->savedState;
}
//loop waiting for stuff to do.
while(1) {
//Read all pending events.
intident;
intevents;
structandroid_poll_source* source;
//If not animating, we will block forever waiting for events.
//If animating, we loop until all events are read, then continue
//to draw the next frame of animation.
while((ident=ALooper_pollAll(engine.animating ? 0 : -1, NULL, &events,
(void**)&source))>= 0) {
//Process this event.
if(source != NULL) {
source->process(state,source);
}
//If a sensor has data, process it now.
if(ident == LOOPER_ID_USER) {
if(engine.accelerometerSensor != NULL) {
ASensorEventevent;
while(ASensorEventQueue_getEvents(engine.sensorEventQueue,
&event,1) > 0) {
LOGI("accelerometer:x=%f y=%f z=%f",
event.acceleration.x,event.acceleration.y,
event.acceleration.z);
}
}
}
//Check if we are exiting.
if(state->destroyRequested != 0) {
engine_term_display(&engine);
return;
}
}
if(engine.animating) {
//Done with events; draw next animation frame.
engine.state.angle+= .01f;
if(engine.state.angle > 1) {
engine.state.angle= 0;
}
//Drawing is throttled to the screen update rate, so there
//is no need to do timing here.
engine_draw_frame(&engine);
}
}
}
这个文件
android_native_app_glue.h
是必须包含的,所有的
c
语言的
ativity
都要包含这个文件。
EGL/egl.h
和
GLES/gl.h
是窗口用到的一些库,代码中用到了
android_main
函数,这个函数是必须的,和
c
或者
c
++的
main
是一样的,整个程序的入口。在该函数中,
app_dummy();
是必须的,不能省略,告诉系统这是不能忽略或者优化掉的,android_native_app_glue.h中是有说明的。
参考:
http://blog.csdn.net/janepen/article/details/7177028