編輯:關於Android編程
從去年4月項目就一直用起了JsBridge,前面也針對jsBridge使用姿勢介紹過一篇入門篇,本篇接著繼續深入,通過再次優化封裝,大大優化了部分代碼,簡化上層調用流程,快速部署你的Hybridge APP。
再進行具體編碼前 ,我先進行了一般商業APP對WebView的需求
可加載本地和雲端H5 擁有cookie持久能力 添加公共參數 回退前進功能 Js與本地navtive交互 擁有加載默認錯誤頁面能力 加載網頁可展現進度好為了滿足以上常用功能,大致對webview相關知識進行下普及。
谷歌提供的系統組件,用來加載和展現html網頁,其采用webkit內核驅動,來實現網頁浏覽功能。
擁有load() URL和本地html文件
// 雲端 webView.loadUrl("https://www.baidu.com"); // 本地 webView.loadUrl("file:///android_asset/demo.html");
WebViewClient主要輔助WebView執行處理各種響應請求事件的,比如:
onLoadResource onPageStart onPageFinish onReceiveError onReceivedHttpAuthRequest shouldOverrideUrlLoading本次加載失敗頁面,和攔截加入header頭必須用到它,由於android無法攔截h5本身ajax的請求,所以對header同步不是很好,建議大家對於ajax請求采用cookie形式,以防止url參數服務端無法獲取的問題。
加入header 一般直接使用webView.load(url, header)
view.loadUrl(url, header);
為了方便上層開發者調用,可以將此code加入到WebViewClient 的shouldOverrideUrlLoading中執行
姿勢那就是這樣:
public boolean shouldOverrideUrlLoading(WebView view, String url) { if(this.onPageHeaders(url) != null) { view.loadUrl(url, this.onPageHeaders(url)); } return super.shouldOverrideUrlLoading(view, url); }
錯誤頁面也是復寫WebViewClient的onReceivedError() 來加入自定義的抽象onPageError(),姿勢如下:
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { view.loadUrl(this.onPageError(failingUrl)); }
onPageHeaders()便是上層抽象出來的接口,方便我們直接加入header,而onPageError()是方便指定加載錯誤頁面,那麼在activity中就是這樣了,
mProgressBarWebView.setWebViewClient(new CustomWebViewClient(mProgressBarWebView.getWebView()) { @Override public String onPageError(String url) { return "file:///android_asset/error.html"; } @Override public MaponPageHeaders(String url) { return CookieManger.getHeader(getContext()); } });
主要輔助WebView處理Javascript的對話框、網站Logo、網站title、load進度等處理。
onCloseWindow(關閉WebView) onCreateWindow() onJsAlert () onJsPrompt onJsConfirm onProgressChanged onReceivedIcon onReceivedTitle onShowCustomViewWebView只是用來處理一些html的頁面內容,只用WebViewClient就行了,如果需要更豐富的處理效果,比如JS、進度條等,就要用到WebChromeClient。因為這次功能要用加載進度,不得不說它。
為了加入頂部的加載進度條,復寫WebChromeClient中onProgressChanged,在這裡更改我們加入的ProgressBar的進度,你也可以設置網頁標題,甚至可以全屏!
public class CustomWebChromeClient extends WebChromeClient { private NumberProgressBar mProgressBar; public CustomWebChromeClient(NumberProgressBar progressBar) { this.mProgressBar = progressBar; } public void onProgressChanged(WebView view, int newProgress) { if(newProgress >= 95) { this.mProgressBar.setVisibility(8); } else { if(this.mProgressBar.getVisibility() == 8) { this.mProgressBar.setVisibility(0); } this.mProgressBar.setProgress(newProgress); } super.onProgressChanged(view, newProgress); } //獲取tittle @Override public void onReceivedTitle(WebView view, String title) { super.onReceivedTitle(view, title); } //全屏 @Override public void onShowCustomView(View view, CustomViewCallback callback) { super.onShowCustomView(view, callback); } }
好了准備好了同步Header和進度條之後,就的考慮cookie同步問題
CookieManager
CookieManager是用來管理Cookie的,主要來管理cookie相關,提供如下API
CookieSyncManager
CookieSyncManagerl繼承WebSyncManager,來管理同步cookie相關,主要有以下API
resetSync() stopSync() sync() syncFromRamToFlash() checkInstanceIsAllowed()你想問這些api什麼意思,請保留點你的童真,不要問這麼簡單的問題好嗎?
接著我們就可以這樣操作來實現cookie同步了,
CookieManager cookieManager = CookieManager.getInstance(); // 接受服務器cookie cookieManager.setAcceptCookie(true); //移除之前的cookie cookieManager.removeSessionCookie(); // 注入cookies Listcookies = getCookies(customCookies); for (String cookie : cookies) { cookieManager.setCookie(uri.getHost(), cookie); } // 同步cookie CookieSyncManager.getInstance().sync();
這裡需要注意棒棒糖以上的會出現無法同步問題那麼請這樣做
if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { cookieManager.flush(); } else { CookieSyncManager.getInstance().sync(); }
經測試,完美!
你可能想問?我想自定義像header一樣加入一些自定義cookie,行,沒問題,繼續看!
public static ListcreateCustomCookies() { List cookies = new ArrayList<>(); cookies.add(“author ” + "= " + "tamic"); cookies.add(“data” + "= " + "2016.8.15"); cookies.add(“key” + "= " + 4fdfsfd34dfdfswer"); cookies.add(“chanel” + "= " + "簡書"); return cookies; }
很可能會遇到處理緩存問題,設置緩存webView緩存模式!這裡在普及下相關姿勢!
緩存模式
webview緩存模式有5種,具體方式:
- LOAD_CACHE_ONLY: 不使用網絡,只讀取本地緩存數據
- LOAD_DEFAULT: 根據cache-control決定是否從網絡上取數據。
- LOAD_CACHE_NORMAL: API level 17中已經廢棄, 從API level 11開始作用同LOAD_DEFAULT模式
- LOAD_NO_CACHE: 不使用緩存,只從網絡獲取數據.
- LOAD_CACHE_ELSE_NETWORK,只要本地有,無論是否過期,或者no-cache,都使用緩存中的數據。
www.baidu.com的cache-control為no-cache,在模式LOAD_DEFAULT下,無論如何都會從網絡上取數據,如果沒有網絡,就會出現錯誤頁面;在LOAD_CACHE_ELSE_NETWORK模式下,無論是否有網,只要本地有緩存,都會加載緩存。本地沒有緩存時才從網絡上獲取,
這個和Http緩存一致,我不在過多介紹,如果你想自定義緩存策略和時間,可以嘗試下,
清除緩存
CacheManager來處理webview緩存相關:
clearCache(boolean) CacheManager.clear
在4.4以上的此api已經無法使用,也就是說緩存清空涉及安全,需要你自己去實現,就類似picasso, okhttp緩存,一樣要開發者自我去實現。
當然也可以這樣:
WebView.clearCache(true);
清空歷史記錄
mWebview.clearHistory();
需要在onPageFinished()的方法之後調用
學習了上面基礎知識,我這裡就開始進行自定義的進度條ProgressBarWebView的封裝了,這裡我直接對BridgeWebView進行擴展。下面是主要部分。
public class ProgressBarWebView extends LinearLayout { static final String TAG = ProgressBarWebView.class.getSimpleName(); private NumberProgressBar mProgressBar; private BridgeWebView mWebView; public ProgressBarWebView(Context context) { super(context); this.init(context, (AttributeSet)null); } public ProgressBarWebView(Context context, AttributeSet attrs) { super(context, attrs); this.init(context, attrs); } @TargetApi(11) public ProgressBarWebView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); this.init(context, attrs); } @TargetApi(21) public ProgressBarWebView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); this.init(context, attrs); }
為了使webview能有後退功能!我屏蔽了長按事件,並且對返回鍵建進行了攔截。
mWebView.setOnLongClickListener(new OnLongClickListener() { public boolean onLongClick(View v) { return true; } }); this.mWebView.setOnKeyListener(new OnKeyListener() { public boolean onKey(View v, int keyCode, KeyEvent event) { if(event.getAction() == 0 && keyCode == 4 && ProgressBarWebView.this.mWebView.canGoBack()) { ProgressBarWebView.this.mWebView.goBack(); return true; } else { return false; } } });
如果防止webview代碼產生內存洩漏,請及時在activity銷毀時,清空webview
@Override public void onDestroy() { super.onDestroyView(); if (mProgressBarWebView.getWebView() != null) { mProgressBarWebView.getWebView().destroy(); } }
看了構造方法你已明白,裡面包含一個BridgeWebView和一個NumberProgressBar 成員屬性,
接著就是對JsBridge進行封裝了
Java調用js代碼:
public void registerHandler(final String handlerName, final JsHandler handler) { this.mWebView.registerHandler(handlerName, new BridgeHandler() { public void handler(String data, CallBackFunction function) { if(handler != null) { handler.OnHandler(handlerName, data, function); } } }); }
js調用Native
public void callHandler(final String handlerName, String javaData, final JavaCallHandler handler) { this.mWebView.callHandler(handlerName, javaData, new CallBackFunction() { public void onCallBack(String data) { if(handler != null) { handler.OnHandler(handlerName, data); } } }); }
看可jsBridge的可能問這個JsHandler誰神馬。本來在jsBridge源碼中沒這個東東的, 是為了方便上層調用我自己封裝的接口,
public interface JsHandler { void OnHandler(String var1, String var2, CallBackFunction var3);
好了 關鍵的東西已經介紹完
接著使用我們封裝好的ProgressBarWebView
初始化
ProgressBarWebView mProgressBarWebView = (ProgressBarWebView) findViewById(R.id.login_progress_webview);
設置自定義WebViewClient
mProgressBarWebView.setWebViewClient(new CustomWebViewClient(mProgressBarWebView.getWebView()) { @Override public String onPageError(String url) { //指定網絡加載失敗時的錯誤頁面 return "file:///android_asset/error.html"; } @Override public MaponPageHeaders(String url) { // 可以加入header return null; } });
加載指定Url
mProgressBarWebView.loadUrl("file:///android_asset/demo.html");
當然,也可以支持網絡url;
注冊Js回調函數
ArrayListmHandlerNames = new ArrayList<>(); mHandlers.add("login"); mHandlers.add("callNative"); mHandlers.add("callJs"); mHandlers.add("open");
回調js的方法
mProgressBarWebView.registerHandlers(mHandlers, new JsHandler() { @Override public void OnHandler(String handlerName, String responseData, CallBackFunction function) { if (handlerName.equals("login")) { Toast.makeText(MainActivity.this, responseData, Toast.LENGTH_SHORT).show(); } else if (handlerName.equals("callNative")) { Toast.makeText(MainActivity.this, responseData, Toast.LENGTH_SHORT).show(); function.onCallBack("我在上海"); } else if (handlerName.equals("callJs")) { Toast.makeText(MainActivity.this, responseData, Toast.LENGTH_SHORT).show(); // 想調用你的方法: function.onCallBack("好的 這是圖片地址 :xxxxxxx"); } if (handlerName.equals("open")) { mfunction = function; pickFile(); } } });
Native調用js
mProgressBarWebView.callHandler("callNative", "hello H5, 我是java", new JavaCallHandler() { @Override public void OnHandler(String handlerName, String jsResponseData) { Toast.makeText(MainActivity.this, "h5返回的數據:" + jsResponseData, Toast.LENGTH_SHORT).show(); } });
Native發送消息給js
mProgressBarWebView.send("哈喽", new CallBackFunction() { @Override public void onCallBack(String data) { Toast.makeText(MainActivity.this, data, Toast.LENGTH_SHORT).show(); } }); }
Xml文件和js代碼這裡不做介紹,具體看項目案列中源碼:
GtiHub:https://github.com/NeglectedByBoss/JsWebView
如果喜歡,請star!
PS:用了一下個推.感覺實現第三方應用的推送功能還是比較簡單的.官方文檔寫的也非常的明確.學習內容:1.使用個推實現第三方應用的推送.所有的配置我最後會給一個源代碼,內部
前言EventBus框架 EventBus是一個通用的叫法,例如Google出品的Guava,Guava是一個龐大的庫,EventBus只是它附帶的一個小功能,因此實際項
導語手機直播一般都會通過移動屏幕來調節音量的大小,本篇只實現了圖例,並不能改變音量。先看效果:需要的素材:小喇叭圖片,點擊這裡獲取預熱如果你將這哥們的十幾篇帖子都看完了的
這麼長時間沒寫博客感覺手都要生了啊,最近因為工作的關系來到了上海,目前還算穩定,所以抓緊時間寫篇博客壓壓驚。標題早已經看穿一切,這次我們來模仿一下