Android 跨应用间调用: URL Scheme

作者: 登高且赋 | 来源:发表于2017-02-04 19:35 被阅读8453次

    在Android开发中,不同Activity之间的跳转和切换是很常见的,这使得APP的内容更加丰富,功能更为多样,但是一个应用所能包括的功能毕竟有限,在很多的场景下需要跨应用调用,比如在应用中跳转到微信支付的页面,或者使用第三方账号登陆时跳转到QQ登陆等等。这样的情况下,要跳转的Activity或者Application是第三方开发的,很可能不知道要具体跳转到哪一个Activity中,或者直接跳转并不安全,诸如这些问题都需要在跨应用间调用时被周全地考虑到。为了更好地解决这一问题,下面就来介绍Android中的URL Scheme协议

    URL Scheme简介

    Android中的自定义的URL Scheme是一种页面内跳转协议,也可以被称为URLRouter,就是通过类似打开网页的方式去通过路由打开一个Activity,而非直接通过显式Intent方式去进行跳转。这样隐式intent的方法跳转好处如下:

    1. 降低耦合性:不需要知道具体要跳转哪个界面,只需要根据需求,按照约定好的URL路由协议发送Intent即可;
    2. 更为安全:不显示Intent跳转,只要是符合协议的Intent都会有对应的Activity来匹配,避免了跳转到不该出现的页面;
    3. 更为灵活: 有着更为广泛的应用场景,一下场景中都可以使用URL Scheme
    • 服务器下发跳转路径,客户端根据服务器下发跳转路径跳转相应的页面
    • H5页面点击锚点,根据锚点具体跳转路径APP端跳转具体的页面
    • APP端收到服务器端下发的PUSH通知栏消息,根据消息的点击跳转路径跳转相关页面
    • APP根据URL跳转到另外一个APP指定页面

    URL Scheme协议格式

    上文已经说过,URL Scheme是就通过类似打开网页的方式去通过路由打开一个Activity,其协议格式和我们打开网页输入的网址类似。

    一个完整的完整的URL Scheme协议格式由scheme、host、port、path和query组成,其结构如下所示:

    <scheme>://<host>:<port>/<path>?<query>

    其中scheme既可以是Android中常见的协议,也可以是我们自定义的协议。Android中常见的协议包括content协议、http协议、file协议等,自定义协议可以使用自定义的字符串,当我们启动第三方的应用时候,多是使用自定义协议。

    如下是一个自定义协议的URI:

    xl://goods:8888/goodsDetail?goodsId=10011002

    通过上面的路径 Scheme、Host、port、path、query全部包含:

    • xl,即为Scheme,代表该Scheme 协议名称
    • goods,即为Host,代表Scheme作用于哪个地址域
    • 8888,即为port,代表该路径的端口号
    • goodsDetail,即为path, 代表Scheme指定的页面
    • goodsId,即为query,代表传递的参数

    URL Scheme的使用方法

    URL Scheme的使用方法简要言之就是先在manifest中配置能接受Scheme方式启动的activity;当需要调用时,将Scheme协议的URi以Data的形式加入到Intent中,隐式调用该activity。

    1). 在AndroidManifest.xml中对<activity >标签增加<intent-filter>设置Scheme

    <activity android:name=".MainActivity">
        <intent-filter> <!--正常启动-->
            <action android:name="android.intent.action.MAIN"/>
            <category android:name="android.intent.category.LAUNCHER"/>
        </intent-filter>
        <intent-filter> <!--URL Scheme启动-->
            <!--必有项-->
            <action android:name="android.intent.action.VIEW"/>
            <!--如果希望该应用可以通过浏览器的连接启动,则添加该项-->
            <category android:name="android.intent.category.BROWSABLE"/>
            <!--表示该页面可以被隐式调用,必须加上该项-->
            <category android:name="android.intent.category.DEFAULT"/>
            <!--协议部分-->
            <data android:scheme="urlscheme"
                android:host="auth_activity">
        </intent-filter>
        <intent-filter>
            <action   android:name="emms.intent.action.check_authorization"/>
            <category android:name="android.intent.category.DEFAULT"/>
            <category android:name="emms.intent.category.authorization"/>
        </intent-filter>
    </activity>
    

    上面的设置中可以看到,MainActivity包含多个<intent-filter>设置,第一个是正常的启动,也就是在应用列表中启动;第二个是通过URL Scheme方式启动,其本身也是隐式Intent调用的一种,不同在于添加了<data>属性,定义了其接受URL Scheme协议格式为urlschemel://auth_activity

    这里需要说明下,URL Scheme协议格式中,组成URI的这些属性在<data >标签中都是可选的 ,但存在如下的依赖关系:

    • 如果没有指定scheme,那么host参数会被忽略
    • 如果没有指定host,那么port参数会被忽略
    • 如果scheme和host都没有指定,path参数会被忽略

    当我们将intent对象中的Uri参数与intent-filter中的<data>标签指定的URI格式进行对比时,我们只对比intent-filter的<data>标签指定的部分,例如:

    • 如果intent-filter中只指定了scheme,那么所有带有该sheme的URI都能匹配到该intent-filter。
    • 如果intent-filter中只指定了scheme和authority(authority包括host和port两部分)而没有指定path,那么所有具有相同scheme和authority的URI都能匹配到该intent-filter,而不用考虑path为何值。
    • 如果intent-filter中同时指定了scheme、authority和path,那么只有具有相同scheme、authority和path的URI才能匹配到该intent-filter。

    需要注意的是,intent-filter的<data>标签在指定path的值时,可以在里面使用通配符*,起到部分匹配的效果。

    2). 使用URL启动Activity

      Uri data = Uri.parse("urlschemel://auth_activity");
      Intent intent = new Intent(Intent.ACTION_VIEW,data);
      //保证新启动的APP有单独的堆栈,如果希望新启动的APP和原有APP使用同一个堆栈则去掉该项
      intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
      try {
        startActivityForResult(intent, RESULT_OK);
      } catch (Exception e) {
        e.printStackTrace();
        Toast.makeText(MainActivity.this, "没有匹配的APP,请下载安装",Toast.LENGTH_SHORT).show();
      }
    

    当然可以在网页中调用

    <a href="urlschemel://auth_activity">打开新的应用</a>
    

    或者是在JS中调用

    window.location = "urlschemel://auth_activity";
    

    3.)如何判断URL Scheme是否有效

    boolean checkUrlScheme(Intent intent){
        PackageManager packageManager = getPackageManager();
        List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0);
        return !activities.isEmpty();
    }
    

    将子APP在Home Launcher中隐藏

    有时候需要把一些辅助性的、较为独立的APP在Home Launcher中隐藏起来,只允许一些特定的APP调用。这个时候,我们可以利用URL Scheme协议来做到这一点,设置AndroidManifest.xml中对<activity >标签如下

    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN"/>
            <category android:name="android.intent.category.LAUNCHER"/>
            <category android:name="android.intent.category.BROWSABLE"/>
            <!--表示该页面可以被隐式调用,必须加上该项-->
            <category android:name="android.intent.category.DEFAULT"/>
            <!--协议部分-->
            <data android:scheme="urlscheme"
                android:host="auth_activity">
        </intent-filter>
    </activity>
    

    因为Home Launcher列出的应用图标要求必须有Activity同时满足

    • <action android:name="android.intent.action.MAIN"/>
    • <category android:name="android.intent.category.LAUNCHER"/>

    上面的配置中有多余的category和data限制存在,所以并不匹配,不会在Home Launcher出现,但是可以使用URL Scheme来启动。

      Uri data = Uri.parse("urlschemel://auth_activity");
      Intent intent = new Intent(Intent.ACTION_MAIN,data);
      intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    

    这样就可以将一组APP设置一个统一的入口,然后根据实际需要在调用不同子APP,即所谓的APP业务组件化URL Scheme在其中有着重要的作用,更进一步讨论会在以后的文章中呈现,敬请期待。

    参考文献:
    http://blog.csdn.net/iispring/article/details/48481793
    http://blog.csdn.net/hb707934728/article/details/53196419
    http://www.cnblogs.com/whoislcj/p/5825333.html

    相关文章

      网友评论

      • BUG君:将参数暴露在 url 中, 安全吗
        登高且赋:第一,URL Scheme的目的是应用之间的跳转,参数信息必然是公开给系统的,当然不建议在参数里涉及到敏感信息,尤其是用户名密码,这样不符合URL Scheme的初衷;第二,如果你一定要传输敏感信息,请加密并签名;
      • 莫如许老:第三种请解释下,启动代码是?
        <intent-filter>
        <action android:name="emms.intent.action.check_authorization"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="emms.intent.category.authorization"/>
        </intent-filter>
      • guoerye:大佬,startactivityforresult()跳转的怎么回传值啊?
        登高且赋:@guoerye 这里有篇很详细的讲解,你可以参看一下 http://www.cnblogs.com/fuck1/p/5456337.html
        guoerye:大佬,我finish了,但是没反应:我是这样写的,我不知道intent构造的时候还需要传什么参数吗?刚学这个没多长时间,麻烦指点一下吧
        Intent intent = new Intent();
        setResult(RESULT_OK,intent);
        finish();
        登高且赋:如果页面A使用startActivityForResult方法跳转到页面B,就是在页面Bfinish()动作或者back动作后,会回调页面A的onActivityResult()方法
      • 0445981d6022:可以可以

      本文标题:Android 跨应用间调用: URL Scheme

      本文链接:https://www.haomeiwen.com/subject/jwpdittx.html