In an answer to a previous question of mine someone indicated that there is some flakiness (for lack of a better word) inherent in the Android class UriMatcher. Can anyone pinpoint the known issues with UriMatcher? I'm designing a Content Provider that relies on UriMatcher to match my Uris correctly (as opposed to incorrectly I suppose). Are there workarounds to the known issues? Or is there a better strategy for matching Uris?
Example:
Here is the code setting my UriMatcher
private static final int MEMBER_COLLECTION_URI = 1;
private static final int MEMBER_SINGLE_URI = 2;
private static final int SUBMATERIAL_COLLECTION_URI = 3;
private static final int SUBMATERIAL_SINGLE_URI = 4;
private static final int JOBNAME_COLLECTION_URI = 5;
private static final int JOBNAME_SINGLE_URI = 6;
private static final int ALL_MEMBERS_URI = 7;
private static final int ALL_SUBMATERIAL_URI = 8;
static
{
//return the job and fab for anything matching the provided jobName
// JobNames/jobName
uriMatcher.addURI(JobMetaData.AUTHORITY, "JobNames/*/",
JOBNAME_SINGLE_URI);
//return a collection of members
// jobName/member/attribute/value
uriMatcher.addURI(JobMetaData.AUTHORITY, "*/member/*/*/",
MEMBER_COLLECTION_URI);
//return a single member
// jobName/member/memberNumber
uriMatcher.addURI(JobMetaData.AUTHORITY, "*/member/*/",
MEMBER_SINGLE_URI);
//return a collection of submaterial
// jobName/submaterial/attribute/value
uriMatcher.addURI(JobMetaData.AUTHORITY, "*/submaterial/*/*",
SUBMATERIAL_COLLECTION_URI);
//return a single piece of submaterial
// jobName/submaterial/GUID
//GUID is the only way to uniquely identify a piece of submaterial
uriMatcher.addURI(JobMetaData.AUTHORITY, "*/submaterial/*",
SUBMATERIAL_SINGLE_URI);
//Return everything in the member and submaterial tables
//that has the provided attribute that matches the provided value
// jobName/attribute/value
//not currently used
uriMatcher.addURI(JobMetaData.AUTHORITY, "JobNames/",
JOBNAME_COLLECTION_URI);
//return all members in a job
uriMatcher.addURI(JobMetaData.AUTHORITY, "*/members/",
ALL_MEMBERS_URI);
}
Add another Uri:
private static final int MEMBER_COLLECTION_URI = 1;
private static final int MEMBER_SINGLE_URI = 2;
private static final int SUBMATERIAL_COLLECTION_URI = 3;
private static final int SUBMATERIAL_SINGLE_URI = 4;
private static final int JOBNAME_COLLECTION_URI = 5;
private static final int JOBNAME_SINGLE_URI = 6;
private static final int ALL_MEMBERS_URI = 7;
private static final int ALL_SUBMATERIAL_URI = 8;
//ADDITIONAL URI
private static final int REVERSE_URI = 9;
static
{
//return the job and fab for anything matching the provided jobName
// JobNames/jobName
uriMatcher.addURI(JobMetaData.AUTHORITY, "JobNames/*/",
JOBNAME_SINGLE_URI);
//return a collection of members
// jobName/member/attribute/value
uriMatcher.addURI(JobMetaData.AUTHORITY, "*/member/*/*/",
MEMBER_COLLECTION_URI);
//return a single member
// jobName/member/memberNumber
uriMatcher.addURI(JobMetaData.AUTHORITY, "*/member/*/",
MEMBER_SINGLE_URI);
//return a collection of submaterial
// jobName/submaterial/attribute/value
uriMatcher.addURI(JobMetaData.AUTHORITY, "*/submaterial/*/*",
SUBMATERIAL_COLLECTION_URI);
//return a single piece of submaterial
// jobName/submaterial/GUID
//GUID is the only way to uniquely identify a piece of submaterial
uriMatcher.addURI(JobMetaData.AUTHORITY, "*/submaterial/*",
SUBMATERIAL_SINGLE_URI);
//Return everything in the member and submaterial tables
//that has the provided attribute that matches the provided value
// jobName/attribute/value
//not currently used
uriMatcher.addURI(JobMetaData.AUTHORITY, "JobNames/",
JOBNAME_COLLECTION_URI);
//return all members in a job
uriMatcher.addURI(JobMetaData.AUTHORITY, "*/members/",
ALL_MEMBERS_URI);
//ADDITIONAL URI
uriMatcher.addURI(JobMetaData.AUTHORITY, "*/reverse/*",
REVERSE_URI);
}
And the last Uri is not recognized using: uriMatcher.match(uri)
On the previous question (mentioned earlier) it was recommended that I move the offending Uri to the top of the calls to UriMatcher.put(String, int). That solved the previous problem (and left me with a bad taste in my mouth). Trying the same solution with this code results in the current first Uri (JOBNAME_SINGLE_URI) going unrecognized. I'm fairly sure that the issue isn't in my code (I've managed to create a working ContentProvider utilizing Uris and debug all of the problems with them prior to this issue), but rather is an issue with Uri matching in Android.
UPDATE:
public final static String AUTHORITY = "dsndata.sds2mobile.jobprovider";
Sample Uri:
content://dsndata.sds2mobile.jobprovider/SDS2MobileDemo/reverse/C_1
There are three rules that aren't well documented but that are crucial to understanding the matching mechanism of UriMatcher:
Here are some examples using the following url: content://dsndata.sds2mobile.jobprovider/SDS2MobileDemo/reverse/C_1
The first two rules are easy to understand:
The third rule is a little harder to understand. If you add the following Uris in this exact order:
then it won't find a match because that translates into the following (pseudo) code:
if ("*".matches("SDS2MobileDemo")) {
// tries to match the other parts but fails
}
else if ("SDS2MobileDemo".matches("SDS2MobileDemo")) {
// will never be executed
}
If you reverse the order the (pseudo) code becomes:
if ("SDS2MobileDemo".matches("SDS2MobileDemo")) {
// tries to match the other parts and succeeds
}
else if ("*".matches("SDS2MobileDemo")) {
// will never be executed
}
Now as far as the original question goes. SDS2MobileDemo/reverse/C_1 will get matched by */reverse/* but not e.g. JobNames/reverse/C_1 because that one will go down the JobNames/* path... Also it's clear that moving */reverse/* to the top isn't the solution because all other patterns not starting with * won't match any more. There's really no telling what the correct solution is as long as it's unknown which patterns should match which Uris.