在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è)專門負(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