总的来说ANDROID的NDK远不及其应用开发的SDK完善(虽然经过一番不算复杂的折腾发现NDK用起来很方便),而且它本身也不推荐使用这种做法,至少目前也不将此作为重点。但是某些中间层面系统测试(主要如多媒体和OpenGL ES的测试和演示等)必须通过本地代码实现,因此NDK应当是必由之路。

最近尝试了一下,目前将JNI部分基本理顺(而后续则需要链接相关的ANDROID本地库,如OpenCore系统)。
网上这方面相关介绍也有不少,但是多不太完整,此处权作工作记录。

1 NDK使用

1.1 配置NDK


本处讨论在Windows下使用cygwin处理NDK的Windows版本。Linux下的使用方法基本一致。根据unix系系统的规范,所有讨论中涉及名称的字符串均大小写敏感。这里仅NDK的配置和C代码编译须用cygwin,此后的ANDROID调试等均可使用普通的命令行操作。
参考链接:http://developer.android.com/sdk/ndk/1.5_r1/index.html
收到NDK后首先在NDK的主目录(其中包含apps, build, docs, …文件夹)下,输入命令:
build/host-setup.sh
用来配置NDK工具(例如编译器的使用,目标平台等),最终生成out/host/config-host。由于out必须在主目录中,因此上述命令须在主目录中输入。

1.2 编译本地源码

本地源码(主要如C文件)均放在sources下。NDK提供了两个示例,放在sources/sample目录下。
编译只需要在主目录中输入命令:
make APP=<名称>
对于上述示例,分别为hello-jni和two-libs。

1. sources文件夹配置
由于NDK已将MAKE生成系统建立妥善,所以只需要在sources中建立包含源文件的文件夹。
由于NDK的配置是以sources目录作为源文件工程的根节点,因此如果要将源文件工程放在更深的目录,例如sources/package1/proj1,那么就需要在中间的目录中加入一个Android.mk文件,用以转到更深的目录其内容示例可见sources/samples/Android.mk。
上述文件夹proj1名称建议以源文件模块的名称命名。
在源文件工程文件夹中需要有至少一个Android.mk文件用以定义源文件编译信息。可以参照sources/samples中的两个工程中的示例。其中LOCAL_MODULE变量必须定义成指定源文件工程(模块)的名称。

2. C源程序JNI入口
C源程序的入口遵照JNI规范:
Java_<PackageName>_<JAVA类名>_<FuncName>
其中包名称和Android的JAVA类所属包需要保持一致,只是“.”用“_”替换;JAVA类即是包含这个(实例)方法的类;FuncName则是呈现在JAVA中使用的方法名称。

3. apps文件夹配置
在apps文件夹中创建一个ADNROID工程文件夹,名称为APP工程(JAVA)名称,在其中新建一个Application.mk的配置文件,参照两个示例工程设置。主要设置两个变量:
APP_PROJECT_PATH,这个是ANDROID工程路径和相应指定库生成目录(复制而来,名称为“lib源文件模块名”),一般设置成$(call
my-dir)/project,即当前目录下project中,而库生成目录就是project/libs。
APP_MODULES则是这个ANDROID将包含的上述源文件工程,填入涉及的一个或多个源文件工程名称。
最后在主目录中用make APP=<APP工程名称>

1.3 创建工程
NDK两个例程已经含有完整的ANDROID例程,可以在Eclipse中直接导入打开。
如果新建一个工程,只需要仿照ANDROID工程的一般过程开始,由于本地库so处于工程目录下,Eclipse会自动将其包含在工程中,并最终一并链入apk。

1.4 关于JAVA本地(Native)接口JNI
一些参考文档:
1. http://java.sun.com/docs/books/jni/
2. http://java.sun.com/j2se/1.5.0/docs/guide/jni/spec/functions.html
3. http://journals.ecs.soton.ac.uk/java/tutorial/native1.1/implementing/method.html
有几个注意点:
1. 不能在本地代码中跨线程使用JNI量,而目前又暂没找到联入正确JNI环境量和对象或者JVM的方法,因此只能在调用线程中使用回调,意味着设计需要让回调发生在JAVA调用者线程中。
2. CallXXXMethodX(env, obj, methodid, va_arg)中,va_arg必须输入指针(对象的指针如jstring *,原子的指针如int *)。
3. 对于跨线程的UI操作侧需要用runOnUiThread。

2 ANDROID工具使用

2.1 虚拟设备创建(AVD)


使用android命令
创建:
android create avd –n <虚拟设备名称> -t <目标ID>
在通常情况下接受默认选项(不建立hardware profile)
删除:
android delete avd –n <虚拟设备名称>
列印:
android list

2.2 (在命令行)运行虚拟机

命令:
emulator –avd <虚拟设备名称>

2.3 ADB常用命令

参考:http://oxen.javaeye.com/blog/142373
安装程序:
adb install <待安装APK文件本地位置>
运行命令SHELL:
adb shell ,进入SHELL,可以操作访问设备文件系统
adb shell ,直接执行命令(SHELL中可以执行logcat)
复制文件:
adb push <本地源> <设备文件系统绝对地址> ,复制入文件
adb pull <设备文件系统绝对地址> <本地位置> ,复制出文件
adb devices ,查看运行的模拟器/设备状态


【示例程序】

一个简单的在屏幕上间歇打印的程序。

本地C代码
(仅用于示例,不保证正确性和安全性)

#include <jni.h>#include <stdio.h>#include <string.h>#include <malloc.h>#include <pthread.h>#include <unistd.h>typedef struct { JNIEnv* env; jobject thiz; jclass cls;} UpdateTextContext;static int gRunnerRunning = 0;static int update_text(UpdateTextContext *context, char *buf) { JNIEnv* env = context->env; jobject thiz = context->thiz; jclass cls = context->cls; jmethodID mid = (*env)->GetMethodID(env, cls, "appendText", "(Ljava/lang/String;)V"); if (mid == NULL) return -1; jstring s = (*env)->NewStringUTF(env, buf); (*env)->CallVoidMethodV(env, thiz, mid, &s); return 0;}void Java_com_eden_sample_Sample_initTextGenerator(JNIEnv* env, jobject thiz) { gRunnerRunning = 0;}void Java_com_eden_sample_Sample_runTextGenerator(JNIEnv* env, jobject thiz) { char buf[64]; int counter = 0; UpdateTextContext context; context.env = env; context.thiz = thiz; context.cls = (*env)->GetObjectClass(env, thiz); gRunnerRunning = 1; while (gRunnerRunning) { sprintf(buf, "sample counting: %d/n", counter); int r = update_text(&context, buf); if (r != 0) break; counter++; sleep(1); /* sleep for one second */ } gRunnerRunning = 0;}void Java_com_eden_sample_Sample_stopTextGenerator(JNIEnv* env, jobject thiz) { gRunnerRunning = 0;}void Java_com_eden_sample_Sample_stopTextGenerator(JNIEnv* env, jobject thiz) { gRunnerRunning = 0;}


JAVA程序
(JAVA本不十分熟悉,权当c#写了)

package com.vendor.sample;  /* package that must keep in accordance with the native code */import android.app.Activity;import android.os.Bundle;import android.widget.TextView;import java.util.*;public class Sample extends Activity {        enum UpdateType {                Modify,                Append        }            private class UpdateTextRunner implements Runnable {                public UpdateTextRunner(String s, UpdateType type) {                        mS = s;                        mType = type;                }                        public void run() {                        if (mType ==  UpdateType.Modify) {                                mLines.clear();                                mLines.add(mS);                                mTV.setText(mS);                        } else {                                mLines.add(mS);                                /* intended to display no more than `mMaxLineCount'                                  * lines and scroll, however this is not                                  * always the case, consider if mS is broken                                  * into several lines                                  */                                 while (mLines.size() > mMaxLineCount)                                         mLines.remove(0);                                                  StringBuilder sb = new StringBuilder();                                 for (int i = 0; i < mLines.size(); i++)                                        sb.append(mLines.get(i));                                                      mTV.setText(sb.toString());                        }                }                        private String mS;                private UpdateType mType;        }    /** Called when the activity is first created. */        @Override        public void onCreate(Bundle savedInstanceState) {                super.onCreate(savedInstanceState);                mTV = new TextView(this);                mTV.setText( "initial text" );                setContentView(mTV);                initTextGenerator();                        /* The following thread object simply contains                  * an overriden run method which invokes                   * runTextGenerator on this Sample object                  */                mThread = new TextUpdatorThread(this);                mThread.start();        }            @Override       public void onDestroy() {                stopTextGenerator();                try {                        mThread.join();                } catch (InterruptedException e) {                        e.printStackTrace();                }                super.onDestroy();        }            public void modifyText(String s) {                this.runOnUiThread(new UpdateTextRunner(s, UpdateType.Modify));        }            public void appendText(String s) {                this.runOnUiThread(new UpdateTextRunner(s, UpdateType.Append));        }            public native void initTextGenerator();        public native void stopTextGenerator();        public native void runTextGenerator();            private TextView mTV;        private ArrayList mLines = new ArrayList();        private int mMaxLineCount = 20;        private TextUpdatorThread mThread;            static {                System.loadLibrary("sample");   // the corresponding C library is libsample.so        }}

更多相关文章

  1. Android Studio导入Github工程问题总结-download gradle非常慢
  2. android studio导入gbk编码的工程文件导致的乱码问题
  3. Android客户端性能优化(魅族资深工程师毫无保留奉献)
  4. 一步一步学Android ROM开发(一)——修改现有ROM资源文件
  5. 浅谈如何在Eclipse下的Android工程配置Git的.gitignore文件
  6. android工程中的R.java文件

随机推荐

  1. 探索PHP 生命周期
  2. 探索php+ajax实现带进度条的大数据排队导
  3. PHP如何自定义的 printf 函数
  4. php上传多张图片时,选择图片后即可预览的
  5. php session不过期的实现方法
  6. 详解PHP中错误与异常及其相关知识
  7. php工厂方法模式是什么
  8. 浅谈CGI、FastCGI、PHP-CGI、PHP-FPM!
  9. php如何安装zip模块?(方法介绍)
  10. 解析PHP vsprintf()函数格式化字符串操作