Relationship between Cursor, CursorAdapter, LoaderManager, Loader - java

So I've been working with IOS development for the past couple of months and have used Core Data and NSFetchedResultsController extensively.
Core Data + NSFetchedResultsController : Detects changes to the database automatically and updates table elements accordingly.
Now I've switched over to Android development and I've been looking for an equivalent of the above. I've looked at various different classes available and am a little confused.
The different types of classes:
Cursor: Provides access to the result of a database query.
CursorAdapter : Links the (list, recycler) view with the a cursor and displays the objects.
ContentProvider: Provides access to database objects. Required for use of LoaderManager.
LoaderManager: Implemented by an Activity or a Fragment to load data without blocking UI
Loader: Loader which generates cursor objects when content has changed.
I think it's also worth mentioning I am using greendao, and I am using its generated ContentProvider.
Flow of updating
This is where I got a little iffy. This is my assumption.
Content Provider maintains a cursor -- which is used by LoaderManager and possibly also CursorAdapter.
When a change occurs from the database, the Loader has a ForceLoadContentObserver which observes the cursor and calls onLoadFinished when its content is changed.
Normally a cursor adapter would call swap() inside the onLoadFinished(), resulting in a update of the table.
Keeping the above in mind, I created a ContentProvider and implemented LoaderManager but the function onLoadFinished() isn't being called when I persist a new object (using greenDao) -- which led me to start questioning whether I am understanding the process correctly. Here is a snippet of code to show what I have generally coded so far.
Fragment class
public class MissionPageFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {
// Various initialization methods
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getLoaderManager().initLoader(0, savedInstanceState, this);
}
#Override
public Loader onCreateLoader(int id, Bundle args) {
ContentProvider.daoSession = Main.daoSession;
Uri uri = ContentProvider.CONTENT_URI;
// Other initializations
CursorLoader cursorLoader = new CursorLoader(getContext(), uri, projections, null, null, null);
return cursorLoader;
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
System.out.println("Should be called when a new item is persisted... but not called =(");
}
// Other methods
}
If someone could confirm whether I am thinking about the process correctly and/or shed light on what might be going wrong, I would appreciate it a lot.
Edit 1
Here is a snippet of the query() function in the ContentProvider subclass generated by greenDao.
#Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
int uriType = sURIMatcher.match(uri);
switch (uriType) {
case MISSION_DIR:
queryBuilder.setTables(TABLENAME);
break;
case MISSION_ID:
queryBuilder.setTables(TABLENAME);
queryBuilder.appendWhere(PK + "="
+ uri.getLastPathSegment());
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
SQLiteDatabase db = getDatabase();
Cursor cursor = queryBuilder.query(db, projection, selection,
selectionArgs, null, null, sortOrder);
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
Solution
I just wanted to share how I implemented all of the aforementioned classes with GreenDao. Let me know if there's a better way to do this, but I feel like this is good enough.
Generate a ContentProvider subclass by adding the following code snippet to the GreenDaoGenerator class
Entity entity = new Entity();
entity.addContentProvider();
Check if the ContentProvider subclass methods are called appropriately. Add the javadoc part to AndroidManifest.xml. For me, I also had to change the generated ContentProvider class BASE_PATH variable to another variable like the following.
public static final String BASE_PATH = "MYTABLENAME";
Add LoaderManager, Loader to the Activity/Fragment class like the one I wrote above. You will also have to set the daoSession object before the ContentProvider is used. I've moved the below snippet to another initializer class so that other Activity/Fragment classes may use it as well.
ContentProvider.daoSession = Main.daoSession;
I'm using a RecyclerView so I subclassed the CursorRecyclerViewAdapter class provided here https://gist.github.com/skyfishjy/443b7448f59be978bc59 with a little customization. You should be able to use a simple CursorAdapter if you are using a ListView. Since the Loader is listening for updates, the Adapter doesn't need to listen for updates.
Now the ContentProvider subclass is able to update the view due to this line
getContext().getContentResolver().notifyChange(uri, null);
which is automatically generated by GreenDao. What this means is that you can use ContentProvider for all CRUD operations and the view will automatically update accordingly. This seemed to defeat the purpose of using an ORM. As such, I now do normal GreenDao operations and call the notifyChange manually after each insertion, deletion, update.
GreenDaoObj obj = new GreenDaoObj();
obj.insert();
getContext.getContentResolver().notifyChange(MyContentProvider.CONTENT_URI, null);
I don't think there is a better way than this as I don't really want to touch the GreenDao generated code for Model and Dao objects. I suppose one could add custom CRUD functions which nests the generated CRUD methods, but that should be trivial.
I've been looking around and there wasn't a well documented way to use GreenDao with Loader/LoaderManager so I thought I would organize this here. Hopefully this helps anyone who is planning on implementing this.

There are just a couple of things you didn't get exactly right.
ContentProviders are Android components that provide access to data. That data can be stored in a relational database, flat file, a remote server, etc. Providers have a common REST-like interface and serve as an abstraction layer over the different options for data storage you have. ContentProviders can also be accessed from external applications (if configured properly) and this makes them the default way of sharing data between apps. Accessing them doesn't require a LoaderManager. They should be accessed via a ContentResolver.
LoaderManager is responsible for the Loader lifecycle and coordinating it with the Activity and Fragment lifecycles.
Loaders provide a lifecycle aware way to load data into activities or fragments. The CursorLoader is a particular implementation that comes with a tone of features - it uses a worker thread to keep the loading of the UI thread, it abstract using a ContentResolver to access a ContentProvider and also sets up a ContentObserver. The ContentObserver will listen for updates on the query URL and reload data if needed.
In order to get ContentObserver notifications to work, you have to make sure a couple of things are in place. First, your ContentProvider should propagate changes to the observers. This is done using getContext().getContentResolver().notifyChange(url, observer). Then, if you have a CursorLoader that executed a query on the notified URL, it will be automatically reloaded.

Related

Using same sqlite for three activities

I am using three activities which are opened at the same time. All activities are retreive data from sqlite. I don't close or re open my connection when i am going from activity a->b or from b->c.
I just dispose my db when activity is destroying.
Activity A
SqliteConnection db;
OnCreate method
db = new SqliteConnection(mypath);
OnDestroy
db.Dispose();
db=null;
Onbuttonclick
startActivity(new Intent(this, ActivityB));
Same code is running when i am going from activity b->c.
Inside the same activity i use sqlite plenty of times.
Is this a good practice? Should i dispose my connection immediatelly after a use? Or should i close my connection on pause and reopen on resume? Or can i pass the same opened connection to the next activity? Which is the best approach?
Question modifieded
class databaseHelper
{
private static SqliteConnection db;
public static SqliteConnection openDatabase(Context context)
{
if(db==null)
db = new SqliteConnection(mypath);
return db;
}
}
And inside my activity on create
databaseHelper.openDatabase(this).myquery....
I don`t roll with Java nor xamarin. Here is a Kotlin code, it is pretty self-explanatory.
class DatabaseHelper { //Public class
companion object { ///This is equiavalent to java static.
private var instance: YourDatabase? = null
fun getDbInstance(context: Context): YourDatabase? //This functions returns the initialized DB instance.
{
if(instance == null)
instance = YourDatabase(context) // initializing the DB only one time
return instance
}
}
}
Just create a public class and name it for example "DatabaseHelper". Inside the class, create one static variable of your database type. Create a public function that returns the static variable. Inside the function, first, check if the static instance is null and if it is null, then initialize it with your database instance. This way, when you need to use your database instance, just, access the static function, provide it with the context and it will return you the initialized database instance.
In Kotlin
DatabaseHelper.getDbInstance(this).yourDbFunction()
UPDATE
Since this answer took off, I would like to suggest improvements to my previous solution. Instead of passing a context of activity to initialize the database, use application context. If you give an activity context to the static database instance, a memory leak will occur because the database instance holds a strong reference to the activity and the activity will NOT be eligible for garbage collection.
Proper usage:
val myDb = MyDb(applicationContext)
In general we should encapsulate access to a local store in another class such as a DAO/Repository/Service instead of having them directly in the Activity. this promotes loose coupling between views and data/network access. This also decouples the lifecycle of your DB connection, from the lifecycle of the currently running activity, giving you more control and opportunity for reuse.
Try using a bound Service to and have your DB connections there. Because it is a bound Service, it'll only be around if there is an Activity around that binds to it. Each Activity will bind to the same instance of the Service so it means you wont have duplicate connections. When no Activities are bind to it, it'll automatically be destroyed, destroying the connection along with it.
For a more modern, structured approach, using Jetpack components, you can look at https://github.com/googlesamples/android-sunflower

updating UI with a volley request from a different class

I am having trouble with volley (again). I want to run a volley request in a class, so more than one activity can feed off its results and update their UI's accordingly. I have got it return data and call the request from the UI but now im struggling to update the UI with the new data. I have looked at answers but I'm trying to understand the structure and I am at a loss, can some please advise/ talk me through it
assuming I understand what you mean as being:
A Volley request returns, updates some data set through some activity
In this case, assuming the calling activity contains everything, and reminding that this is a very general example, what you should usually do (usually, since there are exceptions to the case), is just insert the data into the data set contained in your UI holder (e.g. your recycler adapter) and update it, an example would be your adapter holding a method similar to this:
public void updateDataSet(List<Item> items)
{
//mItemList is the adapters member list
if (null != mItemList)
{
mItemList.clear();
mItemList.addAll(items);
}
else
mItemList = items;
notifyDataSetChanged();
}
you call this inside the request callback you fired earlier, just make sure to initialize everything BEFORE you fire the request, e.g.
#Override
public void onResponse(JSONObject response)
{
Log.d(TAG + ": ", "somePostRequest Response : " + response.toString());
// here you need to parse to JSON to a list and then call...
List<Item> items = parseResponse(response);
myAdapter.updateDataSet(items);
}
Now, if what you meant was
A Volley request returns in some Activity, I want it to update stuff in another place
there are a couple of options:
As someone said in the comments - you could go for EventBus.
You could hold a DataManager class, which would be a global singleton, in which case you can either hold the data and update it there, and then every activity (in it's onResume or other relevant lifecycle method) knows to pull that data.
You could do the same as option 2, with the exception of that DataManager holding a reference to other UI parts (e.g. Fragments), and triggering member methods in them that pass the data and trigger the updates.
Personally I find option 3 cumbersome and somewhat bad practice, but if all else fails, (and it shouldn't, but if it does) then you can try.
There are more options out there, it depends and varies according to the data, your app architecture, coding style and other stuff you apply.
Hope this helps!
You can use EventBus. To use EventBus you need to register class where you will receive update and when you publish event for those event all classes will receive it.
Here is an example using greenrobot's EventBus :
Event Model :
public class MessageEvent {
/* Additional fields if needed */
}
Subscribe :
EventBus.getDefault().register(this); // In Activity onCreate method
#subscribe
public void onMessageEvent(MessageEvent event){
// this is the method to receive event
}
Publish event :
EventBus.getDefault().post(new MessageEvent());
Now every class subscribed for this event model will be updated.
Note : subscribed classes have to alive, If anyone destroyed they won't receive update.

how to properly pass context between methods to use database repository?

Ok this is a bit more theoretical question.
I have PlayerRepository. This is a class that is used to make actions on my SQLite database. I've implemented there actions like select, insert, update etc.
public PlayerRepository(Context context) {
super(context, com.fixus.portals.model.Player.class);
open();
}
super in constructor is cause PlayerRepository extends Repository which is also my class. The most important part of Repository is this one
public class Repository<T> {
protected static SQLiteDatabase db = null;
protected static MainHelper helper = null;
protected Context context;
private Class<T> type;
public Repository(Context context, Class<T> classz) {
this.type = classz;
this.context = context;
if(helper == null) {
helper = new MainHelper(context.getApplicationContext());
}
}
public static void open() {
if(db == null) {
db = helper.getWritableDatabase();
}
}
}
As you can see when I create the repository I'm opening DB if it wasn't open before. To do that I need to pass the Context of the application/activity. That is not a problem.
BUT sometimes I want to use my repository out side of an activity. In some kind of tool class that need to get data. So I have two ways that I can think about
I get the data in activity and pass it to my tool class/method so I don't need to use repository in it. This is not very flexible
I need to pass context to my tool class/method. But that means that every kind of operation need to receive a context and I'm not sure this is a good way
Am I missing something ? is there any better way to handle it ?
You always need a Context to access the SQLite database so what u could do is change the constructor of that specific tool class and pass a new instance of PlayerRepository as a parameter. This prevents your tool class of needing a context itself.
Imo if u have multiple classes using the database best approach is to create a new class whose only job is doing database actions and put all the needed action inside that one.
Just create an object of this database class with the Context of the current activity the to Tools and PlayerRepository constructors. This way neither your PlayerRepository or Tools classes need Context and both can make actions on the database.
Even if you should really need Context in PlayerRepository it is always best to keep all database related functions centralized in a single class.
I understand that this is an old question but still i'll write for those like me who will pass by this in future.
In order to get rid of the context problem with repository pattern used for accessing database you can implement DI (Dependency Injection) pattern in your project. There are many reasons to do such and that question illustrates one of them.
If you implement DI you would have only one instance of database repository amongst the entire module (or app). This instance would be created in a class which has context, and injected to those classes where needed.
One of the simpliest approaches for using DI is to use Dagger 2 library. All of the related information you could find on their site.

Dynamically set the authority of a ContentProvider

Perhaps the title is a bit misleading. My problem is that I have an Android library project which is shared between two standard Android projects: one for a free version of the app and the other for a paid version. The library currently has the code for a ContentProvider, including a contract class with several static String variables for things such as the URI and column names. Now I want the "authority" for the URI to change depending on which app is using the library. One solution that comes to mind is storing the authority as a string resource and loading that string at run-time into the static final String variable. However, I'm not sure how to do this as the contract class has a private constructor and no Context object in order to load the string resource. What other options are available to solve my problem?
Here's a better solution for those using newer versions of the build tools: make the authority relative to your application ID. You can do this automatically using ${applicationId}, which is expanded into your app's application ID during the build process.
<provider
android:name=".MyContentProvider"
android:authorities="${applicationId}.provider"/>
Let's say your application IDs are com.example.app.paid and com.example.app.free. When you build your app, the authority will become com.example.app.paid.provider and com.example.app.free.provider, correspondingly.
To reference the provider authority in your code, use BuildConfig.APPLICATION_ID + ".provider".
Using different authorities for the free and the paid version makes sense in case the user tries to install both versions.
I'm defining a different authority for the two versions in the manifest like so:
<provider
android:name="MyApp.MyProvider"
android:authorities="MyApp.MyProvider.free"
android:grantUriPermissions="true"/>
Then I configure the provider in an xml file (I use a special config.xml file because I have more configuration data like the provider authority, but you can use strings.xml of course):
<string name="my_provider_authority">MyApp.MyProvider.free</string>
The code retrieves the provider authority as any other string resource. To access string resources without a context use the application context. I'm using an application class to have access to the application context from anywhere in my app (there are two exceptions though):
public class MyApplication extends Application {
private static Context sContext;
#Override
public void onCreate() {
super.onCreate();
sContext = this;
}
public static Context getContext() {
return sContext;
}
}
Of course you need to define MyApplication in your manifest.
This allows you to access string and other resources from anywhere in your app.
There are two exception though:
ContentProviders. ContentProviders can be started before Application starts and so you won't have an Application context available. That's no problem though because ContentProviders get their own context through getContext().
Static code: the context might not be available outside the life cycle of Android components (Activities, Fragments, BroadcastReceivers, Services etc.). Static initializers that are relying on the application context are therefore not a good idea. But that's also not a real issue because using a context outside the life cycle of Android components isn't allowed anyway and static methods accessing a context would always be called from within that life cycle. E.g. if an Activity needs to know a ContentProvider's authority it would call a static method in your contract class and that call would be from one of the activity's onXYZ() methods like onCreate() or onStart() which would make sure that the context is initialized. So all you need to do is lazy initialize the variables in your contract class and make sure the caller does retrieve the variables only when it's clear that Application.onCreate() has been called before. Of course from within an activity you could retrieve the string resources directly. The real advantage of my method will become obvious when you need the resources in other classes/objects. These objects would still be tied to the life cycle of some Android component but you wouldn't have to pass around the context to all these objects, which is 1) very cumbersome and 2) very error prone when it comes to leaking the context which could lead to memory usage issues (one of the most common problems with Android apps).
Why change the authority at all? You're not required to export the provider, which means that nobody could even see the authority name except by deconstructing the app. Even then, they wouldn't be able to access the provider.
If it's for your own internal convenience, then I'd use the same authority but put different security on the URIs.
In short, your idea is interesting, but I wouldn't do it that way. Too much of a mess.

Is it OK to use static "database helper" class?

I have some Android projects and most of them are connected with SQLite databases. I'm interested is it a good programming practice (or a bad habbit) to use some static class like "DatabaseHelper.class" in which I would have all static method related for database manipulation. For example
public static int getId(Context context, String name) {
dbInit(context);
Cursor result = db.rawQuery("SELECT some_id FROM table WHERE some_name = '" + name + "'", null);
result.moveToFirst();
int id = result.getInt(result.getColumnIndex("some_id"));
result.close();
return id;
}
where dbInit(context) (which is used in all my static methods for database manipluation) is
private static void dbInit(Context context) {
if (db == null) {
db = context.openOrCreateDatabase(DATABASE_NAME, Context.MODE_PRIVATE, null);
}
}
Then when I need something I can easily call those method(s) with for example
int id = DatabaseHelper.getId(this, "Abc");
EDIT: Do I have to use dbClose on every connection or leave it open per-activity and close per-activity? So do I have change that upper code to something like this?
...
dbClose();
return id;
}
private static void dbClose() {
if (db != null) {
db.close();
}
}
I would suggest you get into the habit of getting a database connection every time you need one, and releasing it back when you are done with it. The usual name for such a facility is a "database connection pool".
This moves the connection logic out of your actual code and into the pool, and allow you to do many things later when you need them. One simple thing, could be that the pool logs how long time a connection object was used, so you can get information about the usage of the database.
Your initial pool can be very simple if you only need a single connection.
I would definitely have your database related code in a separate class, but would really recommend against using a static class or Singleton. It might look good at first because of the convenience, but unfortunately it tightly couples your classes, hides their dependencies, and also makes unit testing harder.
The drawbacks section in wikipedia gives you a small overview of why you might want to explore other techniques. You can also head over here or over there where they give concrete examples of a class that uses a database access singleton, and how using dependency injection instead can solve some of the issues I mentioned.
As a first step, I would recommend using a normal class that you instantiate in your constructor, for ex:
public class MyActivity extends Activity {
private DBAccess dbAccess;
public MyActivity() {
dbAccess = new DBAccess(this);
}
}
As a second step, you might want to investigate frameworks like RoboGuice to break the hard dependency. You code would look something like:
public class MyActivity extends Activity {
#Inject private DBAccess dbAccess;
public MyActivity() {
}
}
Let us know if you want more details!
If you're going to use a singleton the very minimum requirement is that you make it stateless/threadsafe. If you use your getId method as it stands concurrent invocations could potentially cause all manner of strange bugs...
dbInit(context);
May be called for Thread A which then stops processing before hitting the query statement. Subsequently Thread B executes getId and also calls dbInit passing in a different context all together. Thread A would then resume and attempt to execute the query on B's context.
Maybe this isn't a problem in your application but I'd recommend sticking a synchronized modifier on that getId method!

Categories