欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JNI-开发流程 so文件生成.c实现文件拆分,合并,jni中的线程

程序员文章站 2022-06-06 20:39:27
...

使用so库和头文件开发

gradle指定开发平台,在defaultConfig目录里面

    ndk {
            abiFilters "armeabi","x86"
        }

引入第三方库,在app.gralde中的android目录下

    sourceSets.main {
            jniLibs.srcDirs = ['libs']
                jni.srcDirs = []
            }

c++中找不到外部函数,需要在头文件前加extern “C” {}

生成so文件 只需要 编译后在这里找到
JNI-开发流程 so文件生成.c实现文件拆分,合并,jni中的线程

或者

https://www.cnblogs.com/jymblog/p/5526865.html
https://blog.csdn.net/xiejunna/article/details/70875064

c 文件拆分

在之前写的动态注册基础上写代码.

java

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";

    // Used to load the 'native-lib' library on application startup.
    private static String SD_CARD_PATH = Environment.getExternalStorageDirectory().getAbsolutePath();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

       // diff();
    }


    public void diff(View v){
        Log.d(TAG, "diff begin----");
        String path = SD_CARD_PATH + File.separatorChar + "myVideo.mp4";
        File file = new File(path);
        Log.d(TAG, "diff: 路径是否存在 path = "+file.exists());
        String pattern_Path = SD_CARD_PATH + File.separatorChar + "myVideo_%d.mp4";
        FileUtils.diff(path,pattern_Path,4);
        Log.d(TAG, "diff end----");
    }

}

c代码


//
// Created by liuml on 2018/8/15.
//

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include <android/log.h>
#include <assert.h>
#include <malloc.h>
/* Header for class androidrn_myjni_FileUtils */

#define TAG "jni_LOG"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
#define NELEM(x) ((int)(sizeof(x)/sizeof((x)[0])))
//int __android_log_print(int prio, const char* tag, const char* fmt, ...)
/*
 * Class:     androidrn_myjni_FileUtils
 * Method:    diff
 * Signature: (Ljava/lang/String;Ljava/lang/String;I)V
 */
//JNIEXPORT void JNICALL Java_androidrn_myjni_FileUtils_diff
//        (JNIEnv *env, jclass jclazz, jstring path, jstring pattern_Path, jint file_num) {
//    LOGI("jna log test");
////    LOGI("JNI 动态注册");
//}

//获取文件的大小
long get_file_size(const char *path) {

    FILE *fp = fopen(path, "rb");//打开一个文件, 文件必须存在,只读运行
    fseek(fp, 0, SEEK_END);//文件指针定位到文件末尾,偏移0个字节
    long ret = ftell(fp);//函数ftell 用于得到文件位置指针当前位置相对于文件首的偏移字节数
    fclose(fp);
    LOGI("获取文件的大小 %l",ret);
    return ret;
}

JNIEXPORT void JNICALL native_diff
        (JNIEnv *env, jclass jclazz, jstring path, jstring pattern_Path, jint file_num) {
    LOGI("JNI native_diff begin");
    //这里是 将传入的字符串转化c的指针. path_Str 为文件地址的路径  pattern_Path_str为 分割出来的文件名字
    const char *path_Str = (*env)->GetStringUTFChars(env, path, NULL);
    const char *pattern_Path_str = (*env)->GetStringUTFChars(env, pattern_Path, NULL);

    //申请二维字符数据, 存放子文件名
    char **patches = (char **) malloc(sizeof(char *) * file_num);

    int i = 0;
    for (; i < file_num; i++) {
        //给每个文件名申请地址
        LOGI("char 和char 指针占用的字节 char = %d char * = %d", sizeof(char), sizeof(char *));
        patches[i] = (char *) malloc(sizeof(char) * 100);
        // 需要分割的文件 myVideo.mp4
        // 每个子文件名称 myVideo_n.mp4
        //sprintf 函数 格式化打印
        sprintf(patches[i], pattern_Path_str, i);//格式化为 myVideo_n.mp4 传递进来是带%d 的 所以能够格式化
        LOGI("patch path : %s", patches[i]);
    }
    //获取文件的大小
    int fileSize = get_file_size(path_Str);
    //打开文件
    LOGI("打开文件 %d",path_Str);
    FILE *fpr = fopen(path_Str, "rb");
    /*
    * 1.判断文件大小能够被 file_num整除
    * 2.能整除就平分
    * 3.不能整除就先分 file_num -1
    * */

    if (fileSize % file_num == 0) {//如果可以被传入的file_num 整除
        LOGI("刚好被整除fileSize fileSize= %d ", fileSize);
        int part = fileSize / file_num;//每个被拆分的文件大小
        for (int i = 0; i < file_num; ++i) {
            //wb 文件如果已经存在就删除,只写运行. 不存在则创建
            LOGI("外层循环 i = %d", i);
            FILE *fpw = fopen(patches[i], "wb");
            for (int j = 0; j < part; ++j) {
                //fputc 把参数 char 指定的字符(一个无符号字符)写入到指定的流 stream 中,并把位置标识符往前移动
                //fgetc 从指定的流 stream 获取下一个字符(一个无符号字符),并把位置标识符往前移动。
                // LOGI("开始读取stream j = %d", j);
                fputc(fgetc(fpr), fpw);
            }
            fclose(fpw);
        }
    } else {//不能整除,先分 file_num -1
        LOGI("不能整除 fileSize= %d ", fileSize);
        int part = fileSize / (file_num - 1);
        for (int i = 0; i < file_num - 1; i++) {
            LOGI("外层循环 i = %d", i);
            FILE *fpw = fopen(patches[i], "wb");
            for (int i = 0; i < part; i++) {
                fputc(fgetc(fpr), fpw);
            }
            fclose(fpw);
        }
        //处理最后一个
        LOGI("处理最后一个");
        FILE *fpw = fopen(patches[file_num - 1], "wb");

        for (int i = 0; i < fileSize % (file_num - 1); i++) {
            fputc(fgetc(fpr), fpw);
        }

        fclose(fpw);
    }
    LOGI("释放指针");
    fclose(fpr);

    //释放指针  记住每次malloc后 必须释放
    for (int i = 0; i < file_num; i++) {
        free(patches[i]);
    }
    free(patches);
    //同样释放
    (*env)->ReleaseStringUTFChars(env, path, path_Str);
    (*env)->ReleaseStringUTFChars(env, pattern_Path, pattern_Path_str);

}


static const JNINativeMethod gMethods[] = {
        {
                "diff", "(Ljava/lang/String;Ljava/lang/String;I)V", (void *) native_diff
        }
};

//注册本地方法
static int registerNatives(JNIEnv *env) {

    jclass clazz;
    clazz = (*env)->FindClass(env, "androidrn/myjni/FileUtils");
    if (clazz == NULL) {
        LOGI("clazz == NULL");
        return JNI_FALSE;
    }
    LOGI("clazz != NULL");
    if ((*env)->RegisterNatives(env, clazz, gMethods, NELEM(gMethods)) < 0) {
        LOGI("RegisterNatives JNI_FALSE");
        return JNI_FALSE;
    }
    return JNI_TRUE;
}


JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    LOGI("jni onload begin");
    /* 这段代码直接拷贝系统的源码 在系统的onload里面  不过系统的是cpp 这里改造成c的*/
    JNIEnv *env = NULL;
    jint result = -1;

    if ((*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_4) != JNI_OK) {
        LOGI("ERROR: GetEnv failed\n");
        return -1;
    }
    assert(env != NULL);
    /*结束*/

    //注册
    registerNatives(env);

    return JNI_VERSION_1_4;

}

JNI-开发流程 so文件生成.c实现文件拆分,合并,jni中的线程
TODU:
给拆分加上进度

解释:

--------- beginning of crash

android 读写权限 用个工具类动态获取下

 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
 ```
- 二维数组 用于存放文件名字

| 0 | 1 | 2 | 3 |
| ------ | ------ | ------ | ------ |
| myVideo_0.mp4 | myVideo_1.mp4 | myVideo_2.mp4 |myVideo_3.mp4 |

- fopen 的mode
- r 以只读方式打开文件,该文件必须存在。
- r+ 以可读写方式打开文件,该文件必须存在。
- rb+ 读写打开一个二进制文件,允许读数据。
- rw+ 读写打开一个文本文件,允许读和写。
- w 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。
- w+ 打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。
a 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。(EOF符保留)
- a+ 以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾后,即文件原先的内容会被保留。 (原来的EOF符不保留)
- wb 只写打开或新建一个二进制文件;只允许写数据。
- wb+ 读写打开或建立一个二进制文件,允许读和写。
- ab+ 读写打开一个二进制文件,允许读或在文件末追加数据。
- at+ 打开一个叫string的文件,a表示append,就是说写入处理的时候是接着原来文件已有内容写入,不是从头写入覆盖掉,t表示打开文件的类型是文本文件,+号表示对文件既可以读也可以写。

上述的形态字符串都可以再加一个b字符,如rb、w+b或ab+等组合,加入b 字符用来告诉函数库以二进制模式打开文件。如果不加b,表示默认加了t,即rt,wt,其中t表示以文本模式打开文件。由fopen()所建立的新文件会具有S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH(0666)权限,此文件权限也会参考umask 值。

有些C编译系统可能不完全提供所有这些功能,有的C版本不用"r+","w+","a+",而用"rw","wr","ar"等,读者注意所用系统的规定。

二进制和文本模式的区别

1.在windows系统中,文本模式下,文件以"\r\n"代表换行。若以文本模式打开文件,并用fputs等函数写入换行符"\n"时,函数会自动在"\n"前面加上"\r"。即实际写入文件的是"\r\n" 。

2.在类Unix/Linux系统中文本模式下,文件以"\n"代表换行。所以Linux系统中在文本模式和二进制模式下并无区别

出自: 
http://www.runoob.com/cprogramming/c-function-fopen.html

# c 文件合并

基本上是同一个道理 

文件的合并


java 代码

package androidrn.myjni;
import android.os.Bundle;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;

import java.io.File;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";

    // Used to load the 'native-lib' library on application startup.
    private static String SD_CARD_PATH = Environment.getExternalStorageDirectory().getAbsolutePath();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

       // diff();
    }


    public void diff(View v){
        Log.d(TAG, "diff begin----");
        String path = SD_CARD_PATH + File.separatorChar + "myVideo.mp4";
        File file = new File(path);
        Log.d(TAG, "diff: 路径是否存在 path = "+file.exists());
        String pattern_Path = SD_CARD_PATH + File.separatorChar + "myVideo_%d.mp4";
        FileUtils.diff(path,pattern_Path,4);
        Log.d(TAG, "diff end----");
    }

    public void patch(View v){
        Log.d(TAG, "patch: begin---");
        String path = SD_CARD_PATH + File.separatorChar + "myVideo_new.mp4";
        File file = new File(path);
        Log.d(TAG, "patch: 路径是否存在 path = "+file.exists());
        String pattern_Path = SD_CARD_PATH + File.separatorChar + "myVideo_%d.mp4";
        FileUtils.patch(path,pattern_Path,4);
    }

}


package androidrn.myjni;

/**
 * @author liuml
 * @explain
 * @time 2018/8/15 20:35
 */
public class FileUtils {
    static {
        System.loadLibrary("native-lib");
    }

//    public static native void diff(String path, String pattern_path, int file_num);
    public static native void diff(String path, String pattern_path, int file_num);

    public static native void patch(String merger_path, String pattern_Path, int file_num);
}

c 代码


/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include <android/log.h>
#include <assert.h>
#include <malloc.h>
/* Header for class androidrn_myjni_FileUtils */

#define TAG "jni_LOG"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
#define NELEM(x) ((int)(sizeof(x)/sizeof((x)[0])))
//int __android_log_print(int prio, const char* tag, const char* fmt, ...)
/*
 * Class:     androidrn_myjni_FileUtils
 * Method:    diff
 * Signature: (Ljava/lang/String;Ljava/lang/String;I)V
 */
//JNIEXPORT void JNICALL Java_androidrn_myjni_FileUtils_diff
//        (JNIEnv *env, jclass jclazz, jstring path, jstring pattern_Path, jint file_num) {
//    LOGI("jna log test");
////    LOGI("JNI 动态注册");
//}

//获取文件的大小
long get_file_size(const char *path) {

    FILE *fp = fopen(path, "rb");//打开一个文件, 文件必须存在,只读运行
    fseek(fp, 0, SEEK_END);//文件指针定位到文件末尾,偏移0个字节
    long ret = ftell(fp);//函数ftell 用于得到文件位置指针当前位置相对于文件首的偏移字节数
    fclose(fp);
    return ret;
}

//拆分函数
JNIEXPORT void JNICALL native_diff
        (JNIEnv *env, jclass jclazz, jstring path, jstring pattern_Path, jint file_num) {
    LOGI("JNI native_diff begin");
    //这里是 将传入的字符串转化c的指针. path_Str 为文件地址的路径  pattern_Path_str为 分割出来的文件名字
    const char *path_Str = (*env)->GetStringUTFChars(env, path, NULL);
    const char *pattern_Path_str = (*env)->GetStringUTFChars(env, pattern_Path, NULL);

    //申请二维字符数据, 存放子文件名
    char **patches = (char **) malloc(sizeof(char *) * file_num);

    int i = 0;
    for (; i < file_num; i++) {
        //给每个文件名申请地址
        LOGI("char 和char 指针占用的字节 char = %d char * = %d", sizeof(char), sizeof(char *));
        patches[i] = (char *) malloc(sizeof(char) * 100);
        // 需要分割的文件 myVideo.mp4
        // 每个子文件名称 myVideo_n.mp4
        //sprintf 函数 格式化打印
        sprintf(patches[i], pattern_Path_str, i);//格式化为 myVideo_n.mp4 传递进来是带%d 的 所以能够格式化
        LOGI("patch path : %s", patches[i]);
    }
    //获取文件的大小
    int fileSize = get_file_size(path_Str);
    //打开文件
    LOGI("打开文件 %d", path_Str);
    FILE *fpr = fopen(path_Str, "rb");
    /*
    * 1.判断文件大小能够被 file_num整除
    * 2.能整除就平分
    * 3.不能整除就先分 file_num -1
    * */

    if (fileSize % file_num == 0) {//如果可以被传入的file_num 整除
        LOGI("刚好被整除fileSize fileSize= %d ", fileSize);
        int part = fileSize / file_num;//每个被拆分的文件大小
        for (int i = 0; i < file_num; ++i) {
            //wb 文件如果已经存在就删除,只写运行. 不存在则创建
            LOGI("外层循环 i = %d", i);
            FILE *fpw = fopen(patches[i], "wb");
            for (int j = 0; j < part; ++j) {
                //fputc 把参数 char 指定的字符(一个无符号字符)写入到指定的流 stream 中,并把位置标识符往前移动
                //fgetc 从指定的流 stream 获取下一个字符(一个无符号字符),并把位置标识符往前移动。
                // LOGI("开始读取stream j = %d", j);
                fputc(fgetc(fpr), fpw);
            }
            fclose(fpw);
        }
    } else {//不能整除,先分 file_num -1
        LOGI("不能整除 fileSize= %d ", fileSize);
        int part = fileSize / (file_num - 1);
        for (int i = 0; i < file_num - 1; i++) {
            LOGI("外层循环 i = %d", i);
            FILE *fpw = fopen(patches[i], "wb");
            for (int i = 0; i < part; i++) {
                fputc(fgetc(fpr), fpw);
            }
            fclose(fpw);
        }
        //处理最后一个
        LOGI("处理最后一个");
        FILE *fpw = fopen(patches[file_num - 1], "wb");

        for (int i = 0; i < fileSize % (file_num - 1); i++) {
            fputc(fgetc(fpr), fpw);
        }

        fclose(fpw);
    }
    LOGI("释放指针");
    fclose(fpr);

    //释放指针  记住每次malloc后 必须释放
    for (int i = 0; i < file_num; i++) {
        free(patches[i]);
    }
    free(patches);
    //同样释放
    (*env)->ReleaseStringUTFChars(env, path, path_Str);
    (*env)->ReleaseStringUTFChars(env, pattern_Path, pattern_Path_str);

}

//合并函数
JNIEXPORT void JNICALL native_patch
        (JNIEnv *env, jclass jclazz, jstring merge_path, jstring pattern_Path, jint file_num) {
    LOGI("JNI native patch begin");
    //这里是 将传入的字符串转化c的指针. path_Str 为文件地址的路径  pattern_Path_str为 分割出来的文件名字
    const char *path_Str = (*env)->GetStringUTFChars(env, merge_path, NULL);
    const char *pattern_Path_str = (*env)->GetStringUTFChars(env, pattern_Path, NULL);

    //申请二维字符数据, 存放子文件名
    char **patches = (char **) malloc(sizeof(char *) * file_num);
    LOGI("分割几部分 %d", file_num);

    int i = 0;
    for (; i < file_num; i++) {
        patches[i] = (char *) malloc(sizeof(char *) * 100);
        // 需要分割的文件 myVideo.mp4
        // 每个子文件名称 myVideo_n.mp4
        //sprintf 函数 格式化打印
        sprintf(patches[i], pattern_Path_str, i);//格式化为 myVideo_n.mp4 传递进来是带%d 的 所以能够格式化
        LOGI("patch path : %s", patches[i]);
    }
    FILE *fpw = fopen(path_Str,"wb");

    for (int i = 0; i < file_num; i++) {
        //获取拆分后的每个文件大小
        int filesize = get_file_size(patches[i]);
        FILE *fpr = fopen(patches[i],"rb");//从二维数组里面读取文件的名字
        for (int j = 0; j < filesize; j++) {
            //合并文件
            fputc(fgetc(fpr),fpw);
        }
        fclose(fpr);
    }
    fclose(fpw);

    //释放申请的空间
    for (int i = 0; i < file_num; i++) {
        free(patches[i]);//每一个malloc 对应一个free
    }
    free(patches);
    (*env)->ReleaseStringUTFChars(env, merge_path, path_Str);
    (*env)->ReleaseStringUTFChars(env, pattern_Path, pattern_Path_str);
}

static const JNINativeMethod gMethods[] = {
        {
                "diff",  "(Ljava/lang/String;Ljava/lang/String;I)V", (void *) native_diff
        },
        {
                "patch", "(Ljava/lang/String;Ljava/lang/String;I)V", (void *) native_patch
        }
};

//注册本地方法
static int registerNatives(JNIEnv *env) {

    jclass clazz;
    clazz = (*env)->FindClass(env, "androidrn/myjni/FileUtils");
    if (clazz == NULL) {
        LOGI("clazz == NULL");
        return JNI_FALSE;
    }
    LOGI("clazz != NULL");
    if ((*env)->RegisterNatives(env, clazz, gMethods, NELEM(gMethods)) < 0) {
        LOGI("RegisterNatives JNI_FALSE");
        return JNI_FALSE;
    }
    return JNI_TRUE;
}


JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    LOGI("jni onload begin");
    /* 这段代码直接拷贝系统的源码 在系统的onload里面  不过系统的是cpp 这里改造成c的*/
    JNIEnv *env = NULL;
    jint result = -1;

    if ((*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_4) != JNI_OK) {
        LOGI("ERROR: GetEnv failed\n");
        return -1;
    }
    assert(env != NULL);
    /*结束*/

    //注册
    registerNatives(env);

    return JNI_VERSION_1_4;

}


最终效果:

JNI-开发流程 so文件生成.c实现文件拆分,合并,jni中的线程

JNI 中的线程

概念:
JavaVM *g_jvm 一个进程有一个
env —>一个线程有一个

引入头文件

#include <pthread.h>

创建线程的函数

pthread_create

默认的jvm 会给创建一个env 但是自己创建的没有 所以必须调用一次

if ((*g_jvm)->AttachCurrentThread(g_jvm, &env, NULL) != JNI_OK) {

线程 直接看这里把

https://blog.csdn.net/shaohuazuo/article/details/43149193

相关标签: jni