zoukankan      html  css  js  c++  java
  • andorid jni入门教程一之helloworld

    开发环境:windows2007, eclipse

    做anroid越深发现用到底层开发的时候越多,但是我以前也没有搞过,因此现在打算好好学习学习。先从最简单的做起。正所谓万事开头难啊。

    搞了近一天终于把在windows下,用eclipse开发Android JNI给倒腾通了。下面将详细讲解其操作步骤和我在其中遇到的问题

    参考:http://www.cnblogs.com/bastard/archive/2012/05/19/2508913.html

             http://blog.csdn.net/cghs123/article/details/7044826

    1.新建Android工程 JNITest

    在HelloWorld的Activity中添加加载c库的代码,系统运行后自动执行该段

        static
        {
            //加载库文件
            System.loadLibrary("HelloWorldJni");
        }

    声明c库中需要自定义的原生函数

        private native String printJNI(String inputStr);

    HelloWorld整段代码如下:

     1 package com.sirc.jni;
     2 
     3 import android.os.Bundle;
     4 import android.app.Activity;
     5 import android.util.Log;
     6 import android.view.Menu;
     7 
     8 public class HelloWorld extends Activity {
     9 
    10     @Override
    11     protected void onCreate(Bundle savedInstanceState) {
    12         super.onCreate(savedInstanceState);
    13         setContentView(R.layout.activity_main);
    14 
    15         String jniStr=printJNI("I am HelloWorld Activity");
    16         Log.v("android", jniStr);
    17         
    18     }
    19     static
    20     {
    21         //加载库文件
    22         System.loadLibrary("HelloWorldJni");
    23     }
    24     //声明原生函数 参数为String类型 返回类型为String
    25     private native String printJNI(String inputStr);
    26 
    27     
    28     @Override
    29     public boolean onCreateOptionsMenu(Menu menu) {
    30         // Inflate the menu; this adds items to the action bar if it is present.
    31         getMenuInflater().inflate(R.menu.main, menu);
    32         return true;
    33     }
    34 
    35 }

    2.生成共享库的头文件

    详细步骤见上一文章《Android JNI开发生成.h头文件问题》http://www.cnblogs.com/gisdream/p/3521090.html

    我们都知道在Eclipse中新建类后,eclipse会帮助我们在项目下自动建立去对应的class文件,位于项目目录下的inclasses中。共享库的头文件可以根据class文件通过javah编译生成。

    通过命令进入项目根目录

    再输入命令:javah -classpath binclasses -d jni com.sirc.jni.HelloWorld 

    可以得到头文件com_sirc_jni_HelloWorld.h,位于跟目录/jni/下面

    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include <jni.h>
    /* Header for class com_sirc_jni_HelloWorld */
    
    #ifndef _Included_com_sirc_jni_HelloWorld
    #define _Included_com_sirc_jni_HelloWorld
    #ifdef __cplusplus
    extern "C" {
    #endif
    /*
     * Class:     com_sirc_jni_HelloWorld
     * Method:    printJNI
     * Signature: (Ljava/lang/String;)Ljava/lang/String;
     */
    JNIEXPORT jstring JNICALL Java_com_sirc_jni_HelloWorld_printJNI
      (JNIEnv *, jobject, jstring);
    
    #ifdef __cplusplus
    }
    #endif
    #endif

    头文件中定义了与上层的访问接口Java_com_sirc_jni_HelloWorld_printJNI,方法名是按照”Java_包名_类名_方法名“来命名的。

    3.实现JNI原生函数源文件

    在jni文件夹下面新建com_sirc_jni_HelloWorld.c文件,文件代码如下:

    #include <jni.h>
    #define LOG_TAG "HelloWorld"
    #include <android/log.h>
    
    #define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, fmt, ##args)
    #define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args)
    #define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, fmt, ##args)
    /* Native interface, it will be call in java code */
    JNIEXPORT jstring JNICALL Java_com_sirc_jni_HelloWorld_printJNI(JNIEnv *env,
            jobject obj, jstring inputStr) {
        LOGI("dufresne Hello World From libhelloworld.so!");
        // 从 instring 字符串取得指向字符串 UTF 编码的指针
        const char *str = (const char *) (*env)->GetStringUTFChars(env, inputStr,
                JNI_FALSE);
        LOGI("dufresne--->%s", (const char *)str);
        // 通知虚拟机本地代码不再需要通过 str 访问 Java 字符串。
        (*env)->ReleaseStringUTFChars(env, inputStr, (const char *) str);
        return (*env)->NewStringUTF(env, "Hello World! I am Native interface");
    }
    
    /* This function will be call when the library first be load.
     * You can do some init in the libray. return which version jni it support.
     */
    jint JNI_OnLoad(JavaVM* vm, void* reserved) {
        void *venv;
        LOGI("dufresne----->JNI_OnLoad!");
        if ((*vm)->GetEnv(vm, (void**) &venv, JNI_VERSION_1_4) != JNI_OK) {
            LOGE("dufresne--->ERROR: GetEnv failed");
            return -1;
        }
        return JNI_VERSION_1_4;
    }

    其中#include <android/log.h>应用了打印日志头文件,并声明了集中打印方法,包括info,debug,error。我想这应该是调用的android的内部系统文件,在这里对其进行了一个重声明

    #define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, fmt, ##args)
    #define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args)
    #define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, fmt, ##args)

     JNI_OnLoad函数JNI规范定义的,当共享库第一次被加载的时候会被回调。

    Java_com_sirc_jni_HelloWorld_printJNI是头文件中方法的实现,可以通过这里获取上层传过来的参数,并在这里返回数据给上层框架使用。

    注:这段代码是从其他网站上copy过来的,运行时如果出现错误stray '/241' in program 该错误是指源程序中有非法字符,需要去掉非法字符。

    解决方法:来源于http://blog.sina.com.cn/s/blog_6bc5571a0100zmft.html

    (1)把所粘的文字放到记事本里就行了 

    (2)把出错行的空格删掉重新打一下试试。

    4.编译生成so

    4.1在jni文件夹下面新建Android.mk文件,文件内容如下:

    LOCAL_PATH:= $(call my-dir)
    # 一个完整模块编译
    include $(CLEAR_VARS)
    LOCAL_SRC_FILES:=com_sirc_jni_HelloWorld.c
    LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
    LOCAL_MODULE := libHelloWorldJni
    LOCAL_SHARED_LIBRARIES := libutils
    LOCAL_PRELINK_MODULE := false
    LOCAL_MODULE_TAGS :=optional
    LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog
    include $(BUILD_SHARED_LIBRARY)

    系统变量解析:

      LOCAL_PATH - 编译时的目录
      
    $(call 
    目录,目录….) 目录引入操作符
        
    如该目录下有个文件夹名称 src,则可以这样写 $(call src),那么就会得到 src 目录的完整路径

      include $(CLEAR_VARS) -清除之前的一些系统变量
      LOCAL_MODULE 
    - 编译生成的目标对象
      LOCAL_SRC_FILES 
    - 编译的源文件
      LOCAL_C_INCLUDES 
    - 需要包含的头文件目录
      LOCAL_SHARED_LIBRARIES 
    - 链接时需要的外部库
      LOCAL_PRELINK_MODULE 
    - 是否需要prelink处理 
      include$
    BUILD_SHARED_LIBRARY - 指明要编译成动态库

      android.mk编译模块添加具体方法参考:http://blog.csdn.net/yili_xie/article/details/4906865

    4.2编译

    我这里使用NDK来进行编译,首先要下载安装NDK。

    Android NDK安装很简单,直接到http://developer.android.com/tools/sdk/ndk/index.html 下载android-ndk-r8e-windows-x86.zip 解压即可,接着设置环境变量。右击我的电脑属性,切换到高级选项卡,单击环境变量,在系统变量下单击编辑在Path变量名下直接变量值;D:Androidandroid-ndk-r8e-windows-x86android-ndk-r8e ,也就是你的解压路径,其中有个封号与前面的变量值分割。单击确定NDK就安装好了。

    在命令行中使用ndk-build编译,过程如下

     

    编译成功后会自动生成libs/armeabi/libHelloWorldJni.so文件。

    5.运行测试

    运行JNITest项目,在logcat中查看运行结果。可以看到

    上层打印情况:

    底层打印情况:

     

    以上便是一个完整的开发JNI的实例,下一篇将详细研究参数传值,java中基本类型和c++基本类型的关联。

     

  • 相关阅读:
    痞子衡嵌入式:MCUXpresso IDE下SDK工程在Build配置上与IAR,MDK差异
    13万字详细分析JDK中Stream的实现原理
    扫码登录是这样登录的
    [Vue深入组件-边界情况处理] 控制更新
    [Vue深入组件-边界情况处理] 模板定义的替代品
    [Vue深入组件]:递归组件和组件的循环引用
    #antdv 清除指定字段验证 #antdv表单验证指定清除
    [Vue深入组件-边界情况处理] 程序化的事件监听器
    [Vue深入组件-边界情况处理] 访问元素 & 组件
    [Vue深入组件]:Slot插槽
  • 原文地址:https://www.cnblogs.com/gisdream/p/3522190.html
Copyright © 2011-2022 走看看