Проблемы с Android UriMatcher
В ответе на предыдущий мой вопрос кто-то указал, что есть некоторая flakiness (из-за отсутствия лучшего слова), присущая классу Android UriMatcher. Кто-нибудь может определить известные проблемы с UriMatcher? Я проектирую контент-провайдера, который полагается на UriMatcher, чтобы он правильно соответствовал моему Урису (в отличие от неправильного, я полагаю). Существуют ли обходные пути для известных проблем? Или есть лучшая стратегия для сопоставления Uris?
Пример:
Вот настройка кода моего 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);
}
Добавьте еще один 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);
}
И последний Uri не распознается, используя: uriMatcher.match(URI)
В предыдущем вопросе (упомянутом ранее) было рекомендовано переместить оскорбительный Uri в начало вызовов UriMatcher.put(String, int). Это решило предыдущую проблему (и оставило меня с плохим вкусом в моем рту). Попытка того же решения с этим кодом приводит к тому, что текущий первый Uri (JOBNAME_SINGLE_URI) не распознается. Я уверен, что проблема не в моем коде (мне удалось создать рабочий ContentProvider с использованием Uris и отладить все проблемы с ними до этой проблемы), а скорее проблема с Uri-совместимостью в Android.
ОБНОВЛЕНИЕ:
public final static String AUTHORITY = "dsndata.sds2mobile.jobprovider";
Пример Uri:
Содержание://dsndata.sds2mobile.jobprovider/SDS2MobileDemo/reverse/C_1
Ответы
Ответ 1
Существует три правила, которые недостаточно хорошо документированы, но имеют решающее значение для понимания механизма сопоставления UriMatcher:
- UriMatcher пытается сопоставить весь Uri с шаблоном. Подобно методу match() метода java.util.regex.Matcher, который возвращает true только в том случае, если вся последовательность регионов соответствует шаблону сопряжения (по сравнению с методом find(), который возвращает true для частичных совпадений).
- Подстановочные знаки применяются только к одному сегменту пути, то есть * или SDS2MobileDemo/* никогда не будет соответствовать SDS2MobileDemo/reverse/C_1, но */*/* или */*/C_1 будет.
- Как только он найдет соответствие для сегмента пути, он не найдет альтернативного соответствия для этого конкретного сегмента пути.
Вот несколько примеров, используя следующий URL: content://dsndata.sds2mobile.jobprovider/SDS2MobileDemo/reverse/C_1
Первые два правила легко понять:
- SDS2MobileDemo/*/* будет соответствовать
- */reverse/* будет соответствовать
- */* не будет совпадать, поскольку он соответствует только двум сегментам пути
- SDS2MobileDemo не будет соответствовать, поскольку он соответствует только одному сегменту пути
Третье правило немного сложнее понять.
Если вы добавите следующий Uris в этом точном порядке:
- */неправильно/C_1
- SDS2MobileDemo/реверс/C_1
то он не найдет совпадение, потому что это означает следующий (псевдо) код:
if ("*".matches("SDS2MobileDemo")) {
// tries to match the other parts but fails
}
else if ("SDS2MobileDemo".matches("SDS2MobileDemo")) {
// will never be executed
}
Если вы измените порядок, то код (псевдо) будет выглядеть следующим образом:
if ("SDS2MobileDemo".matches("SDS2MobileDemo")) {
// tries to match the other parts and succeeds
}
else if ("*".matches("SDS2MobileDemo")) {
// will never be executed
}
Теперь, насколько оригинальный вопрос.
SDS2MobileDemo/reverse/C_1 будет соответствовать */reverse/*, но не к примеру. JobNames/reverse/C_1, потому что тот будет спускаться по пути JobNames/*...
Также ясно, что перемещение */reverse/* в начало не является решением, потому что все остальные шаблоны, не начинающиеся с *, больше не будут соответствовать.
Там действительно не известно, какое правильное решение до тех пор, пока неизвестно, какие шаблоны должны соответствовать тому, что Uris.
Ответ 2
Хум.... Ах опечатка? Кажется, есть пробелы после/обратного/
uriMatcher.addURI(JobMetaData.AUTHORITY, "*/reverse/*", REVERSE_URI);
должен работать
ИЗМЕНИТЬ
Ниже приведен пример, попробуйте с вашими значениями и проверьте окно LogCat для предупреждений
Проверьте значение в JobMetaData.AUTHORITY.
public static final String PROVIDER ="org.dummy.provider" ;
public static void doTest()
{
testUri("content://"+PROVIDER +"/test/reverse/xb32") ;
testUri("content://"+PROVIDER +"/JobNames/YES/") ;
}
private static void testUri(String pStr)
{
Uri uri = Uri.parse(pStr);
Log.w("Test", "uri = " + pStr) ;
int result = uriMatcher.match(uri) ;
Log.w("Test", "result = " + result) ;
List<String> list = uri.getPathSegments() ;
for (int i = 0 ; i < list.size() ; i++)
Log.w("Test", "Segment" + i + " = " + uri.getPathSegments().get(i)) ;
}
Ответ 3
Мы можем использовать Regex как решение для поиска.
Вот выражение
https?://([-\w\.]+)+(:\d+)?(/([\w/_\.]*(\?\S+)?)?)?
Он должен работать!
Ответ 4
Я просто столкнулся с проблемой UriMatcher и решил ее. Убедитесь, что вы всегда ставите дикие карты в точном соответствии. Если вы не наберете, код UriMatcher сменится подстановочным знаком и не вернется обратно, если он выбрал неправильный путь.
К сожалению, я не вижу точно, что не так в вашем случае. Я бы попытался переместить JOBNAME_COLLECTION_URI на самый верх или удалить его; Интересно, вызвало ли это проблему.