閱讀之前推薦閱讀博客大佬的這2篇
Android開發:最全面、最易懂的Webview使用詳解
最全面總結 Android WebView與 JS 的交互方式
本文作者: @youyuge
個人博客站點: https://youyuge.cn
參考自imooc實戰課程,感謝猿猿老師,另外猿猿老師讓我發一下課程鏈接~~(笑,非廣告)http://coding.imooc.com/learn/list/116.html
后續篇目:
Android開罐頭——WebView高可擴展性封裝(二)
Android開罐頭——WebView高可擴展性封裝(三)
一、架構搭建
首先,我們確定第一步應該完成什么:
- 作為封裝,一個抽象的父類是必須要的,比如初始化
webView
,webView
的生命周期管理,因為大家知道webView
容易內存泄漏。我們建立一個抽象類WebDelegate.java
繼承自fragment
。 - 同時,子類實現一些具體的做法,比如
webSettings
的設置。 - 既然如此,父類就應當和子類通信,因為有些
webView
的設置必須在子類實現,而這些設置,應該在父類初始化webView
的時候就執行。很明顯,我們需要一個接口類,進行回調通信。我們建立一個接口類IWebViewInitializer
。
子類通過繼承獲取父類數據,而父類通過接口回調獲得數據。關系圖:
初步架構通信圖
二、IWebViewInitializer回調接口
我們想讓子類必須實現這個接口,注意是必須,若非必須可參考onClick
點擊事件的設計,而由于這里是必須實現,所以我們先在WebDelegate
基類中創建抽象方法:
public abstract IWebViewInitializer setInitializer();
因此,父類已經可以獲取到我們的接口實例了。具體接口如下:
public interface IWebViewInitializer {
WebView initWebViewSettings(WebView webView);
//針對瀏覽器本身行為的控制,如前進后退的回調
WebViewClient initWebViewClient();
//針對頁面的控制,如js交互
WebChromeClient initWebChromeClient();
}
子類需要實現3個具體的方法,具體實現以后再談,我們先考慮父類。
三、父類WebDelegate
3.1 繼承自fragment,首先定義一些變量,其中:
- 為了弱引用創建了一個引用隊列
- 使用布爾變量
mIsWebViewAvailable
,這里是參考sdk里自帶的WebViewFragment
類(api25及其以下才有),這個類是為了顯示一個WebView
,而用一個布爾變量,能讓webView
在fragment pause
或者resume
的時候自動也跟著pause
和resume
(原理很簡單的)
private WebView mWebView = null;
private final ReferenceQueue<WebView> WEB_VIEW_QUEUE = new ReferenceQueue<>();
private String mUrl = null;
private boolean mIsWebViewAvailable = false;
3.2 初始化WebView
初始化工作,此方法在onCreate()
中執行:
- 注意初始化的時候使用了 弱引用防止內存泄漏
- 初始化后
mIsWebViewAvailable
變為true
表示之后webview
可用了 -
addJavascriptInterface
方法會讓網頁里的js能調用本地LatteWebInterface
實例里的方法,此類在章節4里寫
@SuppressLint("JavascriptInterface")
private void initWebView() {
if(mWebView !=null){
mWebView.removeAllViews();
mWebView.destroy();
}else {
//獲取子類回調傳回來的接口實例
final IWebViewInitializer initializer = setInitializer();
if(initializer !=null){
final WeakReference<WebView> webViewWeakReference =
new WeakReference<WebView>(new WebView(getContext()),WEB_VIEW_QUEUE);
mWebView = webViewWeakReference.get();
mWebView = initializer.initWebViewSettings(mWebView);
mWebView.setWebViewClient(initializer.initWebViewClient());
mWebView.setWebChromeClient(initializer.initWebChromeClient());
//為webView添加js接口對象映射,讓網頁上的js能調用本地代碼,注意此方法要加注解
mWebView.addJavascriptInterface(LatteWebInterface.create(this),"latte");
//webview可用了
mIsWebViewAvailable = true;
}else {
throw new NullPointerException("Initializer is null!");
}
}
}
- 主要的初始化完畢后,其次是在一些回調事件(比如
onPause
)中處理webView
的生命周期,防止內存泄漏,完整的基類暫時如下:
/**
* @function 網絡服務的抽象基類
* Created by 尤晟 on 2017-07-29.
*/
//這里繼承的BaseDelegate基類暫時理解為封裝過的Fragment,第二篇中會有詳細封裝過程
public abstract class WebDelegate extends BaseDelegate{
private WebView mWebView = null;
private final ReferenceQueue<WebView> WEB_VIEW_QUEUE = new ReferenceQueue<>();
private String mUrl = null;
private boolean mIsWebViewAvailable = false;
public WebDelegate() {
}
public abstract IWebViewInitializer setInitializer();
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Bundle args = getArguments();
mUrl = args.getString(RouteKeys.URL.name());
initWebView();
}
//初始化webview
@SuppressLint("JavascriptInterface")
private void initWebView() {
if(mWebView !=null){
mWebView.removeAllViews();
mWebView.destroy();
}else {
final IWebViewInitializer initializer = setInitializer();
if(initializer !=null){
final WeakReference<WebView> webViewWeakReference =
new WeakReference<WebView>(new WebView(getContext()),WEB_VIEW_QUEUE);
mWebView = webViewWeakReference.get();
mWebView = initializer.initWebViewSettings(mWebView);
mWebView.setWebViewClient(initializer.initWebViewClient());
mWebView.setWebChromeClient(initializer.initWebChromeClient());
mWebView.addJavascriptInterface(LatteWebInterface.create(this),"latte");
//webview可用了
mIsWebViewAvailable = true;
}else {
throw new NullPointerException("Initializer is null!");
}
}
}
public WebView getWebView() {
if (mWebView == null) {
throw new NullPointerException("WebView IS NULL!");
}
return mIsWebViewAvailable ? mWebView : null;
}
public String getUrl() {
if (mUrl == null) {
throw new NullPointerException("WebView IS NULL!");
}
return mUrl;
}
@Override
public void onPause() {
super.onPause();
if(mWebView !=null){
mWebView.onPause();
}
}
@Override
public void onResume() {
super.onResume();
if (mWebView != null) {
mWebView.onResume();
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
mIsWebViewAvailable = false;
}
@Override
public void onDestroy() {
super.onDestroy();
if (mWebView != null) {
mWebView.removeAllViews();
mWebView.destroy();
mWebView = null;
}
}
}
四、創建js交互的本地對象類LatteWebInterface
此類在父基類WebDelegate
中的addJavascriptInterface
方法中就被調用,進行對象映射,為了讓js代碼執行此類中的方法。
/**
* @function 用來和原生進行交互,js代碼中通過反射調用此類中的方法
* Created by 尤晟 on 2017-07-30.
*/
public class LatteWebInterface {
private final WebDelegate DELEGATE;
private LatteWebInterface(WebDelegate DELEGATE) {
this.DELEGATE = DELEGATE;
}
//簡單工廠模式
static LatteWebInterface create(WebDelegate delegate){
return new LatteWebInterface(delegate);
}
//js中會調用此方法
public String event(String params) {
final String action = JSON.parseObject(params).getString("data");
return null;
}
}
五、總結
至此,基類,子類的回調接口,js本地類三個文件已暫時編寫完成,已可以初步使用,但是還遠遠不夠,后續篇目中將繼續封裝,可關注本人~~
17.7.31 更新:
17.8.3更新: