Android 积累一些JNI与NDK的知识

    xiaoxiao2021-04-04  41

    最近项目中要用到一些同屏操作,起初的方案是不断记录坐标,上传坐标,来达到画笔的同屏。但实际演示的时候效果并不好,于是我在想可不可以像视频录制一样直接调用底层,体验永远是程序质量的第一标准,不能因为自己的能力限制所妥协。但问题接踵而至,因为jvm的跨平台的特性,注定导致java跟底层几乎没什么联系。但是java能调用c方法,通过JNI的方式。在android程序中要想调用c方法,就要用到NDK,平常JNI和NDK说的很溜,其实我也不知道JNI与NDK的区别:

    1.JNI与NDK的定义:

    JNI:

    Java Native Interface(Java 本地接口) 方便Java调用C,C++等本地代码所封装的一层接口

    NDK:

    Android 提供的一个工具集合(native develop kits),这套工具集允许你为android使用c或c++代码,并提供众多的平台库让你管理原生的activity和物理设备组件 通过NDK可以在android中更加方便的通过JNI来访问本地代码 交叉编译的工具链:在一个平台下,编译出来在另一个平台下可以执行的代码 .c 与 .java 1.编译:把源代码编译成一个低级语言, 2.链接:根据具体平台的特性,链接成一个二进制可执行的程序

    工作环境()

    应该是一个android程序员想要调用c方法,他要写成native方法,然后把nativie方法弄成头文件, c程序员根据头文件里的方法,对其中的方法做实现,然后给android程序员再弄成so文件,弄成文件就简单了,开发过百度地图的都知道,建立jniLibs,将so文件放入,调用方法,实现功能。

    运行环境:

    as2.3.1

    window64

    ndk:android-ndk-r13b

    NDK比较古老的使用方式:使用ndk-build命令

    之前eclipse上使用ndk,或者ndk更早版本的时候,在官方的ndk目录里有ndk-build的文件,可惜只能在linux环境下运行,估计还要下cygwin模拟linux环境还要配置环境变量,而现在比较高的版本的ndk文件目录下有ndk-build.cmd文件,方便里我们可以直接在Android studo的Terminal控制台下直接编译成so库。

    前言:

    1.运行环境很重要,本人实验了一下午,发现低版本的ndk害死人,老是出Error:Execution failed for task ':app:compileDebugNdk'.这个错误,纠结代码半天,其实压根跟代码没啥关系

    2.32位的系统androidstudio用不了cmake

    3.android_studio看不了.mk文件

    操作流程:

    1.配置NDK与环境变量 配置ndk的环境变量跟配置jdk的环境变量差不多,自行百度 2.建立项目声明native方法 package com.wyt.hcy.libjpegandroid; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.TextView; public class MainActivity extends AppCompatActivity { static { System.loadLibrary("Hello"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView tv= (TextView) findViewById(R.id.tv); tv.setText(helloFromC()); } public native String helloFromC(); } 3.形成头文件 在studio terminal中输入指令,切换到项目的目录,并输入javah 包名. MainActivity 刷新就会看到产生的头文件 点开这个头文件: /* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_wyt_hcy_libjpegandroid_MainActivity */ #ifndef _Included_com_wyt_hcy_libjpegandroid_MainActivity #define _Included_com_wyt_hcy_libjpegandroid_MainActivity #ifdef __cplusplus extern "C" { #endif /* * Class: com_wyt_hcy_libjpegandroid_MainActivity * Method: helloFromC * Signature: ()Ljava/lang/String; */ /** * 函数名的格式Java_包名_类名_方法 * JNIEnv:表示一个指向JNi环境的指针 * jobject:表示java中的this * JNICALL,JNIEXPORT:表示JNi定义的宏 */ JNIEXPORT jstring JNICALL Java_com_wyt_hcy_libjpegandroid_MainActivity_helloFromC (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif 4.根据头文件实现方法 建立jni文件夹,选择main目录右键,new/folder/jnifolder 将头文件剪切到jni目录下 这里以c语言为例,在jni目录下建立Hello.c文件,要引入头文件 #include<stdio.h> #include<jni.h> #include "com_wyt_hcy_libjpegandroid_MainActivity.h" JNIEXPORT jstring JNICALL Java_com_wyt_hcy_libjpegandroid_MainActivity_helloFromC(JNIEnv *env, jobject obj) { return (*(*env)).NewStringUTF(env, "HelloWorldFromC"); } 5.编成so库 A。在jni目录下建立Android.mk文件(此文件名称固定:交叉编译器在编译c或者c++语言所依赖的配置文件符合linux下makefile的语法ziji),文件名字固定(不知道为什么),as是能建立.mk文件的,但打不开。 这里我用subnlite text打开它,在android.mk里输入 LOCAL_PATH := ${call my-dir} include ${CLEAR_VARS} LOCAL_MODULE :=Hello LOCAL_SRC_FILES :=Hello.c include ${BUILD_SHARED_LIBRARY}

    LOCAL_PATH := ${call my-dir}  获取当前Android.mk文件的路径

    include ${CLEAR_VARS}       变量的初始化但不会初始化LOCAL_PATH

    LOCAL_MODULE :=Hello        libHello.so 表示模块名称,即:MainActivity静态代码块填写的

    LOCAL_SRC_FILES :          表示参与编译的源文件 B。在项目的gradle.properties里配置: android.useDeprecatedNdk=true

    C。最后在module的build.grade里输入:(一定要在android里的 defaultConfig代码块里写:

    android { compileSdkVersion 25 buildToolsVersion "25.0.2" defaultConfig { applicationId "com.wyt.hcy.libjpegandroid" minSdkVersion 15 targetSdkVersion 25 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" ndk{ moduleName "Hello" // abiFilters "armeabi", "armeabi-v7a", "x86" //输出指定三种abi体系结构下的so库。目前可有可无。 } } 在Terninal控制台里输入ndk-build成功就会返回

    编译成功的话:在jni同级目录下会多了个libs目录,还能看到libHello.so

    然后clean,运行就会看到textview调用了c代码 运行中出错:Error:Execution failed for task ':app:compileDebugNdk'.  个人觉得不应该向其他人说的添加一个空的c文件就能解决的:

    1.看看您 在项目的gradle.properties里配置:

    2.看看项目的gradle里有没有设置ndk,或者当前项目的ndk版本是否跟你环境变量的版本一致

    ndk-build clean 该命令可以删除编译的缓存

    下面我们进行更深层次的探索

    1.什么是cmake

    一款外部构建工具,可与 Gradle 搭配使用来构建原生库。如果您只计划使用 ndk-build,则不需要此组件。

    2.什么是LLDB

    一种调试程序,Android Studio 使用它来调试原生代码。  

    //最近我一直想使用libjpeg-turbo这个开源库,但我一直没找到如何将这个开源库编译成android的版本的方法,一开始我看教程上说,只要把开源库里的c文件得名字都放进Android.mk文件里just like:

     

    我展示too young,以为直接编译能成功结果问题太多

    jinclude.h:23:10: fatal error: 'jconfig.h' file not found jpeglib.h:69:9: error: unknown type name 'JSAMPLE'

    结果我就傻眼了,百度这些问题,压根没找到有用的,由于自己c语言早已忘得一干二净

    于是我今天又找了个项目:项目下载链接

    只要将文件复制到项目的jni目录下:打开as 控制台输入:

    ndk-build APP_ABI=armeabi-v7a,armeabi虽然有些警告,但直接编译成功了

    转载请注明原文地址: https://ju.6miu.com/read-666134.html

    最新回复(0)