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

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


 

http://www.xdnf.cn/news/7634.html

相关文章:

  • 常见的 API 及相关知识总结
  • 如何查看Python内置函数列表
  • 面试之MySQL慢查询优化干货分享
  • AT2659S低噪声放大器芯片:1.4-3.6V宽电压供电,集成50Ω匹配
  • springboot+vue实现服装商城系统(带用户协同过滤个性化推荐算法)
  • 使用SFunction获取属性名,减少嵌入硬编码
  • 初识Linux 进程:进程创建、终止与进程地址空间
  • js绑定事件
  • RabbitMQ ⑤-顺序性保障 || 消息积压 || 幂等性
  • 在CuPy中使用多节点多GPU环境
  • C#基础:yield return关键字的特点
  • 2025ICPC武汉邀请赛-F
  • 游戏启动DLL文件缺失怎么解决 解决dll问题的方法
  • Vue学习路线
  • leetcode hot100刷题日记——6.和为 K 的子数组
  • 【Axure视频教程】动态地图路线
  • 实现rpc通信机制(待定)
  • R语言空间分析实战:地理加权回归联合主成份与判别分析破解空间异质性难题
  • 封装POD与PinMap文件总结学习-20250516
  • Go 语言简介
  • 操作系统的基础概念
  • 初步认识HarmonyOS NEXT端云一体化开发
  • AbMole| Phorbol 12-myristate 13-acetate(CAS号16561-29-8;目录号M4647)
  • vue+threeJs 生成烟花效果
  • PEFT简介及微调大模型DeepSeek-R1-Distill-Qwen-1.5B
  • 【css知识】flex-grow: 1
  • LibreHardwareMonitor:.Net开发的开源硬件监控项目
  • 中国机加工的市场概况及冷镦技术对于机加工替代的趋势
  • 如何在 Windows 11/10 PC 上擦除外部硬盘驱动
  • 什么叫生成式人工智能?职业技能的范式转移与能力重构