When I call an Android ContentProvider I get the following exception:
java.lang.RuntimeException: Unable to
start activity
ComponentInfo{de.harm.android.couchone/de.harm.android.couchone.CouchContactClient}:
java.lang.IllegalArgumentException:
Unknown URL
content://de.harm.android.couchone.provider/test2
These are the projects:
https://github.com/der-harm/CouchOneProvider
.../CouchOneContacts
Android uses the so-called ContentResolver to communicate with ContentProvider which in turn handles the persistence functionality - accessing the database.
The ContentProvider registers itself with a unique Uri. The ContentResolver calls the ContentProvider with this Uri and passes additional data, like a SQL query string and/or data to be saved.
In the CouchOneProvider/AndroidManifest.xml I have the following:
<provider android:authorities="de.harm.android.couchone.provider"
android:name=".Provider" />
The Provider uses
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(PROVIDER_NAME, DB_Name, URI_COLLECTION);
uriMatcher.addURI(PROVIDER_NAME, DB_Name + "/#", URI_ENTITY);
}
and
public static boolean isCollectionUri(Uri uri) {
return uriMatcher.match(uri) == URI_COLLECTION;
}
to process the CONTENT_URI used by the ContentResolver to call the ContentProvider:
Am I missing permissions in any of both AndroidManifest.xml?
Am I defining the authority in AndroidManifest.xml wrongly?
Is the CONTENT_URI wrong?
Update:
I have additional information:
Logcat says:
Failed to find provider info for
de.harm.android.couchone.provider
This should be the starting point. But so far I couldn't find any solution.
The fully qualified classname of the ContentProvider implementation is:
de.harm.android.couchone.Provider
In AndroidManifext.xml exactly this is specified as authority, except for the name being to lower case, but this should be fine.
The package name is defined previously in the xml file, so ".Provider" should be ok, too.
As to be seen in the exception, the client calls:
content://de.harm.android.couchone.provider/test2
Logcats answer is:
Failed to find provider info for de.harm.android.couchone.provider
I don't see what's missing, perhaps it's Eclipse or emulator problem?
I install the provider as "run project as Android application".
I have resolved this problem:
Both projects had the same package structure. I changed de.harm.android.couchone to de.harm.android.couchone.provider and de.harm.android.couchone.client.
I think this link is related to question topic. How to implement a custom content-provider.
Related
I have implemented a ContentProvider that uses a Room database to store the data. The implementation is done in kotlin and it follows the same pattern shown in this Google example.
The ContentProvider works fine when used in an app. Now I want to write some tests and I am relying on ProviderTestRule for doing so. The configuration I have seems fine, but unfortunately I am getting the following exception, which looks like some initialisation is missing and then the context is not available.
java.lang.UnsupportedOperationException
at androidx.test.rule.provider.DelegatingContext.getSystemService(DelegatingContext.java:277)
at androidx.room.RoomDatabase$JournalMode.resolve(RoomDatabase.java:517)
at androidx.room.RoomDatabase$Builder.build(RoomDatabase.java:943)
I wasn't able to find any example of how to test this scenario. Any hint would be really helpful!
ProviderTestRule internally uses DelegatingContext, which is a wrapper around the application context that purposely limits its capabilities.
From the source code you can see that context.getSystemService is stubbed out, throwing UnsupportedOperationException most of the time:
/**
* This method only supports retrieving {#link android.app.AppOpsManager}, which is needed by
* {#link android.content.ContentProvider#attachInfo}.
*/
#Override
public Object getSystemService(#NonNull String name) {
checkArgument(!TextUtils.isEmpty(name), "name cannot be empty or null");
// getSystemService(Context.APP_OPS_SERVICE) is only used in ContentProvider#attachInfo for
// API level >= 19.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
&& Context.APP_OPS_SERVICE.equals(name)) {
return context.getSystemService(Context.APP_OPS_SERVICE);
}
throw new UnsupportedOperationException();
}
I have no clear explaination why they forbid access to system services for ProviderTestRule in the first place.
Unfortunately, it seems that Room requires access to the ActivityManager in order to find the most appropriate JournalMode.
What you can try to workaround the situation:
Force the JournalMode of you Room database to JournalMode.WRITE_AHEAD_LOGGING (or JournalMode.TRUNCATE), or
If it did not solve the situation, you'd have to write your own ProviderTestRule that uses the real application context to and allow access to the desired system service.
As I understand from the docs, one SyncAdapter defined in a SyncService is limited to receive only one ContentProvider authority to work on.
But, at the same time, it has access to ContentResolver which it allows to run queries on other ContentProviders as well. I don't understand this particular design concept if a developer is needed to provide a single content authority to SyncAdapter and nonetheless she is able to do whatever she wants on whatever ContentProvider she has access to. My question is: What are the consequences of ignoring onPerformSync's parameters: String authority and ContentProviderClient provider and going with pure ContentResolver?
My application's (actually its SyncService) idea is simple: query a calendar server (OwnCloud in my case) to get not only events (synced with com.android.calendar) but also VTODOS, which are then distributed between various task management apps I can get source code and/or ContentProviderContract. I also thought of my own "Hub" ContentProvider, which has basic VTODO/Task structure, and is the only one compared to the server. It should be able to sync 2-way with different content providers of task management apps and then it syncs with the server.
I have read using ContentProviderClient vs ContentResolver to access content provider and I think I understand the difference. I'm now puzzled why there is so strong suggestion from android SDK to use a single ContentProvider in a single SyncAdapter and yet you are allowed to use ContentResolver to bypass that limitation.
I spent all day figuring this out and searched hundreds of SO/Google resources on the matter (some of them multiple times). I have also seen questions regarding using one SyncAdapter to sync multiple ContentProviders, but none of the answers were any close to suggesting using ContentResolver instead.
There is no special limitation on ContentResolver's API when used from the context of SyncAdapter. IMHO, the only reason why the framework passes ContentProviderClient and authority to onPerformSync() is convenience and kind of a hint to developers as to how SyncAdapter intended work.
This fact is easily seen in the source code for AbstractThreadedSyncAdapter.SyncThread - the ContentProviderClient passed to onPerformSync() is obtained in a standard fashion:
#Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// Trace this sync instance. Note, conceptually this should be in
// SyncStorageEngine.insertStartSyncEvent(), but the trace functions require unique
// threads in order to track overlapping operations, so we'll do it here for now.
Trace.traceBegin(Trace.TRACE_TAG_SYNC_MANAGER, mAuthority);
SyncResult syncResult = new SyncResult();
ContentProviderClient provider = null;
try {
if (isCanceled()) {
return;
}
provider = mContext.getContentResolver().acquireContentProviderClient(mAuthority);
if (provider != null) {
AbstractThreadedSyncAdapter.this.onPerformSync(mAccount, mExtras,
mAuthority, provider, syncResult);
} else {
syncResult.databaseError = true;
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_SYNC_MANAGER);
if (provider != null) {
provider.release();
}
if (!isCanceled()) {
mSyncContext.onFinished(syncResult);
}
// synchronize so that the assignment will be seen by other threads
// that also synchronize accesses to mSyncThreads
synchronized (mSyncThreadLock) {
mSyncThreads.remove(mThreadsKey);
}
}
}
Therefore, the bootom line: you can use ContentResolver in your SyncAdapter as you wish - just call getContext().getContentResolver() and access any exported ContentProvider.
I am creating a content provider for an android application, but I am having a problem correctly matching the uri using UriMatacher.
For example, I add the uri's to match (snipped from the link)
sURIMatcher.addURI("content://com.example", "people", PEOPLE);
sURIMatcher.addURI("content://com.example", "people/#", PEOPLE_ID);
sURIMatcher.addURI("content://com.example", "people/#/phones", PEOPLE_PHONES);
And then attempt to access contacts/people/1/phones. The successful match ends up being with PEOPLE_ID instead of PEOPLE_PHONES.
The query is initially generated by this code.
Uri uri = Uri.parse("content://com.example/people/#/phones");
ContentUris.appendId(uri.buildUpon(), 1).build();
With some logging statements thrown in, I see that the following:
The uri passed to the query gives this:
content://com.example/people/1#/phones
but uri.getPath() gives this:
/people/1
The third path part of the uri is clearly dropped, which explains why it was matching the wrong uri.
The example from the Android developer website seems to indicate that there shouldn't be a problem with this. Am I creating the uri incorrectly? Is it just a bug? Is this intended functionality (and therefore the example from android developers is a bad one)?
Uri.parse() is ignorant of the UriMatcher's wildcards; here, the # is the fragment identifier of a URI, so when you parse content://com.example/people/#/phones, it becomes content://com.example/people + fragment /phones. The id is correctly appended to the end of the URI, and then the fragment is carried over. In this case, you can't rely on ContentUris, but rather need to build the Uri the long way:
path = new Uri.Builder()
.scheme( ContentResolver.SCHEME_CONTENT )
.authority( DataProvider.AUTHORITY )
.appendPath( "people" )
.appendPath( "1" )
.appendPath( "phones" ) ).build();
I'm targeting Android 2.2 and newer. This error was generated on a device running 4.x. I am using ORMLite 4.38 libraries.
I need to guarantee every record instance is unique for any number of devices. I was happy to see that ORMLite supports UUIDs as IDs. I've created a UUID - id abstract base class for my database record definitions. allowGeneratedIdInsert is the perfect solution. But this feature seems to cause an 'IllegalStateException: could not create data element in dao'. I tested by removing this annotation, and no issue. Put it back in...same issue. Put the base class stuff in one record definition...same issue.
LogCat also reports:
Caused by: java.sql.SQLException: Unable to run insert stmt on object - objectid: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx
public abstract class UUIDDaoEnabled<T> extends BaseDaoEnabled<T, UUID> {
//allowGeneratedIdInsert allows us to set UUID when this device db didn't create it
#DatabaseField(generatedId = true, allowGeneratedIdInsert=true)
private UUID id;
...
public void setUUIDFromSerializedSource(SerializedModelBinaryInputStream stream, Dao<T, UUID> dao) throws SQLException { //only place we can set UUIDs
if(id == null)
dao.refresh((T)this);
if(id != null)
throw new SQLException("Trying to set UUID on existing object");
id = stream.getCurrentUUID();
}
}
I'll specialize like so:
#DatabaseTable()
public class Type extends UUIDDaoEnabled<Type> { ... }
I can't explain this from the documentation for allowGeneratedIdInsert and generatedId. In fact the documentation for alloeGeneratedIdInsert says it overrides the default behavior of generatedId. It also says
This only works if the database supports this behavior
Yet, I have read in other posts that ORMLite 4.25 (?) and newer supports this behavior on Android devices. So, either that's not entirely true. Or I'm doing something stupid...anyone???
UPDATE: after thinking about it for a minute, I realized that neither allowGeneratedIdInsert support, nor inheritance can be the root cause, because I instantiate other objects based on the same abstract class. What I can't figure out is why one particular class is causing the issue. The only unique thing about the offending record type (compared to other types that create) is it is a many in a one to many, and it contains several to manies. Could these properties, combined with allowGenereatedIdInsert, be the root issue? Rather, I should ask, has anyone seen this issue in this circumstance?
UPDATE: nevermind the question. I can use updateId(...) instead of allowGeneratedIdInsert.
So I'm not sure about this but it looks to me that you are trying to insert an element twice into a table with the same UUID id. The exception is saying there is a constraints failure:
IllegalStateException: Could not create data element in dao
at BaseForeignCollection.add(BaseForeignCollection.java:57)
...
Caused by: SQLiteConstraintException: error code 19: constraint failed
If you call foreignCollection.add(...); it does the same thing as dao.create(...); -- and you can't do both of these with the same object. If you have an existing object that has already been created by the DAO and you want to associate it with another object, you should do something like:
// associate this object with another
existingObject.setForeignField(...);
// now update it in the db
existingObjectDao.update(existingObject);
You can't add it to the foreignField's foreign collection.
I had a similar problem. But it was caused by using create instead createOrUpdate to save the object.
It is also important to uninstall the application before changing this to ensure that the database has been removed and will not keep the old behavior.
Edit: createOrUpdate is very time expensive. It's better use just create with great amounts of data.
Edit 2:It is also bether to use a TransactionManager.callInTransaction.
I am creating a custom ContentProvider in Android, all the examples I find show the database name being hardcoded, instantiated like this:
public class ItemProvider extends ContentProvider {
private static String DATABASE_NAME = "xyz";
public static class ItemDatabaseHelper extends SQLiteOpenHelper {
ItemDatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
What I want to do is use a variable for the database name at runtime, I dont want to hard code the database name in the class. I have tried to find examples of doing this using the ContentProvider class, and have looked through the documentation as well. I can do this if I shed the ContentProvider class and just use a SQLiteOpenHelper class because I can pass in the database name to the constructor as a parameter but I cannot figure out if its possible for ContentProvider. Here is how I get a variable name for the database using SQLiteOpenHelper:
public static class ItemDatabaseHelper extends SQLiteOpenHelper {
ItemDatabaseHelper(Context context, String dbname) {
super(context, dbname, null, DATABASE_VERSION);
}
Can anyone help me get a variable database name for a Content Provider?
Thanks.
I've not really used ContentProviders myself, but looking at the docs I'm guessing the root of the problem is that you don't instantiate the ContentProvider yourself, but that Android does when it is needed to handle a request.
If you want the database name to be defined at runtime by your application, then you could simply use a public static variable, which you set appropriately from another part of your application. You could then reference this from your ContentProvider.
Alternatively, if you're wanting the person who requests something from the ContentProvider to be able to specify the database to query, then from the docs it looks like you could use the path of the request URI to specify the database to query.
The structure of the request URI is detailed here: http://developer.android.com/guide/topics/providers/content-providers.html#urisum and further up this page it says:
The authority is what identifies the provider, not the path; your provider can interpret the path part of the URI in any way you choose.
So as an example, I would imagine you could use an URI of the form:
content://com.example.yourprovider/DB_REF/ID
Then in your implementation of the abstract methods of ContentProvider you could parse the URI to determine the DB to use.
One word of warning though - if you are going to use this approach, then I would suggest not using the value passed in directly. It would be much better to use some kind of validation against a finite list, so that people can't just query any of your databases (if they know their name).
Hopefully that makes sense :)
First, let me say that I've looked at lots of examples for ContentProvider code on the web, and the one that ended up helping me the most was this one:
http://mobile.tutsplus.com/tutorials/android/android-sdk_content-providers/
I have a very similar situation to yours, where I want to have several differently-named databases containing vehicle fuel mileage data. Each of the names consists of a vehicle name (specified by the user) and the current year, plus the fixed text "fuel_data" to make the database filenames human-readable.
Unlike all of the examples I found, I do not create an instance of the database in the ContentProvider's onCreate method. This is because I don't yet know the vehicle name at that point, thanks to the way my code is written (and the fact that ContentProviders are instantiated so early in the Activity lifecycle).
Once I do know the vehicle name (from which I can easily construct the database name), I call a simple little method that I added to my ContentProvider class. As you can see, all it does is close the prior database (if one was open) and open the new one (using the name that I now have available).
I call this method again every time the user selects a new vehicle.
` private static FuelDatabase mDB = null;
public static void switchDatabases( Context context, String newVehicleName )
{
if ( mDB != null )
{
mbB.close();
}
mDB = new FuelDatabase( context, newVehicleName + "." + getCurrentYear( context ) );
}`