Android: Make GameFileCacheManager use LiveData, part 2

Gets rid some uses of the deprecated LocalBroadcastManager.

One note about the changes in GameFileCacheManager itself:
The change from compareAndSet to getValue followed by
setValue is actually safe, because startLoad and startRescan
only run from the main thread, and only the main thread ever
sets the flags to true. So it's impossible for any other thread
to change the flag in between the getValue and the setValue.
This commit is contained in:
JosJuice 2021-11-17 21:49:51 +01:00
parent 857963b336
commit 2941cf8d94
5 changed files with 31 additions and 76 deletions

View file

@ -2,16 +2,12 @@
package org.dolphinemu.dolphinemu.activities;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import androidx.fragment.app.FragmentActivity;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import org.dolphinemu.dolphinemu.model.GameFile;
import org.dolphinemu.dolphinemu.services.GameFileCacheManager;
@ -69,22 +65,13 @@ public class AppLinkActivity extends FragmentActivity
mAfterDirectoryInitializationRunner = new AfterDirectoryInitializationRunner();
mAfterDirectoryInitializationRunner.run(this, true, () -> tryPlay(playAction));
IntentFilter gameFileCacheIntentFilter = new IntentFilter(GameFileCacheManager.DONE_LOADING);
BroadcastReceiver gameFileCacheReceiver = new BroadcastReceiver()
GameFileCacheManager.isLoading().observe(this, (isLoading) ->
{
@Override
public void onReceive(Context context, Intent intent)
if (!isLoading && DirectoryInitialization.areDolphinDirectoriesReady())
{
if (DirectoryInitialization.areDolphinDirectoriesReady())
{
tryPlay(playAction);
}
tryPlay(playAction);
}
};
LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(this);
broadcastManager.registerReceiver(gameFileCacheReceiver, gameFileCacheIntentFilter);
});
DirectoryInitialization.start(this);
GameFileCacheManager.startLoad(this);
@ -110,7 +97,7 @@ public class AppLinkActivity extends FragmentActivity
// If game == null and the load isn't done, wait for the next GameFileCacheService broadcast.
// If game == null and the load is done, call play with a null game, making us exit in failure.
if (game != null || !GameFileCacheManager.isLoading())
if (game != null || !GameFileCacheManager.isLoading().getValue())
{
play(action, game);
}

View file

@ -3,13 +3,10 @@
package org.dolphinemu.dolphinemu.services;
import android.content.Context;
import android.content.Intent;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import org.dolphinemu.dolphinemu.DolphinApplication;
import org.dolphinemu.dolphinemu.model.GameFile;
import org.dolphinemu.dolphinemu.model.GameFileCache;
import org.dolphinemu.dolphinemu.ui.platform.Platform;
@ -20,27 +17,19 @@ import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Loads game list data on a separate thread.
*/
public final class GameFileCacheManager
{
/**
* This is broadcast when the service is done with all requested work, regardless of whether
* the contents of the cache actually changed. (Maybe the cache was already up to date.)
*/
public static final String DONE_LOADING =
"org.dolphinemu.dolphinemu.GAME_FILE_CACHE_DONE_LOADING";
private static GameFileCache gameFileCache = null;
private static final MutableLiveData<GameFile[]> gameFiles =
new MutableLiveData<>(new GameFile[]{});
private static final ExecutorService executor = Executors.newFixedThreadPool(1);
private static final AtomicBoolean loadInProgress = new AtomicBoolean(false);
private static final AtomicBoolean rescanInProgress = new AtomicBoolean(false);
private static final MutableLiveData<Boolean> loadInProgress = new MutableLiveData<>(false);
private static final MutableLiveData<Boolean> rescanInProgress = new MutableLiveData<>(false);
private GameFileCacheManager()
{
@ -108,19 +97,24 @@ public final class GameFileCacheManager
}
/**
* Returns true if in the process of either loading the cache or rescanning.
* Returns true if in the process of loading the cache for the first time.
*/
public static boolean isLoading()
public static LiveData<Boolean> isLoading()
{
return loadInProgress.get();
return loadInProgress;
}
/**
* Returns true if in the process of rescanning.
*/
public static boolean isRescanning()
public static LiveData<Boolean> isRescanning()
{
return rescanInProgress.get();
return rescanInProgress;
}
public static boolean isLoadingOrRescanning()
{
return loadInProgress.getValue() || rescanInProgress.getValue();
}
/**
@ -130,8 +124,9 @@ public final class GameFileCacheManager
*/
public static void startLoad(Context context)
{
if (loadInProgress.compareAndSet(false, true))
if (!loadInProgress.getValue())
{
loadInProgress.setValue(true);
new AfterDirectoryInitializationRunner().run(context, false,
() -> executor.execute(GameFileCacheManager::load));
}
@ -144,8 +139,9 @@ public final class GameFileCacheManager
*/
public static void startRescan(Context context)
{
if (rescanInProgress.compareAndSet(false, true))
if (!rescanInProgress.getValue())
{
rescanInProgress.setValue(true);
new AfterDirectoryInitializationRunner().run(context, false,
() -> executor.execute(GameFileCacheManager::rescan));
}
@ -194,9 +190,7 @@ public final class GameFileCacheManager
}
}
loadInProgress.set(false);
if (!rescanInProgress.get())
sendBroadcast(DONE_LOADING);
loadInProgress.postValue(false);
}
/**
@ -232,9 +226,7 @@ public final class GameFileCacheManager
}
}
rescanInProgress.set(false);
if (!loadInProgress.get())
sendBroadcast(DONE_LOADING);
rescanInProgress.postValue(false);
}
private static void updateGameFileArray()
@ -243,10 +235,4 @@ public final class GameFileCacheManager
Arrays.sort(gameFilesTemp, (lhs, rhs) -> lhs.getTitle().compareToIgnoreCase(rhs.getTitle()));
gameFiles.postValue(gameFilesTemp);
}
private static void sendBroadcast(String action)
{
LocalBroadcastManager.getInstance(DolphinApplication.getAppContext())
.sendBroadcast(new Intent(action));
}
}

View file

@ -2,16 +2,14 @@
package org.dolphinemu.dolphinemu.ui.main;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import androidx.appcompat.app.AlertDialog;
import androidx.core.app.ComponentActivity;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.lifecycle.Observer;
import org.dolphinemu.dolphinemu.BuildConfig;
import org.dolphinemu.dolphinemu.R;
@ -43,7 +41,6 @@ public final class MainPresenter
private final MainView mView;
private final ComponentActivity mActivity;
private BroadcastReceiver mBroadcastReceiver = null;
private String mDirToAdd;
public MainPresenter(MainView view, ComponentActivity activity)
@ -59,30 +56,16 @@ public final class MainPresenter
GameFileCacheManager.getGameFiles().observe(mActivity, (gameFiles) -> mView.showGames());
IntentFilter filter = new IntentFilter();
filter.addAction(GameFileCacheManager.DONE_LOADING);
mBroadcastReceiver = new BroadcastReceiver()
Observer<Boolean> refreshObserver = (isLoading) ->
{
@Override
public void onReceive(Context context, Intent intent)
{
switch (intent.getAction())
{
case GameFileCacheManager.DONE_LOADING:
mView.setRefreshing(false);
break;
}
}
mView.setRefreshing(GameFileCacheManager.isLoadingOrRescanning());
};
LocalBroadcastManager.getInstance(mActivity).registerReceiver(mBroadcastReceiver, filter);
GameFileCacheManager.isLoading().observe(mActivity, refreshObserver);
GameFileCacheManager.isRescanning().observe(mActivity, refreshObserver);
}
public void onDestroy()
{
if (mBroadcastReceiver != null)
{
LocalBroadcastManager.getInstance(mActivity).unregisterReceiver(mBroadcastReceiver);
}
}
public void onFabClick()
@ -138,11 +121,10 @@ public final class MainPresenter
mDirToAdd = null;
}
if (sShouldRescanLibrary && !GameFileCacheManager.isRescanning())
if (sShouldRescanLibrary && !GameFileCacheManager.isRescanning().getValue())
{
new AfterDirectoryInitializationRunner().run(mActivity, false, () ->
{
mView.setRefreshing(true);
GameFileCacheManager.startRescan(mActivity);
});
}

View file

@ -122,7 +122,7 @@ public final class TvMainActivity extends FragmentActivity
mSwipeRefresh.setOnRefreshListener(this);
setRefreshing(GameFileCacheManager.isLoading());
setRefreshing(GameFileCacheManager.isLoadingOrRescanning());
final FragmentManager fragmentManager = getSupportFragmentManager();
mBrowseFragment = new BrowseSupportFragment();

View file

@ -73,7 +73,7 @@ public final class PlatformGamesFragment extends Fragment implements PlatformGam
mRecyclerView.addItemDecoration(new GameAdapter.SpacesItemDecoration(8));
setRefreshing(GameFileCacheManager.isLoading());
setRefreshing(GameFileCacheManager.isLoadingOrRescanning());
showGames();
}