FileObserver引起的bug


前言

最近做文件下載緩存的時候,有這么一個需求,緩存文件有一個最大值限制,如果文件下載下來要超過緩存的最大值,那么就不進行下載.

我的方案

  1. 使用固定核心線程數的線程池執行下載任務
  2. 每次下載文件之前,先獲取文件長度,看當前文件大小加上本地已有的文件大小會不會超出最大緩存大小.
  3. 因為三個線程並行下載,可能三個線程同時走到判斷大小的位置,如果都判斷沒有超過最大值就進行下載,那么可能下載后就超出大小了.
  4. 為了解決第3點提到的問題,使用了FileObserver對緩存文件夾的相關事件進行觀察.如果下載完成后的總的文件大小超過了最大緩存size,就刪除剛剛下載的文件.

FileObserver的使用

初始化FileObserver對象,復寫onEvent方法,並開啟監聽,代碼片段如下.

private void initFileObserver() {
    mObserver = new FileObserver(mCacheDir) {
        @Override
        public void onEvent(int event, @Nullable String path) {
            switch (event) {
                case FileObserver.CLOSE_WRITE:
                    Log.i(TAG, "onEvent: CLOSE_WRITE," + path);
                    File newFile = null;
                    if (path != null) {
                        newFile = new File(mCacheDir, path);
                    }
                    mExecutor.submit(new CountFileCallable(newFile));
                    break;
                case FileObserver.DELETE:
                    Log.i(TAG, "onEvent: DELETE," + path);
                    mExecutor.submit(new CountFileCallable(null));
                    break;
                default:
            }
        }
    };
    mObserver.startWatching();
}

FileObserver的實現基本都是本地方法,大致是開啟一個線程對文件的各種事件監聽,java層就只是實現了觀察者模式.

FileObserver可以監聽的事件類型也很多,大概一般的文件操作都有對應的事件,如下

/** 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;

FileObserver只傳遞一個文件路徑的構造函數就是監聽所有的事件,只要有對應的事件觸發,你就可以在onEvent方法中收到回調.

這里我監聽了寫入關閉和刪除文件的事件,收到事件便提交了一個任務

private class CountFileCallable implements Callable<Void> {

    private final File addFile;

    CountFileCallable(File addFile) {
        this.addFile = addFile;
    }

    @Override
    public Void call() {
        computeFileSize(addFile);
        return null;
    }
}

 private void computeFileSize(File addFile) {
        long totalSize = getTotalSize();
        boolean accepted = totalSize < mCacheSize;
        Log.i(TAG, "totalSize: " + totalSize + "-- mCacheSize: " + mCacheSize);
        if (!accepted) {
            //這里是保證不會讓緩存超出大小
            if (addFile != null) {
                addFile.delete();
                Log.i(TAG, "delete file: " + addFile.getPath());
            }
        }
    }

這個任務主要是計算當前緩存文件夾的大小,如果超過緩存最大size,就刪除最后添加的文件.整體的思路就是這樣的,看起來沒什么問題,這是改正后的版本.

我之前監聽的是CREATE事件,即文件創建事件,此時如果提交任務,文件可能還沒有下載完成,此時是不能計算出准確的緩存文件夾大小的,所以最后是超出了最大緩存大小.被測試提了bug.

總結

在使用不是很熟悉的api時,還是得先看一遍源碼,至少得把主要方法看一遍,尤其是官方注釋文檔,其實文檔對每個事件的定義解釋得很清楚(當時沒看,想當然了),看不懂盡量使用翻譯工具,可以避免很多不必要的坑.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM