四种方式遍历SDCard

一、java语言

首先都要在清单文件中申请读、写、管理SDCard的权限

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

activity_main.xml,部分代码

<Button
android:id="@+id/btn_start"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAllCaps="false"
android:text="遍历SDCard的Download目录"
/>

<TextView
android:id="@+id/tv_show"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

MainActivity.java,部分

public class MainActivity extends AppCompatActivity
{
TextView tv;
MyHandler handler;
ArrayList<String>al;
public void updateUI01(String fp)
{
fp = fp.replace(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath,"");
if(al.size()<20)
{
al.add(fp);
}
else
{
al.remove(0);
al.add(fp);
}
StringBuffer sb = new StringBuffer();
for(int i = 0;i<al.size();i++)
{
sb.append(al.get(i)+"\r\n");
}
tv.setText(sb.toString());
}
@Override
protected void onCreate(Bundle saveInstanceStage)
{
Button btn = binding.btnsStart;
btn.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View view)
{
try
{
tv.setText("");
al.clear();
String tmpPath = Environment.getExternalStorageDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();
new MyThread(handler,tmpPath).start();
}catch(Exception e)
{
e.printStackTrace();
}
}
});
}
}

MyHandler.java

public class MyHandler extends Handler
{
private MainActivity mainActivity;
private MyHandler(MainActivity mact)
{
mainActivity = mact;
}
@Override
public void handleMessage(@NonNull Message msg)
{
super.handleMessage(msg);
switch(msg.what)
{
case 1:
{
mainActivity.updateUI01(msg.getData().getString("fp"));
break;
}
default:
{
Log.i("qqqqqqqq","未知消息类型"+msg.what);
}
}
}
}

MyThread.java

public class MyThread extends Thread
{
MyHandler myHandler;
String fp;
public MyThread(MyHandler mm,String fpath)
{
myHandler = mm;
fp = fpath;
}
private void getFiles(String path)
{
File[] allFiles = new File(path).listFiles();
if(file.isFile())
{
Message message = Message.obtain();
message.what = i;
Bundle bundle = new Bundle();
message.setData(bundle);
myHandler.sendMessage(message);
try
{
Thread.sleep(100);
}catch(InterruptedException e)
{
throw new RuntimeException(e);
}
else if(!file.getAbsolutePath().contains(".thumbnail"))
{
getFiles(file.getAbsolutePath());
}
@Override
public void run()
{
super.run();
getFiles(fp);
}
}
}
}

二、java反射

private void getFile2(Object path)
{
Class cls_File = Class.forName("java.io.File");
Constructor constructor = cls_File.getConstructor(String.class);
Object ins_file = constructor.newInstance(path);
Method method_listFiles = cls_File.getMethod("ListFile");
Method method_isFile = cls_File.getMthod("isFile");
Method method_getAbsolutePath = cls_File.getMethod("getAbsolutePath");

Class cls_String = Class.forName("java.lang.String");
Class cls_CharSequence = Class.forName("java.lang.CharSquence");
Method method_contains = cls_String.getMethod("contains",cls_CharSequence);

Object[] files = (Object[])method_isFile.invoke(ins_file);
Object obj_path = method_getAbsolutePath.invoke(ins_file);
for(Object one:files)
{
Object obj_one_path = method_getAbsolutePath.invoke(one);
Object obj_isfile = method_listFiles.invoke(one);
if((boolean)obj_isFile)
{
Class cls_Message = Class.forName("android.os.Message");
Method method_obtain = cls_Message.getMethod("obtain");
Object ins_message = method_obtain.invoke(cls_Message);
Filed filed_what = cls_Message.getField("what");
filed_what.set(ins_message,1);

Class cls_Bundle = Class.forName("android.os.Bundle");
Constructor constructor_Bundle = cls_Bundle.getConstructor();
Object ins_Bundle = constructor_Bundle.newInstance();
Method method_putString = cls_Bundle.getMethod("putString",clsString,clsString);
method_putString.invoke(ins_Bundle,"fp",obj_one_path);

Method method_setData = cls_Message.getMethod("setData",cls_Bundle);
method_setData.invoke(ins_message,ins_Bundle);
Thread.sleep(100);
}
else if(!(boolean)(method_contains.invoke(obj_one_path,".thumbnali")))
{
getFile(obj_one_path);
}
}
}

三、JNI

前置知识

//struct timeval tval; 定义了一个名为 tval 的结构体变量,其类型是 struct timeval
struct timeval {
long tv_sec; // 秒
long tv_usec; // 微秒
};
static void sleep_ms(unsigned int secs)
{
struct timeval tval;
tval.tv_sec = secs/1000;// 将毫秒转换为秒部分
tval.tv_usec = (secs*1000)%1000000; // 将毫秒转换为微秒部分,这里使用了取余运算符 % 来确保微秒数在 0 到 999999 之间。
select(0,NULL,NULL,NULL,&tval);
//select(0, NULL, NULL, NULL, &tval); 是一个系统调用,通常在 Unix-like 系统(包括 Linux)中用于多路复用 I/O 操作。在这里,它被用来实现一个简单的睡眠功能,即暂停程序的执行一段时间,直到指定的时间间隔(由 tval 结构体指定)过去为止。
/*参数解释:
第一个参数 (nfds):
在此处设为 0,表示不监视任何文件描述符(通常是输入输出设备),因为我们只是希望进行定时等待,而不需要监听任何 I/O 事件。

后续参数 (readfds, writefds, exceptfds):
这三个参数分别为读取、写入和异常文件描述符的集合,用于指定应该监视哪些文件描述符上的事件。在这里,我们将它们都设为 NULL,表示不监视任何文件描述符。

最后一个参数 (timeout):
这是一个指向 struct timeval 结构体的指针,用来指定 select 函数应该等待的时间长度。具体地,它包含了两个成员:
tv_sec:等待的秒数部分。
tv_usec:等待的微秒数部分。
在这段代码中,通过设置 &tval,即将 select 函数设定为等待 tval 所指定的时间长度。当 tval 中的时间过去后,select 函数返回,程序继续执行。*/
//功能说明:这种用法实现了一个简单的延时功能。通过调整 tval 结构体中的 tv_sec 和 tv_usec 成员,可以精确地控制程序的睡眠时间。在这个例子中,程序将会休眠 secs 毫秒(即 secs 秒加上 secs * 1000 微秒)。
}
pthread_t pid; //这行代码是在 C 或 C++ 中声明了一个变量 pid,其类型为 pthread_t。这里的 pthread_t 是 POSIX 线程库中用来表示线程的数据类型。
void* subWalkDir(void* args)
{
JNIEnv *env;
javaVM->AttachCurrentThread(&env, 0);
((PARAM*)args)->a = env;

void* ret = doWork2(args);
javaVM->DetachCurrentThread();
return ret;
void* subWalkDir(void* args):
/*这是一个函数定义,返回类型为 void*,接受一个 void* 类型的参数 args,通常用来传递函数的参数或数据。

JNIEnv *env;:
JNIEnv 是一个结构体指针,用于与 Java 虚拟机进行通信,可以通过 javaVM 访问。

javaVM->AttachCurrentThread(&env, 0);:
javaVM 是一个指向 Java 虚拟机的指针或引用。
AttachCurrentThread 函数用于将当前线程附加到 Java 虚拟机,以便在本地代码中与 Java 代码进行交互。通过此函数,可以获取当前线程的 JNIEnv 环境变量 env,从而能够调用 Java 方法和操作 Java 对象。
第二个参数 0 表示线程附加时使用默认的线程挂钩(Thread Attach Detach Support)。

((PARAM*)args)->a = env;:
将 args 强制转换为 PARAM* 类型,并将其中的 a 成员设置为当前的 env,即将 env 传递给 args 中的 a 成员。这样在后续的工作函数中可以使用 env 来与 Java 层交互。

void* ret = doWork2(args);:
调用函数 doWork2,并传递 args 参数。doWork2 函数执行具体的工作任务,可能会涉及到与 Java 层的交互或其他操作。它返回一个 void* 类型的结果,将其赋给 ret 变量。

javaVM->DetachCurrentThread();:
DetachCurrentThread 函数用于将当前线程从 Java 虚拟机中分离,结束与 Java 层的交互。这样做是为了避免资源泄漏或其他问题,确保线程的安全退出。*/
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void *reserved)
{//注意不能在里面做耗时操作
//void *reserved:这是一个保留参数,通常不会在函数体内使用。
// 获取到 JavaVM
javaVM = vm;

// 打印日志
LOGI("%s", "JNI_OnLoad");

// 返回 JNI 版本号
return JNI_VERSION_1_4;
}
//JNI_OnLoad 函数是 JNI 库加载时自动调用的初始化函数。它的主要作用是获取并保存 Java 虚拟机实例,并且可以执行一些初始化操作。返回的 JNI 版本号指示本地库需要的 JNI 接口版本。
Java_a_b_c_practice_MainActivity_walkDir(JNIEnv *env, jobject thiz, jstring dirPath_, jobject h_handler)
{
pthread_t pid; // 声明一个 pthread_t 类型的变量 pid,用来存储线程的标识符

PARAM* param = (PARAM*)malloc(sizeof(PARAM)); // 分配 PARAM 结构体的内存空间,并将指针赋给 param
param->a = env; // 将 JNIEnv 指针 env 赋给 param 结构体中的成员变量 a
param->b = env->NewGlobalRef(thiz); // 创建 thiz 对象的全局引用,并赋给 param 结构体中的成员变量 b
param->c = static_cast<jstring>(env->NewGlobalRef(dirPath_)); // 将 dirPath_ 转换为 jstring 类型,并创建其全局引用,赋给 param 结构体中的成员变量 c
param->d = env->NewGlobalRef(h_handler); // 创建 h_handler 对象的全局引用,并赋给 param 结构体中的成员变量 d

// 创建一个新线程,并调用 subWalkDir 函数执行目录遍历操作,传入 param 结构体作为参数
pthread_create(&pid, 0, subWalkDir, param);
}
DIR *dir = opendir(dirPath);
/*opendir(dirPath):
opendir 是 POSIX 标准库中用来打开目录的函数。
dirPath 是一个 const char * 类型的参数,表示要打开的目录的路径。
opendir 函数将指定路径 dirPath 的目录打开,并返回一个指向 DIR 结构体的指针 dir。

DIR *dir:
dir 是一个指向 DIR 结构体的指针,用于表示打开的目录。在 POSIX 系统中,DIR 结构体用于维护目录流的状态信息。*/
//dirent 是一个结构体,用于表示目录中的一个条目(文件或子目录
struct dirent {
ino_t d_ino; /* inode number */
off_t d_off; /* offset to the next dirent */
unsigned short d_reclen;/* length of this record */
unsigned char d_type; /* type of file; not supported by all file system types */
char d_name[256]; /* filename */
};

Eg:
#include <dirent.h>
#include <stdio.h>

int main() {
DIR *dir;
struct dirent *file;

// 打开目录
dir = opendir("/path/to/directory");
if (dir == NULL) {
perror("opendir");
return -1;
}

// 读取目录中的每一个文件条目
while ((file = readdir(dir)) != NULL) {
printf("File name: %s\n", file->d_name);
}

// 关闭目录
closedir(dir);

return 0;
}
jfieldID whatFieldID = env->GetFieldID(cls, "what", "I");
/*jfieldID 是 JNI 中表示 Java 字段的数据类型,它用于唯一标识一个特定的 Java 字段。
env 是 JNI 环境指针,可以通过它来调用 JNI 的各种函数。
GetFieldID 是 JNI 提供的一个函数,用于获取指定 Java 类中的字段的 jfieldID。
cls 是一个 jclass 对象,表示要操作的 Java 类。通常通过 GetObjectClass 函数从 Java 对象获取。
"what" 是要获取的字段的名称,这里是一个 int 类型的字段名。
"I" 是 JNI 字段描述符,表示字段类型是 int。*/

MainActivity.java,部分代码

//点击按钮响应,和上面一样
String tmpPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();
walkDir(tmpPath,handler);
public native void walkDir(String fpath, MyHandler hHandler);

native-lib.cpp

#include<jni.h>
#include<string>
#<android/log.h>
#include<dirent.h>

#define TAG "qqqqqqqq"
#define LOGD(...)__android_log_print(ANDROID_LOG_DEBUG,TAG,__VA_ARGS__);
#define LOGD(...)__android_log_print(ANDROID_LOG_INFO,TAG,__VA_ARGS__);
#define LOGW(...)__android_log_print(ANDROID_LOG_WARN,TAG,__VA_ARGS__);
#define LOGE(...)__android_log_print(ANDROID_LOG_ERROR,TAG,__VA_ARGS__);
#define LOGF(...)__android_log_print(ANDROID_LOG_FATAL,TAG,__VA_ARGS__);

const int PATH_MAX_LEGTH = 26;
JavaVM *javaVM;
struct PARAM
{
JNIEnv *a;
jobject b;
jstring c;
jobject d;
};

static void sleep_ms(unsigned int secs)
{
struct timeval tval;
tval.tv_sec = secs/1000; //将毫秒转换位秒
tval.tv_usec = (secs*1000)%1000000; //将毫秒转换为微妙部分,这里使用了取余运算符 % 来确保微妙数在0 到 999999 之间。
select(0,0,NULL,NUll,NULL,&tval);
}

void *doWork(void *args)
{
PARAM tmp = *(PARAM*)args;
JNIEnv *env = tmp.a;
jobject thiz = tmp.b;
jstring dirPath_ = tmp.c;
jobject h_handler = tmp.d;
if(dirPath_ == nullptr)
{
LOGE("dirPath is null!");
return nullptr;
}

const char *dirPath = env->GetStringUTFchars(dirPath_,nullptr);//GetStringUTFChars 是 JNIEnv 提供的一个函数,用于获取一个 Java 字符串对象的 UTF-8 编码的 C 字符串表示
//长度判断
if(strlen(dirPath) == 0)
{
LOGE("dirPath length is 0!");
return nullptr;
}
//打开文件夹读取流
DIR *dir = opendir(dirPath);
if(nullptr == dir)
{
LOGE("can not open dir, check path or permission!");
return nullptr;
}
struct dirent *file;
while((file = readdir(dir))!=nullptr)
{
//判断是不是 .或者 ..文件夹
if(strcmp(file->d_name,".") == 0||strcmp(file->d_name,"..") == 0)
{
continue;
}
char *path = new char[PATH_MAX_LEGTH];
memset(path,0,PATH_MAX_LEGTH);
strcpy(path,dirpath);
strcat(path,"/");
strcat(path,file->d_name);
jstring tDir = env->NewStringUTF(path);
//GetStringUTFChars:从 jstring 中获取 UTF-8 编码的 C 字符串,用于读取 Java 字符串数据。
//NewStringUTF:将 UTF-8 编码的 C 字符串转换为 Java 字符串对象,用于创建新的 Java 字符串。
if(file->d_type == DT_DIR) //DT_DIR 是 <dirent.h> 头文件中定义的常量,表示目录的类型。
{
PARAM param;
param.a = env;
param.b = thiz;//这个thiz不理解
param.c = tDir;
param.d = h_handler;
doWork(&param);
}
else
{
//打印文件名
LOGI("%s",path);
jclass cls = env->FindClass("android/os/Message");
jmethodID obtainMethod = env->GetStaticMethodID(cls,"obtain","()Landroid/os/Message;");
jobject ins_Message = env->CallStaticObjectMethod(cls,obtainMethod);

jfieldID whatFieldID = env->GetFiledID(cls,"what","I");
//参数解释
/*env:这是一个 JNIEnv* 类型的指针,它代表了JNI环境。
cls:这是一个 jclass 类型的对象,表示要操作的Java类的类对象。
"what":这是要获取字段ID的字段名,这里是 "what"。
"I":这是字段的签名,其中 "I" 表示这是一个整数类型的字段(Java中的 int)*/
env->SetIntField(ins_Message,whatFieldID,1);
jobject ins_bundle = env->NewObject(cls_bundle,method_init_bundle);
//clazz:表示要实例化的Java类的 jclass 对象,即类的句柄或引用。methodID:表示要调用的构造方法的 jmethodID 对象,它标识了要使用的构造方法。
jmethodID method_putString = env->GetMethodID(cls_bundle,"putString","(Ljava/lang/String;Ljava/lang/String;)V");
jstring jstr_key = env->NewStringUTF("fp");
evn->CallVoidMethod(ins_bundle,method_putString,jstr_key,tDir);

jmethodID method_setData = env->GetMethodID(cls,"setData","(Landroid/os/Bundle;)V");
env->CallVoidMethod(ins_Message,method_setData,ins_bundle);

jclass jcls_handler = env->GetObject(h_handler);
jmethodID method_sendMessage = env->GetMethodID(jcls_handler,"sendMessage","(Landroid/os/Message;)Z");
env->CallBooleanMethod(h_handler,method_sendMessage,ins_Message);
sleep_ms(100);
}
//释放文件内存路径
delete path;
}
//关闭读取流
closedir(dir);
env->ReleaseStringUTFChars(dirPath_,dirPath);
}
void* subWalkDir(void* args)
{
JNIEnv *env;
javaVM->AttachCurrentThread(&env,0);
((PARAM*)args)->a = env;
void *ret = doWork(args);
javaVM ->DetachCurrentThread();
return ret;
}
extern "C"
JNIEXPORT void JNICALL
MainActivity.waklDri(JNIEnv *env,jobject thiz,jstring dirPath_, jobject h_handler)
{
pthread_t pid;//pthread_t 是用来表示线程的数据类型
PARAM* param = (PARAM*)malloc(sizeof(PARAM));
param->a = env;
param->b = env->NewGloblaRef(thiz);
param->c = static_cast<jsting>(env->NewGlobalRef(dirPath_));////static_cast 是 C++ 中的一种类型转换操作符,用于执行编译时的类型转换
//在 JNI(Java Native Interface)中,env 是指向 JNI 环境的指针,NewGlobalRef 函数用于创建一个全局引用,确保对象在 Java 层面不会被垃圾回收器回收,直到显式释放为止
param->d = env->NewGlobalRef(h_handler);

pthread_create(&pid,0,subWalkDir,param);
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm,void *reserved)
{
//获取到JavaVM
javaVM = vm;
LOGI("%s","JNI_OnLoad");
return JNI_VERSION_1_4;
}

四、ndk反射(和JNI差距不大)

MainActivity.java,部分代码

public native void walkDir2(String fpath, MyHandler hHandler);
String tmpPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();
walkDir2(tmpPath,handler);

native-lib.cpp

#include<jni.h>
#include<string>
#include<android/log.h>
#include<dirent.h>
#define TAG "qqqqqqqq"
#define LOGD(...)__android_log_print(ANDROID_LOG_DEBUG,TAG,__VA_ARGS__);
#define LOGI(...)__android_log_print(ANDROID_LOG_INFO,TAG,__VA_ARGS__);
#define LOGW(...)__android_log_print(ANDROID_LOG_WARN,TAG,__VA_ARGS__);
#define LOGE(...)__android_log_print(ANDROID_LOG_ERROR,TAG__VA_ARGS__);
#define LOGF(...)__android_log_print(ANDROID_LOG_FATAL,TAG__VA__ARGS__);
const int PATH_MAX_LEGTH = 26;
JavaVM *javaVM;
struct PARAM
{
JNIEnv *a;
jobject b;
jstring c;
jstring d;
};

static void sleep_ms(unsigned int secs)
{
struct timeval tval;
tval.tv_sec = secs/1000;
tval.tv_usec = (secs*1000)%1000000;
select(0,NULL,NULL,NULL,&tval);
}

void *doWork2(void *args)
{
PARAM tmp = *(PARAM*)args;
JNIEnv *env = tmp.a;
jobject thiz = tmp.b;
jstring dirPath_ = tmp.c;
jobject h_handler = tmp.d;
if(dirPath_ == nullptr)
{
LOGE("dirPath is null");
return nullptr;
}
const char *dirPaht = env->GetStringUTFChars(dirPath_,nullptr);
//长度判断
if(strlen(dirPath) == 0)
{
LOGE("dirPath length is 0!");
return nullptr;
}

jclass cls_File = env->FindClass("java/io/File");
jmethodID methodID_init_File = env->GetMethodID(cls_File,"<init>","(Ljava/lang/String;)V");
//<init> 是构造方法的特殊名称,用于表示构造方法。
jobject obj_File = env->NewObject(cls_File,methodID_init_File,dirPath);

jmethodID methodID_listFiles_File = env->GetMethod(cls_File,"listFiles","()Ljava/io/File;");
jobjectArray ins_Files = (jobjectArray)(env->CallObjectMethod(obj_File,methodID_listFiles_File));
jsize size = env->GetArrayLength(ins_Files);

jmethodID methodID_isFile_File = env->GetMethodID(cls_File,"isFile","()Z");
jmethodID methoID_getAbsolutePath_File = env->GetMethod(cls_File,"getAbsolutePath","()Ljava/lang/String;");

jclass cls_String = env->FindClass("java/lang/String");
jmethodID methodID_contains_String = env->GetMethodID(cls_String,"contains","(Ljava/lang/CharSequence;)Z");

jstring jstring_tmp = env->NewStringUTF(".thumnail");

for(int i=0;i<(size);i++)
{
jobject jobject_InsFile = env->GetObjectArrayElement(ins_Files,i);
jboolean isfile = env->CallBooleanMethod(jobject_InsFile,methodID_isFile_File);

jobject jstring_fpath = env->CallObjectMethod(jobject_InsFile,methodID_getAbsolutePath_File);
jboolean iscontains = env->CallBooleanMethod((jstring)jstring_fpah,methodID_contains_String,jstring_tmp);

if(isfile)
{
jclas cls = env->FindClass("android/os/Message");
jmethodID obtainMethod = env->GetStaticMethodID(cls,"obtain","()Landroid/os/Message;");
jobject ins_Message = env->CallStaticObjectMethod(cls,obtainMethod);

jfileID whatField = emv->GetFileID(cls,"what","1");
env->SetIntField(ins_Message,whatField,1);

jclass cls_bundle = env->FindClass("android/os/Bundle");
jmethodID method_init_bundle = env->GetMethodID(cls_bundle,<init>,"()V");
jobject ins_bundle = env->NewObject(cls_bundle, method_init_bundle);

jmethodID method_putString = env->GetMethodID(cls_bundle,"putString","(Ljava/lang/String;Ljava/lang/String)V");
jstring jstr_key = env->NewStringUTF("fp");
env->CallVoidMethod(ins_bundle,method_putString,jstr_key,(jstring)jstring_fpath);

jmethodID method_setData = env->GetMethodID(cls,"setData","(Landroid/os/Bundle;)V");
env->CallVoidMehtod(ins_Message,method_setData,ins_bundle);

jclass jcls_handler = env->GetObjectClass(h_handler);
jmethodID method_sendMessage = env->GetMethodID(jcls_handler,"sendMessage","(Landroid/os/Message;)Z");

env->CallBooleanMethod(h_handler,method_sendMessage,ins_Message);

sleep_ms(100);
}
else if(!iscontains)
{
PARAM param;
param.a = env;
param.b = thiz;
param.c = (jstring)jstring_fpath;
param.d = h_handler;
doWork2(&param);
}
}

env->ReleaseStringUTFChars(dirPath_,dirPath);
return nullptr;



}
void *subWalkDir(void* args)
{
JNIE *env;
javaVM->AttachCurrentThread)(&env,0);
/*&env:这是一个 JNIEnv* 类型的指针,用于接收当前线程附加后的JNI环境。
0:这个参数是一个可选的 void* 类型的数据指针,用于在线程附加时传递额外的数据。在这里,使用 0 表示不传递任何额外数据。*/
((PARAM*)args)->a = env;

void *ret = doWork2(args);
javaVM->DetachCurrentThread();
return ret;
}

extern "C"
JNIEXPORT void JNICALL
Java_a_b_c_practice_MainActivity_walkDir(JNIEnv *env, jobject thiz, jstring dirPath_, jobject h_handler)
{
pthread_t pid;
PARAM *param = (PARAM*)malloc(sizeof(PARAM));
param->a = env;
param->b = env->NewGlobalRes(thiz);
param->c = static_cast<jstring>(env->NewGlobalRef(dirPath_));
param->d = env->NewGlobalRef(h_handler);
pthread_create(&pid,0,sunWalkDir,param);
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm,void *reserved)
{
//获取到JavaVM
javaVm = vm;
LOGI("%s","JNI_OnLoad");
return JNI_VERSION_1_4;
}