Android 中关于 FileObserver类监听文件状态的实践

2023-05-16

文章目录

  • 需求背景
  • 走进源码
  • 实现示例
  • 参考

需求背景

当某一个目录的文件发生变化(创建、修改、删除、移动)时,需要给一个回调事件给其他端调用。
其他场景:阅后即焚等等。

比如在 Android 的 VR 设备中,有一个用于部署的文件,在Android 系统中发生变化时,需要给 Unity 端的一个回调,Unity 端基于该回调做相应的操作。

涉及到的技术点:

Unity 和 Android 端的数据交互,Android系统中 接口的设计、以及 AIDL 跨进程的通信等等,此处不在展开,后期再更新。本文只介绍一下,文件监听的使用及注意事项。

android.os下的FileObserver类是一个用于监听文件访问、创建、修改、删除、移动等操作的监听器,基于linux的inotify。

FileObserver 是个抽象类,必须继承它才能使用。每个FileObserver对象监听一个单独的文件或者文件夹,如果监视的是一个文件夹,那么文件夹下所有的文件和级联子目录的改变都会触发监听的事件。

所能监听的事件类型如下:

  • ACCESS,即文件被访问
  • MODIFY,文件被 修改
  • ATTRIB,文件属性被修改,如 chmod、chown、touch 等
  • CLOSE_WRITE,可写文件被 close
  • CLOSE_NOWRITE,不可写文件被 close
  • OPEN,文件被 open
  • MOVED_FROM,文件被移走,如 mv
  • MOVED_TO,文件被移来,如 mv、cp
  • CREATE,创建新文件
  • DELETE,文件被删除,如 rm
  • DELETE_SELF,自删除,即一个可执行文件在执行时删除自己
  • MOVE_SELF,自移动,即一个可执行文件在执行时移动自己
  • CLOSE,文件被关闭,等同于(IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)
  • ALL_EVENTS,包括上面的所有事件

走进源码

/**
	FileObserver 类是一个用于监听文件访问、创建、修改、删除、移动等操作的监听器,基于linux的inotify。
    FileObserver 是个抽象类,必须继承它才能使用。
	每个FileObserver对象监听一个单独的文件或者文件夹,如果监视的是一个文件夹,那么文件夹下所有的文件和级联子目录的改变都会触发监听的事件。
**/
public abstract class FileObserver {
    /** @hide */
    @IntDef(flag = true, value = {
            ACCESS,
            MODIFY,
            ATTRIB,
            CLOSE_WRITE,
            CLOSE_NOWRITE,
            OPEN,
            MOVED_FROM,
            MOVED_TO,
            CREATE,
            DELETE,
            DELETE_SELF,
            MOVE_SELF
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface NotifyEventType {}

    /** Event type: Data was read from a file */
    public static final int ACCESS = 0x00000001;
    /** Event type: Data was written to a file */
    public static final int MODIFY = 0x00000002;
    /** Event type: Metadata (permissions, owner, timestamp) was changed explicitly */
    public static final int ATTRIB = 0x00000004;
    /** Event type: Someone had a file or directory open for writing, and closed it */
    public static final int CLOSE_WRITE = 0x00000008;
    /** Event type: Someone had a file or directory open read-only, and closed it */
    public static final int CLOSE_NOWRITE = 0x00000010;
    /** Event type: A file or directory was opened */
    public static final int OPEN = 0x00000020;
    /** Event type: A file or subdirectory was moved from the monitored directory */
    public static final int MOVED_FROM = 0x00000040;
    /** Event type: A file or subdirectory was moved to the monitored directory */
    public static final int MOVED_TO = 0x00000080;
    /** Event type: A new file or subdirectory was created under the monitored directory */
    public static final int CREATE = 0x00000100;
    /** Event type: A file was deleted from the monitored directory */
    public static final int DELETE = 0x00000200;
    /** Event type: The monitored file or directory was deleted; monitoring effectively stops */
    public static final int DELETE_SELF = 0x00000400;
    /** Event type: The monitored file or directory was moved; monitoring continues */
    public static final int MOVE_SELF = 0x00000800;

    /** Event mask: All valid event types, combined */
    @NotifyEventType
    public static final int ALL_EVENTS = ACCESS | MODIFY | ATTRIB | CLOSE_WRITE
            | CLOSE_NOWRITE | OPEN | MOVED_FROM | MOVED_TO | DELETE | CREATE
            | DELETE_SELF | MOVE_SELF;

    private static final String LOG_TAG = "FileObserver";

    private static class ObserverThread extends Thread {
        private HashMap<Integer, WeakReference> m_observers = new HashMap<Integer, WeakReference>();
        private int m_fd;

        public ObserverThread() {
            super("FileObserver");
            m_fd = init();
        }

        public void run() {
            observe(m_fd);
        }

        public int[] startWatching(List<File> files,
                @NotifyEventType int mask, FileObserver observer) {
            final int count = files.size();
            final String[] paths = new String[count];
            for (int i = 0; i < count; ++i) {
                paths[i] = files.get(i).getAbsolutePath();
            }
            final int[] wfds = new int[count];
            Arrays.fill(wfds, -1);

            startWatching(m_fd, paths, mask, wfds);

            final WeakReference<FileObserver> fileObserverWeakReference =
                    new WeakReference<>(observer);
            synchronized (m_observers) {
                for (int wfd : wfds) {
                    if (wfd >= 0) {
                        m_observers.put(wfd, fileObserverWeakReference);
                    }
                }
            }

            return wfds;
        }

        public void stopWatching(int[] descriptors) {
            stopWatching(m_fd, descriptors);
        }

        @UnsupportedAppUsage
        public void onEvent(int wfd, @NotifyEventType int mask, String path) {
            // look up our observer, fixing up the map if necessary...
            FileObserver observer = null;

            synchronized (m_observers) {
                WeakReference weak = m_observers.get(wfd);
                if (weak != null) {  // can happen with lots of events from a dead wfd
                    observer = (FileObserver) weak.get();
                    if (observer == null) {
                        m_observers.remove(wfd);
                    }
                }
            }

            // ...then call out to the observer without the sync lock held
            if (observer != null) {
                try {
                    observer.onEvent(mask, path);
                } catch (Throwable throwable) {
                    Log.wtf(LOG_TAG, "Unhandled exception in FileObserver " + observer, throwable);
                }
            }
        }

        private native int init();
        private native void observe(int fd);
        private native void startWatching(int fd, String[] paths,
                @NotifyEventType int mask, int[] wfds);
        private native void stopWatching(int fd, int[] wfds);
    }

    @UnsupportedAppUsage
    private static ObserverThread s_observerThread;

    static {
        s_observerThread = new ObserverThread();
        s_observerThread.start();
    }

    // instance
    private final List<File> mFiles;
    private int[] mDescriptors;
    private final int mMask;

    /**
     * Equivalent to FileObserver(path, FileObserver.ALL_EVENTS).
     *
     * @deprecated use {@link #FileObserver(File)} instead.
     */
    @Deprecated
    public FileObserver(String path) {
        this(new File(path));
    }

    /**
     * Equivalent to FileObserver(file, FileObserver.ALL_EVENTS).
     */
    public FileObserver(@NonNull File file) {
        this(Arrays.asList(file));
    }

    /**
     * Equivalent to FileObserver(paths, FileObserver.ALL_EVENTS).
     *
     * @param files The files or directories to monitor
     */
    public FileObserver(@NonNull List<File> files) {
        this(files, ALL_EVENTS);
    }

    /**
     * Create a new file observer for a certain file or directory.
     * Monitoring does not start on creation!  You must call
     * {@link #startWatching()} before you will receive events.
     *
     * @param path The file or directory to monitor
     * @param mask The event or events (added together) to watch for
     *
     * @deprecated use {@link #FileObserver(File, int)} instead.
     */
    @Deprecated
    public FileObserver(String path, @NotifyEventType int mask) {
        this(new File(path), mask);
    }

    /**
     * Create a new file observer for a certain file or directory.
     * Monitoring does not start on creation!  You must call
     * {@link #startWatching()} before you will receive events.
     *
     * @param file The file or directory to monitor
     * @param mask The event or events (added together) to watch for
     */
    public FileObserver(@NonNull File file, @NotifyEventType int mask) {
        this(Arrays.asList(file), mask);
    }

    /**
     * Version of {@link #FileObserver(File, int)} that allows callers to monitor
     * multiple files or directories.
     *
     * @param files The files or directories to monitor
     * @param mask The event or events (added together) to watch for
     */
    public FileObserver(@NonNull List<File> files, @NotifyEventType int mask) {
        mFiles = files;
        mMask = mask;
    }

    protected void finalize() {
        stopWatching();
    }

    /**
     * Start watching for events.  The monitored file or directory must exist at
     * this time, or else no events will be reported (even if it appears later).
     * If monitoring is already started, this call has no effect.
     */
    public void startWatching() {
        if (mDescriptors == null) {
            mDescriptors = s_observerThread.startWatching(mFiles, mMask, this);
        }
    }

    /**
     * Stop watching for events.  Some events may be in process, so events
     * may continue to be reported even after this method completes.  If
     * monitoring is already stopped, this call has no effect.
     */
    public void stopWatching() {
        if (mDescriptors != null) {
            s_observerThread.stopWatching(mDescriptors);
            mDescriptors = null;
        }
    }

    /**
     * The event handler, which must be implemented by subclasses.
     *
     * <p class="note">This method is invoked on a special FileObserver thread.
     * It runs independently of any threads, so take care to use appropriate
     * synchronization!  Consider using {@link Handler#post(Runnable)} to shift
     * event handling work to the main thread to avoid concurrency problems.</p>
     *
     * <p>Event handlers must not throw exceptions.</p>
     *
     * @param event The type of event which happened
     * @param path The path, relative to the main monitored file or directory,
     *     of the file or directory which triggered the event.  This value can
     *     be {@code null} for certain events, such as {@link #MOVE_SELF}.
     */
    public abstract void onEvent(int event, @Nullable String path);
}

源码解读及注意事项:

相关实现类并不复杂,代码也不多,这里可以完整看一下,学习一下实现原理。

  1. ALL_EVENTS 这个事件由 “|”位运算实现,位运算相关知识回顾。这里用或运算,后面在监听时的回调 onEvent会用到。

    符号描述运算规则
    &两个位都为1时,结果才为1
    |两个位都为0时,结果才为0
    ^异或两个位相同为0,相异为1
    ~取反0变1,1变0
    <<左移各二进位全部左移若干位,高位丢弃,低位补0
    >>右移各二进位全部右移若干位,对无符号数,高位补0,有符号数,各编译器处理方法不一样,有的补符号位(算术右移),有的补0(逻辑右移)
  2. onEvent的回调事件处理中,我们得注意 用 "&"来监听,否则会出现返回未确定定义的 event type.这里其实不是bug.是我们用错的方式。

    @Override
    public void onEvent(int event, String path) {
        Log.d(TAG, "event: " + event);
        /* event的值是与 0x40000000 进行或运算后的值,所以在 case 之前需要先和 FileObserver.ALL_EVENTS进行与运算*/
        int e = event & FileObserver.ALL_EVENTS;
        switch (e) {
            case FileObserver.CREATE:
                break;
             case FileObserver.DELETE:
                break;
        }
    }
    
    

    如果不做 与&运算,你会得到以下的测试数字,以为是 bug. 其实不是。我们了解一下位运算就知道了。

类型值含义
1073742080“文件夹”的创建(Create)操作
1073742336“文件夹”的删除(Delete)操作
1073741888“文件夹”的移出(MOVE_FROM) 操作
1073741952“文件夹”的移入(MOVE_TO) 操作
32768“文件夹” 的打开操作 (OPEN) 操作
在这里插入图片描述

实现示例

FileObserver是一个抽象类,使用的时候我们需要自己实现一个类来继承FileObserver。

/**
 * <pre>
 *     @author : JuneYang
 *     time   : 2023/01/20
 *     desc   :
 *     version: 1.0
 * </pre>
 */
public class SDCardFileObServer extends FileObserver {
    public static final String TAG = SDCardFileObServer.class.getSimpleName();

    public SDCardFileObServer(String path) {
        /*
         * 这种构造方法是默认监听所有事件的,如果使用 super(String,int)这种构造方法,
         * 则int参数是要监听的事件类型.
         */
        super(path);
    }

    @RequiresApi(api = Build.VERSION_CODES.Q)
    public SDCardFileObServer(@NonNull File file, int mask) {
        super(file, mask);
    }

    @Override public void onEvent(int event, @Nullable String path) {
        //注意点
        int e = event & FileObserver.ALL_EVENTS;
        switch (e) {
            case FileObserver.CREATE:
                break;
            case FileObserver.DELETE:
                break;
           case FileObserver.MODIFY:
                break;
            default:
                break;
        }
    }

    // 调用
    public static void main(String[] args) {
        String path = "xx/xx/xx";
        // 初始化操作
        SDCardFileObServer sdCardFileObServer = new SDCardFileObServer(path);
        sdCardFileObServer.startWatching();

        // 服务结束后关闭监听
        sdCardFileObServer.stopWatching();
    }

测试用例:
以监听某个目录为例,当目录下发生文件的状态变化时,测试情况如下:

  1. 拷贝文件时,如果文件过大,modify 方法会每 50ms 左右回调一次接口,因为文件在一直变化,直到不再变化为止。
  2. 替换文件时,会回调 deletecreatemodify 方法。
  3. 该路径下的两个文件如果执行拷贝、删除、替换,有几个文件就会执行几个文件的几种状态的回调。
  4. 文件夹删除时也会执行删除 delete回调,文件夹新建时会有 create 回调.
  5. 文件夹合并时不会有回调

Tips: 在项目中,由于 FileObserver对象必须保持一个引用,确保不被垃圾收集器回收掉,否则就不会触发事件。我们可以考虑使用 Service 服务。

也就是说在 Service 中的 Oncreate中初始化(startWatching),在OnDestory中(stopWatching)。

参考

位运算在Java编程中的应用

Android中巧妙的位运算_钟秀的博客-CSDN博客_android 视图标志位 或运算

Android系统中Flag的位操作设计

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android 中关于 FileObserver类监听文件状态的实践 的相关文章

  • selenium 中 css-寻找元素

    等同于 tag名 不改变 elements 61 wd find elements by css selector 39 div 39 elements 61 wd find elements by tag name 39 div 39 i
  • 解决 The following packages have unmet dependencies: 问题

    The following packages have unmet dependencies libvtk5 dev Depends libfreetype6 dev but it is not going to be installed
  • 2.1Ubuntu20.4安装QT5.14.2

    QT简介 xff1a Qt是一个跨平台的C 43 43 图形用户界面库 xff0c 我们平时所说所使用的Qt xff0c 准确的来说是它的GUI编程部分 Qt提供给应用程序开发者建立图形用户界面所需要的功能 xff0c 并且Qt很容易扩展
  • 美国出台最严技术出口管制!14项前沿科技面临封锁

    关注ITValue xff0c 查看企业级市场最新鲜 最具价值的报道 xff01 xff08 本文转载自量子位公众号 xff0c ID xff1a QbitAI xff0c 作者 xff1a 乾明 夏乙 问耕 xff09 美国又打出一套七伤
  • sftp文件上传详解

    JSch是Java Secure Channel的缩写 JSch是一个SSH2的纯Java实现 它允许你连接到一个SSH服务器 xff0c 并且可以使用端口转发 xff0c X11转发 xff0c 文件传输等 xff0c 当然你也可以集成它
  • 数据库设计 ER图

    一 E R图构成要素 E R图也称实体 联系图 Entity Relationship Diagram xff0c 提供了表示实体类型 属性和联系的方法 xff0c 用来描述现实世界的概念模型 它是描述现实世界关系概念模型的有效方法 是表示
  • ssh-keygen -t rsa详解

    ssh keygen q 安静模式 b bits 位数 t dsa ecdsa ed25519 rsa rsa1 加密算法 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 6
  • samba源码编译安装(版本4.13.0)

    目录 前言编译安装samba下载三个包安装依赖共需要安装samba gnutls nettle1 进入解压后的samba目录执行 xff1a 2 进入解压后的gnutls目录执行3 进入解压后的nettle目录执行3 1 卸载已存在nett
  • mybatis+postgresql10插入返回主键ID

    MyBatis如果使用useGeneratedKeys去生成自增列会造成不成功 xff0c 因为官方提供只支持这些数据库 xff1a mybatis generatedkeys xff0c 那么如果要用在oracle和postgresql上
  • Mac 环境下使用 homebrew 安装和彻底卸载 MySQL5.7

    一 安装 MySQL5 7 在终端执行下列命令 xff1a brew install mysql 64 5 7 使用 brew 安装好 MySQL 5 7 版本后 xff0c 此时 MySQL root 用户的密码是空的 我们需要修改一下
  • python 爬取网站首页并获取资源文件

    一 使用requests模块 xff0c 如果没有安装请使用如下命令 xff0c 安装requests模块 pip install requests 二 打开PyCharm xff0c 创建一个新的py文件 1 请求网站 xff0c 获取网
  • github pages部署静态网页

    github pages部署静态网页 如果你的项目只是一个静态网站 xff0c 就没有必要再去整什么服务器 xff0c github pages 提供了搭建静态网站的功能 xff1b 为什么使用Github Pages 1 搭建简单而且免费
  • 使用 @PostConstruct、@Bean(initMethod=“”)注解指定初始化方法 、及实现InitializingBean接口 在 Spring Bean 完成初始化后进行一些响应的操作

    如果需要在 某个特定的bean 被初始化后 xff0c 执行一些代码逻辑 xff0c 可以使用 64 PostConstruct 64 Bean xff08 initMethod 61 xff09 注解指定初始化方法 及实现Initiali
  • RK3399环境搭建----glibc2.29编译

    硬件 xff1a firefly RK3399 JD4 软件 xff1a ubuntu18 04 需求 xff1a 在RK3399开发板上进行在线调试 调试需要用到GDB xff0c 之前拿到的gdb运行的时候报错 xff1a 提示libm
  • Sql Server索引

    1 创建索引时遵循一般原则 在需要经常搜索的列上面创建索引 在主键上创建索引 在外键上创建索引 在经常用于Where子句的列上创建索引 在经常需要排序的列上创建索引 对于数据类型为text xff0c image xff0c bit的列不应
  • 迅雷下载提示:应版权方要求/包含违规内容, 文件无法下载

    问题 在使用迅雷下载资源时 xff0c 可能会提示应版权方要求 xff0c 无法下载或包含违规内容 xff0c 无法下载 xff0c 如下图 很多人可能此时已经放弃了 xff0c 不过 xff0c 迅雷可以配合Free Download M
  • 有趣的教程:在PyCharm2019.2版本中集成Jupyter Notebooks!

    PyCharm是一种Python IDE xff0c 其带有一整套可以帮助用户在使用Python语言开发时提高其效率的工具 此外 xff0c 该IDE提供了一些高级功能 xff0c 以用于Django框架下的专业Web开发 嗨 xff0c
  • 分享文件的另一种选择--Firefox Send

    提起 Firefox xff0c 大家熟知的应该就是 Firefox Browser xff0c 即 火狐浏览器 xff0c 但是今天要说的是火狐旗下的文件分享产品 Firefox Send xff0c 可以让你安全地进行文件分享 适用场景
  • 消除if...else的十种方法

    前言 if else 是所有高级编程语言都有的必备功能 但现实中的代码往往存在着过多的 if else 虽然 if else 是必须的 xff0c 但滥用 if else 会对代码的可读性 可维护性造成很大伤害 xff0c 进而危害到整个软
  • Qt学习笔记(三)运行库依赖

    正常应该写release版本的依赖库 xff0c 不过都差不多 xff0c 自行转化就对了 自己使用qt版本 Qt5 15 2 xff0c 因为我只编译了debug版本程序 xff0c 暂时只能拿debug说事了 直接查找依赖可以找到依赖有

随机推荐

  • 宽带测速网站收集

    国际通用 xff1a https www speedtest net http www speedtest cn xff08 国产 xff0c 主要服务器节点多 xff09 电信 xff1a https 10000 gd cn 移动 xff
  • 使用Git为每个项目配置独立的用户名和邮箱

    使用Git为每个项目配置独立的用户名和邮箱 平时在开发的过程中 xff0c 用到git时 xff0c 要做一些特殊的配置 xff0c 例如自己公司内网https的SSL证书未经过第三方机构签署 xff0c 直接操作Git就会报错 xff0c
  • 电脑重装系统后文件还能恢复吗?2个方法助你还原文件

    电脑重装系统后文件还能恢复吗 xff1f 此时可能你面临一个难题 xff1a 已经重装了电脑 xff0c 但是发现丢失的问题 实际上 xff0c 一般当电脑出现运行缓慢 xff0c 或者是电脑系统出现故障无法修复的时候 xff0c 大家都会
  • C盘重装还能恢复以前文件嘛,重装C盘数据备份清单

    重装系统主要是对C盘的重装 xff0c 也就是对C盘的格式化 xff0c 电脑安装系统的位置是C盘 xff0c 常见的重装方法只影响C盘而不会丢失其他盘的数据 那么C盘重装还能恢复以前的文件嘛 xff1f 重装C盘时什么文件必须备份呢 xf
  • HTML Parsing Error:Unable to modify the parent container element before the child element is closed

    一 又涨见识了 IE8报下面错误 xff0c 而且是我的机器不报 xff0c 同事的机器报 xff0c 试了4台 xff0c 两个报 xff0c 两个不报 xff0c IE版本都一样 xff0c 没想明白 解决 xff1a 1 查看是否有未
  • 架构师必须学会的几种 UML图

    文章目录 一 UML 是什么定义作用 二 UML 画图的工具推荐三 UML 类图中的关系1 泛化 继承 2 实现 xff08 Realization xff09 3 关联 xff08 Association 4 聚合 xff08 Aggre
  • 算法:如何实现两个大数相加

    文章目录 问题要求思路代码实现 问题 实现两个很大很大的数相加 xff0c 求出它们的和 要求 1 是整数 xff1b 2 两个数无限大 xff0c long 都装不下 xff1b 3 不能用 BigInteger xff1b 4 不能用任
  • 关于Android 未来开发的方向探讨

    文章目录 背景Anroid 应用开发工程师技能参考Android 基础设施建设技能参考Android 逆向开发工程师技能参考Android Framework工程师技能参考 xff08 车载系统 系统开发方向 xff09 Android 性
  • Git Commit Message 规范实践

    文章目录 背景示例FeatureBugRefactorDocsTestStyleChore 每个Merge Request只有一个Commit小结附录 背景 本文总结了项目实践中的 Commit Message 规范 任何一笔提交都是有原因
  • github push 报错的问题记录

    文章目录 背景解决办法示例 背景 当 git 执行 git push xff08 origin origin master xff09 后出现错误提示 xff1a git push origin origin master Password
  • Mac 平台 Android FFmpeg 编译与集成实践

    文章目录 FFmpeg 是什么为什么要学 FFmpeg 开发FFmpeg 编译1 编译环境准备2 FFmpeg 环境配置3 完整的脚本4 FQA 编译小结在Android Studio 中的集成集成问题记录写在最后 FFmpeg 是什么 F
  • 音频中文件大小计算公式(转)

    声卡对声音的处理质量可以用三个基本参数来衡量 xff0c 即采样频率 采样位数和声道数 采样频率是指单位时间内的采样次数 采样频率越大 xff0c 采样点之间的间隔就越小 xff0c 数字化后得到的声音就越逼真 xff0c 但相应的数据量就
  • 「设计模式」六大原则之一:单一职责小结

    文章目录 1 单一职责原则定义2 如何理解单一职责原则 xff08 SRP xff09 xff1f 3 如何判断类的职责是否足够单一 xff1f 4 类的职责是否设计得越单一越好 xff1f 5 应用体现6 应用示例18 应用示例2 xff
  • 「设计模式」六大原则之二:开闭职责小结

    文章目录 1 开闭原则定义2 如何理解 对扩展开放 对修改关闭 xff1f 3 如何做到 对扩展开放 修改关闭 xff1f 4 如何在项目中灵活应用开闭原则 xff1f 5 示例 xff1a 6 小结 xff1a 设计模式 六大原则系列链接
  • 「设计模式」六大原则之三:里氏替换原则小结

    文章目录 1 里式替换原则定义2 举例说明示例1 xff1a 示例2 xff1a 3 哪些代码明显违背了 LSP xff1f 子类违背父类声明要实现的功能子类违背父类对输入 输出 异常的约定子类违背父类注释中所罗列的任何特殊说明开发 Tip
  • 「设计模式」六大原则之四:接口隔离原则小结

    文章目录 1 接口隔离原则定义2 如何理解3 接口隔离原则与单一职责原则的区别2 举例说明4 小结 设计模式 六大原则系列链接 设计模式 六大原则之一 xff1a 单一职责小结 设计模式 六大原则之二 xff1a 开闭职责小结 设计模式 六
  • 「设计模式」六大原则之五:依赖倒置原则小结

    文章目录 1 依赖倒置原则 xff08 DIP xff09 定义4 DIP举例说明2 如何理解控制反转 xff08 IOC xff09 3 如何理解依赖注入 xff08 DI xff09 4 小结 设计模式 六大原则系列链接 设计模式 六大
  • 「设计模式」六大原则之六:最小知识原则小结

    文章目录 1 最小知识原则 xff08 LOD xff09 定义2 什么是 高内聚 呢3 什么是 松耦合 xff1f 4 应用举例 设计模式 六大原则系列链接 设计模式 六大原则之一 xff1a 单一职责小结 设计模式 六大原则之二 xff
  • Android NDK 中堆栈日志 add2line 的分析实践

    文章目录 目的常用的辅助工具分析步骤参考 目的 Android NDK 中出现的 crash 日志分析定位 xff0c 使用 addr2line 对库中定位so 动态库崩溃位置 xff0c 定位到某个函数的具体的代码行 常用的辅助工具 ad
  • Android 中关于 FileObserver类监听文件状态的实践

    文章目录 需求背景走进源码实现示例参考 需求背景 当某一个目录的文件发生变化 xff08 创建 修改 删除 移动 xff09 时 xff0c 需要给一个回调事件给其他端调用 其他场景 xff1a 阅后即焚等等 比如在 Android 的 V