外部開(kāi)啟Activity新姿勢(shì)(scheme)

在H5頁(yè)面瘋狂的今天,H5和Native的交互就至關(guān)重要,而且交互的方式有很多,google提供了一個(gè)公共的方式:js與native互調(diào),即js可以調(diào)用Native方法,Native同樣也可以調(diào)用js方法。不過(guò)今天要講的并不是Url攔截的方式和JavaScript注入方式,因?yàn)檫@種交互方式存在著不少問(wèn)題:

1、Java 調(diào)用 js 里面的函數(shù)、效率并不是很高、估計(jì)要200ms左右吧、做交互性很強(qiáng)的事情、這種速度很難讓人接受、而js去調(diào)Java的方法、速度很快、50ms左右、所以盡量用js調(diào)用Java方法
2、Java 調(diào)用 js 的函數(shù)、沒(méi)有返回值、調(diào)用了就控制不到了
3、Js 調(diào)用 Java 的方法、返回值如果是字符串、你會(huì)發(fā)現(xiàn)這個(gè)字符串是 native 的、轉(zhuǎn)成 locale 的才能正常使用、使用 toLocaleString() 函數(shù)就可以了、不過(guò)這個(gè)函數(shù)的速度并不快、轉(zhuǎn)化的字符串如果很多、將會(huì)很耗費(fèi)時(shí)間
4、Android4.2以下的系統(tǒng)存在著webview的js對(duì)象注入漏洞
所以處于這些原因,我們并未采用這種方式用于Native與webview交互,而是要介紹核武器—scheme,采用scheme + cookie的方式。
那你可能會(huì)思考什么是scheme? 到底哪些場(chǎng)景適合?具體怎么使用?
表要捉急,慢慢來(lái)介紹。

什么是scheme?

客戶端應(yīng)用可以向操作系統(tǒng)注冊(cè)一個(gè) URL scheme,該 scheme 用于從瀏覽器或其他應(yīng)用中啟動(dòng)本應(yīng)用。通過(guò)指定的 URL 字段,可以讓?xiě)?yīng)用在被調(diào)起后直接打開(kāi)某些特定頁(yè)面,比如車輛詳情頁(yè)、訂單詳情頁(yè)、消息通知頁(yè)、促銷廣告頁(yè)等等。也可以執(zhí)行某些指定動(dòng)作,如訂單支付等。也可以在應(yīng)用內(nèi)通過(guò) html 頁(yè)來(lái)直接調(diào)用顯示 app 內(nèi)的某個(gè)頁(yè)面。

scheme格式?

客戶端自定義的 URL 作為從一個(gè)應(yīng)用調(diào)用另一個(gè)的基礎(chǔ),遵循 RFC 1808 (Relative Uniform Resource Locators) 標(biāo)準(zhǔn)。這跟我們常見(jiàn)的網(wǎng)頁(yè)內(nèi)容 URL 格式一樣。
先來(lái)個(gè)完整的URL Scheme協(xié)議格式:

xl://goods:8888/goodsDetail?goodsId=10011002
通過(guò)上面的路徑 Scheme、Host、port、path、query全部包含,基本上平時(shí)使用路徑就是這樣子的。

xl代表該Scheme 協(xié)議名稱
goods代表Scheme作用于哪個(gè)地址域
goodsDetail代表Scheme指定的頁(yè)面
goodsId代表傳遞的參數(shù)
8888代表該路徑的端口號(hào)
舉個(gè)栗子:
(該 URL 會(huì)調(diào)起車輛詳情頁(yè)):uumobile://mobile/carDetail?car_id=123456,其中 scheme 為 uumobile,host 為 mobile,relativePath 為 /carDetail,query 為 car_id=123456。

在什么場(chǎng)景使用?

下面介紹一下本人曾經(jīng)常用的場(chǎng)景:

其他應(yīng)用想要調(diào)用你APP的某個(gè)頁(yè)面
自己的H5頁(yè)面想要調(diào)用native的某個(gè)頁(yè)面
服務(wù)器下發(fā)路徑,客戶端根據(jù)服務(wù)器下發(fā)跳轉(zhuǎn)路徑跳轉(zhuǎn)相應(yīng)的頁(yè)面
APP端收到服務(wù)器端下發(fā)的PUSH通知欄消息,根據(jù)消息的點(diǎn)擊跳轉(zhuǎn)路徑跳轉(zhuǎn)相關(guān)頁(yè)面
這樣說(shuō)大家沒(méi)有在具體業(yè)務(wù)中使用可能不是很清楚,那么舉個(gè)例子:
我們進(jìn)入到h5的活動(dòng)頁(yè)面,這時(shí)候點(diǎn)擊某個(gè)鏈接,要求跳回我們的native,那么就用到了scheme。

scheme的使用

使用起來(lái)還是非常簡(jiǎn)單的:

1.在Androidmanifest.xml中定義scheme

<activity android:name=".ProcessActivity">

<!-- 要想在別的App上能成功調(diào)起App,必須添加intent過(guò)濾器 -->
<intent-filter>
    <!-- 協(xié)議部分,名字隨便設(shè)置 -->
    <data
        android:host="open.app.example"
        android:scheme="external" />
    <!-- 下面這幾行也必須得設(shè)置 -->
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />

    <action android:name="android.intent.action.VIEW" />
</intent-filter>

</activity>

??:切記 Android 小寫(xiě) 。

2.獲取Scheme跳轉(zhuǎn)的參數(shù)

private static final String TAG = ProcessActivity.class.getSimpleName().toString();

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

    distribute();
}

private void distribute() {
    Uri uri = getIntent().getData();
    if (uri != null) {

        //url部分
        Log.e(TAG, "uri -----> " + uri);

        // scheme部分
        String scheme = uri.getScheme();
        Log.e(TAG, "scheme -----> " + scheme);

        // host部分
        String host = uri.getHost();
        Log.e(TAG, "host -----> " + host);

        // 訪問(wèn)路勁
        String path = uri.getPath();
        Log.e(TAG, "path -----> " + path);

        // Query部分
        String query = uri.getQuery();
        Log.e(TAG, "query -----> " + query);

        //獲取指定參數(shù)值
        String isShowSplash = uri.getQueryParameter("isShowSplash");
        Log.e(TAG, "isShowSplash -----> " + isShowSplash);

        String infomation = uri.getQueryParameter("infomation");
        Log.e(TAG, "infomation -----> " + infomation);

    } else {
        finish();
    }
}

3.使用

只需要調(diào)用如下代碼就可以:

startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("external://open.app.example/first?isShowSplash=true&infomation='我是攜帶的信息'")));
1
1
4.效果展示

讓我們看一下打印出來(lái)的log日志:

圖。。。。。

這是我們接收到的uri傳遞的相關(guān)信息,只打印了一部分不是全部,有興趣大家可以自行打印更多信息。

實(shí)戰(zhàn)演示

接下來(lái)我們完成一個(gè)小DEMO,主要功能通過(guò)外部APP打開(kāi)對(duì)應(yīng)的Activity并傳遞相關(guān)數(shù)據(jù)。
我們先看一下ExternalOpen這個(gè)工程里面最主要的就是配置了scheme能夠通過(guò)uri的方式被啟動(dòng)。

看下目錄結(jié)構(gòu):

這里寫(xiě)圖片描述

功能清單文件

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="externalopen.libin.com.externalopen">

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:launchMode="singleTop"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme.NoActionBar">

    <activity
        android:name="MainActivity"
        android:label="@string/app_name"
        android:theme="@style/AppTheme.NoActionBar">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <activity android:name=".FirstActivity" />
    <activity android:name=".SecondActivity" />
    <activity android:name=".ThirdActivity" />
    <activity android:name=".ProcessActivity">
        <!-- 要想在別的App上能成功調(diào)起App,必須添加intent過(guò)濾器 -->
        <intent-filter>
            <!-- 協(xié)議部分,名字隨便設(shè)置 -->
            <data
                android:host="open.app.example"
                android:scheme="external" />
            <!-- 下面這幾行也必須得設(shè)置 -->
            <category android:name="android.intent.category.DEFAULT" />
            <category android:name="android.intent.category.BROWSABLE" />

            <action android:name="android.intent.action.VIEW" />
        </intent-filter>
    </activity>
</application>

</manifest>

這里包含一個(gè)負(fù)責(zé)跳轉(zhuǎn)的ProcessActivity和四個(gè)等候演示用到的展示的Activity界面。

我們先預(yù)覽一下效果:
這里寫(xiě)圖片描述

可能只看效果圖,可能會(huì)看的很迷茫,接下來(lái)看下講解。

首先我們安裝兩個(gè)app,一個(gè)專門(mén)負(fù)責(zé)啟動(dòng)相應(yīng)的activity的OmnipotentFrame工程,另外一個(gè)是我們的主角ExternalOpen工程。

首先我們看到有的會(huì)顯示歡迎界面,有的不顯示,都是根據(jù)isShowSplash控制。
我們?cè)趇sShowSplash=true 顯示歡迎界面,,在isShowSplash=false時(shí)不現(xiàn)實(shí)歡迎界面。

然后具體跳轉(zhuǎn)哪一個(gè)Activity根據(jù)path決定,然后傳遞的信息內(nèi)容放在information里面。
我們看到我們通過(guò)OmnipotentFrame可以開(kāi)啟ExternalOpen里面的任意activity。
主要是通過(guò)scheme和自定義processActivity控制的。

除了在外部app可以打開(kāi),在內(nèi)部也可以使用這個(gè)方法,同樣H5頁(yè)面也可,這樣APP之間的交互就方便多了,不過(guò)具體的還是要和業(yè)務(wù)相關(guān)聯(lián)
更多參數(shù)可以自己根據(jù)業(yè)務(wù)來(lái)定,這里只是給大家一個(gè)啟發(fā)。

負(fù)責(zé)分發(fā)跳轉(zhuǎn)的activity

/**

  • 負(fù)責(zé)分發(fā)跳轉(zhuǎn)的activity
    */

public class ProcessActivity extends AppCompatActivity {

private static final String TAG = ProcessActivity.class.getSimpleName().toString();
public static final String FIRST = "/first";
public static final String SECOND = "/second";
public static final String THIRD = "/third";
public static final String URI = "uri";
public static final String ISSHOWSPLASH = "isShowSplash";
public static final String INFOMATION = "infomation";

private ImageView iv_bg;

String path;
Uri uri;

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

    iv_bg = (ImageView) findViewById(R.id.iv_bg);

    try {
        distribute();
    } catch (Exception e) {
        Log.e(TAG, "分發(fā)異常");
    }

}

private void distribute() {
    uri = getIntent().getData();
    if (uri != null) {

        //url部分
        Log.e(TAG, "uri -----> " + uri);

        // scheme部分
        String scheme = uri.getScheme();
        Log.e(TAG, "scheme -----> " + scheme);

        // host部分
        String host = uri.getHost();
        Log.e(TAG, "host -----> " + host);

        // 訪問(wèn)路勁
        path = uri.getPath();
        Log.e(TAG, "path -----> " + path);

        // Query部分
        String query = uri.getQuery();
        Log.e(TAG, "query -----> " + query);

        //獲取指定參數(shù)值
        String isShowSplash = uri.getQueryParameter(ISSHOWSPLASH);
        Log.e(TAG, "isShowSplash -----> " + isShowSplash);

        String infomation = uri.getQueryParameter(INFOMATION);
        Log.e(TAG, "infomation -----> " + infomation);

        //===============================以上為log信息方便理解==============================>


        if (path != null && !path.isEmpty()) {

            /**
             *  是否展示歡迎頁(yè)面
             *  很多情況下從外部開(kāi)啟APP需要展示廣告或者歡迎頁(yè)面,這里模擬下一歡迎頁(yè)
             */
            if (isShowSplash.equals("true")) {
                iv_bg.setVisibility(View.VISIBLE);
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        iv_bg.setVisibility(View.INVISIBLE);
                        controlActivity(path, uri);
                        finish();
                    }
                },2000);
            }else {
                controlActivity(path, uri);
                finish();
            }

        }

    } else {
        finish();
    }
}

/**
 * 根據(jù)Path確定開(kāi)啟哪個(gè)Activity
 * @param path
 * @param uri
 */
private void controlActivity(String path, Uri uri) {

    if (path.equals(FIRST)) {
        Intent intent = new Intent(this, FirstActivity.class);
        intent.putExtra(INFOMATION, uri.getQueryParameter(INFOMATION));
        startActivity(intent);
    } else if (path.equals(SECOND)) {
        Intent intent = new Intent(this, SecondActivity.class);
        intent.putExtra(INFOMATION, uri.getQueryParameter(INFOMATION));
        intent.putExtra(URI, uri);
        startActivity(intent);
    } else if (path.equals(THIRD)) {
        Intent intent = new Intent(this, ThirdActivity.class);
        intent.putExtra(INFOMATION, uri.getQueryParameter(INFOMATION));
        intent.putExtra(URI, uri);
        startActivity(intent);
    }

}

使用

btn_1.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("external://open.app.example/first?isShowSplash=true&infomation=Path---->FirstActivity******isShowSplash---->true")));

        }
    });
    btn_2.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("external://open.app.example/second?isShowSplash=false&infomation=Path---->SecondActivity******isShowSplash---->false")));

        }
    });
    btn_3.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("external://open.app.example/third?isShowSplash=true&infomation=Path---->ThirdActivity******isShowSplash---->true")));

        }
    });
    btn_4.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("external://open.app.example/home?isShowSplash=true&infomation=首頁(yè)")));

        }
    });

在需要使用的地方只需要添加uri即可。

這里方便演示只進(jìn)行了外部APP進(jìn)行調(diào)轉(zhuǎn),更多如h5跳轉(zhuǎn)童鞋們可以自行驗(yàn)證。
注:原文地址:http://blog.csdn.net/github_33304260/article/details/73194544

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,063評(píng)論 25 709
  • 哎呀呀 ,馬上就要面臨找工作了,媛媛心里緊張呀. 作為一個(gè)即將畢業(yè)的Android程序媛,開(kāi)始面臨找工作了,...
    左神話閱讀 5,156評(píng)論 7 59
  • 最近剛從舊公司離職,為面試在做準(zhǔn)備,因?yàn)槠綍r(shí)開(kāi)發(fā)CV大法用得比較多,很多基礎(chǔ)知識(shí)掌握得不是很牢靠以及很多工具框架只...
    黎清海閱讀 2,331評(píng)論 1 19
  • 1.什么是Activity?問(wèn)的不太多,說(shuō)點(diǎn)有深度的 四大組件之一,一般的,一個(gè)用戶交互界面對(duì)應(yīng)一個(gè)activit...
    JoonyLee閱讀 5,858評(píng)論 2 51
  • 八月桂花八月開(kāi),花香悄悄迎面來(lái)。 雖然偏居墻角旁,傲氣長(zhǎng)存如依然。 拍攝于北京植物園盆景園南側(cè)一角落的一株桂花樹(shù)。
    退休人老高閱讀 509評(píng)論 0 1

友情鏈接更多精彩內(nèi)容