Android之NDK开发初探

北慕城南 提交于 2020-02-05 03:52:18

 总的来说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    
    }
}

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!