第二十五节(关于文件状态的监听)

看下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文件

《第二十五节(关于文件状态的监听)》 image.png

打开这个文件:

《第二十五节(关于文件状态的监听)》 image.png

这种写法是动态注册,前面也有讲过

1、

我们打算模仿着四个方法来实现对app卸载的监听,例如在用户卸载之后,自动打开一个网页手机用户反馈信息。
android_os_fileobserver_init方法只做了一件事,调用jni内置的方法inotify_init();

《第二十五节(关于文件状态的监听)》 image.png

这里有详细介绍

https://www.ibm.com/developerworks/cn/linux/l-inotifynew/index.html

《第二十五节(关于文件状态的监听)》 image.png
《第二十五节(关于文件状态的监听)》 image.png
《第二十五节(关于文件状态的监听)》 image.png

2、

第二个native方法observe()实际调用的是android_os_fileobserver_observe()方法

《第二十五节(关于文件状态的监听)》 image.png

3、

第三个native方法startWatching方法实际调用的是android_os_fileobserver_startWatching()方法

《第二十五节(关于文件状态的监听)》 image.png

4、

第四个native方法stopWatching方法实际调用的是android_os_fileobserver_stopWatching()方法

《第二十五节(关于文件状态的监听)》 image.png

我们看下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);
}

点击写入

《第二十五节(关于文件状态的监听)》 image.png
《第二十五节(关于文件状态的监听)》 image.png

比如说监听到卸载之后去打开一个网页:
我们直接监听/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);
                }
    原文作者:最美下雨天
    原文地址: https://www.jianshu.com/p/4e2defadeed4
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞