Android NDK开发环境搭建与软件优化方法记录

本文详细介绍了一个Java层与JNI层参数与数据交互的应用程序开发过程,包括构建基本Android应用程序、配置引导界面、XML文件及运行应用程序等内容。同时,还介绍了构建Java与C/C++交互的JNI层接口的方法。

前言


  本文记录一个Java层与JNI层参数与数据交互的应用程序开发过程,为实现一个功能完整的带Java与JNI的应用程序打下基础。本文假设读者已搭建好Android的Eclipse与NDK开发环境,包括通过ADB连接手机的配置。


1. 构建基本的Android应用程序

 

1.1 引导界面配置


  打开Eclipse,"File"->"New"->"Android Application Project",在弹出界面,配置如下(红色框表示不是默认,是作者修改过的地方,下同):



  之后,一路点击"Next >",直到"Finish"。点击"Finish"之后,自动回打开如下界面:


  

1.2 XML文件配置


  我们可以看到一个虚拟的应用界面,其中有一行文字"Hello world!"。点击上图右下角的activity_main.xml,其中有如下定义:

<TextView  
    android:layout_width="wrap_content"  
    android:layout_height="wrap_content"  
    android:text="@string/hello_world" />  
其中的字符串就定义在res文件夹下values文件夹中的strings.xml中。我们对hello_world的值做修改,如下:

<?xml version="1.0" encoding="utf-8"?>  
<resources>  
  
    <string name="app_name">app</string>  
    <string name="action_settings">Settings</string>  
    <string name="hello_world">Napolean: Hello Android Application!</string>  
  
</resources>  

根据需要,可以将上图中模拟应用界面的左边栏中的控件拖入模拟应用界面上放置,相应地,XML文件中就会有该控件的布局描述。

1.3 运行应用程序

  可以再src目录下有个MainActivity.java,这是上面引导界面配置完成后自动生成的代码。
package com.napolean.app;  
  
import android.os.Bundle;  
import android.app.Activity;  
import android.view.Menu;  
  
public class MainActivity extends Activity {  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
    }  
  
    @Override  
    public boolean onCreateOptionsMenu(Menu menu) {  
        // Inflate the menu; this adds items to the action bar if it is present.  
        getMenuInflater().inflate(R.menu.main, menu);  
        return true;  
    }  
  
}  
插入手机连接上ADB,或者配置一个Android模拟器,点击运行程序,即可看到应用程序运行并显示我们所修改的字符串。关于手机如何通过ADB连接以及Android模拟器的配置,在此不作展开。


2. 构建Java与C/C++交互的JNI层接口


  Java与C/C++交互分为两边:一边是Java层:在Java层的类中声明本地接口函数,并在公共接口函数调用,使得需要访问JNI层函数的类调用这些公共函数即可;另一边是JNI层:JNI层有对应于Java层中本地函数的对应函数,也有其它普通函数,在Java层本地函数被调用时,实际上最终会执行对应的C/C++层函数。

2.1 Java层包含JNI本地函数的类

  
  右键点击src目录下的com.napolean.app,选择"New"->"Class",在弹出的界面中输入类名"NativeInterface","Finish"即可完成NativeInterface类的创建。在生成的NativeInterface.java中的空类NativeInterface中添加Native函数及其封装的公共接口,具体代码如下:
package com.napolean.app;  
  
public class NativeInterface {  
      
    private native void native_init();  
    private native void native_exit();  
      
    private native byte[] native_process(byte[] in_buffer, int width, int height);  
      
    public void NativeInit() {  
        native_init();  
    }  
      
    public void NativeExit() {  
        native_exit();  
    }  
      
    public void NativeProcess(byte[] in_buffer, int width, int height)   
    {  
        native_process(in_buffer, width, height);  
    }  
      
    static {  
            System.loadLibrary("NativeInterface");  
        }  
      
}  
所创建的Native函数有初始化函数、退出函数和处理函数。其中处理函数包含输入字节流、Buffer宽度、Buffer高度,返回处理后的字节流。接下来三个函数分别是三个Native函数的公共接口。

  最后在static中调用System.loadLibrary(),其中引号内为后面JNI层动态库库名。实际生成的动态库名会在Android.mk中指定的库名前添加lib之后添加.so,即声明为xxx的库,最终生成libxxx.so。值得注意的是:在上述加载函数中必须使用xxx而不能使用全名,并且在Android中指定的库名不能带lib前缀,否则无法加载。

2.2 本地函数在JNI层的实现


  首先创建jni文件夹:右键点击工程名,"New"->"Folder",输入"jni",创建jni文件夹。



  接着,在终端输入javah命令,创建根据NativeInterface中声明native函数对应的JNI层函数接口,在工程根目录下具体命令为
javah -classpath bin/classes -d jni com.napolean.app.NativeInterface 
 运行命令后,在jni文件夹下就会生成一个名为com_napolean_app_NativeInterface.h的头文件,里面包含有的接口函数如下:
/* DO NOT EDIT THIS FILE - it is machine generated */  
#include <jni.h>  
/* Header for class com_napolean_app_NativeInterface */  
  
#ifndef _Included_com_napolean_app_NativeInterface  
#define _Included_com_napolean_app_NativeInterface  
#ifdef __cplusplus  
extern "C" {  
#endif  
/* 
 * Class:     com_napolean_app_NativeInterface 
 * Method:    native_init 
 * Signature: ()V 
 */  
JNIEXPORT void JNICALL Java_com_napolean_app_NativeInterface_native_1init  
  (JNIEnv *, jobject);  
  
/* 
 * Class:     com_napolean_app_NativeInterface 
 * Method:    native_exit 
 * Signature: ()V 
 */  
JNIEXPORT void JNICALL Java_com_napolean_app_NativeInterface_native_1exit  
  (JNIEnv *, jobject);  
  
/* 
 * Class:     com_napolean_app_NativeInterface 
 * Method:    native_process 
 * Signature: ([BII)[B 
 */  
JNIEXPORT jbyteArray JNICALL Java_com_napolean_app_NativeInterface_native_1process  
  (JNIEnv *, jobject, jbyteArray, jint, jint);  
  
#ifdef __cplusplus  
}  
#endif  
#endif  
生成这个头文件的目的是免去手动编写JNI层的接口函数,并无其它用途,使用后可删除。在jni文件夹下创建NativeInterface.cpp,将上述头文件中所有内容拷入,经过改造,如下:

  1. #include <jni.h>  
  2. #include <android/log.h>  
  3.   
  4. #define LOG_TAG "APP"  
  5. #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)  
  6. #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)  
  7.   
  8. #ifndef _Included_com_napolean_app_NativeInterface  
  9. #define _Included_com_napolean_app_NativeInterface  
  10.   
  11. #ifdef __cplusplus  
  12. extern "C" {  
  13. #endif  
  14.   
  15. /* 
  16.  * Class:     com_napolean_app_NativeInterface 
  17.  * Method:    native_init 
  18.  * Signature: ()V 
  19.  */  
  20. JNIEXPORT void JNICALL Java_com_napolean_app_NativeInterface_native_1init  
  21.   (JNIEnv *, jobject)  
  22. {  
  23.     LOGI("APP native init.");  
  24. }  
  25.   
  26. /* 
  27.  * Class:     com_napolean_app_NativeInterface 
  28.  * Method:    native_exit 
  29.  * Signature: ()V 
  30.  */  
  31. JNIEXPORT void JNICALL Java_com_napolean_app_NativeInterface_native_1exit  
  32.   (JNIEnv *, jobject)  
  33. {  
  34.     LOGI("APP native exit.");  
  35. }  
  36.   
  37. /* 
  38.  * Class:     com_napolean_app_NativeInterface 
  39.  * Method:    native_process 
  40.  * Signature: ([BII)[B 
  41.  */  
  42. JNIEXPORT jbyteArray JNICALL Java_com_napolean_app_NativeInterface_native_1process  
  43.   (JNIEnv *, jobject, jbyteArray, jint, jint)  
  44. {  
  45.     LOGI("APP native process.");  
  46.   
  47.   
  48. }  
  49.   
  50. #ifdef __cplusplus  
  51. }  
  52. #endif  
  53. #endif  
上述改造的代码主要是将函数声明改为函数定义,并且添加了打印信息的支持。

2.3 JNI层动态库编译配置


  如果你亲手创建工程,就会发现头文件和函数定义的字符下面有黄色下划线,这是因为Eclipse找不到jni.h和android/log.h的头文件。所以需要在Eclipse配置JNI层特有的头文件路径。

  第一步,添加C/C++特性。右键点击jni文件夹,"New"->"Other",在弹出界面如下选择:



在下一个界面中的指定工程类型中选择"Shared Library"(这一步并不重要,因为后面需要修改编译工具),点击"Finish",Eclipse就开始转换工作。

  第二步,修改编译工具。右键点击工程名,"Properties",弹出如下界面,其中左边栏中的"C/C++ Build"与"C/C++ General"是上一步转换工作完成后新的。选择"C/C++ Build",在"Builder Settings"标签中,去掉"Use default build command"前的勾,输入"ndk-build",点击"Apply"应用,如下所示:



  第三步,配置头文件路径。在"C/C++ General"->"Paths and Symbols"->"Includes"->"GNU C++"中添加头文件路径。点击右边的"Add...",添加四个路径,然后点击"Apply"应用。



其中,四个头文件路径为:
/home/work/android/android-ndk-r9/platforms/android-18/arch-arm/usr/include/  
/home/work/android/android-ndk-r9/sources/cxx-stl/gnu-libstdc++/4.6/include/  
/home/work/android/android-ndk-r9/sources/cxx-stl/gnu-libstdc++/4.6/libs/armeabi-v7a/include/  
/home/work/android/android-ndk-r9/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86/lib/gcc/arm-linux-androideabi/4.6/include/ 
 第四步,创建Android.mk。右键点击jni文件夹,"New"->"File",输入Android.mk。添加如下代码:

LOCAL_PATH := $(call my-dir)  
  
include $(CLEAR_VARS)  
  
LOCAL_MODULE    := NativeInterface  
LOCAL_SRC_FILES := NativeInterface.cpp  
  
LOCAL_LDLIBS := -llog  
  
include $(BUILD_SHARED_LIBRARY)  

第五步,编译生成动态库。点击Eclipse菜单栏中的"Project"->"Build Project",即可完成编译。在libs/armeabi/文件夹下生成了"libNativeInterface.so"动态库。

2.4 Java层中调用本地函数


  Java层调用JNI层本地函数的方法,如下所示。
package com.napolean.app;  
  
import android.os.Bundle;  
import android.app.Activity;  
import android.view.Menu;  
  
public class MainActivity extends Activity {  
  
    private NativeInterface myJNI;  
      
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
          
        byte[] in_buffer = new byte[1920*1080*3/2];  
        int width = 1920;  
        int height = 1080;  
          
        myJNI = new NativeInterface();  
          
        myJNI.NativeInit();  
        myJNI.NativeProcess(in_buffer, width, height);  
    }  
  
    @Override  
    public boolean onCreateOptionsMenu(Menu menu) {  
        // Inflate the menu; this adds items to the action bar if it is present.  
        getMenuInflater().inflate(R.menu.main, menu);  
        return true;  
    }  
      
    protected void onDestroy() {  
        myJNI.NativeExit();  
        super.onDestroy();  
    }  
  
}  

转自:http://blog.youkuaiyun.com/cloud_desktop/article/details/30304273

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值