Android中X5WebView详解
據路透社報道,推特(Twitter)公司周五宣布,它已經禁止總部位于莫斯科的卡巴斯基實驗室在其社交平臺上打廣告,稱這家網絡安全公司的商業模式與其廣告規則存在沖突,并援引美國政府的說法稱卡巴斯基實驗室與俄羅斯情報機構有關聯。推特通過電子郵件證實了這項封殺令。
歡迎回到周一,新的一周繼續加油努力吧!
本篇來自?騎小豬看流星?的投稿,分享了他如何封裝X5WebView以及如何在項目中靈活運用的過程?,一起來看看!希望大家喜歡。
騎小豬看流星?的博客地址:
https://www.jianshu.com/u/0111a7da544b
這一篇的目標就是怎么樣快速封裝X5WebView,如何有效的同步以及管理Cookie,使用IntentService優化預加載,如何監聽進度條等一些在項目中使用的常用功能。
功能需求
需求一:客戶端賬號密碼登錄成功以后,調用H5界面(也就是使用X5webView,以下簡稱X5)。H5界面也需要去記錄你的狀態,比如你客戶端本地登錄成功以后,H5界面也需要顯示登錄成功的狀態。那么,客戶端和H5如何去同步狀態?
需求二:因為X5加載的時候,會有一段時間會顯示空白或者卡頓,如何去監聽并利用這個進度并優化?
需求三:如何簡單封裝X5WebView基本功能?
需求分析
需求一
(針對需求一,我真的是參考了很多哥們的技術博客,然后由于筆者上周也就是3月9號接到的開發需求,其中遇到了很多坑,所以我希望這篇博客可以把這個需求寫的詳細,盡可能的造福以后遇到同樣需求的朋友讓他們節約時間少走彎路)
對于Cookie,我們并不是很陌生,這里繼續多一嘴,這個Cookie簡單理解主要是用來進程保活的,它具有時效性(持久化和非持久化),它是通過服務器的請求,在響應頭里面拿到,然后在第二次http請求上以請求頭的方式將參數帶過去,優點是減少后臺查庫壓力等等,更加具體的可以參考上面的鏈接文章,那么,在Android中,也就是WebView中,我們如何去管理Cookie?
首先:CookieSyncManager與CookieManager
Android中關于Cookie的說明
早期的cookie是由CookieSyncManager進行管理的,之后CookieSyncManager被拋棄了,換成了CookieManager來進行管理。兩個版本的分割線就是Android SDK -- 21。
Android中Cookie的存儲位置
目前Android系統WebView是將cookie存儲data/data/package_name/app_webview這個目錄下的一個叫Cookies的數據中。
CookieSyncManager在內存和存儲器之間同步瀏覽器的cookie。另外,CookieSyncManager的同步策略是在一個獨立的線程里定時進行同步。
注意:每次同步的時間間隔是5分鐘。
CookieSyncManager類下的常用API介紹
cookie同步策略:
CookieSyncManager.createInstance(context); ?CookieSyncManager.getInstance().startSync();
cookie停止同步:
CookieSyncManager.getInstance().stopSync()cookie立即同步:調用了該方法會立即進行cookie的同步,代碼如下:?
CookieSyncManager.getInstance().sync()刪除cookie操作:?
CookieSyncManager.createInstance(this);CookieManager.getInstance().removeAllCookie();
CookieManager.getInstance().removeSessionCookie();
CookieSyncManager.getInstance().sync();
CookieSyncManager.getInstance().startSync();
CookieManager管理cookie:從sdk21之后,webview已經內置了cookie的同步操作了。雖然不再需要關注cookie的同步,但是依然需要掌握刪除cookie的操作。
刪除cookie操作:底層實現是異步清除數據庫的記錄
CookieManager.getInstance().removeAllCookies(null);CookieManager.getInstance().flush();
立即同步:注意到這個flush()方法就是立即同步cookie的操作,本質上與CookieSyncManager中的sync()方法是一樣的。于是乎,關于同步cookie我們可以有如下簡單的寫法:
? ?com.tencent.smtt.sdk.CookieSyncManager.getInstance().sync();
}else {
? ?com.tencent.smtt.sdk.CookieManager.getInstance().flush();
}
然后,筆者就遇到了第一個坑,按照如下寫法以后,cookie居然神奇的不同步(下面是偽代碼,下面是偽代碼)
? ? ? ?CookieSyncManager.createInstance(getContext());
? ? ? ?if (!TextUtils.isEmpty(url)) {
? ? ? ? ? ?CookieManager cookieManager = CookieManager.getInstance();
? ? ? ? ? ?cookieManager.setAcceptCookie(true);
? ? ? ? ? ?cookieManager.removeSessionCookie();// 移除
? ? ? ? ? ?cookieManager.removeAllCookie();
? ? ? ? ? ?String[] split = cookie.split(";");
? ? ? ? ? ?for (String string : split) {
? ? ? ? ? ? ? ?//為url設置cookie
// ? ? ? ? ? ? ? ? ajax方式下 ?cookie后面的分號會丟失
? ? ? ? ? ? ? ?cookieManager.setCookie(url, string);
? ? ? ? ? ?}
? ? ? ? ? ?String newCookie = cookieManager.getCookie(url);
? ? ? ? ? ?Log.i(TAG, "syncCookie: newCookie == " + newCookie);
// ? ? ? ? ?sdk21之后CookieSyncManager被拋棄了,換成了CookieManager來進行管理。
? ? ? ? ? ?if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
? ? ? ? ? ? ? ?CookieSyncManager.getInstance().sync();//同步cookie
? ? ? ? ? ?} else {
? ? ? ? ? ? ? ?CookieManager.getInstance().flush();
? ? ? ? ? ?}
? ? ? ?} else {
? ? ? ?}
? ?}
之前筆者通過字符串拼接,也就是append字符串 ,拼接字符串以后,我想直接通過cookieManager.setCookie(url, cookie); 在x5WebView.loadUrl(url);調用之前去設置cookie,然后,cookie就是同步不了。沒得辦法,打印日志之后發現,手動設置的cookie值,神奇的只有一個分號 !?
谷歌百度后,有哥們說是因為cookie Value的值在讀取時,只會讀取到第一個分號時,當發現第一個分號即認為讀取結束。所以分號后面的cookie的值將不會讀取,實際測試確實是這樣。那么,我們該如何解決拼接cookie讀取失敗的問題?
解決辦法如下,我們可以一個個手動設置cookie,即可拼接完整的Cookie。(這種辦法雖然笨拙,但的確可以有效解決分號切割問題)
cookieManager.setCookie(url,"xxx");cookieManager.setCookie(url,"xxx");
cookieManager.setCookie(url,"xxx");
cookieManager.setCookie(url,"xxx");
當然,我在公司項目里用的是Okhttp,(為什么這里用Okhttp,因為!這樣就可以用Okhttp、Retrofit、OkGo等網絡框架,直接集成?我在項目中給大家提供的攔截器、自定義CookieJar使用了)。通過自定義攔截器,實現CookieJar去完成同步客戶端和H5的cookie狀態。這里先上下最終效果圖:(筆者的代碼可能不是唯一實現功能需求的,但湊合還能用。實現方式有很多種,寫的不好也請大家見諒)
OkHttpClient okHttpClient = new OkHttpClient.Builder()? ? ? ?.connectTimeout(10, TimeUnit.SECONDS)
? ? ? ?.readTimeout(10, TimeUnit.SECONDS)
? ? ? ?.writeTimeout(10, TimeUnit.SECONDS)
? ? ? ?.addNetworkInterceptor(new HttpLoggingInterceptor().
? ? ? ? setLevel(HttpLoggingInterceptor.Level.BODY))
? ? ? ?.addInterceptor(new AddCookiesInterceptor())
? ? ? ?.addInterceptor(new SaveCookiesInterceptor())
? ? ? ?.cookieJar(new SaCookieManger(HomeApplication.getInstance())).build();
關于AddCookiesInterceptor以及SaveCookiesInterceptor這兩個攔截器,主要就是存Cookie和使用Cookie,然后我們點進自定義cookieJar中的?SaCookieManger
? ?private static final String TAG = LogTAG.cookie;
? ?private static Context mContext;
? ?public SaCookieManger(Context context) {
? ? ? ?mContext = context;
? ?}
? ?
? ?public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
? ? ? ?SaasCookieManager.loadCookie(cookies,url.host());
? ?}
? ?
? ?public List<Cookie> loadForRequest(HttpUrl url) {
? ? ? ?return new ArrayList<>();
? ?}
}
這個類定義了一個context構造參數,在保存cookie的saveFromResponse方法中,調用了SaasCookieManager.loadCookie(cookies,url.host());方法,我們點進SaasCookieManager
public class SaasCookieManager {? ?static final String TAG = LogTAG.cookie;
? ?/**
? ? * 獲取cookie
? ? * @param cookies
? ? * @param url
? ? */
? ?public static void loadCookie(List<Cookie> cookies,String url){
? ? ? ?List<String> convertCookies ?= new ArrayList<>();
? ? ? ?for (int i = 0; i < cookies.size(); i++) {
? ? ? ? ? ?String temp = cookies.get(i).toString();
? ? ? ? ? ?convertCookies.add(temp);
? ? ? ?}
? ? ? ?com.tencent.smtt.sdk.CookieManager cookieManager= com.tencent.smtt.sdk.CookieManager.getInstance();
? ? ? ?cookieManager.setAcceptCookie(true);
? ? ? ?for (String aCookiesArray : convertCookies) {
? ? ? ? ? ?cookieManager.setCookie(url, aCookiesArray);
? ? ? ?}
? ? ? ?if (Build.VERSION.SDK_INT <21){
? ? ? ? ? ?com.tencent.smtt.sdk.CookieSyncManager.getInstance().sync();
? ? ? ?}else {
? ? ? ? ? ?com.tencent.smtt.sdk.CookieManager.getInstance().flush();
? ? ? ?}
? ?}
通過遍歷添加到集合里面,然后一個個的setCookie( url ,cookie )、接著判斷SDK版本號進行同步刷新即可,具體可以參考項目源代碼。當然大家也可以在這里面根據開發需求去增加自己的實際功能。
通過上面的步驟,我們就可以簡單的實現 客戶端與H5端同步cookie。筆者的項目里面,是客戶端登錄成功以后,進入H5頁面,H5頁面上直接顯示已登錄狀態。
拓展:有部分手機使用后可能還是無法同步,那么我們可以嘗試,設置跨域讀取cookie,開啟webview對第三方cookie的支持。
public final void setAcceptThirdPartyCookies() {? ?if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
? ? ? ?CookieManager.getInstance().setAcceptThirdPartyCookies(this, true);
? ?}else {
? ? ? ?CookieManager.getInstance().setAcceptCookie(true);
? ?}
}
當頁面加載完畢的時候,我們可以通過下面的截圖代碼,去獲取H5上面的cookie,我們也可以打印日志、進行同步
public void onPageFinished(WebView webView, String s) {
? ?CookieManager cookieManager = CookieManager.getInstance();
? ?cookieManager.setAcceptCookie(true);
? ?String endCookie = cookieManager.getCookie(s);
? ?Log.i(TAG, "onPageFinished: endCookie : " + endCookie);
? ?if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
? ? ? ?CookieSyncManager.getInstance().sync();//同步cookie
? ?} else {
? ? ? ?CookieManager.getInstance().flush();
? ?}
? ?super.onPageFinished(webView, s);
}
需求二
監聽進度,是這樣,WebView里面的WebChromeClient這個類,里面有個onProgressChanged方法,重寫這個方法就可以獲取加載進度。獲取到X5Webview的進度之后,還需要通過WebView的setWebChromeClient方法,將我們自定義的WebChromeClient對象傳進去,即可完成進度監聽。
? ?public void onProgressChanged(WebView view, int newProgress) {? ? ? ? ? ?// TODO Auto-generated method stub
? ? ? ? ? ?progressBar.setProgress(newProgress);
? ? ? ? ? ?if (progressBar != null && newProgress != 100) {
// ? ? ? ? ? ? ?Webview加載沒有完成 就顯示我們自定義的加載圖
? ? ? ? ? ? ? ?progressBar.setVisibility(VISIBLE);
? ? ? ? ? ?} else if (progressBar != null) {
// ? ? ? ? ? ? ?Webview加載完成 就隱藏進度條,顯示Webview
? ? ? ? ? ? ? ?progressBar.setVisibility(GONE);
? ? ? ? ? ? ? ?imageView.setVisibility(GONE);
? ? ? ? ? ?}
? ? ? ?}
需求三
封裝X5Webview基本功能
常用設置
比如設置對JS的支持等等一些比較常用的,我們可以直接這樣設置
setBackgroundColor(getResources().getColor(android.R.color.white));setWebViewClient(client);
setWebChromeClient(chromeClient);
setDownloadListener(downloadListener);
addJavascriptInterface(new H5JsInterface(getContext(), this), "h5app");
setClickable(true);
setOnTouchListener(new OnTouchListener() {
? ?
? ?public boolean onTouch(View v, MotionEvent event) {
? ? ? ?return false;
? ?}
});
WebSettings webSetting = getSettings();
webSetting.setJavaScriptEnabled(true);
webSetting.setBuiltInZoomControls(true);
webSetting.setJavaScriptCanOpenWindowsAutomatically(true);
webSetting.setDomStorageEnabled(true);
webSetting.setAllowFileAccess(true);
webSetting.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
webSetting.setSupportZoom(true);
webSetting.setUseWideViewPort(true);
webSetting.setSupportMultipleWindows(true);
webSetting.setAppCacheEnabled(true);
webSetting.setGeolocationEnabled(true);
webSetting.setAppCacheMaxSize(Long.MAX_VALUE);
webSetting.setPluginState(WebSettings.PluginState.ON_DEMAND);
webSetting.setRenderPriority(WebSettings.RenderPriority.HIGH);
滾動條(內側、外側的設置),隱藏或顯示的基本使用
? ? ? ?setHorizontalScrollBarEnabled(false);//水平不顯示小方塊
? ? ? ?setVerticalScrollBarEnabled(false); //垂直不顯示小方塊
? ? ? ?setScrollBarStyle(View.SCROLLBARS_OUTSIDE_OVERLAY);//滾動條在WebView內側顯示
// ? ? ?setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);//滾動條在WebView外側顯示
處理預加載
有的小伙伴說,X5預加載不是很友好。在加載X5內核的時候,X5內核需要進行一些初始化,這些初始化如果不明確指出運行的線程,它就會在你啟動頁面的時候,默認在主線程中執行,因此就會出現卡頓(這個現象時有時無,但是我們在代碼層面盡可能的去規避使用風險),所以,我們可以寫個?IntentService?去幫我們管理預加載問題:
public class X5NetService extends IntentService {? ?public static final String TAG = LogTAG.x5webview;
? ?public X5NetService(){
? ? ? ?super(TAG);
? ?}
? ?public X5NetService(String name) {
? ? ? ?super(TAG);
? ?}
? ?
? ?public void onHandleIntent(@Nullable Intent intent) {
? ? ? ?initX5Web();
? ?}
? ?public void initX5Web() {
? ? ? ?if (!QbSdk.isTbsCoreInited()) {
? ? ? ? ? ?QbSdk.preInit(getApplicationContext(), null);// 設置X5初始化完成的回調接口
? ? ? ?}
? ? ? ?QbSdk.initX5Environment(getApplicationContext(), cb);
? ?}
? ?QbSdk.PreInitCallback cb = new QbSdk.PreInitCallback() {
? ? ? ?
? ? ? ?public void onViewInitFinished(boolean arg0) {
? ? ? ? ? ?// TODO Auto-generated method stub
? ? ? ?}
? ? ? ?
? ? ? ?public void onCoreInitFinished() {
? ? ? ? ? ?// TODO Auto-generated method stub
? ? ? ?}
? ?};
}
多提一嘴:IntentService是Service的子類,比普通的Service增加了額外的功能。IntentService會創建獨立的worker線程來處理所有的Intent請求;會創建獨立的worker線程來處理onHandleIntent()方法實現的代碼,無需處理多線程的問題;所有請求處理完成后,IntentService會自動停止,開發者無需手動調用stopSelf()方法停止Service;
寫完之后我們在去自定義Application里面注冊服務:(別忘了去清單文件配置Services)
public class HomeApplication extends Application{? ?private static HomeApplication homeApplication;
? ?
? ?public void onCreate(){
? ? ? ?super.onCreate();
? ? ? ?homeApplication = this;
? ? ? ?preInitX5Core();
? ?}
? ?public static HomeApplication getInstance(){
? ? ? ?return homeApplication;
? ?}
? ?private void preInitX5Core(){
? ? ? ?//預加載x5內核
? ? ? ?Intent intent = new Intent(this,X5NetService.class);
? ? ? ?startService(intent);
? ?}
}
返回鍵的處理
這個就根據大家開發需求具體使用了,有的要求返回鍵按下兩次才允許退出等等
生命周期的處理、釋放資源的處理:這個就不說了,大家查閱資料集成功能即可
攔截廣告的處理
有些哥們說,使用這個經常會出現廣告,解決這個辦法有兩個辦法
使用Https
設計攔截url規則,對允許的url進行放行加載,不允許的url,WebView禁止加載
好了,文章到這里就結束了,具體demo地址如下:
https://github.com/zuowutan/ShareX5WebViewDemo
歡迎長按下圖?->?識別圖中二維碼
或者?掃一掃?關注我的公眾號
總結
以上是生活随笔為你收集整理的Android中X5WebView详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: oracle中索引的类型,oracle索
- 下一篇: Android集成腾讯X5WebView