Last Couple of days I have been spending times on learning new Android Architecture Components . After following up some blog posts, documentation & tutorials , every components were getting clear to me . But Suddenly I realised what about our old friend Content Provider . I might sound silly , because before writing this question I have spent quite a time searching , Am I be the only one came up with this question . I hadn't got any helpful solution . Anyways here is it , if I want to build up an app with local DB , I will now obviously choose new Architecture Components (live data , view model , room ) without any farther thinking this will be very helpful to make app 10x robust . But If I want my DB datas accessible to other app , for instance To Widget How do I integrate Content Provider with Room ?
I had the same question by the way. And I found a sample here which answers my question. Hope it does the same with you.
In short, this is in the DAO object which would be called from Content Provider's query()
method.
/**
* Select all cheeses.
*
* @return A {@link Cursor} of all the cheeses in the table.
*/
@Query("SELECT * FROM " + Cheese.TABLE_NAME)
Cursor selectAll();
Notice how it returns Cursor
object. Other operations, you can see for yourself in more detail in the sample.
This here is choice number 3 in the answer by @CommonsWare, I think.
if I want to build up an app with local DB , I will now obviously choose new Architecture Components (live data , view model , room )
I would not use the term "obviously" there. The Architecture Components are an option, but not a requirement.
But If I want my DB datas accessible to other app , for instance To Widget How do I integrate Content Provider with Room ?
An app widget is unrelated to a ContentProvider
. IMHO very few apps should be exposing databases to third parties via ContentProvider
, and no apps should be using a ContentProvider
purely for internal purposes.
That being said, you have a few choices:
Do not use Room, at least for the tables to be exposed via the
ContentProvider
Use Room for internal purposes, but then use classic SQLite programming techniques for the
ContentProvider
, by callinggetOpenHelper()
on yourRoomDatabase
Use Room in the
ContentProvider
, writing your own code to build up aMatrixCursor
from the Room entities that you retrieve (forquery()
) or creating the entities for use with other operations (forinsert()
,update()
,delete()
, etc.)
RemoteViewsFactory
run in a separate process (host's)? Isn't ContentProvider
needed there to avoid multiple RoomDatabase
s? –
Midyear AppWidgetProvider
delegate the work to a service, for simple app widgets. "Doesn't RemoteViewsFactory run in a separate process (host's)?" -- not by default, as everything is in one process by default. Your problem in that case is threading, and I don't know what the "state of the art" is for RemoteViewsFactory
and threading models. –
Oringas SQLiteDatabase
is thread-safe, outside of deadlock scenarios. Even for those, a repository would offer a lighter-weight approach than would a ContentProvider
. You are welcome to do what you want, but bear in mind that conventional wisdom has moved on from your techniques, and so new approaches (e.g., Room) may not necessarily be conducive to your approach. –
Oringas iosched
app has exported its provider, meaning that it is for external purposes, not internal ones. SyncAdapter
is for external purposes, not internal ones (and, few people use SyncAdapter
). The documentation that you link to has "content providers are primarily intended to be used by other applications". Hence, I stand by my answer as written. –
Oringas SyncAdapter
. "What is your proposed solution, if not to use SyncAdapter? " -- server push (FCM), JobScheduler
, AlarmManager
, and manual request (e.g., pull-to-refresh). –
Oringas SyncAdapter
is not especially popular, and I do not recommend it. –
Oringas Room Library does not have any particular support for Content Provider. You can only write Content Provider on your own and then use Room to query a database.
If you want to use Android Architecture Components and you want to work with SQLite based Content Providers, consider using Kripton Persistence Library: it allows to generate Live Data from DB queries, generate Content Provider for you, and much more. Least but not last: why do you have to write the entire SQL, when you need only to write the where conditions?
Just to be clear, I'm the author of Kripton Persistence Library. I wrote it because I didn't find a unique library that fit all my need in terms of persistence management (and yes, because I like to program).
I wrote an converted version of Google Content Provider Sample with Kripton. You can found it here.
Just to simplify the reading. With Kripton, you only need to define a DAO interface. Content provider will be generated by the annotations. The same DAO converted in Kripton will be:
@BindContentProviderPath(path = "cheese")
@BindDao(Cheese.class)
public interface CheeseDao {
@BindSqlSelect(fields="count(*)")
int count();
@BindContentProviderEntry
@BindSqlInsert
long insert(String name);
@BindContentProviderEntry()
@BindSqlSelect
List<Cheese> selectAll();
@BindContentProviderEntry(path = "${id}")
@BindSqlSelect(where ="id=${id}")
Cheese selectById(long id);
@BindContentProviderEntry(path = "${id}")
@BindSqlDelete(where ="id=${id}")
int deleteById(long id);
@BindContentProviderEntry(path = "${cheese.id}")
@BindSqlUpdate(where="id=${cheese.id}")
int update(Cheese cheese);
}
The generated Content Provider exposes DAO's method with URIs. For clearification, I put here only the generated JavaDoc (always by Kripton).
More information about Kripton on its wiki, my site and on my articles .
Late post but I bumped in the same issue recently. Finally ended up in using the same Room Database instance for both local and content provider purpose.
So the app itself uses Room Database as usual and Content Provider "wraps" Room Database with "open helper" as follows:
class DatabaseProvider : ContentProvider() {
override fun onCreate(): Boolean {
return true
}
override fun query(uri: Uri?, projection: Array<out String?>?, selection: String?, selectionArgs: Array<out String?>?, sortOrder: String?): Cursor? {
val db = roomDatabase.openHelper.readableDatabase
db.query(...)
}
override fun insert(uri: Uri?, values: ContentValues?): Uri? {
val db = roomDatabase.openHelper.writableDatabase
db.insert(...)
}
override fun update(uri: Uri?, values: ContentValues?, selection: String?, selectionArgs: Array<out String?>?): Int {
val db = roomDatabase.openHelper.writableDatabase
db.update(...)
}
override fun delete(uri: Uri?, selection: String?, selectionArgs: Array<out String?>?): Int {
val db = roomDatabase.openHelper.writableDatabase
db.delete(...)
}
override fun getType(uri: Uri?): String? {
}
}
you'd better use the SupportOpenHelper
public class MyContentProvider extends ContentProvider {
public MyContentProvider() {
}
@Override
public String getType(Uri uri) {
// TODO: Implement this to handle requests for the MIME type of the data
// at the given URI.
throw new UnsupportedOperationException("Not yet implemented");
}
UserDatabase database;
@Override
public boolean onCreate() {
database = Room.databaseBuilder(getContext(), UserDatabase.class, "user.db").allowMainThreadQueries().build();
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
return database.query(SupportSQLiteQueryBuilder.builder("user").selection(selection, selectionArgs).columns(projection).orderBy(sortOrder).create());
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return database.getOpenHelper().getWritableDatabase().update("user", 0, values, selection, selectionArgs);
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return database.getOpenHelper().getWritableDatabase().delete("user", selection, selectionArgs);
}
@Override
public Uri insert(Uri uri, ContentValues values) {
long retId = database.getOpenHelper().getWritableDatabase().insert("user", 0, values);
return ContentUris.withAppendedId(uri, retId);
}
}
© 2022 - 2024 — McMap. All rights reserved.