Android 的 UriMatcher 问题

发布于 2024-10-18 01:10:17 字数 4920 浏览 2 评论 0原文

在回答我之前的问题时,有人指出 Android 类 UriMatcher 固有的一些脆弱性(因为缺乏更好的词)。谁能指出 UriMatcher 的已知问题吗?我正在设计一个依赖 UriMatcher 来正确匹配我的 Uris 的内容提供程序(而不是我认为的错误匹配)。已知问题有解决方法吗?或者说有更好的策略来匹配Uris吗?

示例:

代码

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);

}

这是设置我的 UriMatcher添加另一个 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) 无法识别。我相当确定问题不在我的代码中(我已经成功地利用 Uris 创建了一个工作 ContentProvider,并在出现此问题之前调试了它们的所有问题),而是 Android 中的 Uri 匹配问题。

更新:

public final static String AUTHORITY = "dsndata.sds2mobile.jobprovider";

示例 Uri:
内容://dsndata.sds2mobile.jobprovider/SDS2MobileDemo/reverse/C_1

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

素罗衫 2024-10-25 01:10:17

有三个规则没有详细记录,但对于理解 UriMatcher 的匹配机制至关重要:

  1. UriMatcher 尝试将整个 Uri 与模式进行匹配。与 java.util.regex.Matcher 的 matches() 方法非常相似,仅当整个区域序列与匹配器的模式匹配时才返回 true(与部分匹配返回 true 的 find() 方法相比)。
  2. 通配符仅应用于一个路径段,这意味着 * 或 SDS2MobileDemo/* 永远不会匹配 SDS2MobileDemo/reverse/C_1,但 */*/* 或 */*/C_1 会匹配。
  3. 一旦找到路径段的匹配项,它就不会找到该特定路径段的任何替代匹配项。

以下是使用以下 url 的一些示例: content://dsndata.sds2mobile.jobprovider/SDS2MobileDemo/reverse/C_1

前两条规则很容易理解:

  • SDS2MobileDemo/*/* 将匹配
  • */reverse/* 将匹配
  • */*不会匹配,因为它只匹配两个路径段
  • SDS2MobileDemo 不会匹配,因为它只匹配一个路径段

第三条规则有点难以理解。
如果您按照以下确切顺序添加以下 Uris:

  • */wrong/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 匹配,就真的不知道正确的解决方案是什么。

There are three rules that aren't well documented but that are crucial to understanding the matching mechanism of UriMatcher:

  1. UriMatcher tries to match the entire Uri against the pattern. Much like the matches() method of java.util.regex.Matcher that returns true only if the entire region sequence matches the matcher's pattern (compared to the find() method that returns true for partial matches).
  2. Wildcards are applied to one path segment only, meaning * or SDS2MobileDemo/* will never match SDS2MobileDemo/reverse/C_1 but */*/* or */*/C_1 will.
  3. Once it finds a match for a path segment it won't find any alternative match for that particular path segment.

Here are some examples using the following url: content://dsndata.sds2mobile.jobprovider/SDS2MobileDemo/reverse/C_1

The first two rules are easy to understand:

  • SDS2MobileDemo/*/* will match
  • */reverse/* will match
  • */* won't match because it matches only two path segments
  • SDS2MobileDemo won't match because it matches only one path segment

The third rule is a little harder to understand.
If you add the following Uris in this exact order:

  • */wrong/C_1
  • SDS2MobileDemo/reverse/C_1

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.

情释 2024-10-25 01:10:17

嗯....啊错字?似乎 /reverse/ 之后有一些空格

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)) ;   
}

Hum .... Ah typo ? Seems there is some whitespace after /reverse/

uriMatcher.addURI(JobMetaData.AUTHORITY, "*/reverse/*", REVERSE_URI);

should work

EDIT

The example below works, try with your values and check your LogCat Window for warnings

Check the value in 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)) ;   
}
旧瑾黎汐 2024-10-25 01:10:17

我们可以使用正则表达式作为环顾解决方案。

这是表达

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

它应该有效!

We can use Regex as a look around solution.

Here is the expression

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

It should work!

姐不稀罕 2024-10-25 01:10:17

我刚刚遇到了 UriMatcher 问题并解决了它。确保您始终放置完全匹配的通配符。如果不这样做,UriMatcher 代码将沿着通配符路径前进,如果选择了错误的路径,则不会返回。

不幸的是,我不明白你的情况到底出了什么问题。我会尝试将 JOBNAME_COLLECTION_URI 移动到最顶部或将其删除;我想知道这是否造成了问题。

I just ran into a UriMatcher problem and solved it. Make sure that you always put wild cards exact matches. If you don', the UriMatcher code goes down the wildcard path and doesn't go back up if it chose the wrong path.

Unfortunately, I don't see exactly what's wrong in your case. I would try moving JOBNAME_COLLECTION_URI to the very top or removing it; I wonder if that's causing a problem.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文