Android Studio2.0 NDK开发,JNI技术实现小Demo,诠释JNI的基本开发

前段时间写过一篇关于JNI开发的文章,不小心被删了,今天补上

NDK和JNI的介绍:

ndk(Native Development Kit)是工具的集合,帮助开发者快速开发C(C++)动态库,并将.so文件和Java应用一起打包成apk;
jni(Java Native Inteface)java本地接口,是一种互通机制,建立了Java和C(C++)互通的桥梁。

Android Studio2.0使用NDK开发

最早使用Eclipse开发Android项目时,NDK开发始终是比较让人头疼的一块,因为需要手动关联代码提示,需要手动创建jni文件,Android.mk文件,手动生成.h头文件,先编译成.so文件才可以运行。AS2.0之前也是比较麻烦的,这里介绍一下Android2.0之后的NDK开发流程:
Android Studio2.0NDK的开发主要是使用了CMake工具进行开发,这就是他的简便之处
CMake:CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。他能够输出各种各样的makefile或者project文件,能测试编译器所支持的C++特性,类似UNIX下的automake。只是 CMake 的组态档取名为 CMakeLists.txt。Cmake 并不直接建构出最终的软件,而是产生标准的建构档(如 Unix 的 Makefile 或 Windows Visual C++ 的 projects/workspaces),然后再依一般的建构方式使用。这使得熟悉某个集成开发环境(IDE)的开发者可以用标准的方式建构他的软件,这种可以使用各平台的原生建构系统的能力是 CMake 和 SCons 等其他类似系统的区别之处。感兴趣的可以上网搜一下CMake的进一步学习下
在这之前你需要做一些准备工作,下载NDK,CMake,LLDB这三个包
LLDB包是用来调试C代码的
这里写图片描述
下载完之后,在你AS的SDK目录下,会找到下载好的这三个包,把其中的ndk-bundle配置到你的环境变量path中

第一类,创建项目时候就已经确定了使用NDK开发

在创建项目的时候勾选Include C++ Support选项,然后按照正常流程创建项目
这里写图片描述
完成项目创建之后,你会发现工程结构多了一些东西如下
这里写图片描述
其中cpp文件家里面放的就是你的C++代码,并且已经帮你自动写好了一部分C++代码,其实到这一步你就已经完成了一个简单的JNI的Demo,你可以直接运行起来试试,当然如果有报错的情况,你可以Open Module Setting来看一下NDK是不是已经自动导入,如果没有,需要你手动导入一下
这里写图片描述

CMakeLists.txt里面是相关的配置,包括你的.so文件名,你的c代码路径,以及log日志的一些配置。
再来看一下gradle文件也多出了两行相应的配置,分别配置了c代码编辑和CMakeList.txt文件
这里写图片描述
到这里就是android studio2.0的NDK项目的创建

第二类,开发中途遇到使用NDK开发

我想这个是我们比较关心的,因为很多情况下需求是中途加上来的,在项目中使用NDK开发,才是我们需要关注的。
首先我们要创建好一个jni文件夹来放我们的C代码,当然你也可以创建cpp文件夹,放我们的C++代码
这里写图片描述
然后在该文件夹下创建我们的C文件
这里写图片描述
接下来我们回忆下刚才在创建JNI项目的时候,多出来的那个CMakeLists.txt文件和在gradle文件里面多出来的那两处配置,就可以用的上了,首先在gradle文件defaultConfig节点里配置如下代码

 externalNativeBuild {
            cmake {
                cppFlags ""
            }
        }

然后在buildTypes节点的外面配置如下代码

 externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }

那么现在还缺少CMakeLists.txt文件了,这个文件里面的东西不太好记,我是直接拷贝的(随便创建一个JNI项目就可以自动或取代CMakeLists.txt文件),将CMakeLists.txt文件copy到app目录下,同时要对这个文件进行修改,因为这个文件配置了动态链接库以及C代码的路径等(我觉得和以前的Android.mk文件差不多),具体需要修改的地方如下:
这里写图片描述
这里写图片描述
到这里已经基本完成了,接下来你要Rebuild Project一下,这时候你可以尝试着写一个native方法来测试一下,发现并没有代码提示啊,不会在C中自动创建代码啊,不要着急,光标放到你的native方法名上面,然后alt+enter键弹出相应的解决方案,选择第三个
这里写图片描述
它会自动比对一下,并且gradle几秒钟,这个时候你把光标重新移到native方法名上,alt+enter就可以看到代码提示,自动帮你在C文件中创建对应的方法,有时候会自动给你创建另一个C文件,并在里面自动写好了方法,你可以把里面代码copy到你的C文件中,把自动创建的C文件删掉,下一次再写native方法,就会自动在你的C文件中自动添加方法了。到这里项目就创建完成了,代码提示有了,还可以看相应的源码,比如.h文件的源码。
这里并未详细介绍C++使用,其实创建过程和C是一样的,只不过习惯性的不需要叫你文件夹,而是叫做cpp文件夹,对应的需要配置好CMakeLists.txt文件,cpp文件夹下创建的文件需要以.cpp为后缀。C++代码使用的JNIEnv和C代码的不一样的,是进一步的封装,感兴趣的可以自己研究一下。
另外,在CMakeLists.txt文件里面的SHARED代表的是生成动态链接库
这里写图片描述
动态链接库(SHARED):.so文件,占用的体积较小
静态链接库(STATIC):.a文件,占用的体积较大
最后生成的.so文件对应的路径在:app\build\intermediates\cmake\debug\obj
接下来我把我写的JNI练习的相关代码看一下
MainActivity的代码

package com.herenit.jnidemo05;

import android.app.Activity;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import java.lang.reflect.Method;

/**
 * C代码中调用java某一个类中的方法,用到了C的反射技术
 */
public class MainActivity extends Activity {

    private JavaSimple javaSimple;
    private EditText et_num1,et_num2;
    private TextView tv_result;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        javaSimple = new JavaSimple();
        et_num1 = (EditText) findViewById(R.id.et_num1);
        et_num2 = (EditText) findViewById(R.id.et_num2);
        tv_result = (TextView) findViewById(R.id.tv_sum);
    }

    /**
     * 使用反射技术调用某一个类的某个方法
     * @param view
     */
    public void reflectOnclick(View view){
        try {
            //1,获取该类的字节码文件
            Class clazz = JavaSimple.class;
            //2,根据获取该类的实例对象
            Object javaSimple = clazz.newInstance();
            //3,获取该类的某一个方法对象(Method)
            Method mGetSum = clazz.getDeclaredMethod("getSum",int.class,int.class);
            //4,调用对应的方法
            int result = (int) mGetSum.invoke(javaSimple,3,2);
            Toast.makeText(this, "反射调用成功:result="+result, Toast.LENGTH_SHORT).show();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 点击按钮,用C调用java的JavaSimple的sayHello方法
     * Log日志看现象
     * @param view
     */
    public void cCallJava_sayHello(View view){
        javaSimple.cCallJava_sayHello();
    }

    /**
     * 点击按钮,C调用java的JavaSimple的getSum方法
     * 界面操作看现象
     * @param view
     */
    public void cCallJava_getSum(View view){
        String str1 = et_num1.getText().toString().trim();
        String str2 = et_num2.getText().toString().trim();
        if(!TextUtils.isEmpty(str1) && !TextUtils.isEmpty(str2)){
            int num1 = Integer.valueOf(str1);
            int num2 = Integer.valueOf(str2);
            int result = javaSimple.cCallJava_getSum(num1,num2);
            tv_result.setText(""+result);
        }
    }

    /**
     * 点击按钮,C调用java的JavaSimple的LogString方法
     * 从log日志看现象
     * @param view
     */
    public void cCallJava_LogString(View view){
        javaSimple.cCallJava_LogString();
    }

    /**
     * 点击按钮,C调用java的JavaSimple的staticFunction静态方法
     * 从Log日志看现象
     * @param view
     */
    public void cCallJava_staticFunction(View view){
        javaSimple.cCallJava_staticFunction();
    }
}

JavaSimple中的代码

package com.herenit.jnidemo05;

import android.util.Log;

/**
 * Created by HouBin on 2017/3/8.
 */

public class JavaSimple {
    /**
     * 加载动态链接库
     */
    static {
        System.loadLibrary("native-lib");
    }

    private static final String TAG = JavaSimple.class.getSimpleName();


    public void sayHello() {
        Log.e(TAG, "C调用Java的无参数无返回值方法:sayHello()");
    }

    public int getSum(int a, int b) {
        return a + b;
    }

    public void LogString(String text) {
        Log.e(TAG, "C调用Java的String参数方法:LogString(String text)---" + text);
    }

    public static void staticFunction(String text) {
        Log.e(TAG, "C调用Java的静态String参数方法:staticFunction(String text)---" + text);
    }

    public native void cCallJava_sayHello();

    public native int cCallJava_getSum(int num1, int num2);

    public native void cCallJava_LogString();

    public native void cCallJava_staticFunction();
}

native-lib.c中的代码

#include <jni.h>
#include <syslog.h>

/**
 * 在这个方法中,使用C代码调用JavaSimple中的sayHello方法,使用的是C的反射技术
 */
JNIEXPORT void JNICALL
Java_com_herenit_jnidemo05_JavaSimple_cCallJava_1sayHello(JNIEnv *env, jobject instance) {
    /*1,获取该类的字节码文件
     * jclass      (*FindClass)(JNIEnv*, const char*);
     * char*参数 传入的是全类名(需要把点(.)改成斜线(/)
     *
     * */
    jclass clazz = (*env)->FindClass(env, "com/herenit/jnidemo05/JavaSimple");


    /*2,获取该类的某一个方法对象(jmethodID)
     * jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char* str1, const char* str2);
     * jclass 要调用的java类的字节码
     * str1 要调用的类的方法的方法名
     * str2 对应的这个方法的签名(可以在该类的.class文件所在的根目录下执行命令行:javap -s 全类名(不包括.java))得到
     * */
    jmethodID methodId = (*env)->GetMethodID(env, clazz, "sayHello", "()V");

//    3,根据获取该类的实例对象
    jobject javaSimple = (*env)->AllocObject(env, clazz);
    //4,调用对应的方法
    (*env)->CallVoidMethod(env, javaSimple, methodId);
}

/**
 * C调用java中的带参数的方法
 */
JNIEXPORT jint JNICALL
Java_com_herenit_jnidemo05_JavaSimple_cCallJava_1getSum(JNIEnv *env, jobject instance, jint num1,
                                                        jint num2) {
    jclass clazz = (*env)->FindClass(env, "com/herenit/jnidemo05/JavaSimple");
    jmethodID methodId = (*env)->GetMethodID(env, clazz, "getSum", "(II)I");
    jobject javaSimple = (*env)->AllocObject(env, clazz);
    int result = (*env)->CallIntMethod(env, javaSimple, methodId, num1, num2);
    return result;
}

/**
 * C调Java的 LogString(String text)方法
 */
JNIEXPORT void JNICALL
Java_com_herenit_jnidemo05_JavaSimple_cCallJava_1LogString(JNIEnv *env, jobject instance) {

    jclass clazz = (*env)->FindClass(env, "com/herenit/jnidemo05/JavaSimple");
    jmethodID methodId = (*env)->GetMethodID(env, clazz, "LogString", "(Ljava/lang/String;)V");
    jobject javaSimple = (*env)->AllocObject(env, clazz);

    jstring text = (*env)->NewStringUTF(env, "I am param");
    (*env)->CallVoidMethod(env, javaSimple, methodId, text);
}
/**
 * C调用java的静态方法static void staticFunction(String text)
 */
JNIEXPORT void JNICALL
Java_com_herenit_jnidemo05_JavaSimple_cCallJava_1staticFunction(JNIEnv *env, jobject instance) {
    //1,获取该方法对应的类的字节码文件类
    jclass clazz = (*env)->FindClass(env,"com/herenit/jnidemo05/JavaSimple");
    //2,获取methodId
    jmethodID methodId = (*env)->GetStaticMethodID(env,clazz,"staticFunction","(Ljava/lang/String;)V");
    //3,调用java的静态方法
    jstring text = (*env)->NewStringUTF(env,"I am from C,call Java staticFunction!");
    (*env)->CallStaticVoidMethod(env,clazz,methodId,text);
}

最后给大家推荐个网址,我学习JNI看过的感觉比较有帮助的网址:
JNI技术的一些基础知识
最后给大家我的Demo源码地址
源码下载
希望各位大神多多给出评价和意见,谢谢!

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目标检测(Object Detection)是计算机视觉领域的一个核心问题,其主要任务是找出图像中所有感兴趣的目标(物体),并确定它们的类别和位置。以下是对目标检测的详细阐述: 一、基本概念 目标检测的任务是解决“在哪里?是什么?”的问题,即定位出图像中目标的位置并识别出目标的类别。由于各类物体具有不同的外观、形状和姿态,加上成像时光照、遮挡等因素的干扰,目标检测一直是计算机视觉领域最具挑战性的任务之一。 二、核心问题 目标检测涉及以下几个核心问题: 分类问题:判断图像中的目标属于哪个类别。 定位问题:确定目标在图像中的具体位置。 大小问题:目标可能具有不同的大小。 形状问题:目标可能具有不同的形状。 三、算法分类 基于深度学习的目标检测算法主要分为两大类: Two-stage算法:先进行区域生成(Region Proposal),生成有可能包含待检物体的预选框(Region Proposal),再通过卷积神经网络进行样本分类。常见的Two-stage算法包括R-CNN、Fast R-CNN、Faster R-CNN等。 One-stage算法:不用生成区域提议,直接在网络中提取特征来预测物体分类和位置。常见的One-stage算法包括YOLO系列(YOLOv1、YOLOv2、YOLOv3、YOLOv4、YOLOv5等)、SSD和RetinaNet等。 四、算法原理 以YOLO系列为例,YOLO将目标检测视为回归问题,将输入图像一次性划分为多个区域,直接在输出层预测边界框和类别概率。YOLO采用卷积网络来提取特征,使用全连接层来得到预测值。其网络结构通常包含多个卷积层和全连接层,通过卷积层提取图像特征,通过全连接层输出预测结果。 五、应用领域 目标检测技术已经广泛应用于各个领域,为人们的生活带来了极大的便利。以下是一些主要的应用领域: 安全监控:在商场、银行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值