Web 喚起 Android app

Tania7453 7年前發布 | 15K 次閱讀 安卓開發 Android開發 移動開發

最近在項目中遇到 web 喚起 Android app 的需求,實現很簡單,簡單記錄下實現方式與背后原理。

實現

先不管喚起的原理,用一個簡單的例子描述它的實現:

首先需要在 AndroidManifest.xml 中定義 scheme, scheme 不能和 http、https、ftp、sms、mailto 等已使用的相同 。

<activity android:name=".MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
    <!-- web 喚起添加的 filter -->
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data
            android:scheme="my.scheme"
            android:host="my.host"
            />
    </intent-filter>
</activity>

下面是測試網頁:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Web 喚起 app</title>
</head>
<body style="text-align: center">
<a href="my.scheme://my.host?name=xxx&title=xxx" style="font-: 27px">點擊喚起 Demo app</a>
</body>
</html>

上面的鏈接中有 name 和 title 兩個參數,app 也能接收到,所以在喚起 app 時也能傳一些數據

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

    Intent intent = getIntent();
    if (null != intent && null != intent.getData()) {
        Uri uri = intent.getData(); // uri 就相當于 web 頁面中的鏈接
        String name = uri.getQueryParameter("name");
        String title = uri.getQueryParameter("title");
    }
}

原理

my.scheme://my.host?name=xxx&title=xxx 其實也是一個鏈接,為什么點擊這個鏈接瀏覽器就會啟動相應的 app 呢?

其實關鍵在 WebView 的 WebViewClient 的 shouldOverrideUrlLoading 方法,基本上所有的瀏覽器都會有類似的實現,下面分析 Android 瀏覽器的源碼。

Android 6.0 的原生瀏覽器的 shouldOverrideUrlLoading 方法的核心實現在 UrlHandler 這個類中。

boolean shouldOverrideUrlLoading(Tab tab, WebView view, String url) {
    ...
    // The "about:" schemes are internal to the browser; don't want these to
    // be dispatched to other apps.
    if (url.startsWith("about:")) {
        return false;
    }
    ...

    if (startActivityForUrl(tab, url)) {
        return true;
    }

    if (handleMenuClick(tab, url)) {
        return true;
    }

    return false;
}

從上面第 5 行代碼中可以看到 scheme 也不能為 about ,這是原生瀏覽器內部用的,喚起 app 的關鍵在第 10 行的 startActivityForUrl 方法。

boolean startActivityForUrl(Tab tab, String url) {
    Intent intent;
    // perform generic parsing of the URI to turn it into an Intent.
    try {
        intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
    } catch (URISyntaxException ex) {
        Log.w("Browser", "Bad URI " + url + ": " + ex.getMessage());
        return false;
    }

    // check whether the intent can be resolved. If not, we will see
    // whether we can download it from the Market.
    ResolveInfo r = null;
    try {
        r = mActivity.getPackageManager().resolveActivity(intent, 0);
    } catch (Exception e) {
        return false;
    }
    ...
    // sanitize the Intent, ensuring web pages can not bypass browser
    // security (only access to BROWSABLE activities).
    intent.addCategory(Intent.CATEGORY_BROWSABLE);
    intent.setComponent(null);
    Intent selector = intent.getSelector();
    if (selector != null) {
    selector.addCategory(Intent.CATEGORY_BROWSABLE);
    selector.setComponent(null);
    }
    ...
    try {
        intent.putExtra(BrowserActivity.EXTRA_DISABLE_URL_OVERRIDE, true);
    if (mActivity.startActivityIfNeeded(intent, -1)) { // 喚起 app 的最終代碼在這里
        // before leaving BrowserActivity, close the empty child tab.
        // If a new tab is created through JavaScript open to load this
        // url, we would like to close it as we will load this url in a
        // different Activity.
        mController.closeEmptyTab();
            return true;
        }
    } catch (ActivityNotFoundException ex) {
        // ignore the error. If no application can handle the URL,
        // eg about:blank, assume the browser can handle it.
    }

    return false;
}

上面 22 行 intent.addCategory(Intent.CATEGORY_BROWSABLE); 也可以看出我們之前在 加` `的原因。

而如果第三方的瀏覽器在這個地方對 scheme 屏蔽,就可以讓 web 喚起 app 實效,微信中網頁不能喚起應用就是這個原因。

 

來自:http://johnnyshieh.github.io/android/2017/03/30/web-evoke-app/

 

 本文由用戶 Tania7453 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!