Merge pull request #3483 from sigmabeta/android-rx-db-access

[Android] Refactor DB read code to use RxJava
This commit is contained in:
Dolphin Bot 2016-01-09 19:14:56 +01:00
commit aa89516cf3
6 changed files with 99 additions and 166 deletions

View File

@ -89,6 +89,9 @@ dependencies {
// For loading huge screenshots from the disk.
compile 'com.squareup.picasso:picasso:2.5.2'
// Allows FRP-style asynchronous operations in Android.
compile 'io.reactivex:rxandroid:1.1.0'
}
task setupCMake(type: Exec) {

View File

@ -18,6 +18,7 @@
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name=".DolphinApplication"
android:label="@string/app_name"
android:icon="@drawable/ic_launcher"
android:allowBackup="true"

View File

@ -0,0 +1,19 @@
package org.dolphinemu.dolphinemu;
import android.app.Application;
import org.dolphinemu.dolphinemu.model.GameDatabase;
public class DolphinApplication extends Application
{
public static GameDatabase databaseHelper;
@Override
public void onCreate()
{
super.onCreate();
databaseHelper = new GameDatabase(this);
}
}

View File

@ -1,9 +1,6 @@
package org.dolphinemu.dolphinemu.activities;
import android.app.LoaderManager;
import android.content.CursorLoader;
import android.content.Intent;
import android.content.Loader;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.os.Bundle;
@ -34,20 +31,11 @@ import org.dolphinemu.dolphinemu.utils.StartupHandler;
* The main Activity of the Lollipop style UI. Manages several PlatformGamesFragments, which
* individually display a grid of available games for each Fragment, in a tabbed layout.
*/
public final class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor>
public final class MainActivity extends AppCompatActivity
{
public static final int REQUEST_ADD_DIRECTORY = 1;
public static final int REQUEST_EMULATE_GAME = 2;
/**
* It is important to keep track of loader ID separately from platform ID (see Game.java)
* because we could potentially have Loaders that load things other than Games.
*/
public static final int LOADER_ID_ALL = 100; // TODO
public static final int LOADER_ID_GAMECUBE = 0;
public static final int LOADER_ID_WII = 1;
public static final int LOADER_ID_WIIWARE = 2;
private ViewPager mViewPager;
private PlatformPagerAdapter mPlatformPagerAdapter;
@ -175,132 +163,6 @@ public final class MainActivity extends AppCompatActivity implements LoaderManag
}
}
/**
* Callback that's invoked when the system has initialized the Loader and
* is ready to start the query. This usually happens when initLoader() is
* called. Here, we use it to make a DB query for games.
*
* @param id The ID value passed to the initLoader() call that triggered this.
* @param args The args bundle supplied by the caller.
* @return A new Loader instance that is ready to start loading.
*/
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args)
{
Log.d("DolphinEmu", "Creating loader with id: " + id);
// Take action based on the ID of the Loader that's being created.
switch (id)
{
case LOADER_ID_ALL:
// TODO Play some sort of load-starting animation; maybe fade the list out.
return new CursorLoader(
this, // Parent activity context
GameProvider.URI_GAME, // URI of table to query
null, // Return all columns
null, // No selection clause
null, // No selection arguments
GameDatabase.KEY_GAME_TITLE + " asc" // Sort by game name, ascending order
);
case LOADER_ID_GAMECUBE:
case LOADER_ID_WII:
case LOADER_ID_WIIWARE:
// TODO Play some sort of load-starting animation; maybe fade the list out.
return new CursorLoader(
this, // Parent activity context
GameProvider.URI_GAME, // URI of table to query
null, // Return all columns
GameDatabase.KEY_GAME_PLATFORM + " = ?", // Select by platform
new String[]{Integer.toString(id)}, // Platform id is Loader id
GameDatabase.KEY_GAME_TITLE + " asc" // Sort by game name, ascending order
);
default:
Log.e("DolphinEmu", "Bad ID passed in.");
return null;
}
}
/**
* Callback that's invoked when the Loader returned in onCreateLoader is finished
* with its task. In this case, the game DB query is finished, so we should put the results
* on screen.
*
* @param loader The loader that finished.
* @param data The data the Loader loaded.
*/
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data)
{
int id = loader.getId();
Log.d("DolphinEmu", "Loader finished with id: " + id);
PlatformGamesFragment fragment = null;
switch (id)
{
case LOADER_ID_GAMECUBE:
fragment = getPlatformFragment(Game.PLATFORM_GC);
break;
case LOADER_ID_WII:
fragment = getPlatformFragment(Game.PLATFORM_WII);
break;
case LOADER_ID_WIIWARE:
fragment = getPlatformFragment(Game.PLATFORM_WII_WARE);
break;
// TODO case LOADER_ID_ALL:
default:
Log.e("DolphinEmu", "Bad ID passed in.");
break;
}
if (fragment != null)
{
fragment.onLoadFinished(loader, data);
}
}
@Override
public void onLoaderReset(Loader<Cursor> loader)
{
int id = loader.getId();
Log.e("DolphinEmu", "Loader resetting with id: " + id);
PlatformGamesFragment fragment = null;
switch (id)
{
case LOADER_ID_GAMECUBE:
fragment = getPlatformFragment(Game.PLATFORM_GC);
break;
case LOADER_ID_WII:
fragment = getPlatformFragment(Game.PLATFORM_WII);
break;
case LOADER_ID_WIIWARE:
fragment = getPlatformFragment(Game.PLATFORM_WII_WARE);
break;
// TODO case LOADER_ID_ALL:
default:
Log.e("DolphinEmu", "Bad ID passed in.");
break;
}
if (fragment != null)
{
fragment.onLoaderReset();
}
}
@Nullable
public PlatformGamesFragment getPlatformFragment(int platform)
{

View File

@ -1,8 +1,6 @@
package org.dolphinemu.dolphinemu.fragments;
import android.app.Fragment;
import android.app.LoaderManager;
import android.content.Loader;
import android.database.Cursor;
import android.os.Bundle;
import android.support.annotation.Nullable;
@ -15,8 +13,14 @@ import android.view.View;
import android.view.ViewGroup;
import org.dolphinemu.dolphinemu.BuildConfig;
import org.dolphinemu.dolphinemu.DolphinApplication;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.adapters.GameAdapter;
import org.dolphinemu.dolphinemu.model.GameDatabase;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Action1;
import rx.schedulers.Schedulers;
public class PlatformGamesFragment extends Fragment
{
@ -69,9 +73,7 @@ public class PlatformGamesFragment extends Fragment
recyclerView.addItemDecoration(new GameAdapter.SpacesItemDecoration(8));
// Create an adapter that will relate the dataset to the views on-screen. +1 because of LOADER_ID_ALL
getLoaderManager().initLoader(mPlatform, null,
(LoaderManager.LoaderCallbacks<Cursor>) getActivity());
loadGames();
mAdapter = new GameAdapter();
recyclerView.setAdapter(mAdapter);
@ -87,32 +89,35 @@ public class PlatformGamesFragment extends Fragment
public void refresh()
{
Log.d("DolphinEmu", "[PlatformGamesFragment] " + mPlatform + ": Refreshing...");
// +1 because of LOADER_ID_ALL
getLoaderManager().restartLoader(mPlatform, null, (LoaderManager.LoaderCallbacks<Cursor>) getActivity());
loadGames();
}
public void onLoadFinished(Loader<Cursor> loader, Cursor data)
public void loadGames()
{
// TODO Play some sort of load-finished animation; maybe fade the list in.
Log.d("DolphinEmu", "[PlatformGamesFragment] " + mPlatform + ": Loading games...");
Log.d("DolphinEmu", "[PlatformGamesFragment] " + mPlatform + ": Load finished, swapping cursor...");
Log.d("DolphinEmu", "[PlatformGamesFragment] " + mPlatform + ": Cursor size: " + data.getCount());
if (mAdapter != null)
{
mAdapter.swapCursor(data);
}
else
{
Log.e("DolphinEmu", "[PlatformGamesFragment] " + mPlatform + ": No adapter available.");
}
}
GameDatabase databaseHelper = DolphinApplication.databaseHelper;
public void onLoaderReset()
{
Log.e("DolphinEmu", "[PlatformGamesFragment] " + mPlatform + ": Loader reset; clearing data from view.");
if (mAdapter != null)
{
mAdapter.swapCursor(null);
}
databaseHelper.getGamesForPlatform(mPlatform)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Cursor>()
{
@Override
public void call(Cursor data)
{
Log.d("DolphinEmu", "[PlatformGamesFragment] " + mPlatform + ": Load finished, swapping cursor...");
if (mAdapter != null)
{
mAdapter.swapCursor(data);
}
else
{
Log.e("DolphinEmu", "[PlatformGamesFragment] " + mPlatform + ": No adapter available.");
}
}
}
);
}
}

View File

@ -14,6 +14,9 @@ import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import rx.Observable;
import rx.Subscriber;
/**
* A helper class that provides several utilities simplifying interaction with
* the SQLite database.
@ -252,4 +255,44 @@ public final class GameDatabase extends SQLiteOpenHelper
folderCursor.close();
database.close();
}
public Observable<Cursor> getGamesForPlatform(final int platform)
{
return Observable.create(new Observable.OnSubscribe<Cursor>()
{
@Override
public void call(Subscriber<? super Cursor> subscriber)
{
Log.i("DolphinEmu", "[GameDatabase] Reading games list...");
String whereClause = null;
String[] whereArgs = null;
// If -1 passed in, return all games. Else, return games for one platform only.
if (platform >= 0)
{
whereClause = KEY_GAME_PLATFORM + " = ?";
whereArgs = new String[]{Integer.toString(platform)};
}
SQLiteDatabase database = getReadableDatabase();
Cursor resultCursor = database.query(
TABLE_NAME_GAMES,
null,
whereClause,
whereArgs,
null,
null,
KEY_GAME_TITLE + " ASC"
);
// Pass the result cursor to the consumer.
subscriber.onNext(resultCursor);
// Tell the consumer we're done; it will unsubscribe implicitly.
subscriber.onCompleted();
}
});
}
}