Android接入騰訊X5內(nèi)核以及相關(guān)問題以及WebView相關(guān)知識

一菩掏、android WebView 替換方案

1.騰訊X5(推薦)X5內(nèi)核下載地址
2.Crosswalk(包會打10--20mb
可能導(dǎo)致第三方APP無法開啟X5內(nèi)核的情況)

二璃岳、TBS(騰訊瀏覽服務(wù))的優(yōu)勢
  • 速度快:相比系統(tǒng)webview的網(wǎng)頁打開速度有30+%的提升挚赊;

  • 省流量:使用云端優(yōu)化技術(shù)使流量節(jié)省20+%;

  • 更安全:安全問題可以在24小時(shí)內(nèi)修復(fù)逃沿;

  • 更穩(wěn)定:經(jīng)過億級用戶的使用考驗(yàn),CRASH率低于0.15%;

  • 兼容好:無系統(tǒng)內(nèi)核的碎片化問題疲迂,更少的兼容性問題才顿;

  • 體驗(yàn)優(yōu):支持夜間模式、適屏排版尤蒿、字體設(shè)置等瀏覽增強(qiáng)功能郑气;

  • 功能全:在Html5、ES6上有更完整支持腰池;

  • 更強(qiáng)大:集成強(qiáng)大的視頻播放器竣贪,支持視頻格式遠(yuǎn)多于系統(tǒng)webview;

  • 視頻和文件格式的支持x5內(nèi)核多于系統(tǒng)內(nèi)核

  • 防劫持是x5內(nèi)核的一大亮點(diǎn)

三巩螃、騰訊X5內(nèi)核的使用
1 首先我們按照官方文檔集成 SDK演怎,下載jar包,以及配置權(quán)限避乏! 在application里面進(jìn)行初始化爷耀。

具體例子看官方demo,已經(jīng)很詳細(xì)了
騰訊 X5官網(wǎng) http://x5.tencent.com/tbs/

2 MainActivity
 @Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // Set layout
    getWindow().setFormat(PixelFormat.TRANSLUCENT);
    setContentView(R.layout.activity_main);
    mWebView = (com.tencent.smtt.sdk.WebView) findViewById(R.id.forum_context);
    mWebView.getSettings().setJavaScriptEnabled(true);// 支持js
    mWebView.getSettings().setUseWideViewPort(true); //自適應(yīng)屏幕
    mWebView.loadUrl("http://res.ky-express.com/h5/video/72.html");
}
3 activity_main
<com.tencent.smtt.sdk.WebView
    android:id="@+id/forum_context"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:paddingLeft="5dp"
    android:paddingRight="5dp"/>
四拍皮、一些注意的問題
1 多個(gè).So庫的問題

要把對應(yīng)的so包導(dǎo)入歹叮。如果你的項(xiàng)目當(dāng)中也用到了其他的.so庫,這時(shí)你不僅要分包導(dǎo)入铆帽。so庫咆耿,同時(shí)還要在gradle里面進(jìn)行配置!

ndk {
        abiFilters"armeabi","armeabi-v7a","x86","mips"
    }
2 關(guān)于首期啟動加載X5內(nèi)核會出現(xiàn)過慢的問題

可以考慮用線程加載或者服務(wù)加載

@Nullable
@Override
public IBinder onBind(Intent intent) {
    return null;
}

@Override
public void onCreate() {
    super.onCreate();
    initX5();
    preinitX5WebCore();
}

private void initX5() {
    QbSdk.initX5Environment(getApplicationContext(), QbSdk.WebviewInitType.FIRSTUSE_AND_PRELOAD, cb);
    Log.d("gggbbb","預(yù)加載中...");
}

QbSdk.PreInitCallback cb = new QbSdk.PreInitCallback() {

    @Override
    public void onViewInitFinished(boolean arg0) {
        // TODO Auto-generated method stub
        Log.e("0912", " onViewInitFinished is " + arg0);
    }

    @Override
    public void onCoreInitFinished() {
        // TODO Auto-generated method stub

    }
};
 private void preinitX5WebCore() {

    if(!QbSdk.isTbsCoreInited()) {

        // preinit只需要調(diào)用一次爹橱,如果已經(jīng)完成了初始化萨螺,那么就直接構(gòu)造view

        QbSdk.preInit(MainActivity.this, null);// 設(shè)置X5初始化完成的回調(diào)接口

    }
}

Application代碼

@Override
public void onCreate() {
    super.onCreate();
    initX5();
}
private void initX5() {
    Intent intent = new Intent(this, PreLoadX5Service.class);
    startService(intent);
}
3 我們會發(fā)現(xiàn)集成X5后,項(xiàng)目編譯變慢了,可以在build.config里面加上下面這段代碼試試
dexOptions {
    javaMaxHeapSize "4g"
    preDexLibraries = false
}
4 視頻全屏的時(shí)候有個(gè)qq瀏覽器的推廣的去掉

設(shè)置布局改變的監(jiān)聽:
getWindow().getDecorView().addOnLayoutChangeListener()
監(jiān)聽里面通過 getWindow().getDecorView() 的 findViewsWithText() 方法可以拿到顯示推廣文案的 TextView愧驱,把拿到的 view 設(shè)置為 GONE 狀態(tài)就可以去掉了慰技。

五、webview與x5的測試對比

1组砚、一個(gè)鏈接的第一次的加載webview會比x5速度快一點(diǎn)吻商。

2、如果重復(fù)點(diǎn)擊一個(gè)鏈接糟红,來回切換艾帐,只要次數(shù)足夠多,X5速度會比webview快盆偿。

3柒爸、如果點(diǎn)擊不同鏈接,并且次數(shù)不是很多陈肛,webview會比x5速度快揍鸟。


另外補(bǔ)充一下webview的基本常識

關(guān)于webview的加載
//打開本包內(nèi)asset目錄下的index.html文件
 
wView.loadUrl(" file:///android_asset/index.html ");   
 
//打開本地sd卡內(nèi)的index.html文件
 
wView.loadUrl("content://com.android.htmlfileprovider/sdcard/index.html");
 
//打開指定URL的html文件
 
wView.loadUrl(" http://m.oschina.net");

關(guān)于js 調(diào)用 native

有三種方式

  • 第一種方式:通過 addJavascriptInterface 方法進(jìn)行添加對象映射
mWebView.getSettings().setJavaScriptEnabled(true);

這個(gè)函數(shù)會有一個(gè)警告,因?yàn)樵谔囟ǖ陌姹局聲蟹浅NkU(xiǎn)的漏洞,設(shè)置完這個(gè)屬性之后阳藻,Native 需要定義一個(gè)類:

public class JSObject {
    private Context mContext;
    public JSObject(Context context) {
        mContext = context;
    }

    @JavascriptInterface
    public String showToast(String text) {
        Toast.show(mContext, text, Toast.LENGTH_SHORT).show();
        return "success";
    }
}
...

需要注意的是在 API17 版本之后晰奖,需要在被調(diào)用的地方加上 @addJavascriptInterface 約束注解,因?yàn)椴患由献⒔獾姆椒ㄊ菦]有辦法被調(diào)用的腥泥,JS 代碼也很簡單:

function showToast(){
    var result = myObj.showToast("我是來自web的Toast");
}
  • 第二種方式:利用 WebViewClient 接口回調(diào)方法攔截 url
    需要使用WebViewClient 中 shouldOverrideUrlLoading (WebView view, WebResourceRequest request) 利用這個(gè)攔截 url匾南,然后解析這個(gè) url 的協(xié)議,如果發(fā)現(xiàn)是我們預(yù)先約定好的協(xié)議就開始解析參數(shù)蛔外,執(zhí)行相應(yīng)的邏輯蛆楞。
public boolean shouldOverrideUrlLoading(WebView view, String url) {
    //假定傳入進(jìn)來的 url = "js://openActivity?arg1=111&arg2=222",代表需要打開本地頁面夹厌,并且?guī)胂鄳?yīng)的參數(shù)
    Uri uri = Uri.parse(url);
    String scheme = uri.getScheme();
    //如果 scheme 為 js豹爹,代表為預(yù)先約定的 js 協(xié)議
    if (scheme.equals("js")) {
          //如果 authority 為 openActivity,代表 web 需要打開一個(gè)本地的頁面
        if (uri.getAuthority().equals("openActivity")) {
              //解析 web 頁面帶過來的相關(guān)參數(shù)
            HashMap<String, String> params = new HashMap<>();
            Set<String> collection = uri.getQueryParameterNames();
            for (String name : collection) {
                params.put(name, uri.getQueryParameter(name));
            }
            Intent intent = new Intent(getContext(), MainActivity.class);
            intent.putExtra("params", params);
            getContext().startActivity(intent);
        }
        //代表應(yīng)用內(nèi)部處理完成
        return true;
    }
    return super.shouldOverrideUrlLoading(view, url);
}

我們看一下 JS 的代碼

function openActivity(){
    document.location = "js://openActivity?arg1=111&arg2=222";
}

這個(gè)代碼執(zhí)行之后矛纹,就會觸發(fā)本地的 shouldOverrideUrlLoading 方法臂聋,然后進(jìn)行參數(shù)解析,調(diào)用指定方法或南。這個(gè)方式不會存在第一種提到的漏洞問題孩等,但是它也有一個(gè)很繁瑣的地方是,如果 web 端想要得到方法的返回值采够,只能通過 WebView 的 loadUrl 方法去執(zhí)行 JS 方法把返回值傳遞回去肄方,相關(guān)的代碼如下:

//java
mWebView.loadUrl("javascript:returnResult(" + result + ")");

//javascript
function returnResult(result){
    alert("result is" + result);
}

所以說第二種方式在返回值方面還是很繁瑣的,但是在不需要返回值的情況下蹬癌,比如打開 Native 頁面权她,還是很合適的,制定好相應(yīng)的協(xié)議冀瓦,就能夠讓 web 端具有打開所有本地頁面的能力了伴奥。

  • 第三種方式:利用 WebChromeClient 回調(diào)接口的三個(gè)方法攔截消息
    這個(gè)方法的原理和第二種方式原理一樣写烤,都是攔截相關(guān)接口翼闽,只是攔截的接口不一樣:
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
    return super.onJsAlert(view, url, message, result);
}

@Override
public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
    return super.onJsConfirm(view, url, message, result);
}

@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
    //假定傳入進(jìn)來的 message = "js://openActivity?arg1=111&arg2=222",代表需要打開本地頁面洲炊,并且?guī)胂鄳?yīng)的參數(shù)
    Uri uri = Uri.parse(message);
    String scheme = uri.getScheme();
    if (scheme.equals("js")) {
        if (uri.getAuthority().equals("openActivity")) {
            HashMap<String, String> params = new HashMap<>();
            Set<String> collection = uri.getQueryParameterNames();
            for (String name : collection) {
                params.put(name, uri.getQueryParameter(name));
            }
            Intent intent = new Intent(getContext(), MainActivity.class);
            intent.putExtra("params", params);
            getContext().startActivity(intent);
            //代表應(yīng)用內(nèi)部處理完成
            result.confirm("success");
        }
        return true;
    }
    return super.onJsPrompt(view, url, message, defaultValue, result);
}

onJsAlert 方法是彈出警告框感局,一般情況下在 Android 中為 Toast,在文本里面加入\n就可以換行暂衡。onJsConfirm 彈出確認(rèn)框询微,會返回布爾值,通過這個(gè)值可以判斷點(diǎn)擊時(shí)確認(rèn)還是取消狂巢,true表示點(diǎn)擊了確認(rèn)撑毛,false表示點(diǎn)擊了取消。onJsPrompt 彈出輸入框唧领,點(diǎn)擊確認(rèn)返回輸入框中的值藻雌,點(diǎn)擊取消返回 null雌续。


關(guān)于native 調(diào)用 js
//java
mWebView.loadUrl("javascript:show(" + result + ")");

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

function show(result){
    alert("result"=result);
    return "success";
}

</script>

已知的 WebView 任意代碼執(zhí)行漏洞有 4 個(gè):

  • 針對某些特定機(jī)型會存在 addJavascriptInterface API 引起的遠(yuǎn)程代碼執(zhí)行漏洞
  • WebView 中內(nèi)置導(dǎo)出的 “searchBoxJavaBridge_” Java Object 可能被利用,實(shí)現(xiàn)遠(yuǎn)程任意代碼胯杭。
  • WebView 內(nèi)置導(dǎo)出 “accessibility” 和 “accessibilityTraversal” 兩個(gè) Java Object 接口驯杜,可被利用實(shí)現(xiàn)遠(yuǎn)程任意代碼執(zhí)行鸽心。

判斷WebView是否已經(jīng)滾動到頁面底端 或者 頂端:

getScrollY() //方法返回的是當(dāng)前可見區(qū)域的頂端距整個(gè)頁面頂端的距離,也就是當(dāng)前內(nèi)容滾動的距離.
getHeight()或者getBottom() //方法都返回當(dāng)前WebView這個(gè)容器的高度
getContentHeight()返回的是整個(gè)html的高度,但并不等同于當(dāng)前整個(gè)頁面的高度,因?yàn)閃ebView有縮放功能,所以當(dāng)前整個(gè)頁面的高度實(shí)際上應(yīng)該是原始html的高度再乘上縮放比例.因此,更正后的結(jié)果,準(zhǔn)確的判斷方法應(yīng)該是:

if (webView.getContentHeight() * webView.getScale() == (webView.getHeight() + webView.getScrollY())) {
        //已經(jīng)處于底端
    }

    if(webView.getScrollY() == 0){
        //處于頂端
    }


前進(jìn)、后退
goBack()//后退
goForward()//前進(jìn)
goBackOrForward(intsteps) //以當(dāng)前的index為起始點(diǎn)前進(jìn)或者后退到歷史記錄中指定的steps居暖,
                              如果steps為負(fù)數(shù)則為后退,正數(shù)則為前進(jìn)

canGoForward()//是否可以前進(jìn)
canGoBack() //是否可以后退

返回鍵:返回上一次瀏覽的頁面
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if ((keyCode == KeyEvent.KEYCODE_BACK) && mWebView.canGoBack()) {
        mWebView.goBack();
        return true;
    }
    return super.onKeyDown(keyCode, event);
}

在 WebView 中長按保存圖片

//1. 給 WebView添加監(jiān)聽
mWebview.setOnLongClickListener(new View.OnLongClickListener() {
    @Override
    public boolean onLongClick(View v) {

    }
});

//2太闺、獲取點(diǎn)擊的圖片地址:先獲取類型,根據(jù)相應(yīng)的類型來處理對應(yīng)的數(shù)據(jù)。
 WebView.HitTestResult result = ((WebView) v).getHitTestResult();
 int type = result.getType();

//3冀宴、獲取具體信息灭贷,圖片這里就是圖片地址
 String imgurl = result.getExtra();

//4、操作圖片(完整代碼)
mWebView.setOnLongClickListener(new View.OnLongClickListener() {
    @Override
    public boolean onLongClick(View v) {
        WebView.HitTestResult result = ((WebView)v).getHitTestResult();
        if (null == result)
            return false;
        int type = result.getType();
        if (type == WebView.HitTestResult.UNKNOWN_TYPE)
            return false;

        // 這里可以攔截很多類型略贮,我們只處理圖片類型就可以了
        switch (type) {
            case WebView.HitTestResult.PHONE_TYPE: // 處理撥號
                break;
            case WebView.HitTestResult.EMAIL_TYPE: // 處理Email
                break;
            case WebView.HitTestResult.GEO_TYPE: // 地圖類型
                break;
            case WebView.HitTestResult.SRC_ANCHOR_TYPE: // 超鏈接
                break;
            case WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE:
                break;
            case WebView.HitTestResult.IMAGE_TYPE: // 處理長按圖片的菜單項(xiàng)
                // 獲取圖片的路徑
                String saveImgUrl = result.getExtra();

                // 跳轉(zhuǎn)到圖片詳情頁甚疟,顯示圖片
                Intent i = new Intent(MainActivity.this, ImageActivity.class);
                i.putExtra("imgUrl", saveImgUrl);
                startActivity(i);
                break;
            default:
                break;
        }
    }
});

type有這幾種類型:

  • WebView.HitTestResult.UNKNOWN_TYPE 未知類型
  • WebView.HitTestResult.PHONE_TYPE 電話類型
  • WebView.HitTestResult.EMAIL_TYPE 電子郵件類型
  • WebView.HitTestResult.GEO_TYPE 地圖類型
  • WebView.HitTestResult.SRC_ANCHOR_TYPE 超鏈接類型
  • WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE 帶有鏈接的圖片類型
  • WebView.HitTestResult.IMAGE_TYPE 單純的圖片類型
  • WebView.HitTestResult.EDIT_TEXT_TYPE 選中的文字類型

webview的封裝
public class WebViewActivity extends AppCompatActivity  {
    private FrameLayout mFrameLayout;
    private WebView mWebView;
    private MyWebChromeClient mMyWebChromeClient;
    private String URL = "http://m.tv.sohu.com/20130704/n380744170.shtml";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_webview);
        mFrameLayout = (FrameLayout) findViewById(R.id.mFrameLayout);
        mWebView = (WebView) findViewById(R.id.mWebView);
        initWebView();
        mWebView.loadUrl(URL);
    }
    private void initWebView() {
        WebSettings settings = mWebView.getSettings();

        //設(shè)置了這個(gè)屬性后我們才能在 WebView 里與我們的 Js 代碼進(jìn)行交互
        settings.setJavaScriptEnabled(true);

        //WebView 是否支持多窗口,如果設(shè)置為 true逃延,需要重寫 
        //WebChromeClient#onCreateWindow(WebView, boolean, boolean, Message) 函數(shù)览妖,默認(rèn)為 false
        //settings.setSupportMultipleWindows(true);

        //顯示W(wǎng)ebView提供的縮放控件
        settings.setDisplayZoomControls(false);
        settings.setBuiltInZoomControls(true);
        //設(shè)置頁面是否支持縮放
        webSettings.setSupportZoom(true);
        //設(shè)置文本的縮放倍數(shù),默認(rèn)為 100
        //webSettings.setTextZoom(2);

        //打開 WebView 的 storage 功能揽祥,這樣 JS 的 localStorage,sessionStorage 對象才可以使用
        //settings .setDomStorageEnabled(true);

        //打開 WebView 的 LBS 功能讽膏,這樣 JS 的 geolocation 對象才可以使用
        //settings.setGeolocationEnabled(true);
        // settings.setGeolocationDatabasePath("");

         //設(shè)置是否打開 WebView 表單數(shù)據(jù)的保存功能
        //settings.setSaveFormData(true);

        //設(shè)置 WebView 的默認(rèn) userAgent 字符串
        //settings.setUserAgentString("");

        //設(shè)置 WebView 的字體,可以通過這個(gè)函數(shù)拄丰,改變 WebView 的字體府树,默認(rèn)字體為 "sans-serif"
        //settings.setStandardFontFamily("");
        //設(shè)置 WebView 字體的大小,默認(rèn)大小為 16
        //settings.setDefaultFontSize(20);
        //設(shè)置 WebView 支持的最小字體大小料按,默認(rèn)為 8
        //settings.setMinimumFontSize(12);

        //設(shè)置 JS 是否可以打開 WebView 新窗口
        settings.setJavaScriptCanOpenWindowsAutomatically(true);

        //被這個(gè) tag 聲明的寬度將會被使用奄侠,如果頁面沒有這個(gè) tag 或者沒有提供一個(gè)寬度,那么一個(gè)寬型 viewport 將會被使用垄潮。
        settings.setUseWideViewPort(true);

        settings.setPluginState(WebSettings.PluginState.ON);
        settings.setAllowFileAccess(true);
        settings.setLoadWithOverviewMode(true);
       
        settings.setCacheMode(WebSettings.LOAD_NO_CACHE);
        settings.setCacheMode(WebSettings.LOAD_DEFAULT);
        mMyWebChromeClient = new MyWebChromeClient();
        mWebView.setWebChromeClient(mMyWebChromeClient);

        //WebViewClient主要輔助WebView執(zhí)行處理各種響應(yīng)請求事件的
        mWebView.setWebViewClient(new WebViewClient() {
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                view.loadUrl(url);
                return true;
            }

            @Override
            public void onPageFinished(WebView view, String url) {
                super.onPageFinished(view, url);
            }
        });
    }

    //WebChromeClient 主要輔助 WebView 處理J avaScript 的對話框弯洗、網(wǎng)站 Logo、網(wǎng)站 title涂召、load 進(jìn)度等處理
    private class MyWebChromeClient extends WebChromeClient {
        private View mCustomView;
        private CustomViewCallback mCustomViewCallback;
        @Override
        public void onShowCustomView(View view, CustomViewCallback callback) {
            super.onShowCustomView(view, callback);
            if (mCustomView != null) {
                callback.onCustomViewHidden();
                return;
            }
            mCustomView = view;
            mFrameLayout.addView(mCustomView);
            mCustomViewCallback = callback;
            mWebView.setVisibility(View.GONE);
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        }

        public void onHideCustomView() {
            mWebView.setVisibility(View.VISIBLE);
            if (mCustomView == null) {
                return;
            }
            mCustomView.setVisibility(View.GONE);
            mFrameLayout.removeView(mCustomView);
            mCustomViewCallback.onCustomViewHidden();
            mCustomView = null;
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
            super.onHideCustomView();
        }
    }
    @Override
    public void onConfigurationChanged(Configuration config) {
        super.onConfigurationChanged(config);
        switch (config.orientation) {
            case Configuration.ORIENTATION_LANDSCAPE:
                getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
                getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
                break;
            case Configuration.ORIENTATION_PORTRAIT:
                getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
                getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
                break;
        }
    }
    @Override
    public void onPause() {
        super.onPause();
        mWebView.onPause();
    }

    @Override
    public void onResume() {
        super.onResume();
        mWebView.onResume();
    }

    @Override
    public void onBackPressed() {
        if (mWebView.canGoBack()) {
            mWebView.goBack();
            return;
        }
        super.onBackPressed();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mWebView.destroy();
    }
}


一個(gè)完整的Html5Activity

https://github.com/Wing-Li/Html5WebView/tree/master

import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.KeyEvent;
import android.webkit.GeolocationPermissions;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import com.lyl.test.R;

public class Html5Activity extends AppCompatActivity {

    private String mUrl;

    private LinearLayout mLayout;
    private WebView mWebView;

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

        Bundle bundle = getIntent().getBundleExtra("bundle");
        mUrl = bundle.getString("url");

        Log.d("Url:", mUrl);

        mLayout = (LinearLayout) findViewById(R.id.web_layout);


        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        mWebView = new WebView(getApplicationContext());
        mWebView.setLayoutParams(params);
        mLayout.addView(mWebView);

        WebSettings mWebSettings = mWebView.getSettings();
        mWebSettings.setSupportZoom(true);
        mWebSettings.setLoadWithOverviewMode(true);
        mWebSettings.setUseWideViewPort(true);
        mWebSettings.setDefaultTextEncodingName("utf-8");
        mWebSettings.setLoadsImagesAutomatically(true);

        //調(diào)用JS方法.安卓版本大于17,加上注解 @JavascriptInterface
        mWebSettings.setJavaScriptEnabled(true);

        saveData(mWebSettings);

        newWin(mWebSettings);

        mWebView.setWebChromeClient(webChromeClient);
        mWebView.setWebViewClient(webViewClient);
        mWebView.loadUrl(mUrl);
    }

    @Override
    public void onPause() {
        super.onPause();
        webView.onPause();
        webView.pauseTimers(); //小心這個(gè)!G镉尽迫皱!暫停整個(gè) WebView 所有布局、解析卓起、JS戏阅。
    }

    @Override
    public void onResume() {
        super.onResume();
        webView.onResume();
        webView.resumeTimers();
    }

    /**
     * 多窗口的問題
     */
    private void newWin(WebSettings mWebSettings) {
        //html中的_bank標(biāo)簽就是新建窗口打開,有時(shí)會打不開奕筐,需要加以下
        //然后 復(fù)寫 WebChromeClient的onCreateWindow方法
        mWebSettings.setSupportMultipleWindows(false);
        mWebSettings.setJavaScriptCanOpenWindowsAutomatically(true);
    }

    /**
     * HTML5數(shù)據(jù)存儲
     */
    private void saveData(WebSettings mWebSettings) {
        //有時(shí)候網(wǎng)頁需要自己保存一些關(guān)鍵數(shù)據(jù),Android WebView 需要自己設(shè)置
        mWebSettings.setDomStorageEnabled(true);
        mWebSettings.setDatabaseEnabled(true);
        mWebSettings.setAppCacheEnabled(true);
        String appCachePath = getApplicationContext().getCacheDir().getAbsolutePath();
        mWebSettings.setAppCachePath(appCachePath);
    }

    WebViewClient webViewClient = new WebViewClient(){

        /**
         * 多頁面在同一個(gè)WebView中打開离赫,就是不新建activity或者調(diào)用系統(tǒng)瀏覽器打開
         */
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            view.loadUrl(url);
            return true;
        }

    };

    WebChromeClient webChromeClient = new WebChromeClient() {

        //=========HTML5定位==========================================================
        //需要先加入權(quán)限
        //<uses-permission android:name="android.permission.INTERNET"/>
        //<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
        //<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
        @Override
        public void onReceivedIcon(WebView view, Bitmap icon) {
            super.onReceivedIcon(view, icon);
        }

        @Override
        public void onGeolocationPermissionsHidePrompt() {
            super.onGeolocationPermissionsHidePrompt();
        }

        @Override
        public void onGeolocationPermissionsShowPrompt(final String origin, final GeolocationPermissions.Callback callback) {
            callback.invoke(origin, true, false);//注意個(gè)函數(shù),第二個(gè)參數(shù)就是是否同意定位權(quán)限旬盯,第三個(gè)是是否希望內(nèi)核記住
            super.onGeolocationPermissionsShowPrompt(origin, callback);
        }
        //=========HTML5定位==========================================================

        //=========多窗口的問題==========================================================
        @Override
        public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
            WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
            transport.setWebView(view);
            resultMsg.sendToTarget();
            return true;
        }
        //=========多窗口的問題==========================================================
    };

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK && mWebView.canGoBack()) {
            mWebView.goBack();
            return true;
        }

        return super.onKeyDown(keyCode, event);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        if (mWebView != null) {
            mWebView.clearHistory();
            ((ViewGroup) mWebView.getParent()).removeView(mWebView);
            mWebView.loadUrl("about:blank");
            mWebView.stopLoading();
            mWebView.setWebChromeClient(null);
            mWebView.setWebViewClient(null);
            mWebView.destroy();
            mWebView = null;
        }
    }

}

App通過調(diào)用外部瀏覽器打開網(wǎng)頁
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);//"android.intent.action.VIEW"
Uri content_url = Uri.parse("www.ycxc.com");
intent.setData(content_url);

//方案一
//startActivity(Intent.createChooser(intent, "請選擇瀏覽器"));

//方案二
/*if (intent.resolveActivity(getPackageManager()) != null) {
    startActivity(intent);
}*/

//方案三
intent.setClassName("com.android.browser","com.android.browser.BrowserActivity");
startActivity(intent);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末瓢捉,一起剝皮案震驚了整個(gè)濱河市办成,隨后出現(xiàn)的幾起案子迂卢,更是在濱河造成了極大的恐慌,老刑警劉巖而克,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異腾降,居然都是意外死亡碎绎,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門奸晴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來日麸,“玉大人,你說我怎么就攤上這事墩划。” “怎么了走诞?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵蛤高,是天一觀的道長。 經(jīng)常有香客問我塞绿,道長恤批,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任喜庞,我火速辦了婚禮延都,結(jié)果婚禮上雷猪,老公的妹妹穿的比我還像新娘晰房。我一直安慰自己射沟,他們只是感情好与境,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著挥转,像睡著了一般共屈。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上趁俊,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天寺擂,我揣著相機(jī)與錄音,去河邊找鬼怔软。 笑死垦细,一個(gè)胖子當(dāng)著我的面吹牛括改,可吹牛的內(nèi)容都是我干的家坎。 我是一名探鬼主播嘱能,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼虱疏,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了对粪?” 一聲冷哼從身側(cè)響起装蓬,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎牍帚,沒想到半個(gè)月后履羞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體屡久,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡忆首,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年糙及,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片唇聘。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡柱搜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出聪蘸,到底是詐尸還是另有隱情,我是刑警寧澤控乾,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布蜕衡,位于F島的核電站,受9級特大地震影響设拟,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜镰吆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一躲雅、第九天 我趴在偏房一處隱蔽的房頂上張望相赁。 院中可真熱鬧,春花似錦唤衫、人聲如沸绵脯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瞧剖。三九已至,卻和暖如春抓于,著一層夾襖步出監(jiān)牢的瞬間捉撮,已是汗流浹背巾遭。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工迎罗, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留纹安,地道東北人厢岂。 一個(gè)月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像卒茬,于是被迫代替她去往敵國和親圃酵。 傳聞我的和親對象是個(gè)殘疾皇子馍管,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,504評論 25 707
  • 關(guān)于這份調(diào)研報(bào)告,不是從技術(shù)角度深入探索拉盾,重點(diǎn)是從產(chǎn)品本身分析盾剩,通俗易懂才是重點(diǎn)。主要是為了鍛煉平時(shí)做技術(shù)調(diào)研和競...
    石先閱讀 23,288評論 13 48
  • 這篇博客主要來介紹 WebView 的相關(guān)使用方法,常見的幾個(gè)漏洞剩彬,開發(fā)中可能遇到的坑和最后解決相應(yīng)漏洞的源碼喉恋,以...
    Shawn_Dut閱讀 7,209評論 3 55
  • 不知不覺,Hybird App已經(jīng)成了目前比較主流的一種開發(fā)方式氓鄙。 對于用戶體驗(yàn)要求較高或者與硬件交互較多的功能我...
    香辣牛肉面閱讀 10,188評論 3 89
  • - [ ] 晚上到了一家微信公眾號推薦的餐廳吃了蟹煲。味道如何就不做詳述态罪,就是平常的口味并不會有什么出眾向臀。我們?nèi)齻€(gè)...
    vivo劉昱潔閱讀 237評論 0 1