当前位置: 代码迷 >> Android >> android 所有使用NDK开发android应用程序
  详细解决方案

android 所有使用NDK开发android应用程序

热度:119   发布时间:2016-04-28 08:10:27.0
android 全部使用NDK开发android应用程序

androidndk r5版本中,提供了使用NDK来编写android应用。也算是C,C++的福音,其实android源码中,很多设计本质上都是c和C++实现的,然后使用jni调用来使JAVA程序员能够参与开发。但是在开发复杂应用的过程中,暴露出了大量的问题,由于java语言的局限性,使得很多问题很难从根本上解决。比如java内存管理的自动化,使得项目中经常出现空指针问题,但因为java并不提供指针的直接支持,比如浏览大量图片时,产生的内存异常,这一切官方似乎也提供不了有效的解决办法。自从有了ndk,使得大家可以不再满足于知其然而不知其之所以然。


先设置环境变量,下面是ndkadk的设置,如果你没有装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。查看androidsdk

androidsdk

3。已经安装的android版本包

androidlist targets

4。查看模拟器

androidlist avd

5。运行模拟器Gingerbread

[email protected]


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编绎cc++

编译so文件

ndk-build

ndk-build NDK_LOG=1

创建build文件



11。生成用于创建apkbuild.xml文件

androidupdate project --target 3 -p . -s

12。生成apk,分别对应于releasedebug版本

antdebug

antclean release

13。生成证书

keytool-genkey -v -keystore app_signing.keystore -alias release -keyalg RSA-keysize 2048 -validity 10000

JDKkeytool用于创建私钥。
-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是差不多的,区别在于activityname,名字必须是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.xmlactivity的名字,这是必须的这样命名的,一是两人处必须一样,二是这个名字是固定格式,定义的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.hGLES/gl.h是窗口用到的一些库,代码中用到了android_main函数,这个函数是必须的,和c或者c++的main是一样的,整个程序的入口。在该函数中,app_dummy();是必须的,不能省略,告诉系统这是不能忽略或者优化掉的android_native_app_glue.h中是有说明的。


参考:

http://blog.csdn.net/janepen/article/details/7177028

  相关解决方案