閱讀之前推薦閱讀博客大佬的這2篇
Android開發(fā):最全面匕累、最易懂的Webview使用詳解
最全面總結(jié) Android WebView與 JS 的交互方式
本文作者: @youyuge
個人博客站點: https://youyuge.cn
參考自imooc實戰(zhàn)課程缀壤,感謝猿猿老師,另外猿猿老師讓我發(fā)一下課程鏈接~~(笑,非廣告)http://coding.imooc.com/learn/list/116.html
后續(xù)篇目:
Android開罐頭——WebView高可擴展性封裝(二)
Android開罐頭——WebView高可擴展性封裝(三)
一览妖、架構(gòu)搭建
首先,我們確定第一步應(yīng)該完成什么:
- 作為封裝,一個抽象的父類是必須要的,比如初始化
webView
乡括,webView
的生命周期管理,因為大家知道webView
容易內(nèi)存泄漏智厌。我們建立一個抽象類WebDelegate.java
繼承自fragment
诲泌。 - 同時,子類實現(xiàn)一些具體的做法铣鹏,比如
webSettings
的設(shè)置敷扫。 - 既然如此,父類就應(yīng)當(dāng)和子類通信诚卸,因為有些
webView
的設(shè)置必須在子類實現(xiàn)葵第,而這些設(shè)置,應(yīng)該在父類初始化webView
的時候就執(zhí)行惨险。很明顯羹幸,我們需要一個接口類,進行回調(diào)通信辫愉。我們建立一個接口類IWebViewInitializer
栅受。
子類通過繼承獲取父類數(shù)據(jù),而父類通過接口回調(diào)獲得數(shù)據(jù)恭朗。關(guān)系圖:
二屏镊、IWebViewInitializer回調(diào)接口
我們想讓子類必須實現(xiàn)這個接口,注意是必須痰腮,若非必須可參考onClick
點擊事件的設(shè)計而芥,而由于這里是必須實現(xiàn),所以我們先在WebDelegate
基類中創(chuàng)建抽象方法:
public abstract IWebViewInitializer setInitializer();
因此膀值,父類已經(jīng)可以獲取到我們的接口實例了棍丐。具體接口如下:
public interface IWebViewInitializer {
WebView initWebViewSettings(WebView webView);
//針對瀏覽器本身行為的控制误辑,如前進后退的回調(diào)
WebViewClient initWebViewClient();
//針對頁面的控制,如js交互
WebChromeClient initWebChromeClient();
}
子類需要實現(xiàn)3個具體的方法,具體實現(xiàn)以后再談歌逢,我們先考慮父類巾钉。
三、父類WebDelegate
3.1 繼承自fragment秘案,首先定義一些變量砰苍,其中:
- 為了弱引用創(chuàng)建了一個引用隊列
- 使用布爾變量
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()
中執(zhí)行:
- 注意初始化的時候使用了 弱引用防止內(nèi)存泄漏
- 初始化后
mIsWebViewAvailable
變?yōu)?code>true表示之后webview
可用了 -
addJavascriptInterface
方法會讓網(wǎng)頁里的js能調(diào)用本地LatteWebInterface
實例里的方法吼旧,此類在章節(jié)4里寫
@SuppressLint("JavascriptInterface")
private void initWebView() {
if(mWebView !=null){
mWebView.removeAllViews();
mWebView.destroy();
}else {
//獲取子類回調(diào)傳回來的接口實例
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接口對象映射,讓網(wǎng)頁上的js能調(diào)用本地代碼未舟,注意此方法要加注解
mWebView.addJavascriptInterface(LatteWebInterface.create(this),"latte");
//webview可用了
mIsWebViewAvailable = true;
}else {
throw new NullPointerException("Initializer is null!");
}
}
}
- 主要的初始化完畢后黍少,其次是在一些回調(diào)事件(比如
onPause
)中處理webView
的生命周期,防止內(nèi)存泄漏处面,完整的基類暫時如下:
/**
* @function 網(wǎng)絡(luò)服務(wù)的抽象基類
* Created by 尤晟 on 2017-07-29.
*/
//這里繼承的BaseDelegate基類暫時理解為封裝過的Fragment,第二篇中會有詳細(xì)封裝過程
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;
}
}
}
四菩掏、創(chuàng)建js交互的本地對象類LatteWebInterface
此類在父基類WebDelegate
中的addJavascriptInterface
方法中就被調(diào)用魂角,進行對象映射,為了讓js代碼執(zhí)行此類中的方法智绸。
/**
* @function 用來和原生進行交互野揪,js代碼中通過反射調(diào)用此類中的方法
* 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中會調(diào)用此方法
public String event(String params) {
final String action = JSON.parseObject(params).getString("data");
return null;
}
}
五、總結(jié)
至此瞧栗,基類斯稳,子類的回調(diào)接口,js本地類三個文件已暫時編寫完成迹恐,已可以初步使用挣惰,但是還遠(yuǎn)遠(yuǎn)不夠,后續(xù)篇目中將繼續(xù)封裝殴边,可關(guān)注本人~~
17.7.31 更新:
17.8.3更新: