Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Loaders and cursors for DBFlow #1091

Open
wants to merge 56 commits into
base: develop
Choose a base branch
from
Open

Conversation

hilljh82
Copy link
Contributor

@hilljh82 hilljh82 commented Dec 1, 2016

This pull request contains the implementation of the following classes to improve Loader support for DBFlow.

  • FlowCursorAdapter: The purpose of this class is to improve support for CursorAdapter. In particular, we overload the getItem (position) method to return the actual model element at that position instead of a Cursor.
  • FlowSimpleCursorAdapter: This class allows the SimpleCursorAdapter to easily support DBFlow. Similar to FlowCursorAdapter, we overload the getItem(position) method to return a model element, instead of a Cursor.
  • FlowCursorLoader: This class integrates DBFLow with the Android Loader framework. In particular, the loader must be created with a Queriable object. The Queriable object is used by the loader to load models. The end result of this loader is a Cursor that can be used like the cursor returned by CursorLoader. A key feature of this loader is the ability to leverage the model observer framework in DBFlow via the (un)registerForContentChanges() methods. Using this methods will configure the loader to observe changes to models of interest. Any change observed will trigger the loader to automatically reload. This simplifies storing data in the database via DBFlow models, and updating corresponding views as a result of the storage updates.
  • FlowSingleModelLoader: This is the base class for the following single model classes: FlowModelLoader, FlowModelViewLoader, FlowModelQueryLoader. Single model loaders are for queries that return 1 model, not a list of models or a cursor to a list of models. We treat these different from the list loaders because we need to appropriate InstanceAdapter to create the single model, and load it from the cursor. The result of each load is a single, usable model. Similar to the FlowModelLoader, these loaders will automatically reload if the underlying table changes. Also, you can register for updates to models that have nothing to do with the final model. This is of interest when dealing with query models since the final model may be the composition of data from many different tables.

Lastly, each loader has been tested in applications that I am currently using. It would be great if these classes can be incorporated into the next release of DBFlow.

@@ -35,6 +34,6 @@ val <T : Any> T.property: Property<T>
val <T : Any> ModelQueriable<T>.property: Property<T>
get() = PropertyFactory.from(this)

inline fun <reified T : Any> T.propertyString(stringRepresentation: String?): Property<T> {
return PropertyFactory.from(T::class.java, stringRepresentation)
inline fun <reified TModel : Any> TModel.propertyString(stringRepresentation: String?): Property<TModel> {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#? not sure why this type parameter changed?

@@ -0,0 +1,117 @@
package com.raizlabs.android.dbflow.processor;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#req remove this file. It was added back, in develop its a kotlin file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@agrosner I am will take care of that now. I am looking at the changed files to resolve other issues that are similar to this one.

* @param <TModel>
*/
public abstract class FlowCursorAdapter <TModel extends Model> extends CursorAdapter {
private final ModelAdapter<TModel> mModelAdapter;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#req naming. no m-prefix names please.

@Override
public TModel getItem(int position) {
Cursor cursor = (Cursor) super.getItem(position);
return cursor != null ? this.mModelAdapter.loadFromCursor(cursor) : null;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we might want to use a FlowCursorList here so we can provide caching.

@TargetApi(11)
public class FlowCursorLoader extends AsyncTaskLoader<Cursor> {
/// Models to be observed for changes.
private final HashSet<Class<? extends Model>> mModels = new HashSet<>();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#req no m-prefix variables please.

Cursor cursor = this.mQueriable.query();

if (cursor != null) {
cursor.getCount();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#? what is this supposed to do

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This forces the cursor to load the load the count.

}
}

public Collection<Class<? extends Model>> getModels() {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#req rename to something like getModelClasses()

@Override
public void onModelStateChanged(@Nullable Class<?> table, BaseModel.Action action, @NonNull SQLCondition[] primaryKeyValues) {
if (!this.endOfTransaction) {
if (action == BaseModel.Action.INSERT || action == BaseModel.Action.DELETE || action == BaseModel.Action.UPDATE) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#? what about the other two, CHANGE + SAVE?

Copy link
Contributor Author

@hilljh82 hilljh82 Dec 1, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is CHANGE new? I did not add SAVE since for some reasons I was seeing SAVE being fired with either INSERT or UPDATE in 3.1.x. So, it was triggering the loader twice. Is this a true understanding of those events being fired.

How is CHANGE different from UPDATE?

*
* @param <TModel>
*/
public class FlowSimpleCursorAdapter <TModel extends Model> extends SimpleCursorAdapter {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#? not sure the difference here between this and the FlowCursorAdapter

Copy link
Contributor Author

@hilljh82 hilljh82 Dec 1, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This Adapter is specialization of the SimpleCursorAdapter in the Android SDK designed for DBFlow.

The FlowCursorAdapter requires allows the developer to have different layouts for model. For example, you are storing inherited data in a SQL database, and create a query that aggregates different model types into a single result set. In this case, you have a different layout for each data type referenced in the data set. You therefore have to implement newView and bindView.

In some cases, however, the data you result set will be a homogenous data types, and the mapping between columns and views is straightforward text populations. In this case, you only need a single layout, and a mapping between columns and views. The FlowSimpleCursorAdapter is designed to support this need since you only have to provide the layout id and the necessary mapping in the constructor.

* @param <TModel>
*/
@TargetApi(11)
public class FlowModelViewLoader <TModel extends Model, TModelView extends BaseModelView>
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#req no Model restriction on the TModel

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#? How do you ensure the class is only parameterized by DBFlow Model-like classes? Is this no longer a concern with 4.x.x? Also, doing so will mean removing restrictions on certain methods that should only be called with class that derive from Model, such as registerForContentChanges. This would be an introduction of an accidental complicity in the design.

* @param <TModel>
*/
@TargetApi(11)
public class FlowModelLoader <TModel extends Model>
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#req no model restriction anywhere please.

Copy link
Contributor Author

@hilljh82 hilljh82 Dec 1, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#? How do you ensure the class is only parameterized by DBFlow Model-like classes? Is this no longer a concern with 4.x.x? Also, doing so will mean removing restrictions on certain methods that should only be called with class that derive from Model, such as registerForContentChanges. This would be an introduction of an accidental complicity in the design.

@@ -269,7 +269,7 @@ Delete.tables(MyTable1.class, MyTable2.class);

// Delete using query
SQLite.delete(MyTable.class)
.where(DeviceObject_Table.carrier.is("T-MOBILE"))
.where(DeviceObject_Table.carrier.is("TModel-MOBILE"))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#req looks like you did some kind of refactor that change T to TModel. haha oops.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the great words of Homer Simpson, "DOH!!!"

@hilljh82
Copy link
Contributor Author

hilljh82 commented Dec 6, 2016

@agrosner Is there anything blocking this PR for being merged?

@hannesa2
Copy link

This PR is a key feature for me.
@hilljh82 is not alone, I'm happy when this PR is merged soon

@hannesa2
Copy link

hannesa2 commented Mar 2, 2017

@hilljh82 please solve the conflict to let us (who wait for this feature) know, if @agrosner ever will merge this PR. Thank you !

@zubinmehta
Copy link

zubinmehta commented Mar 21, 2017

@hilljh82 I am trying to use FlowCursorLoader class. It works but sometimes, I get

java.lang.IllegalStateException: Cannot execute task: the task is already running.
at android.support.v4.content.ModernAsyncTask.executeOnExecutor(ModernAsyncTask.java:424)
at android.support.v4.content.AsyncTaskLoader.executePendingTask(AsyncTaskLoader.java:219)
at android.support.v4.content.AsyncTaskLoader.dispatchOnCancelled(AsyncTaskLoader.java:232)
at android.support.v4.content.AsyncTaskLoader$LoadTask.onCancelled(AsyncTaskLoader.java:88)
at android.support.v4.content.ModernAsyncTask.finish(ModernAsyncTask.java:474)
at android.support.v4.content.ModernAsyncTask$InternalHandler.handleMessage(ModernAsyncTask.java:493)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5438)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:739)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:629)

Googled a lot but no success. I am probably doing something wrong.
I wonder, if you ever got this issue and/or know how to fix this?

Thanks !

@hilljh82
Copy link
Contributor Author

hilljh82 commented Mar 21, 2017 via email

@Harti
Copy link

Harti commented Oct 26, 2017

sigh I really hate to see this PR just go to waste.

@agrosner Did you make a final decision on declining this PR yet?

@hilljh82
Copy link
Contributor Author

hilljh82 commented Oct 27, 2017

@Harti Yes, it is unfortunate this PR has not been integrated into master—especially since I have twice addressed merge conflicts due to master changing only for my updates to become outdated once again.

If you are interested, I have create a separate project that provides these loaders:

https://github.com/onehilltech/backbone/tree/master/backbone-dbflow

Until this PR gets merged, I have been using this project. It is also a place that I use to implement support classes for DBFlow since the experience with this PR leaves me with little confidence some cool ideas I have will ever be integrated!

In addition, I have been working on another project called backbone-data that is designed to the equivalent of ember-data for Android. It uses DBFlow and Retrofit under the hood. It's a work in progress, but its making my life a LOT easier on Android since it codifies much if the redundant code that I was creating when using DBFlow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants