看下android中监听一个文件状态(修改、删除之类)的使用方式:
FileObserver fileObserver=new FileObserver("filepath") {
@Override
public void onEvent(int event, String path) {
}
};
//开始监听
fileObserver.startWatching();
//停止监听
fileObserver.stopWatching();
FileObserver对文件实现的监听其实也是通过JNI方式来实现的,在FIleObserver.java中有四个native方法:
private native int init();
private native void observe(int fd);
private native int startWatching(int fd, String path, int mask);
private native void stopWatching(int fd, int wfd);
在调用fileObserver.startWatching();的时候
public void startWatching() {
if (m_descriptor < 0) {
m_descriptor = s_observerThread.startWatching(m_path, m_mask, this);
}
}
内部调用的是s_observetThread的方法,这个类在static代码块就完成了初始化操作,并调用了start方法开启线程,它本身是继承了Thread类
private static class ObserverThread extends Thread {
}
static {
s_observerThread = new ObserverThread();
s_observerThread.start();
}
public ObserverThread() {
super("FileObserver");
m_fd = init();
}
在构造函数中调用了native方法init()方法,此后利用调用start开启线程,在run方法中🈶调用了native的observe()方法。
public void run() {
observe(m_fd);
}
之后在startWatching中调用了native的第三个方法startWatching()
public int startWatching(String path, int mask, FileObserver observer) {
int wfd = startWatching(m_fd, path, mask);
Integer i = new Integer(wfd);
if (wfd >= 0) {
synchronized (m_observers) {
m_observers.put(i, new WeakReference(observer));
}
}
return i;
}
第四个native方法自然是通过fileObserver.stopWatching()来间接调用的。
我们看下这四个native方法的具体实现:
/Users/huozhenpeng/Documents/android-6.0.0_r1/frameworks/base/core/jni
找到这个路径下的android_util_FileObserver.cpp文件
打开这个文件:
这种写法是动态注册,前面也有讲过
1、
我们打算模仿着四个方法来实现对app卸载的监听,例如在用户卸载之后,自动打开一个网页手机用户反馈信息。
android_os_fileobserver_init方法只做了一件事,调用jni内置的方法inotify_init();
这里有详细介绍
https://www.ibm.com/developerworks/cn/linux/l-inotifynew/index.html
2、
第二个native方法observe()实际调用的是android_os_fileobserver_observe()方法
3、
第三个native方法startWatching方法实际调用的是android_os_fileobserver_startWatching()方法
4、
第四个native方法stopWatching方法实际调用的是android_os_fileobserver_stopWatching()方法
我们看下inotify.h这个头文件
#ifndef _UAPI_LINUX_INOTIFY_H
#define _UAPI_LINUX_INOTIFY_H
#include <linux/fcntl.h>
#include <linux/types.h>
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
struct inotify_event {
__s32 wd;
__u32 mask;
__u32 cookie;
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
__u32 len;
char name[0];
};
#define IN_ACCESS 0x00000001
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define IN_MODIFY 0x00000002
#define IN_ATTRIB 0x00000004
#define IN_CLOSE_WRITE 0x00000008
#define IN_CLOSE_NOWRITE 0x00000010
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define IN_OPEN 0x00000020
#define IN_MOVED_FROM 0x00000040
#define IN_MOVED_TO 0x00000080
#define IN_CREATE 0x00000100
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define IN_DELETE 0x00000200
#define IN_DELETE_SELF 0x00000400
#define IN_MOVE_SELF 0x00000800
#define IN_UNMOUNT 0x00002000
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define IN_Q_OVERFLOW 0x00004000
#define IN_IGNORED 0x00008000
#define IN_CLOSE (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)
#define IN_MOVE (IN_MOVED_FROM | IN_MOVED_TO)
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define IN_ONLYDIR 0x01000000
#define IN_DONT_FOLLOW 0x02000000
#define IN_EXCL_UNLINK 0x04000000
#define IN_MASK_ADD 0x20000000
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define IN_ISDIR 0x40000000
#define IN_ONESHOT 0x80000000
#define IN_ALL_EVENTS (IN_ACCESS | IN_MODIFY | IN_ATTRIB | IN_CLOSE_WRITE | IN_CLOSE_NOWRITE | IN_OPEN | IN_MOVED_FROM | IN_MOVED_TO | IN_DELETE | IN_CREATE | IN_DELETE_SELF | IN_MOVE_SELF)
#define IN_CLOEXEC O_CLOEXEC
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define IN_NONBLOCK O_NONBLOCK
#endif
这里面描述了所有的可以监听的文件的状态和一个结构体inotify_event
代码:
native-lib.cpp
#include <jni.h>
#include <string>
#include <sys/inotify.h>
#include <fcntl.h>
#include <errno.h>
//android输出日志使用
#include <android/log.h>
#define LOG_TAG "file"
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
extern "C"
JNIEXPORT void JNICALL
Java_com_example_huozhenpeng_fileobserver_MainActivity_stringFromJNI(
JNIEnv *env,
jobject job,jstring pathStr) {
pid_t pid=fork();
if(pid==0)
{
LOGE("fork子进程成功");
int fd=inotify_init();
const char *path=env->GetStringUTFChars(pathStr,NULL);
int wd = inotify_add_watch(fd, path, IN_MODIFY); //监控指定文件的ALL_EVENTS。
char event_buf[512];
struct inotify_event* event;
while (1)
{
int event_pos = 0;
int num_bytes = read(fd, event_buf, sizeof(event_buf));
LOGE("文件正在更改");
if (num_bytes < (int)sizeof(*event))
{
if (errno == EINTR)
continue;
LOGE("***** ERROR! android_os_fileobserver_observe() got a short event!");
return;
}
while (num_bytes >= (int)sizeof(*event))
{
int event_size;
event = (struct inotify_event *)(event_buf + event_pos);
jstring path = NULL;
if (event->len > 0)
{
path = env->NewStringUTF(event->name);
}
LOGE("文件有更改");
// 这个是回调到java层的,利用java代码构造FileObserver的时候不是覆写了个方法onEvent吗
// env->CallVoidMethod(object, method_onEvent, event->wd, event->mask, path);
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
}
if (path != NULL)
{
env->DeleteLocalRef(path);
}
event_size = sizeof(*event) + event->len;
num_bytes -= event_size;
event_pos += event_size;
}
}
}
std::string hello = "Hello from C++";
return ;
}
MainActivity.java
package com.example.huozhenpeng.fileobserver;
import android.os.Environment;
import android.os.FileObserver;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
public class MainActivity extends AppCompatActivity {
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
private String path;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Example of a call to a native method
TextView tv = (TextView) findViewById(R.id.sample_text);
path= getCacheDir()+ File.separator+"observer.txt";
File file=new File(path);
if(!file.exists())
{
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
FileObserver fileObserver=new FileObserver(path,FileObserver.MODIFY) {
@Override
public void onEvent(int event, String path) {
Log.e("abc","文件有修改");
}
};
// fileObserver.startWatching();
// fileObserver.stopWatching();
stringFromJNI(path);
tv.setText("abc");
}
public void write(View view)
{
writeFile(path,"文本");
}
BufferedWriter out = null;
public void writeFile(String file, String conent) {
try {
out = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(file, true)));
out.write(conent);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/
public native void stringFromJNI(String path);
}
点击写入
比如说监听到卸载之后去打开一个网页:
我们直接监听/data/data/应用包名
但是验证的结果是5.0以上不起作用,应该是与fork进程有关
if (sdk < 17) {
execlp("am", "am", "start", "-a", "android.intent.action.VIEW", "-d",
"http://www.baidu.com",NULL);
} else{
execlp("am", "am", "start","--user","0","-a", "android.intent.action.VIEW", "-d",
"http://www.baidu.com",NULL);
}