テンセントインタビュアー:学生だけでなく、Applinkを使用する原理についての話

簡単な紹介

この言葉を通してリンク我々はプッシュ通知や他のシーンを開始し、ライブ、クロスアプリケーションの起動を引く一般的なアプリケーションで使用される、APPに直接ジャンプするには、このリンクを使用し、これはリンクであることがわかります。

プロセス

上のASはすでにここ普遍的な下に皆のための解決の詳細な手順を使用しています

テンセントインタビュアー:学生だけでなく、Applinkを使用する原理についての話

クイッククリック  シフトを  提供するチュートリアルとして統合見つけることAPPLinkを入力し、二回。
ASに使用手順を詳述しており、合計4つのステップに分割されています

URLインテントフィルタを追加

URLを作成します

テンセントインタビュアー:学生だけでなく、Applinkを使用する原理についての話

それともあなたはまた、「どのように動作する」ボタンをクリックすることができます

意図を処理するためのロジックを追加します。

入口の活動を選択することでapplink始めました。
[完了]をクリックした後、ASは自動的に1がAndroidManifestで、2つの場所に変更されます

 <activity android:name=".TestActivity">
            <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="http"
                    android:host="geyan.getui.com" />
            </intent-filter>
        </activity>

ここで1つの以上のデータ、データタグを参照してください、私たちは安全に多分これ、推測することができ  applinkが暗黙のスタートです。
もう1つの変更点は、ポイントです

    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        // ATTENTION: This was auto-generated to handle app links.
        Intent appLinkIntent = getIntent();
        String appLinkAction = appLinkIntent.getAction();
        Uri appLinkData = appLinkIntent.getData();
    }

URLリンクが以前ここに構成されたデータを受信するために使用されるapplinkの値であり、これ以上言うことはありません。

アソシエイトウェブサイト

この手順は、インストールが検証のためのネットワークに行くときJSONファイルにAPP、APPの証明書を生成するための最も重要な、そして必要です。指定されたディレクトリ・サーバーにこのファイルを置くために、あなたが必要とするオンラインの資格情報を選択し、ファイルを取得しますassetlinks.jsonを生成]をクリックします

テンセントインタビュアー:学生だけでなく、Applinkを使用する原理についての話

次のようにセキュリティ上の理由から、この文書はSSL GETリクエストを取得する必要があり、JSON形式は次のとおりです。

[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "com.lenny.myapplication",
    "sha256_cert_fingerprints":
    ["E7:E8:47:2A:E1:BF:63:F7:A3:F8:D1:A5:E1:A3:4A:47:88:0F:B5:F3:EA:68:3F:5C:D8:BC:0B:BA:3E:C2:D2:61"]
  }
}]

sha256_cert_fingerprintsこのパラメータはのkeytoolコマンドによって得ることができ、ここではこれ以上言いません。

最后把这个文件上传到 你配置的地址/.well-know/statements/json,为了避免今后每个 app 链接请求都访问网络,安卓只会在 app 安装的时候检查这个文件。,如果你能在请求 https://yourdomain.com/.well-... 的时候看到这个文件(替换成自己的域名),那么说明服务端的配置是成功的。目前可以通过 http 获得这个文件,但是在M最终版里则只能通过 HTTPS 验证。确保你的 web 站点支持 HTTPS 请求。

若一个host需要配置多个app,assetlinks.json添加多个app的信息。
若一个 app 需要配置多个 host,每个 host 的 .well-known 下都要配置assetlinks.json

有没有想过 url 的后缀是不是一定要写成 /.well-know/statements/json 的?
后续讲原理的时候会涉及到,这里先不细说。

Test device

最后我们本质仅是拿到一个 URL,大多数的情况下,我们会在 url 中拼接一些参数,比如

https://yourdomain.com/products/123?coupon=save90

其中 ./products/123?coupon=save90 是我们之前在第二步填写的 path。
那测试方法多种多样,可以使用通知,也可以使用短信,或者使用 adb 直接模拟,我这边图省事就直接用 adb 模拟了

adb shell am start
-W -a android.intent.action.VIEW
-d "https://yourdomain.com/products/123?coupon=save90"
[包名]

使用这个命令就会自动打开 APP。前提是 yourdomain.com 网站上存在了 web-app 关联文件。

原理

上述这些都简单的啦,依葫芦画瓢就行,下面讲些深层次的东西,不仅要知道会用,还得知道为什么可以这么用,不然和咸鱼有啥区别。

テンセントインタビュアー:学生だけでなく、Applinkを使用する原理についての話

上诉也说了,我们配置的域名是在 activity 的 data 标签的,那是否是可以认为 applink 是一种隐式启动,应用安装的时候根据 data 的内容到这个网页下面去获取 assetlinks.json 进行校验,如果符合条件则把 这个 url 保存在本地,当点击 webview 或者短信里面的 url的时候,系统会自动与本地库中的域名相匹配, 如果匹配失败则会被自动认为是 deeplink 的连接。确认过眼神对吧~~~

也就说在第一次安装 APP 的时候是会去请求 data 标签下面的域名的,并且去请求所获得的域名,那 安装->初次启动 的体验自然会想到是在源码中 PackageManagerService 实现。

一个 APk 的安装过程是极其复杂的,涉及到非常多的底层知识,这里不细说,直接找到校验 APPLink 的入口 PackageManagerService 的 installPackageLI 方法。

PackageMmanagerService.class

private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
    final int installFlags = args.installFlags;
    <!--开始验证applink-->
    startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);
    ...

    }

    private void startIntentFilterVerifications(int userId, boolean replacing,
        PackageParser.Package pkg) {
    ...

    mHandler.removeMessages(START_INTENT_FILTER_VERIFICATIONS);
    final Message msg = mHandler.obtainMessage(START_INTENT_FILTER_VERIFICATIONS);
    msg.obj = new IFVerificationParams(pkg, replacing, userId, verifierUid);
    mHandler.sendMessage(msg);
}

可以看到这边发送了一个 message 为 START_INTENT_FILTER_VERIFICATIONS 的 handler 消息,在 handle 的 run 方法里又会接着调用 verifyIntentFiltersIfNeeded。

private void verifyIntentFiltersIfNeeded(int userId, int verifierUid, boolean replacing,
        PackageParser.Package pkg) {
        ...
        <!--检查是否有Activity设置了AppLink-->
        final boolean hasDomainURLs = hasDomainURLs(pkg);
        if (!hasDomainURLs) {
            if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
                    "No domain URLs, so no need to verify any IntentFilter!");
            return;
        }
        <!--是否autoverigy-->
        boolean needToVerify = false;
        for (PackageParser.Activity a : pkg.activities) {
            for (ActivityIntentInfo filter : a.intents) {
            <!--needsVerification是否设置autoverify -->
                if (filter.needsVerification() && needsNetworkVerificationLPr(filter)) {
                    needToVerify = true;
                    break;
                }
            }
        }
      <!--如果有搜集需要验证的Activity信息及scheme信息-->
        if (needToVerify) {
            final int verificationId = mIntentFilterVerificationToken++;
            for (PackageParser.Activity a : pkg.activities) {
                for (ActivityIntentInfo filter : a.intents) {
                    if (filter.handlesWebUris(true) && needsNetworkVerificationLPr(filter)) {
                        if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
                                "Verification needed for IntentFilter:" + filter.toString());
                        mIntentFilterVerifier.addOneIntentFilterVerification(
                                verifierUid, userId, verificationId, filter, packageName);
                        count++;
                    }    }   } }  }
   <!--开始验证-->
    if (count > 0) {
        mIntentFilterVerifier.startVerifications(userId);
    } 
}

对 APPLink 进行了检查,搜集,验证,主要是对 scheme 的校验是否是 http/https,以及是否有 flag 为 Intent.ACTION_DEFAULT与Intent.ACTION_VIEW 的参数,接着是开启验证

PMS#IntentVerifierProxy.class

public void startVerifications(int userId) {
        ...
            sendVerificationRequest(userId, verificationId, ivs);
        }
        mCurrentIntentFilterVerifications.clear();
    }

    private void sendVerificationRequest(int userId, int verificationId,
            IntentFilterVerificationState ivs) {

        Intent verificationIntent = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION);
        verificationIntent.putExtra(
                PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID,
                verificationId);
        verificationIntent.putExtra(
                PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_URI_SCHEME,
                getDefaultScheme());
        verificationIntent.putExtra(
                PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS,
                ivs.getHostsString());
        verificationIntent.putExtra(
                PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME,
                ivs.getPackageName());
        verificationIntent.setComponent(mIntentFilterVerifierComponent);
        verificationIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);

        UserHandle user = new UserHandle(userId);
        mContext.sendBroadcastAsUser(verificationIntent, user);
    }

目前 Android 的实现是通过发送一个广播来进行验证的,也就是说,这是个异步的过程,验证是需要耗时的(网络请求),发出去的广播会被 IntentFilterVerificationReceiver 接收到。这个类又会再次 start DirectStatementService,在这个 service 里面又会去调用 DirectStatementRetriever 类。在此类的 retrieveStatementFromUrl 方法中才是真正请求网络的地方

DirectStatementRetriever.class

  @Override
    public Result retrieveStatements(AbstractAsset source) throws AssociationServiceException {
        if (source instanceof AndroidAppAsset) {
            return retrieveFromAndroid((AndroidAppAsset) source);
        } else if (source instanceof WebAsset) {
            return retrieveFromWeb((WebAsset) source);
        } else {
            throw new AssociationServiceException("Namespace is not supported.");
        }
    }
  private Result retrieveFromWeb(WebAsset asset)
            throws AssociationServiceException {
        return retrieveStatementFromUrl(computeAssociationJsonUrl(asset), MAX_INCLUDE_LEVEL, asset);
    }
    private String computeAssociationJsonUrl(WebAsset asset) {
        try {
            return new URL(asset.getScheme(), asset.getDomain(), asset.getPort(),
                    WELL_KNOWN_STATEMENT_PATH)
                    .toExternalForm();
        } catch (MalformedURLException e) {
            throw new AssertionError("Invalid domain name in database.");
        }
    }
private Result retrieveStatementFromUrl(String urlString, int maxIncludeLevel,
                                        AbstractAsset source)
        throws AssociationServiceException {
    List<Statement> statements = new ArrayList<Statement>();
    if (maxIncludeLevel < 0) {
        return Result.create(statements, DO_NOT_CACHE_RESULT);
    }

    WebContent webContent;
    try {
        URL url = new URL(urlString);
        if (!source.followInsecureInclude()
                && !url.getProtocol().toLowerCase().equals("https")) {
            return Result.create(statements, DO_NOT_CACHE_RESULT);
        }
        <!--通过网络请求获取配置-->
        webContent = mUrlFetcher.getWebContentFromUrlWithRetry(url,
                HTTP_CONTENT_SIZE_LIMIT_IN_BYTES, HTTP_CONNECTION_TIMEOUT_MILLIS,
                HTTP_CONNECTION_BACKOFF_MILLIS, HTTP_CONNECTION_RETRY);
    } catch (IOException | InterruptedException e) {
        return Result.create(statements, DO_NOT_CACHE_RESULT);
    }

    try {
        ParsedStatement result = StatementParser
                .parseStatementList(webContent.getContent(), source);
        statements.addAll(result.getStatements());
        <!--如果有一对多的情况,或者说设置了“代理”,则循环获取配置-->
        for (String delegate : result.getDelegates()) {
            statements.addAll(
                    retrieveStatementFromUrl(delegate, maxIncludeLevel - 1, source)
                            .getStatements());
        }
        <!--发送结果-->
        return Result.create(statements, webContent.getExpireTimeMillis());
    } catch (JSONException | IOException e) {
        return Result.create(statements, DO_NOT_CACHE_RESULT);
    }
}

到了这里差不多就全部讲完了,本质就是通过 HTTPURLConnection 去发起来一个请求。之前还留了个问题,是不是一定要要 /.well-known/assetlinks.json,到这里是不是可以完全明白了,就是 WELL_KNOWN_STATEMENT_PATH 参数

    private static final String WELL_KNOWN_STATEMENT_PATH = "/.well-known/assetlinks.json";

缺点

  1. 只能在 Android M 系统上支持

在配置好了app对App Links的支持之后,只有运行Android M的用户才能正常工作。之前安卓版本的用户无法直接点击链接进入app,而是回到浏览器的web页面。

  1. 要使用App Links开发者必须维护一个与app相关联的网站

对于小的开发者来说这个有点困难,因为他们没有能力为app维护一个网站,但是它们仍然希望通过web链接获得流量。

  1. 对 ink 域名不太友善
    在测试中发现,国内各大厂商对 .ink 域名不太友善,很多的是被支持了 .com 域名,但是不支持 .ink 域名。
机型 版本 是否识别ink 是否识别com
小米 MI6 Android 8.0 MIUI 9.5
小米 MI5 Android 7.0 MIUI 9.5
魅族 PRO 7 Android 7.0 Flyme 6.1.3.1A
三星 S8 Android 7.0 是,弹框
华为 HonorV10 Android 8.0 EMUI 8.0
OPPO R11Sアンドロイド7.1.1 ColorOS 3.2 それはあります それはあります
OPPO A59sアンドロイド5.1 ColorOS 3.0 アプリにジャンプすることができません アプリにジャンプすることができません
生体内 X6Plus Aアンドロイド5.0.2 Funtouch OS_2.5 ノー それはあります
生体内 767アンドロイド6.0 Funtouch OS_2.6 アプリにジャンプすることができません アプリにジャンプすることができません
生体内 X9のAndroid 7.1.1 Funtouch OS_3.1 アプリにジャンプすることができません アプリにジャンプすることができません

参照

公式ドキュメント

テンセントインタビュアー:学生だけでなく、Applinkを使用する原理についての話

最後に、学ぶためのプログラマー、コンテンツの知識のために、自分自身を向上させるだけ排除すべきでない、あまりにも多くの技術的、環境的順序があり、常に私たちは環境ではなく、私たちに適応する環境に適応するためには!

ここでのセットの数十に関連した上記技術的システム図添付の質問は、19年テンセント、見出し、アリ、米国のグループと他の企業が直面している技術は、終点のビデオとPDF(実際には、予想以上に多くの時間を費やす)となっており、含む知識のコンテキスト+多くの詳細ここではあなたにそれの一部を表示する絵の形で、紙面の都合で、。

私はそれがあなたの収穫の多くをもたらすと信じて:

テンセントインタビュアー:学生だけでなく、Applinkを使用する原理についての話

[上記のHD脳図]、および私を追加することができ、[サポート] PDF技術アーキテクチャは、WX:X1524478394の無料アクセスを!

ときに簡単にプログラマ、優れたプログラマがシニアアーキテクトへのプライマリから上級プログラマー、建築家にジュニアプログラマから学ぶ必要があるとき、または技術的な管理から管理、テクニカルディレクターの各段階に私たちは、さまざまな機能を持っている必要があります。初期の研究では、キャパシティビルディングで仲間を投げるためには、自分のキャリアの方向性を決定します。

おすすめ

転載: blog.51cto.com/14332859/2461779