Android: Как я могу отобразить список недавно открытых файлов?

Может ли кто-нибудь дать мне указания, как получить список недавно открытых файлов в папке?

В моем приложении у меня есть папка, которая содержит .epub файлы. Я хотел бы отобразить отсортированный список недавно прочитанных / открытых книг пользователя.

Заранее спасибо.

1 ответ

Решение

Я просто делюсь своей идеей. Всякий раз, когда пользователь открывает файл, добавьте имя файла или идентификатор в ArrayList, И вы можете легко получить имена файлов, и вы можете отобразить его. ArrayList это один из методов. Существует много способов хранения имен файлов.

Проблема на самом деле не такая уж тривиальная. На то есть две причины - постоянство истории и операции блокировки ввода-вывода.

Фактически, чтобы гарантировать постоянство истории, мы можем использовать несколько решений:

  • база данных (кажется наиболее разумной, но требует больших усилий)
  • внутренний файл (должен быть самым простым способом)
  • общие предпочтения

Итак, я использовал второй способ. В памяти храню простоArrayList<Uri>, и чтобы сохранить его в файл, я конвертирую его в List<String>, как android.net.Uriне поддерживает сериализацию. Во внутренней памяти я сохраняю сериализованный объект, используяObjectIOStream.

Итак, смотрите код:

public class FileHistory {
    private static final String FILE_NAME = "file-history-v1";
    private static final int HISTORY_SIZE = 20;

    @NonNull
    private final Context mAppContext;
    // This is a executor where I can post any runnable
    // and all of them will be executed in one pipeline
    // keeping posting order.
    @NonNull
    private final OneThreadExecutor mExecutor;

    @Nullable
    private ArrayList<Uri> mInternalFilesHistory;
    @NonNull
    private MutableLiveData<List<Uri>> mFilesHistory = new MutableLiveData<>();

    public FileHistory(@NonNull final Context appContext,
                       @NonNull final OneThreadExecutor executor) {
        this.mAppContext = appContext;
        this.mExecutor = executor;
        loadHistory();
    }

    public void addEntry(@NonNull final Uri entry) {
        if (mInternalFilesHistory == null) {
            // The fileHistory is not ready yet.
            // Schedule adding entry as next task of serial executor.
            mExecutor.execute(() -> addEntry(entry));
            return;
        }

        // Remove entry if exists and add it as first element.
        CollectionUtils.removeFirst(mInternalFilesHistory, uri -> uri.equals(entry));
        mInternalFilesHistory.add(0, entry);
        if (mInternalFilesHistory.size() > HISTORY_SIZE) {
            ArrayList<Uri> trimmed = new ArrayList<>(HISTORY_SIZE + 1);
            trimmed.addAll(mInternalFilesHistory.subList(0, HISTORY_SIZE));
            mInternalFilesHistory = trimmed;
        }

        mExecutor.execute(this::rePostHistory);
        mExecutor.execute(this::saveToInternalStorage);
    }

    @NonNull
    public MutableLiveData<List<Uri>> getFilesHistory() {
        return mFilesHistory;
    }

    private void loadHistory() {
        mExecutor.execute(this::loadFromInternalStorage);
        mExecutor.execute(this::rePostHistory);
    }

    private void rePostHistory() {
        if (mInternalFilesHistory != null) {
            mFilesHistory.postValue(Collections.unmodifiableList(mInternalFilesHistory));
        }
    }

    @SuppressWarnings("unchecked")
    @WorkerThread
    private void loadFromInternalStorage() {
        try {
            FileInputStream fis = mAppContext.openFileInput(FILE_NAME);
            ObjectInputStream ois = new ObjectInputStream(fis);

            ArrayList<String> entries = (ArrayList<String>) ois.readObject();
            List<Uri> mapped = CollectionUtils.map(entries, Uri::parse);

            if (mInternalFilesHistory == null) {
                mInternalFilesHistory = new ArrayList<>(HISTORY_SIZE + 1);
            } else {
                mInternalFilesHistory.clear();
            }
            mInternalFilesHistory.addAll(mapped);

            fis.close();
            ois.close();
        } catch (Exception ex) {
            mInternalFilesHistory = new ArrayList<>(HISTORY_SIZE + 1);
        }
    }

    @WorkerThread
    private void saveToInternalStorage() {
        try {
            FileOutputStream fis = mAppContext.openFileOutput(FILE_NAME, Context.MODE_PRIVATE);
            ObjectOutputStream oos = new ObjectOutputStream(fis);

            if (mInternalFilesHistory == null) {
                mInternalFilesHistory = new ArrayList<>();
            }

            List<String> converted = CollectionUtils.map(mInternalFilesHistory, Uri::toString);
            oos.writeObject(converted);

            fis.close();
            oos.close();
        } catch (IOException ignored) {
        }
    }

}

Как видите, для хранения этого файла используется внутреннее хранилище. Таким образом, никаких дополнительных разрешений добавлять не нужно. Синхронизация обеспечивается с помощью исполнителя, который будет выполнять все запросы один за другим, поэтому даже если ввод-вывод будет медленным, порядок или запросы будут сохранены.

Мы не блокируем поток с помощью операций ввода-вывода, потому что все операции с использованием ввода-вывода выполняются. WorkerThread. О результате мы будем уведомлены черезLiveData от android.arch.

На мой взгляд, это самое простое решение. Если нам нужно сохранить статистику, даты и т. Д., Мы можем сохранитьList<MyHistoryEntry>, пока MyHistoryEntry будет сериализован.

В качестве лучшего подхода я бы предложил использовать базу данных (более простая миграция и т. Д.).

Другие вопросы по тегам