当前位置: 首页 > news >正文

IntentUri页面跳转


 

android browser支持支持Intent Scheme URL语法的可以在wrap页面加载或点击时,通过特定的intent uri链接可以打开对应app页面,例如

 <a href="intent://whatsapp/#Intent;scheme=myapp;package=com.xiaoyu.myapp;S.help_url=http://Fzxing.org;end">what can I do</a>  

通过android系统解析查找符合规则的Activity后启动对应activity,如果未找到匹配Activity则browser进行处理。

配置

1
2
3
4
5
6
7
8
<a href="intent://whatsapp/#Intent;scheme=myapp;package=com.xiaoyu.myapp;S.help_url=http://Fzxing.org;end">what can I do</a> 
   //这样如果没有对应应用,该链接就会跳转到S.help_url指定的url上
   <intent-filter> 
       <action android:name="android.intent.action.VIEW" />
       <category android:name="android.intent.category.DEFAULT" /> 
       <category android:name="android.intent.category.BROWSABLE" /><!-- 定义浏览器类型,有URL需要处理时会过滤 --> 
       <data android:scheme="myapp" android:host="whatsapp" android:path="/" /><!-- 打开以whatsapp协议的URL,这个自己定义 --> 
   </intent-filter> 

  

URI的解析与生成:

在Intent 类内部有个parseUri(String uri, int flags)方法用来解析Uri生成Intent返回,下面主要分析该方法:

在分析该解析方法时候,首先我们需要理解intent uri规则,在intent类里规则串如下,

1android-app://com.example.app/<br />#Intent;action=com.example.MY_ACTION;<br />i.some_int=100;S.some_str=hello;end

 根据此规则,看出主要分为三个部分:

Header: android-app://android app为判断是否是android 应用

Action: com.example.MY_ACTION //组件的action内容

Package: com.example.app //包名启动组件使用

Extra: some_int=(int)100,some_str=(String)hello //传递的数据,i代表int,S代表String ,c代表char等基本类型

除了这几部分内容也包含intent内的其它内容,如categry,type,component,selector等

在简单分析理解Uri规则串后,再来分析parseUri就很好理解了,下面看一下主要代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
public static Intent parseUri(String uri, int flags) throws URISyntaxException {
       int i = 0;
       try {
           final boolean androidApp = uri.startsWith("android-app:");//判断开头是否为android,
 
           // Validate intent scheme if requested.
           if ((flags&(URI_INTENT_SCHEME|URI_ANDROID_APP_SCHEME)) != 0) {
               if (!uri.startsWith("intent:") && !androidApp) {//是否为intent Uri
                   Intent intent = new Intent(ACTION_VIEW);
                   try {
                       intent.setData(Uri.parse(uri));
                   catch (IllegalArgumentException e) {
                       throw new URISyntaxException(uri, e.getMessage());
                   }
                   return intent;
               }
           }
 
           i = uri.lastIndexOf("#");//查找标记位,开始解析
           // simple case
           if (i == -1) {
               if (!androidApp) {
                   return new Intent(ACTION_VIEW, Uri.parse(uri));
               }
 
           // old format Intent URI
           else if (!uri.startsWith("#Intent;", i)) {
               if (!androidApp) {
                   return getIntentOld(uri, flags);
               else {
                   i = -1;
               }
           }
 
           // new format
           Intent intent = new Intent(ACTION_VIEW);
           Intent baseIntent = intent;
           boolean explicitAction = false;
           boolean inSelector = false;
 
           // fetch data part, if present
           String scheme = null;
           String data;
           if (i >= 0) {
               data = uri.substring(0, i);
               i += 8// length of "#Intent;"
           else {
               data = uri;
           }
 
           // loop over contents of Intent, all name=value;
           while (i >= 0 && !uri.startsWith("end", i)) {//按类别分离循环处理(解析action,categry,type,extra data)
               int eq = uri.indexOf('=', i);
               if (eq < 0) eq = i-1;
               int semi = uri.indexOf(';', i);
               String value = eq < semi ? Uri.decode(uri.substring(eq + 1, semi)) : "";
 
               // action
               if (uri.startsWith("action=", i)) {
                   intent.setAction(value);
                   if (!inSelector) {
                       explicitAction = true;
                   }
               }
 
               // categories
               else if (uri.startsWith("category=", i)) {
                   intent.addCategory(value);
               }
 
               // type
               else if (uri.startsWith("type=", i)) {
                   intent.mType = value;
               }
 
               // launch flags
               else if (uri.startsWith("launchFlags=", i)) {
                   intent.mFlags = Integer.decode(value).intValue();
                   if ((flags& URI_ALLOW_UNSAFE) == 0) {
                       intent.mFlags &= ~IMMUTABLE_FLAGS;
                   }
               }
 
               // package
               else if (uri.startsWith("package=", i)) {
                   intent.mPackage = value;
               }
 
               // component
               else if (uri.startsWith("component=", i)) {
                   intent.mComponent = ComponentName.unflattenFromString(value);
               }
 
               // scheme
               else if (uri.startsWith("scheme=", i)) {
                   if (inSelector) {
                       intent.mData = Uri.parse(value + ":");
                   else {
                       scheme = value;
                   }
               }
 
               // source bounds
               else if (uri.startsWith("sourceBounds=", i)) {
                   intent.mSourceBounds = Rect.unflattenFromString(value);
               }
 
               // selector
               else if (semi == (i+3) && uri.startsWith("SEL", i)) {
                   intent = new Intent();
                   inSelector = true;
               }
 
               // extra data parse
               else {
                   String key = Uri.decode(uri.substring(i + 2, eq));
                   // create Bundle if it doesn't already exist
                   if (intent.mExtras == null) intent.mExtras = new Bundle();
                   Bundle b = intent.mExtras;
                   // add EXTRA,这里巧妙的对基本数据类型进行处理,(字母.var)值得学习借鉴
                   if      (uri.startsWith("S.", i)) b.putString(key, value);
                   else if (uri.startsWith("B.", i)) b.putBoolean(key, Boolean.parseBoolean(value));
                   else if (uri.startsWith("b.", i)) b.putByte(key, Byte.parseByte(value));
                   else if (uri.startsWith("c.", i)) b.putChar(key, value.charAt(0));
                   else if (uri.startsWith("d.", i)) b.putDouble(key, Double.parseDouble(value));
                   else if (uri.startsWith("f.", i)) b.putFloat(key, Float.parseFloat(value));
                   else if (uri.startsWith("i.", i)) b.putInt(key, Integer.parseInt(value));
                   else if (uri.startsWith("l.", i)) b.putLong(key, Long.parseLong(value));
                   else if (uri.startsWith("s.", i)) b.putShort(key, Short.parseShort(value));
                   else throw new URISyntaxException(uri, "unknown EXTRA type", i);
               }
 
               // move to the next item
               i = semi + 1;
           }
 
           if (inSelector) {
               // The Intent had a selector; fix it up.
               if (baseIntent.mPackage == null) {
                   baseIntent.setSelector(intent);
               }
               intent = baseIntent;
           }
 
           ......
 
           return intent;//解析生产内容,对应到生产的intent,返回处理
 
       catch (IndexOutOfBoundsException e) {
           throw new URISyntaxException(uri, "illegal Intent URI format", i);
       }
   }

经过上面简要分析,我们了解Uri intent生成过程,这种设计非常巧妙可以达到很好的解耦处理,相比于显示与隐士启动方式,这种方式更加灵活。

例如,我们可以由Server端动态下发,本地通过解析后在进行跳转页面,即可达到动态控制页面功能,其实不仅可以应用到页面跳转中,如果进一步扩展,可以应用到更多功能中,

比如我们可以扩展自定义规则,如header改为:http,https,代表页面跳转到webActivity,header改为:tipe时为toast提示,改为dialog时为弹框显示等。

我们还可以更为细致的控制,如可加入版本号指定对应的版本的app执行这规则,其余版本默认行为,适用于修复部分bug。由此可见我们可以通过修改parseUri方法即可以扩展到更多功能中,下面看一下我的修改,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
static final String TAG="PageJump";
    
    static final String SCHEME_INTENT = "page";
    static final String SCHEME_ANDROIDAPP = "android-app:";
    static final String SCHEME_HTTP = "http";
    static final String SCHEME_HTTPS = "https";
    static final String SCHEME_TIPS_DIALOG = "tips_dialog";
    static final String SCHEME_TIPS_TOAST = "tips_toast";
 
//动态解析实现对页面行为控制
    public static void jumpPageUri(Context context, String strUri) throws URISyntaxException{
        //{(2,5][8,12)}android-app://com.example.app/<br />#Intent;action=com.example.MY_ACTION;
            if (TextUtils.isEmpty(strUri)) {
                throw new NullPointerException("parser uri content is empty");
            }
 
            String data = strUri.trim();
            //uri是否在版本内
            final int curVer = Utils.getVerCode(context);
            if(isInRangeVersion(data,curVer)){
                return;
            }
            //remove command version part
            if(data.startsWith("{")) {
                int verEnd = data.indexOf('}'1);
                data = data.substring(verEnd+1);
            }
            String uriData = data;
 
            if(uriData.startsWith(SCHEME_ANDROIDAPP)){
                Intent intent = Intent.parseUri(uriData, Intent.URI_INTENT_SCHEME);
                String appPackage = context.getPackageName();
                ComponentName componentName = intent.getComponent();
 
                //out intent
                if (componentName == null || !appPackage.contains(componentName.getPackageName())){
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    context.startActivity(intent);
                }
            }
            else if (uriData.startsWith(SCHEME_INTENT)) {
                Intent sendIntent = UriProcessor.parseIntentUri(data, Intent.URI_INTENT_SCHEME);
 
                // Verify that the intent will resolve to an activity
//                if (null == sendIntent.resolveActivity(context.getPackageManager())) {
//                    throw new URISyntaxException(data, "not found match page");
//                }
                sendIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                context.startActivity(sendIntent);
            }
            else if (uriData.startsWith(SCHEME_HTTP) || uriData.startsWith(SCHEME_HTTPS)) {
            //  WebViewActivity.launch(context, uri);
            }
            else if(uriData.startsWith(SCHEME_TIPS_DIALOG)){
            //  DialogUtil.showNormal("test");
            }
            else if(uriData.startsWith(SCHEME_TIPS_TOAST)){
            //  ToastUtils.showShortMessage("");
            }
 
    }

规则串前面增加了应用版本范围,{(2,5][8,12)},这里我使用开闭区间的方式来指定及范围,这种方式更精简使用,版本解析处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/**
    * current command version whether contain current app version
    * @param data
    * @param curVer
    * @return
    */
   public static boolean isInRangeVersion(String data,final int curVer){
       if(data.startsWith("{")){
           int verEnd = data.indexOf('}'1);
 
           if(verEnd>0) {
               String verStr = data.substring(0, verEnd+1);
 
               boolean in_range=true;
               int pos=1;
               try {
                   while (pos >= 0 && !verStr.startsWith("}")) {
                       in_range=true;
                       char ch = verStr.charAt(pos);
                       if (ch == '[' || ch == '(') {
                           boolean[] border=new boolean[2];
                           int semi = verStr.indexOf(',', pos);
                           int startVer = Integer.valueOf(verStr.substring(pos + 1, semi));
                           border[0]= (ch=='[');
                           int toVer = 0, flagVer = 0;
                           if ((flagVer = verStr.indexOf(']', semi)) >= 0 || (flagVer = verStr.indexOf(')', semi)) >= 0) {
                               toVer = Integer.valueOf(verStr.substring(semi + 1, flagVer));
                               border[1]= (verStr.charAt(flagVer)==']');
                           }
                           // judge current version code not inside range
                           // jude min version code < <=
                           if((border[0] && curVer<startVer) ||(!border[0] && curVer<=startVer)){
                               in_range=false;
                           }
                           // judge max version code > >=
                           if((border[1] && curVer>toVer) ||(!border[1] && curVer>=toVer)){
                               in_range=false;
                           }
                           pos = flagVer + 1;
                           if (pos + 1 >= verStr.length())
                               break;
                       }
                   }
                   return in_range;
               }catch (NumberFormatException ex){
                   Log.e(TAG,"parse regular expression version error!");
               }
 
           }
           return true;
       }
       return true;
   }

测试使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
//    String jumpUri1="{(2,5][8,12)}android-app://com.example.app/#Intent;action=com.example.MY_ACTION;i.some_int=100;S.some_str=hello;end";
    String jumpUri2="{(0,3][6,12)}android-app://com.example.app/#Intent;action=com.example.MY_ACTION;i.some_int=100;end";
    String jumpUri3="{(0,6]}android-app://com.example.app/#Intent;action=com.example.MY_ACTION;i.some_int=100;end";
    String jumpUriPage="{(2,6]}android-app://com.example.myapp/#Intent;action=com.example.myapp.SecondActivity;package=com.example.myapp;category=android.intent.category.DEFAULT;S.some=systemFrom;end";
    String jumpUriPage2="{[1,8]}page#Intent;action=com.example.myaction;package=com.example.myapp;category=android.intent.category.DEFAULT;S.some=innerFrom;end";
     
    try {
        //    PageJump.jumpPageUri(getApplicationContext(),jumpUri1);
            PageJump.jumpPageUri(getApplicationContext(),jumpUri2);
            PageJump.jumpPageUri(getApplicationContext(),jumpUri3);
        catch (URISyntaxException e) {
            e.printStackTrace();
        }

分析intent的代码设计后,真是觉得源码设计的十分巧妙,值得仔细认真琢磨。

参考:关于Intent Uri页面跳转 - HappyCode002 - 博客园

https://zhuanlan.zhihu.com/p/449084824

https://www.51cto.com/article/554845.html


 

相关文章:

  • 高密度服务器机柜散热方案:高风压风机在复杂风道中的关键作用与选型要点
  • AI应用交付厂商F5打造六大解决方案,助用户应对复杂挑战
  • 【razor】回环结构导致的控制信令错位:例如发送端收到 SR的问题
  • 频率非周期性失稳
  • 10.17 LangChain v0.3核心机制解析:从工具调用到生产级优化的实战全指南
  • 鸿蒙开发进阶:深入解析ArkTS语言特性与高性能编程实践
  • C#学习10——泛型
  • 推扫式高光谱相机VIX-N230重磅发布——开启精准成像新时代
  • 动态规划之爬楼梯模型
  • openjdk底层(hotspot)汇编指令调用(五)——内存访问
  • 无人机报警器360°检测技术分析!
  • docker环境和dockerfile制作
  • Spring Boot 集成 Elasticsearch【实战】
  • KC 喝咖啡/书的复制/奶牛晒衣服/ 切绳子
  • JVM-运行时数据区
  • Prometheus
  • C++之fmt库介绍和使用(2)
  • [Harmony]获取设备参数
  • 新浪《经济新闻》丨珈和科技联合蒲江政府打造“数字茶园+智能工厂+文旅综合体“创新模式
  • 基于Spring Boot + Vue的教师工作量管理系统设计与实现
  • B站一季度净亏损收窄99%:游戏营收大增76%,AI类广告收入增近4倍
  • 中国海警就菲向非法“坐滩”仁爱礁军舰运补发表谈话
  • 国家发改委:进一步完善促进民营经济发展的制度机制
  • 西安集中整治监督教育领域不正之风和腐败问题,举报方式公布
  • 萨洛宁、康托罗夫、长野健……7月夏季音乐节来很多大牌
  • 90后青年学者李海增逝世9个月后文章登上顶刊,同仁缅怀其贡献