PrimWeb https://github.com/JakePrim/PrimWeb
logo.png
PrimWeb is What?
PrimWeb 是一个代理的WebView基于的 Android WebView 和 腾讯 x5 WebView,容易、灵活使用以及功能非常强大的库,提供了 WebView 一系列的问题解决方案 ,并且轻量和灵活, 更方便 webview 的切换.
What Support?
- 支持动态添加WebView
- 支持X5WebView 和 原生的WebView切换
- 简化Js通信
- 灵活的设置WebSetting
- 代理WebViewClient 兼容 X5 WebView和android WebView
- 代理WebChormeClient 兼容 android webview 和 x5 webview
- 支持判断js方法是否存在
- 支持input标签文件上传
- 支持Js通信文件上传
- 简化回退及返回键的处理
- 简化url加载
- webview 安全漏洞的问题修复,更加安全
- 支持权限管理,常用的定位、相册的权限
- 支持电话、短信、邮件的跳转
- 支持自定义进度条指示器
- 支持自定义错误页面
- 支持跳转到其他应用页面
Activity调用PrimWeb | Fragment调用PrimWeb | 识别电话短信邮箱 |
---|---|---|
activity.gif | fragment.gif | sms.gif |
JS通信 | 文件上传 | 自定义错误页面 |
---|---|---|
js.gif | input.gif | error.gif |
How Do I Use?
代码语言:javascript复制PrimWeb.with(getActivity())
.setWebParent(webParent, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT))
.useDefaultUI()
.useDefaultTopIndicator()
.setWebViewType(PrimWeb.WebViewType.X5)
.setListenerCheckJsFunction(this)
.buildWeb()
.lastGo()
.launch(mParam1);
Update Log
- v1.0.0 完善功能
- v1.0.1 1. 回退和返回键的简化处理 2. 添加返回拦截,处理特殊情况 3. 添加进度条指示器可自定义 4. 添加错误页面可自定义 5. 优化文件上传
TODO
- 实现刷新回弹功能
- 实现JS通信文件上传
- webview下载文件
API 详解
动态添加webView,防止内存泄漏
代码语言:javascript复制.setWebParent(frameLayout, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT))
内存泄漏不用担心,已经在内部处理了
@Override
public void destroy() {
removeAllViewsInLayout();
ViewParent parent = getParent();
if (parent instanceof ViewGroup) {//从父容器中移除webview
((ViewGroup) parent).removeAllViewsInLayout();
}
releaseConfigCallback();
super.destroy();
}
动态切换 X5和Android 的webview
如果要是用x5的webview需要在application中调用此方法 PrimWeb.init(this); 初始化x5
代码语言:javascript复制//使用库中X5的webview
.setWebViewType(PrimWeb.WebViewType.X5)
//使用库中Android的webview
.setWebViewType(PrimWeb.WebViewType.Android)
public enum WebViewType {
Android, X5
}
Javascript调Java? 可以addJavascriptInterface 多个,具体请看 SafeJsInterface
代码语言:javascript复制primWeb.getJsInterface().addJavaObject(new MyJavaObject(),"jsAgent")
/** 注入js脚本 */
public class MyJavaObject {
@JavascriptInterface
public void login(String data) {
}
}
Java调用Javascript方法,方便安全的加载js方法可传多个参数,具体请看 SafeCallJsLoaderImpl
代码语言:javascript复制primWeb.getCallJsLoader().callJS("jsMethod");
primWeb.getCallJsLoader().callJS("callByAndroidParam", 1234);
primWeb.getCallJsLoader().callJs("callByAndroidMoreParams", agentValueCallback, getJson(), "prim", true);
//可传多个参数,可使用高级的API
@RequiresApi(Build.VERSION_CODES.KITKAT)
void callJs(String method, AgentValueCallback<String> callback, String... params);
@RequiresApi(Build.VERSION_CODES.KITKAT)
void callJs(String method, AgentValueCallback<String> callback);
void callJS(String method, String... params);
void callJS(String method);
灵活的设置WebSetting如:X5DefaultWebSetting 继承 BaseAgentWebSetting类 注意WebSettings 是X5 的 API
代码语言:javascript复制.setAgentWebSetting(new X5DefaultWebSetting(this))
public class X5DefaultWebSetting extends BaseAgentWebSetting<WebSettings> {
private Context context;
private static final String APP_CACAHE_DIRNAME = "/webcache";
public X5DefaultWebSetting(Context context) {
this.context = context;
}
@SuppressLint("SetJavaScriptEnabled")
@Override
protected void toSetting(WebSettings webSetting) {
webSetting.setJavaScriptEnabled(true);
webSetting.setJavaScriptCanOpenWindowsAutomatically(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
// 通过 file url 加载的 Javascript 读取其他的本地文件 .建议关闭
webSetting.setAllowFileAccessFromFileURLs(false);
// 允许通过 file url 加载的 Javascript 可以访问其他的源,包括其他的文件和 http,https 等其他的源
webSetting.setAllowUniversalAccessFromFileURLs(false);
}
}
......
}
设置 setWebViewClient 使用代理的WebViewClient 兼容android webview 和 x5 webview
代码语言:javascript复制.setAgentWebViewClient(agentWebViewClient)
AgentWebViewClient agentWebViewClient = new AgentWebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(IAgentWebView view, String url) {
Log.e(TAG, "shouldOverrideUrlLoading: " url);
return super.shouldOverrideUrlLoading(view, url);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public boolean shouldOverrideUrlLoading(IAgentWebView view, WebResourceRequest request) {
Log.e(TAG, "shouldOverrideUrlLoading: WebResourceRequest --> " request.getUrl());
return super.shouldOverrideUrlLoading(view, request);
}
};
代理WebChormeClient 兼容android webview 和 x5 webview
代码语言:javascript复制 .setAgentWebChromeClient(agentChromeClient)
AgentChromeClient agentChromeClient = new AgentChromeClient() {
@Override
public void onReceivedTitle(View webView, String s) {
super.onReceivedTitle(webView, s);
if (actionBar != null) {
actionBar.setTitle(s);
}
}
};
设置允许打开其他应用页面
代码语言:javascript复制.alwaysOpenOtherPage(true)
设置进度指示器
代码语言:javascript复制关闭进度指示器:
.colseTopIndicator()
设置默认的进度指示器:
.useDefaultTopIndicator()
如果想改变指示器的颜色可以调用如下:
.useDefaultTopIndicator(@ColorInt int color)
.useDefaultTopIndicator(@NonNull String color)
如果想改变指示器的高可以调用如下:
.useDefaultTopIndicator(@ColorInt int color, int height)
如果想自定义指示器可以调用如下:主要需要继承BaseIndicatorView
.useCustomTopIndicator(@NonNull BaseIndicatorView indicatorView)
设置错误页面
代码语言:javascript复制设置默认的UI
.useDefaultUI()
可以设置布局的layout和点击的id
.useCustomUI(@LayoutRes int errorLayout, @IdRes int errorClickId)
可以设置为一个view
.useCustomUI(@NonNull View errorView)
灵活安全的加载url,具体可以看UrlLoader
代码语言:javascript复制 primWeb.getUrlLoader().loadUrl();
primWeb.getUrlLoader().reload();
primWeb.getUrlLoader().stopLoading();
webview的生命周期
代码语言:javascript复制 @Override
protected void onResume() {
super.onResume();
primWeb.webLifeCycle().onResume();
}
@Override
protected void onPause() {
super.onPause();
primWeb.webLifeCycle().onPause();
}
@Override
protected void onDestroy() {
super.onDestroy();
primWeb.webLifeCycle().onDestory();
}
判断js方法是否存在
代码语言:javascript复制 //设置监听
.setListenerCheckJsFunction(this)
//要检查的js方法
primWeb.getCallJsLoader().checkJsMethod("returnBackHandles");
@Override
public void jsFunExit(Object data) {
Toast.makeText(getActivity(), data.toString() "方法存在", Toast.LENGTH_SHORT).show();
}
@Override
public void jsFunNoExit(Object data) {
Toast.makeText(getActivity(), data.toString() "方法不存在", Toast.LENGTH_SHORT).show();
}
内部是这样处理的,我自己写了一个js方法,来专门判断, 具体请看BaseCallJsLoader
代码语言:javascript复制 @Override
public void checkJsMethod(String method) {
StringBuilder sb = new StringBuilder();
sb.append("function checkJsFunction(){ if(typeof ")
.append(method)
.append(" != "undefined" && typeof ")
.append(method)
.append(" == "function")")
.append("{console.log("")
.append(method)
.append("");")
.append("checkJsBridge['jsFunctionExit']();")
.append("}else{")
.append("if(typeof checkJsBridge == "undefined") return false;")
.append("checkJsBridge['jsFunctionNo']();}}");
call("javascript:" sb.toString() ";checkJsFunction()", null);
}
webview 上传文件
代码语言:javascript复制PrimWeb库已经默认实现了文件上传
如果想禁止文件上传可以设置:
.setAllowUploadFile(false)
如果想使用第三方文件选择库上传文件设置:
.setUpdateInvokThrid(true)
同时具体的上传逻辑需要自己完成,具体的可以看Demo
@Override
public void jsOpenVideos() {
Log.e(TAG, "jsOpenVideos: ");
PrimPicker.with(WebFragment.this)
.choose(MimeType.ofVideo())
.setMaxSelected(1)
.setImageLoader(new MyImageLoad())
.setShowSingleMediaType(true)
.setCapture(false)
.setSpanCount(3)
.lastGo(1001);
}
@Override
public void jsOpenPick() {
PrimPicker.with(WebFragment.this)
.choose(MimeType.ofImage())
.setMaxSelected(1)
.setImageLoader(new MyImageLoad())
.setShowSingleMediaType(true)
.setCapture(false)
.setSpanCount(3)
.lastGo(1001);
}
private ValueCallback<Uri[]> agentValueCallbacks;
private ValueCallback<Uri> agentValueCallback;
AgentChromeClient agentChromeClient = new AgentChromeClient() {
@Override
public boolean onShowFileChooser(View webView, ValueCallback<Uri[]> valueCallback, com.tencent.smtt.sdk.WebChromeClient.FileChooserParams fileChooserParams) {
agentValueCallbacks = valueCallback;
//注意监听需要写在这里 否则监听有时候会收不到
primWeb.setThriedChooserListener(WebFragment.this);
return super.onShowFileChooser(webView, valueCallback, fileChooserParams);
}
@Override
public void openFileChooser(ValueCallback<Uri> valueCallback) {
agentValueCallback = valueCallback;
primWeb.setThriedChooserListener(WebFragment.this);
super.openFileChooser(valueCallback);
}
@Override
public void openFileChooser(ValueCallback<Uri> valueCallback, String acceptType) {
agentValueCallback = valueCallback;
primWeb.setThriedChooserListener(WebFragment.this);
super.openFileChooser(valueCallback, acceptType);
}
@Override
public void openFileChooser(ValueCallback<Uri> valueCallback, String acceptType, String capture) {
agentValueCallback = valueCallback;
primWeb.setThriedChooserListener(WebFragment.this);
super.openFileChooser(valueCallback, acceptType, capture);
}
};
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Log.e(TAG, "onActivityResult: requestCode --> " requestCode " | resultCode --> " resultCode);
if (resultCode == RESULT_CANCELED && requestCode == 1001) {
cancelFilePathCallback();
} else if (resultCode == RESULT_OK && requestCode == 1001) {
uploadImgVideo(PrimPicker.obtainUriResult(data).get(0));
}
}
private void uploadImgVideo(Uri uri) {
Uri result = uri;
Uri[] results = new Uri[]{uri};
if (agentValueCallbacks != null) {
agentValueCallbacks.onReceiveValue(results);
agentValueCallbacks = null;
} else if (agentValueCallback != null) {
if (result == null) {
return;
}
agentValueCallback.onReceiveValue(result);
agentValueCallback = null;
}
}
private void cancelFilePathCallback() {
if (agentValueCallback != null) {
agentValueCallback.onReceiveValue(null);
agentValueCallback = null;
} else if (agentValueCallbacks != null) {
agentValueCallbacks.onReceiveValue(null);
agentValueCallbacks = null;
}
}
回退的处理简化
代码语言:javascript复制返回键的回退简化:
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (primWeb.handlerKeyEvent(keyCode, event)) {
return true;
}
return super.onKeyDown(keyCode, event);
}
返回按钮的回退简化:
if (!primWeb.handlerBack()) {
this.finish();
}