zoukankan      html  css  js  c++  java
  • 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层的来回频繁切换,避免崩溃。

  • 相关阅读:
    Java实现 蓝桥杯VIP 算法提高 排队打水问题
    Java实现 蓝桥杯VIP 算法提高 排队打水问题
    Java实现 蓝桥杯VIP 算法提高 排队打水问题
    Java实现 蓝桥杯VIP 算法提高 特殊的质数肋骨
    Java实现 蓝桥杯VIP 算法提高 特殊的质数肋骨
    Java实现 蓝桥杯VIP 算法提高 特殊的质数肋骨
    Java实现 蓝桥杯VIP 算法提高 特殊的质数肋骨
    现在使用控件, 更喜欢继承(覆盖控件已有的函数,很奇怪的一种使用方式)
    Controls 属性与继承 TShape 类的小练习(使用TShape可以解决很多图形问题)
    QT创建窗口程序、消息循环和WinMain函数(为主线程建立了一个QEventLoop,并执行exec函数)
  • 原文地址:https://www.cnblogs.com/lsc183/p/4800878.html
Copyright © 2011-2022 走看看