Refactor DB read code to use RxJava

This commit is contained in:
sigmabeta 2016-01-09 12:28:29 -05:00
parent 5db10d83f7
commit f58a8561f8
6 changed files with 99 additions and 166 deletions

View File

@ -89,6 +89,9 @@ dependencies {
// For loading huge screenshots from the disk. // For loading huge screenshots from the disk.
compile 'com.squareup.picasso:picasso:2.5.2' 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) { task setupCMake(type: Exec) {

View File

@ -18,6 +18,7 @@
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<application <application
android:name=".DolphinApplication"
android:label="@string/app_name" android:label="@string/app_name"
android:icon="@drawable/ic_launcher" android:icon="@drawable/ic_launcher"
android:allowBackup="true" 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; package org.dolphinemu.dolphinemu.activities;
import android.app.LoaderManager;
import android.content.CursorLoader;
import android.content.Intent; import android.content.Intent;
import android.content.Loader;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.database.Cursor; import android.database.Cursor;
import android.os.Bundle; 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 * 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. * 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_ADD_DIRECTORY = 1;
public static final int REQUEST_EMULATE_GAME = 2; 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 ViewPager mViewPager;
private PlatformPagerAdapter mPlatformPagerAdapter; 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 @Nullable
public PlatformGamesFragment getPlatformFragment(int platform) public PlatformGamesFragment getPlatformFragment(int platform)
{ {

View File

@ -1,8 +1,6 @@
package org.dolphinemu.dolphinemu.fragments; package org.dolphinemu.dolphinemu.fragments;
import android.app.Fragment; import android.app.Fragment;
import android.app.LoaderManager;
import android.content.Loader;
import android.database.Cursor; import android.database.Cursor;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
@ -15,8 +13,14 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import org.dolphinemu.dolphinemu.BuildConfig; import org.dolphinemu.dolphinemu.BuildConfig;
import org.dolphinemu.dolphinemu.DolphinApplication;
import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.adapters.GameAdapter; 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 public class PlatformGamesFragment extends Fragment
{ {
@ -69,9 +73,7 @@ public class PlatformGamesFragment extends Fragment
recyclerView.addItemDecoration(new GameAdapter.SpacesItemDecoration(8)); 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 loadGames();
getLoaderManager().initLoader(mPlatform, null,
(LoaderManager.LoaderCallbacks<Cursor>) getActivity());
mAdapter = new GameAdapter(); mAdapter = new GameAdapter();
recyclerView.setAdapter(mAdapter); recyclerView.setAdapter(mAdapter);
@ -87,32 +89,35 @@ public class PlatformGamesFragment extends Fragment
public void refresh() public void refresh()
{ {
Log.d("DolphinEmu", "[PlatformGamesFragment] " + mPlatform + ": Refreshing..."); Log.d("DolphinEmu", "[PlatformGamesFragment] " + mPlatform + ": Refreshing...");
// +1 because of LOADER_ID_ALL loadGames();
getLoaderManager().restartLoader(mPlatform, null, (LoaderManager.LoaderCallbacks<Cursor>) getActivity());
} }
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..."); GameDatabase databaseHelper = DolphinApplication.databaseHelper;
Log.d("DolphinEmu", "[PlatformGamesFragment] " + mPlatform + ": Cursor size: " + data.getCount());
if (mAdapter != null)
{
mAdapter.swapCursor(data);
}
else
{
Log.e("DolphinEmu", "[PlatformGamesFragment] " + mPlatform + ": No adapter available.");
}
}
public void onLoaderReset() databaseHelper.getGamesForPlatform(mPlatform)
{ .subscribeOn(Schedulers.io())
Log.e("DolphinEmu", "[PlatformGamesFragment] " + mPlatform + ": Loader reset; clearing data from view."); .observeOn(AndroidSchedulers.mainThread())
if (mAdapter != null) .subscribe(new Action1<Cursor>()
{ {
mAdapter.swapCursor(null); @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.HashSet;
import java.util.Set; import java.util.Set;
import rx.Observable;
import rx.Subscriber;
/** /**
* A helper class that provides several utilities simplifying interaction with * A helper class that provides several utilities simplifying interaction with
* the SQLite database. * the SQLite database.
@ -252,4 +255,44 @@ public final class GameDatabase extends SQLiteOpenHelper
folderCursor.close(); folderCursor.close();
database.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();
}
});
}
} }