Проблемы с Android UriMatcher - PullRequest
       12

Проблемы с Android UriMatcher

18 голосов
/ 17 февраля 2011

В ответе на мой предыдущий вопрос кто-то указал, что в Android-классе UriMatcher присуща некоторая неясность (из-за отсутствия лучшего слова).Может кто-нибудь точно определить известные проблемы с UriMatcher?Я разрабатываю контент-провайдера, который полагается на UriMatcher, чтобы правильно соответствовать моему Uris (в отличие от неправильно, я полагаю).Есть ли обходные пути к известным проблемам?Или есть лучшая стратегия для сопоставления 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 не распознаетсяusing: 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:
content: //dsndata.sds2mobile.jobprovider/SDS2MobileDemo/reverse/C_1

Ответы [ 4 ]

35 голосов
/ 22 февраля 2013

Есть три правила, которые недостаточно хорошо документированы, но они важны для понимания механизма сопоставления UriMatcher:

  1. UriMatcher пытается сопоставить весь Uri с шаблоном.Как и метод match () в java.util.regex.Matcher, который возвращает true, только если вся последовательность региона соответствует шаблону сопоставителя (по сравнению с методом find (), который возвращает true для частичных совпадений).
  2. Подстановочные знаки применяются только к одному сегменту пути, означая, что * или SDS2MobileDemo / * никогда не будут совпадать с SDS2MobileDemo / reverse / C_1, но * / * / * или * / * / C_1 будут.
  3. Как только будет найдено совпадение дляСегмент пути не найдет никакого альтернативного соответствия для этого конкретного сегмента пути.

Вот несколько примеров, использующих следующий URL: content: //dsndata.sds2mobile.jobprovider/SDS2MobileDemo/reverse/C_1

Первые два правила легко понять:

  • SDS2MobileDemo / * / * будет соответствовать
  • * / reverse / * будет соответствовать
  • */ * не будет совпадать, потому что он соответствует только двум сегментам пути
  • SDS2MobileDemo не будет совпадать, потому что он соответствует только одному сегменту пути

Третье правило немного сложнее для понимания.Если вы добавите следующие Uris в этом точном порядке:

  • * / неправильно / C_1
  • SDS2MobileDemo / reverse / 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 голосов
/ 21 марта 2011

Гул .... А, опечатка? Кажется, после / reverse /

есть пробелы
uriMatcher.addURI(JobMetaData.AUTHORITY, "*/reverse/*", REVERSE_URI);

должно работать

EDIT

Приведенный ниже пример работает, попробуйте ввести значения и проверьте окно 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)) ;   
}
1 голос
/ 17 февраля 2011

Мы можем использовать Regex в качестве решения для осмотра.

Вот выражение

https?://([-\w\.]+)+(:\d+)?(/([\w/_\.]*(\?\S+)?)?)?

Это должно работать!

0 голосов
/ 11 августа 2011

Я просто столкнулся с проблемой UriMatcher и решил ее.Убедитесь, что вы всегда ставите подстановочные знаки точных совпадений.Если вы этого не сделаете, код UriMatcher идет по подстановочному пути и не возвращается обратно, если выбрал неправильный путь.

К сожалению, я не вижу точно, что не так в вашем случае.Я бы попытался переместить JOBNAME_COLLECTION_URI на самый верх или удалить его;Интересно, вызывает ли это проблему.

...