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
будет сериализован.
В качестве лучшего подхода я бы предложил использовать базу данных (более простая миграция и т. Д.).