吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2129|回复: 0
收起左侧

[Java 转载] android JNI开发C++代码对assets文件的访问

[复制链接]
放假几天 发表于 2022-3-16 11:38
本帖最后由 wushaominkk 于 2022-3-17 13:51 编辑

Android应用往往会有很多资源文件需要使用,这些资源文件一般会放在assets目录编进apk中,当apk中使用了so库的时候,由于编进apk的资源文件没有固定的目录,因此不能在C层通过设置路径的方式读取;这里提供两种C层读取资源文件的方式,仅供参考。
# 方法一(通过绝对路径访问)
由于在apk的安装过程中assets中的文件并没有从apk包中解压出来,所以在JNI的C++代码中不能按照原始的路径直接进行访问,一种常用的方法为将assets中的文件复制到sdcard的目录下,然后传递绝对路径给JNI中的C++代码中进行访问。
#### 将assets中的文件复制到sdcard的目录下 工具类
[C++] 纯文本查看 复制代码
```
package com.zili.rtk.activity;
/**
* copy the files and folders of assets to sdCard to ensure that we can read files in JNI part
* @AuThor Qinghao Hu
* @date   2015/9/22
* @version 1.0
* @Email [email]qinghao.hu@nlpr.ia.ac.cn[/email]
*/

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.content.res.AssetManager;
import android.util.Log;

public class AssetCopyer {

    private static String TAG="AssetCopyer";

    /**
     * copy all the files and folders to the destination
     * @Param context  application context
     * @param destination the destination path
     */
    public  static void copyAllAssets(Context context,String destination)
    {
        copyAssetsToDst(context,"",destination);
    }
    /**
     *
     * @param context :application context
     * @param srcPath :the path of source file
     * @param dstPath :the path of destination
     */
    private  static void copyAssetsToDst(Context context,String srcPath,String dstPath) {
        try {
            String fileNames[] =context.getAssets().list(srcPath);
            if (fileNames.length > 0)
            {
                File file = new File(dstPath);
                file.mkdirs();
                for (String fileName : fileNames)
                {
                    if(srcPath!="")
                    {
                        copyAssetsToDst(context,srcPath + "/" + fileName,dstPath+"/"+fileName);
                    }else{
                        copyAssetsToDst(context, fileName,dstPath+"/"+fileName);
                    }
                }
            }else
            {
                InputStream is = context.getAssets().open(srcPath);
                FileOutputStream fos = new FileOutputStream(new File(dstPath));
                byte[] buffer = new byte[1024];
                int byteCount=0;
                while((byteCount=is.read(buffer))!=-1) {
                    fos.write(buffer, 0, byteCount);
                }
                fos.flush();//刷新缓冲区
                is.close();
                fos.close();
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

```
把assets中的文件中的文件复制到sdcard的目录文件夹下,调用方式为:
```
AssetCopyer.copyAllAssets(this.getApplicationContext(),this.getExternalFilesDir(null).getAbsolutePath());
```
sdcard的目录
```
/storage/emulated/0/Android/data/com.zili.rtk/files
```
目录结构 (assets文件夹在main 下面)
![](/media/202203/2022-03-15_203721_825469.png)

# 方法二 (C层获取AAssetManager指针)
JNI作为C和Java的桥梁可以完成许多工作,因此第一种方法可以通过Java层向C层传一个资源文件类对象,然后C层使用JNI自带的函数来获取,并读取资源内容,如下:

//在assets文件夹下放一个test.txt文件,内容如下:
```
**************************
this is a assets test.
**************************
```
首先,定义native方法:
```
public native int readResourceFile (AssetManager assetManager, String fileName);
```
fileName为放在assets目录下的文件名,assetManager对象可以通过如下代码获得:
```
AssetManager mAssetManager = getAssets();
```
于是Java层可以调用方法
```
result = readResourceFile(mAssetManager, "test.txt");
```
接下来需要看看JNI层代码:
[C++] 纯文本查看 复制代码
```

#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>

extern "C"
JNIEXPORT jint JNICALL
Java_com_camera2glview_chauncy_1wang_readassets_MainActivity_readResourceFile(JNIEnv *env, jobject instance, jobject assetManager, jstring fileName_)
{
        // TODO
        AAssetManager *mAssetManager = AAssetManager_fromJava (env, assetManager);
        if (NULL == mAssetManager)
        {
                LOGCATE("mAssetManager is NULL");
                return -1;
        }
        const char *fileName = env->GetStringUTFChars(fileName_, 0);
        if (NULL == fileName){
                LOGCATE("fileName is NULL");
                return -1;
        }
        LOGCATD ("fileName is %s", fileName);
        AAsset *asset = AAssetManager_open (mAssetManager, fileName, AASSET_MODE_UNKNOWN);
        if (NULL == asset){
                LOGCATE("asset is NULL");
                return -1;
        }
        off_t bufferSize = AAsset_getLength(asset);
        LOGCATV("buffer size is %ld", bufferSize);

        char *buffer = (char*)malloc(bufferSize + 1);
        if (NULL == buffer){
                LOGCATE("buffer memory alloc failed");
                return -1;
        }
        memset(buffer, 0, bufferSize + 1);
        int numBytesRead = AAsset_read(asset, buffer, bufferSize);
        LOGCATV("numBytesRead: %d, buffer: %s", numBytesRead, buffer);
        //readSettingFile(buffer);
        if (buffer){
                free(buffer);
                buffer = NULL;
        }
        AAsset_close(asset);
        env->ReleaseStringUTFChars(fileName_, fileName);
        return 0;
}
```

主要的几个函数:
---------------------- AAssetManager_fromJava
---------------------- AAssetManager_open;
---------------------- AAsset_getLength;
---------------------- AAsset_read;
---------------------- AAsset_close;
Log显示如下:
**************************
this is a assets test.
**************************
注:这种方式需要用到android库,在CMakeList.txt中需要添加依赖库!!
```
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
```
# 方法三 (Java层读数据传到C层)
第二种方式为在Java层读取文件,然后将这个buffer传给C层,这种方式其实就是在C层获取Java层读取资源的buffer块,然后存在C层分配的一个内存块中;提供一段参考代码:
java层类定义:
[JavaFX] 纯文本查看 复制代码
```
public static class ResourceData {
        public byte[] test= null;
        int test_bytes = 0;
    }
```

JNI层代码
[C++] 纯文本查看 复制代码
```
       
        jclass resource_data_cls = env->GetObjectClass(resourceData);
    jfieldID fid_test = env->GetFieldID(resource_data_cls, "test", "[B");
    jfieldID fid_test_bytes = env ->GetFieldID(resource_data_cls, "test_bytes ", "I");
   
    jbyteArray jtestArr = (jbyteArray)env->GetObjectField(resourceData, fid_test );
    if (NULL == jtestArr) {
        LOGCATE ("nativeInit, jbyteArray is NULL");
        return -1;
    }
    // get byte from java
    jbyte *byteTest = NULL;
    byteTest = env->GetByteArrayElements(jtestArr , NULL);
    // get length
        int test_bytes = env->GetArrayLength(jtestArr );  
       
    // free memory
    if (jtestArr ){
        env->ReleaseByteArrayElements(jtestArr , byteTest , JNI_ABORT);
        byteTest = NULL;
        jtestArr = NULL;
    }
```

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-25 08:27

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表