WebView與Android原生通信

概覽

  1. Android中打開本地的HTML
  2. 網(wǎng)頁打開App指定頁面,并傳遞參數(shù)
  3. JS調(diào)用Native
  4. Native調(diào)用JS
  5. WebView優(yōu)化

Android中打開本地的HTML

初始化WebView,加載本地的Html文件

javaMethod = new JavaMethod(this);
        webView = new WebView(this);
        WebSettings settings = webView.getSettings();
        settings.setDomStorageEnabled(true);
        settings.setJavaScriptEnabled(true);
        settings.setBlockNetworkImage(false);
        webView.setWebViewClient(javaMethod.getWebViewClient());
        webView.setWebChromeClient(javaMethod.getWebChromeClient());
        webView.addJavascriptInterface(javaMethod, "android");
        frameLayout.addView(webView);

        // webView加載一個網(wǎng)頁
        webView.loadUrl("file:///android_asset/test_js.html");

js調(diào)用native

1. 借助WebView.addJavascriptInterface實現(xiàn)H5與Native通信

WebView的addJavascriptInterface方法允許Natvive向Web頁面注入Java對象,之后,在js中便可以直接訪問該對象南蹂,使用@JavascriptInterface注解的方法。

public class JavaMethod {
    private MainActivity mainActivity;
    private Handler uiHandler;

    public JavaMethod(MainActivity mainActivity) {
        this.mainActivity = mainActivity;
        uiHandler = new Handler(Looper.getMainLooper());
    }

    @JavascriptInterface
    public void JsToJavaInterface(final String param) {
        uiHandler.post(new Runnable() {
            @Override
            public void run() {
                mainActivity.setTextShow("from JavaInterface: " + param);
            }
        });
    }
}

在前端的js代碼中,是可以直接通過mJsMethodApi.callNative(jsonString)通知Native的艘狭,而且通過addJavascriptInterface注入的對象在H5的任何地方都可以調(diào)用,不存在注入時機跟注入失敗的問題,在H5的head里調(diào)用都沒問題巢音。

<html>

    <head>
        <meta http-equiv="Content-Type" charset="UTF-8"/>

        <script type="text/javascript">
        </script>
    </head>

    <body>

        <h3>Js Method</h3>
        <h3 id="textshow">調(diào)用結(jié)果</h3>
        
        <input type="button" value="JavascriptInterface" onclick="window.android.JsToJavaInterface('我來自Js')"/>

    </body>

</html>
2. shouldOverrideUrlLoading

通過WebViewClient中的shouldOverrideUrlLoading攔截url遵倦,制定一個對應(yīng)協(xié)議。
在Html中增加一條

<input type="button" value="shouldOverrideUrlLoading" onclick="document.location = 'js://jstojava?arg1=1號參數(shù)&arg2=2號參數(shù)'"/><br/>

在Android代碼中攔截url

 /**
     * shouldOverrideUrlLoading:webViewClient加載url時候的回調(diào)
     *
     * @return WebViewClient
     */
    public WebViewClient getWebViewClient() {
        WebViewClient webViewClient = new WebViewClient() {
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
                Uri uri = request.getUrl();
                // 與JS端定義好協(xié)議:  一般協(xié)議格式是Scheme(協(xié)議頭)+ Authority(協(xié)議名)
                if (uri != null) {
                    String scheme = uri.getScheme();
                    if (scheme != null && scheme.equals("js")) {
                        String authority = uri.getAuthority();
                        if (authority != null && authority.equals("jstojava")) {
                            // 獲取參數(shù)
                            String arg1 = uri.getQueryParameter("arg1");
                            String arg2 = uri.getQueryParameter("arg2");

                            showText(MessageFormat.format("參數(shù): {0}; {1}", arg1, arg2));
                            Log.e("==TAG==>", "參數(shù) -> arg1:" + arg1 + " ; arg2:" + arg2);

                        }
                        return true;
                    }
                }
                return super.shouldOverrideUrlLoading(view, request);
            }
        };
        return webViewClient;
    }

這種方式?jīng)]有版本限制和漏洞港谊,不過沒有返回值骇吭,如果Js調(diào)用后需要android返回就得使用loadUrl()或者evaluateJavascript()回傳對應(yīng)的接收方法了。值得一提的是歧寺,這種方式便于和IOS通用一套協(xié)議燥狰,簡便Js端的代碼量。

3. onJsAlert()斜筐、onJsConfirm()龙致、onJsPrompt()

通過 WebChromeClient 中的onJsAlert()、onJsConfirm()顷链、onJsPrompt()攔截Js中的alert()目代、confirm()、prompt() 消息嗤练。而alert榛了、confirm、prompt代表Js中三種常用提示框煞抬,第一種沒有返回值霜大,第二種返回布爾值,第三種可返回任意值革答。由于考慮到靈活性战坤,所以我們可以直接實現(xiàn)對prompt的攔截即可。

在Html中添加

  <script type="text/javascript">
        function jstojavaprompt(param){
             result = prompt(param);
             document.getElementById("textshow").innerHTML = (result);
        }
    </script>
...
<input type="button" value="onJsPrompt"
       onclick="jstojavaprompt('js://jstojava?arg3=1號參數(shù)&arg4=2號參數(shù)')"/><br/>

代碼中設(shè)置setWebChromClient()残拐,攔截onJsPrompt

    /**
     * onJsPrompt:調(diào)用本地方法jstojavaprompt()途茫,然后回調(diào)onJsPrompt()
     *
     * @return WebChromeClient
     */
    public WebChromeClient getWebChromeClient() {
        return new WebChromeClient() {
            @Override
            public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
                Log.e("==TAG==>", "url:" + url);
                Uri uri = Uri.parse(message);
                // 與JS端定義好協(xié)議:  一般協(xié)議格式是Scheme(協(xié)議頭)+ Authority(協(xié)議名)
                if (uri != null) {
                    String scheme = uri.getScheme();
                    if (scheme != null && scheme.equals("js")) {
                        String authority = uri.getAuthority();
                        if (authority != null && authority.equals("jstojava")) {
                            // 獲取參數(shù)
                            String arg1 = uri.getQueryParameter("arg3");
                            String arg2 = uri.getQueryParameter("arg4");

                            showText(MessageFormat.format("參數(shù): {0}; {1}", arg1, arg2));
                            Log.e("==TAG==>", "參數(shù) -> arg1:" + arg1 + " ; arg2:" + arg2);

                            result.confirm("我來自onJsPrompt");

                        }
                        return true;
                    }
                }
                return super.onJsPrompt(view, url, message, defaultValue, result);
            }
        };

    }

Native調(diào)用JS

1. loadUrl() 方式

在Html中添加JS方法

<script type="text/javascript">
var s = '我來自Js方法';
        function javatojscallback(param){
             document.getElementById("textshow").innerHTML = (param);
             window.android.jsCallbackParams(s)
        }
</script>

window.android.JsToJavaInterface(s)是Js調(diào)用android的方法,由于loadUrl()不能從Js返回數(shù)據(jù)溪食,可以讓Js回調(diào)android的方法回傳參數(shù)囊卜。

在Android代碼中添加接收Js回調(diào)傳參的方法

  // Js回調(diào)Android的方法,傳遞參數(shù)
    @JavascriptInterface
    public void jsCallbackParams(String params){
        showText(params);
    }

在Android中調(diào)用JS方法

/**
     * 通過 webView.loadUrl 調(diào)用JS
     *
     * @param view view
     */
    public void loadUrl(View view) {
        webView.loadUrl("javascript:javatojscallback('我來自Java')");
    }
2. evaluateJavascript()方式

在Html中定義js的方法错沃,供Java調(diào)用

var s = '我來自Js方法';
function javatojswith(param){
             document.getElementById("textshow").innerHTML = (param);
             return s;
        }

在Android中通過webView.evaluateJavascript()調(diào)用JS中的方法

/**
     * 通過 webView.evaluateJavascript 調(diào)用JS
     *
     * @param view
     */
    public void evaluate(View view) {
        webView.evaluateJavascript("javascript:javatojswith('這是來自Java的參數(shù)')",
                new ValueCallback<String>() {
                    @Override
                    public void onReceiveValue(String value) {
                        setTextShow(value);
                        Toast.makeText(WebActivity.this, value, Toast.LENGTH_SHORT).show();
                    }
                });
    }

WebView優(yōu)化

為什么擁有Webview的H5頁面打開這么慢边败,是因為它通常會經(jīng)歷以下幾個階段:
1)Webview初始化。
2)到達新的頁面捎废,網(wǎng)絡(luò)連接笑窜,從服務(wù)器下載html,css登疗,js排截,頁面白屏嫌蚤。
3)頁面基本框架出現(xiàn),js請求頁面數(shù)據(jù)断傲,頁面處于loading狀態(tài)脱吱。
4)出現(xiàn)所需的數(shù)據(jù),完成整個頁面的渲染认罩,用戶可交互箱蝠。

1. Webview提前初始化

提前加載WebView,進行WebView的初始化垦垂。加載Webview內(nèi)核宦搬,這是一個重量級的操作,內(nèi)核是以apk的形式存在劫拗。
App生成一個全局webview间校,并且在啟動時初始化,這樣在后面使用時通過動態(tài)獲取這個全局Webview页慷,然后添加到rootview中憔足,這樣就可以進行復(fù)用從而減少初始化的時間。

注意:如果在不同頁面使用Webview時使用不同的設(shè)置酒繁,就需要動態(tài)維護滓彰,而且在不同的頁面跳轉(zhuǎn)前要做好清理工作。

2. H5頁面拉取優(yōu)化

可以把html州袒,css找蜜,js,image等資源預(yù)置在客戶端本地稳析,并和服務(wù)端協(xié)商好前端的版本控制和增量更新策略豌拙,如此一來Webview就可以先快速加載本地緩存頁面資源甚牲,剩下的就只需要拉取那些需要更新的增量資源即可。

而針對這些需要拉取的增量資源窜觉,可以對它們進行webpack+gzip數(shù)據(jù)壓縮和CDN加速處理撰筷,以提升拉取速度陈惰。并且在建立網(wǎng)絡(luò)連接時,可以讓前端請求的域名和客戶端API接口域名一致毕籽,以減少DNS解析時間抬闯。

對于H5頁面來說,圖片資源的拉取是最為耗時的关筒,一個比較好的解決方案就是先加載并展示非圖片內(nèi)容溶握,延遲這些圖片的加載,以提升用戶體驗蒸播。

WebView有一個setBlockNetworkImage(boolean)方法睡榆,該方法的作用是是否屏蔽圖片的加載萍肆。可以利用這個方法來實現(xiàn)圖片的延遲加載:在onPageStarted時屏蔽圖片加載胀屿,在onPageFinished時開啟圖片加載塘揣。

3. H5動態(tài)數(shù)據(jù)拉取并行

正常的順序是在html,css宿崭,js拉取下來之后亲铡,才開始由js發(fā)起前端的ajax請求,獲取到數(shù)據(jù)后才開始進行填充葡兑。

其實我們可以把前端的ajax請求提前到和頁面加載同時進行奖蔓,由客戶端請求數(shù)據(jù),等到H5加載完畢铁孵,直接向客戶端索要即可锭硼,縮短了總體的頁面加載時間。

WebView中的安全漏洞
WebView與Native的通信
WebView緩存優(yōu)化
WebView的加載優(yōu)化

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蜕劝,一起剝皮案震驚了整個濱河市檀头,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌岖沛,老刑警劉巖暑始,帶你破解...
    沈念sama閱讀 218,451評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異婴削,居然都是意外死亡廊镜,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評論 3 394
  • 文/潘曉璐 我一進店門唉俗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嗤朴,“玉大人,你說我怎么就攤上這事虫溜”㈡ⅲ” “怎么了?”我有些...
    開封第一講書人閱讀 164,782評論 0 354
  • 文/不壞的土叔 我叫張陵衡楞,是天一觀的道長吱雏。 經(jīng)常有香客問我,道長瘾境,這世上最難降的妖魔是什么歧杏? 我笑而不...
    開封第一講書人閱讀 58,709評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮迷守,結(jié)果婚禮上犬绒,老公的妹妹穿的比我還像新娘。我一直安慰自己兑凿,他們只是感情好懂更,可當我...
    茶點故事閱讀 67,733評論 6 392
  • 文/花漫 我一把揭開白布眨业。 她就那樣靜靜地躺著,像睡著了一般沮协。 火紅的嫁衣襯著肌膚如雪龄捡。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,578評論 1 305
  • 那天慷暂,我揣著相機與錄音聘殖,去河邊找鬼。 笑死行瑞,一個胖子當著我的面吹牛奸腺,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播血久,決...
    沈念sama閱讀 40,320評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼突照,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了氧吐?” 一聲冷哼從身側(cè)響起讹蘑,我...
    開封第一講書人閱讀 39,241評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎筑舅,沒想到半個月后座慰,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,686評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡翠拣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,878評論 3 336
  • 正文 我和宋清朗相戀三年版仔,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片误墓。...
    茶點故事閱讀 39,992評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡蛮粮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出谜慌,到底是詐尸還是另有隱情然想,我是刑警寧澤,帶...
    沈念sama閱讀 35,715評論 5 346
  • 正文 年R本政府宣布畦娄,位于F島的核電站,受9級特大地震影響弊仪,放射性物質(zhì)發(fā)生泄漏熙卡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,336評論 3 330
  • 文/蒙蒙 一励饵、第九天 我趴在偏房一處隱蔽的房頂上張望驳癌。 院中可真熱鬧,春花似錦役听、人聲如沸颓鲜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽甜滨。三九已至乐严,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間衣摩,已是汗流浹背昂验。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留艾扮,地道東北人既琴。 一個月前我還...
    沈念sama閱讀 48,173評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像泡嘴,于是被迫代替她去往敵國和親甫恩。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,947評論 2 355

推薦閱讀更多精彩內(nèi)容