• Android studio jni


      首先我们要明确几个概念,jni,ndk,共享库(.so)。

      jni是java native interface的缩写,java 本地接口。它提供了若干的API实现了Java和其他语言的通信(主要是C/C++)。从Java1.1开始,jni标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。

      ndk:Android NDK 是在SDK前面又加上了“原生”二字,即Native Development Kit,因此又被Google称为“NDK”。

      .so:共享函数库,在可执行程序启动的时候加载,所有程序重新运行时都可自动加载共享函数库中的函数。

      为何要使用ndk?

        1. 代码的保护,由于apk的java层代码很容易被反编译,而C/C++库反汇难度较大。
        2. 在NDK中调用第三方C/C++库,因为大部分的开源库都是用C/C++代码编写的。
        3. 便于移植,用C/C++写的库可以方便在其他的嵌入式平台上再次使用。
     

      通俗来说,jni提供一套标准,包括定义了一些数据类型,引用类型,对应于Java中的数据类型,引用类型。还有一些转换函数,这些都定义在jni.h中。      

      例如,java传入的String参数,在c文件中被jni转换为jstring的数据类型,在c文件中声明char* test,然后test = (char*)(*env)->GetStringUTFChars(env, jstring, NULL);注意:test使用完后,需要释放指针变量:(*env)->ReleaseStringUTFChars(env, jstring, test);将char* test转换为jstring 用 (*env)->NewStringUTF(env,const char* );(const 指啥意思?)

      Android 函数库是用c/c++写的,框架层不能直接调用它,而是通过jni调用的。我们也可以自己用jni调用native层。

      实战。

      1,配置NDK环境,需要下载NDK开发包并配置。

      2,在app build.gradle里面配置ndk属性。

      3,静态加载动态库,编写naive方法,和普通java方法基本没区别。

    static {
            System.loadLibrary("JniTest");
        }
    
        public native String getStringFromNative();
    

       4,生成头文件。在android studio 的命令行界面中,进入/app/src/main/java目录下,执行命令:

    javah -d ../jni com.example.shengchanglu.test.MainActivity
    

       这样就在src/main/目录中新增了jni目录,以及jni/com_example_shengchanglu_test_MainActivity.h头文件。

      com_example_shengchanglu_test_MainActivity.h头文件内容如下:

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

       看返回值jstring对应于java中得String。

      5,在jni目录中新增main.c文件,去实现com_example_shengchanglu_test_MainActivity.h头文件中定义的方法。

    //
    // Created by shengchang lu on 15/9/2.
    //
    
    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include <jni.h>
    #include <android/log.h>
    
    #ifndef LOG_TAG
    #define LOG_TAG "ANDROID_LAB"
    #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
    #endif
    
    
    #ifndef _Included_com_example_shengchanglu_test_MainActivity
    #define _Included_com_example_shengchanglu_test_MainActivity
    #ifdef __cplusplus
    extern "C" {
    #endif
    /*
     * Class: com_example_shengchanglu_test_MainActivity
     * Method: getStringFromNative
     * Signature: ()Ljava/lang/String;
     */
    JNIEXPORT jstring JNICALL Java_com_example_shengchanglu_test_MainActivity_getStringFromNative(JNIEnv * env , jobject j)
    {
        LOGE("log string from ndk.");
        return (*env)->NewStringUTF(env,"Hello From JNI!");
    }
    
    #ifdef __cplusplus
    }
    #endif
    #endif
    

       6,Android中调用native方法。

      7,编译,运行。在app/build/intermediates/下出现ndk目录,生成了动态库so文件和mk文件。

      上面讲的只是ndk开发最基本的。Java不仅可以调用jni方法,jni也可以调用Java中属性(静态和非静态),方法(静态和非静态)。

    void AppAction::setUndoRedoState(bool undo, bool redo) {
    		JNIEnv* jniEnv = EnvManager::shareInstance()->getEnv();
    		if (jniEnv == NULL) {
    			return;
    		}
    		jclass jclz = NULL;
    		jclz = jniEnv->FindClass(
    				"com/fotoable/fotoproedit/activity/ProEditLightPenActivity");
    		if (jniEnv->ExceptionCheck() == JNI_TRUE) {
    			jniEnv->ExceptionClear();
    			jniEnv->DeleteLocalRef(jclz);
    			return;
    		}
    		jmethodID checkUndoRedoState = jniEnv->GetStaticMethodID(jclz,
    				"checkUndoRedoState", "(ZZ)V");
    		if (checkUndoRedoState == NULL) {
    			jniEnv->DeleteLocalRef(jclz);
    			return;
    		}
    		jniEnv->CallStaticVoidMethod(jclz, checkUndoRedoState,
    				undo,redo);
    		if (jniEnv->ExceptionCheck() == JNI_TRUE) {
    			jniEnv->ExceptionClear();
    		}
    
    		jniEnv->DeleteLocalRef(jclz);
    }
    

       根据上面的jni代码,我们可以反推出在当前项目中有且只有一个类中有这个方法。

       类名:com.fotoable.fotoproedit.activity.ProEditLightPenActivity

       方法:public void static checkUndoRedoState(boolean b1,boolean b1);

      调用方法完毕后,指针变量应该释放,要不然会引起内存泄露,程序崩溃。难点是异常捕获,ExceptionCheck只能捕获上一行代码引发的异常,且不能向Android层抛出,所以必须多加小心。

      关于保存jni环境: EnvManager::shareInstance():

    /*
     * UtilManager.h
     *
     *  Created on: 2013-12-31
     *      Author: Administrator
     */
    
    
    #ifndef UTILMANAGER_H_
    #define UTILMANAGER_H_
    #include <jni.h>
    class EnvManager {
    public:
    	EnvManager();
    	virtual ~EnvManager();
    
    	static EnvManager* shareInstance();
    	static void destroy();
    
        JNIEnv* getEnv();
    
    	void setEnv(JNIEnv* jniEnv);
    
    private:
    	JNIEnv* env;
    };
    
    #endif /* UTILMANAGER_H_ */
    
    /*
     * UtilManager.cpp
     *
     *  Created on: 2013-12-31
     *      Author: Administrator
     */
    #include <stdio.h>
    #include <stdlib.h>
    #include "EnvManager.h"
    
    static EnvManager * sEnvManager = NULL;
    
    EnvManager * EnvManager::shareInstance()
    {
        if (!sEnvManager) {
        	sEnvManager = new EnvManager();
        }
        return sEnvManager;
    }
    
    void EnvManager::destroy()
    {
    	delete sEnvManager;
    	sEnvManager = NULL;
    }
    
    JNIEnv* EnvManager::getEnv(){
    	return env;
    }
    
    void EnvManager::setEnv(JNIEnv* jniEnv){
    	env = jniEnv;
    }
    
    EnvManager::EnvManager() {
    	env = NULL;
    }
    
    EnvManager::~EnvManager() {
    	delete env;
    }
    

     后续:跨平台开发方面。譬如在图片处理方面,Android 和 iOS底层可以使用cocos2dx ,Android 通过 jni 与上层界面交互,iOS可以调用Cocos2dx,这样底层的代码是一样的,iOS和Android只用各写自己的界面就行。开发速度就比较快。

          如果底层有内存泄露引起崩溃,整个引用也会崩溃,不会抛出异常。所以应该尽量少做底层和Android层的来回频繁切换,避免崩溃。

  • 相关阅读:
    SQL0668N 由于表 "db2inst1.test" 上的原因代码 "3",所以不允许操作(解因为LOAD引起的LOAD暂挂状态锁)
    重装系统后,如何恢复DB2数据库?
    db2 导入导出命令大全
    解决tomcat启动时日志出现 javax.naming.NamingException: Invalid byte 1 of 1byte UTF8 sequence.的问题
    【fcntl系统调用】
    C++读书笔记之函数模板
    unix shell笔记
    用setsockopt()来控制recv()与send()的超时 【转】
    Linux守护进程的编程实现 [转]
    GDB调试core文件样例(如何定位Segment fault) 【转】
  • 原文地址:https://www.cnblogs.com/lsc183/p/4800878.html
Copyright © 2020-2023  润新知