最近更新了「知乎」手机客户端,发现了知乎的一个新 feature,在浏览器打开知乎的网页的时候会弹出程序选择窗口,选择在「知乎」程序里或者继续在网页端浏览。

不得不说这是一个很棒的主意,因为「知乎」自己程序里的搜索功能实在是太差了(据说是搜狗提供的),很多时候搜索不到自己想要的答案,而在网页端直接浏览的体验没有手机客户端好,这样倒是完美的结合了两种方式的优点。

刚好自己做的「手机睿思」其实也有类似的毛病,当你在手机上收到一个别人发过来的论坛帖子链接,有时候不在校内就无法打开,有时在校外,而网页端「手机睿思」体验又比较差,如果能实现「知乎」类似的结果就方便很多了。

理论

网页直接启动 Android 程序主要是通过 intent-filter 来实现的,intent-filter 包含很多属性,这里需要用到 <action><category><data>这三个,详细的配置是通过 <data> 标签来完成的。

要添加的这要是这三个标签:

  • action_VIEW:浏览网页使用的就是这个 action,不然无法接收浏览网页请求
  • category.BROWSABLE:目标 Activity 中必须有这个,否则点击超链接后无法成功跳转
  • category.DEFAULT:没有这个的 Activity 不接受隐式启动

以上三个标签负责接收网页跳转,剩下的URL匹配就需要用 <data> 标签了。

URL主要有四个部分组成,schemeauthoritypathqueryString,其中authority有分为hostpath两部分,格式如下

1
scheme://host:port/path?queryString

根据URL的格式,<data> 标签对应有以下属性:

1
2
3
4
5
6
7
8
9
10
11
<data 
android:scheme=""
android:host=""
android:port=""
android:path=""
android:pathPattern=""
android:pathPrefix=""
android:mimeType=""
android:ssp=""
android:sspPattern=""
android:sspPrefix=""/>

https://www.zhihu.com/question/28629301/answer/432294682 为例子,其中

  • scheme: 协议类型,如 httphttpsftp等,也可以是自定义的数据
  • host: 主机地址,如 www.zhihu.com
  • port: 端口,比如常见的 808080端口,默认的 80 端口一般不写
  • path: 路径,比如 /question/28629301/answer/432294682
  • pathPrefix: 路径前缀,只有路径前缀是这个的才可以匹配,用于精确匹配条件
  • pathPattern: 和 pathPrefix 类似,使用正则表达式来精确匹配路径

其他的属性这里用不上,因此不再详细解释,有兴趣可以自行了解。我需要匹配的URL是这样的。

1
http://rs.xidian.edu.cn/forum.php?mod=viewthread&tid=952631

在我的需求下用到了以下属性:

  • scheme,设置为 http
  • host,设置为 rs.xidian.edu.cn
  • path,设置为 /forum.php

实践

下面说一下具体现实步骤

  1. 创建两个ActivityMainActivityHttpActivityMainActivity有一个按钮,用来发送浏览网页请求,HttpActivity用于接收请求。HttpActivity的配置如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <activity android:name=".HttpActivity">
    <intent-filter>
    <action android:name="android.intent.action.VIEW"/>
    <category android:name="android.intent.category.BROWSABLE"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <data
    android:scheme="http"
    android:host="rs.xidian.edu.cn"
    android:pathPrefix="/forum.php"/>
    </intent-filter>
    </activity>
  2. MainActivity里做好发送请求的代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    private Button btn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    btn = findViewById(R.id.btn);
    btn.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
    Uri uri = Uri.parse("http://rs.xidian.edu.cn/forum.php?mod=viewthread&tid=952475");
    Intent i = new Intent(Intent.ACTION_VIEW);
    i.setData(uri);
    MainActivity.this.startActivity(i);
    }
    }
  3. HttpActivity里进行数据接收,可以通过uri.getQueryParameter()方法获取URL携带的参数信息。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public class HttpActivity extends AppCompatActivity {

    public static final String TAG = "HttpActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_http);

    Intent i = getIntent();
    Uri uri = i.getData();
    Log.i(TAG, "link:" + i.getDataString());
    Log.i(TAG, "scheme: " + uri.getScheme());
    Log.i(TAG, "host:" + uri.getHost());
    Log.i(TAG, "path: " + uri.getPath());
    Log.i(TAG, "mod: " + uri.getQueryParameter("mod"));
    Log.i(TAG, "tid: " + uri.getQueryParameter("tid"));
    }

  4. 查看结果

    1
    2
    3
    4
    5
    6
    I/HttpActivity: link:http://rs.xidian.edu.cn/forum.php?mod=viewthread&tid=952475
    I/HttpActivity: scheme: http
    I/HttpActivity: host:rs.xidian.edu.cn
    I/HttpActivity: path: /forum.php
    I/HttpActivity: mod: viewthread
    I/HttpActivity: tid: 952475

成功获取得到了参数,「知乎」用的是类似的方法,不过把host换成了它自己的,另外根据path和其他参数进行不同Activity的跳转。

据我测试发现,以Chrome为例,每新开一个标签页的时候会发送一次action_VIEWIntent,这样就可以触发打开app,不过「睿思」基本没有被搜索网站所收录,网页直接跳转这个基本用不上,现在能解决的痛点大概是聊天的时候直接发链接访问的问题了。