现象:webview js alert没有回调事件,直接调用其他不是altert事件是有的。
推断1:webview的坑(毕竟连返回值都不能直接返回,当然4.4以上用(callEvaluateJavascript)那个方法能返回,只是不向下兼容,而且之前我试的时候返回值的内容不是返回想要的字符串,而是返回所有代码,换行符,加粗符号那些也返回了)
推断2:前端代码有问题,自己写的安卓代码的问题(没分开两点是因为我是个粗心的程序员,感觉已经没威信了)
已知控制台打印出,但是没有回调jsAlert事件
WebViewCallback: Unable to create JsDialog without an Activity
以我的经验告诉我,没有Activity去显示Dialog,这不可能呀,当前Activity并没有销毁(思维误区),前端代码不兼容的问题,导致没有什么内容可以让JsDialog弹出来的,xiaj8扯淡。想想好像Android代码的可能性比较大
百度下没找到相关的,Google下答案在这里
https://chromium.googlesource.com/chromium/src.git/+/lkgr/android_webview/glue/java/src/com/android/webview/chromium/WebViewContentsClientAdapter.java
/**
* Try to show the default JS dialog and return whether the dialog was shown.
*/
private boolean showDefaultJsDialog(JsPromptResult res, int jsDialogType, String defaultValue,
String message, String url) {
// Note we must unwrap the Context here due to JsDialogHelper only using instanceof to
// check if a Context is an Activity.
Context activityContext = AwContents.activityFromContext(mContext);
if (activityContext == null) {
Log.w(TAG, "Unable to create JsDialog without an Activity");
return false;
}
new JsDialogHelper(res, jsDialogType, defaultValue, message, url)
.showDialog(activityContext);
return true;
}
之前因为webview会导致内存泄漏用applicationContext去替换了,结果导致对话框弹窗无效,下面是防止泄漏的方法
public class DKWebView extends WebView {
public DKWebView(Context context) {
super(context.getApplicationContext());
}
public DKWebView(Context context, AttributeSet attrs) {
super(context.getApplicationContext(), attrs);
}
public DKWebView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context.getApplicationContext(), attrs, defStyleAttr);
}
}
protected void destoryWebview(DKWebView dkWebView) {
if (dkWebView != null) {
ViewGroup parent = (ViewGroup) dkWebView.getParent();
if (parent != null) {
parent.removeView(dkWebView);
}
dkWebView.removeAllViews();
dkWebView.destroy();
}
}
很矛盾。那么怎样解决这个问题呢?webview泄漏与显示对话框?
要不传进去弱引用的上下文?可是调用的地方有几个,而且没有父Webview管理的还有其他地方,不可能重改。
关注问题重点是Alert没回调,不是弹出提示框的问题,webview js alert没有回调事件与是否弹出对话框没有直接关系,因为alert事件被webChromeClient拦截了,那我的webChromeClient事件怎么没回调呢?
查看父类相关代码后发现我的initView() 重写了webChromeClient的alert事件后设置给webview,然而原先initData() 又设置了另外一个重写了onReceivedTitle方法的webChromeClient,两个对象重写不同方法,结果总是回调后面设置的webChromeClient,看了下代码也找不到为什么会这样,感觉就跟setText一样吧
- 看了下源码:
/**
* Sets the WebViewClient that will receive various notifications and
* requests. This will replace the current handler.
*
* @param client an implementation of WebViewClient
*/
public void setWebViewClient(WebViewClient client) {
checkThread();//检查是否在同个线程调用webview方法
//All WebView methods must be called on the UI thread,Future versions of WebView may not support use on other threads
All WebView methods must be called on the same thread
mProvider.setWebViewClient(client);
}
private final Looper mWebViewThread = Looper.myLooper();
private void checkThread() {
// Ignore mWebViewThread == null because this can be called during in the super class
// constructor, before this class's own constructor has even started.
if (mWebViewThread != null && Looper.myLooper() != mWebViewThread) {
Throwable throwable = new Throwable(
"A WebView method was called on thread '" +
Thread.currentThread().getName() + "'. " +
"All WebView methods must be called on the same thread. " +
"(Expected Looper " + mWebViewThread + " called on " + Looper.myLooper() +
", FYI main Looper is " + Looper.getMainLooper() + ")");
Log.w(LOGTAG, Log.getStackTraceString(throwable));
StrictMode.onWebViewMethodCalledOnWrongThread(throwable);
if (sEnforceThreadChecking) {
throw new RuntimeException(throwable);
}
}
}
mProvider由工厂类产生,具体接口的实现是怎样就看不到了
static WebViewFactoryProvider getProvider() {
synchronized (sProviderLock) {
// For now the main purpose of this function (and the factory abstraction) is to keep
// us honest and minimize usage of WebView internals when binding the proxy.
if (sProviderInstance != null) return sProviderInstance;
...
Class<WebViewFactoryProvider> providerClass = getProviderClass();//底层库
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "providerClass.newInstance()");
try {
sProviderInstance = providerClass.getConstructor(WebViewDelegate.class)
.newInstance(new WebViewDelegate());
if (DEBUG) Log.v(LOGTAG, "Loaded provider: " + sProviderInstance);
return sProviderInstance;
...
}
Webview下有两个方法确保provider已经产生
private void ensureProviderCreated() {
checkThread();
if (mProvider == null) {
// As this can get called during the base class constructor chain, pass the minimum
// number of dependencies here; the rest are deferred to init().
mProvider = getFactory().createWebView(this, new PrivateAccess());
}
}
private static synchronized WebViewFactoryProvider getFactory() {
return WebViewFactory.getProvider();
}
- 怎样才能在不同的地方调用同个对象但是重写不同的方法?
不太可能。因为重写不同的方法就不是同个对象了。那么用接口的方式吗?应该可以,不过还是利用父类Webview的方法,加多一个标记然后在父类统一管理
下面是子类和父类的方法
public abstract class BaseWebviewActivity extends BaseActivity {
@BindView(R.id.wb)
public DKWebView wb;
NormalDialog normalDialog;
public WebChromeClient webChromeClient;
protected static final String NEED_BACK="needback";
@Override
protected void onInitView() {
WebViewUtil.initWebViewSettings(wb,this);//这个是一些初始化
wb.getSettings().setUseWideViewPort(false);
normalDialog=new NormalDialog(this);
webChromeClient=new WebChromeClient();
}
protected String getType(){
return "";
};
@Override
protected String getActionBarTitle() {
return "";
}
@Override
protected int getLayoutId() {
return R.layout.activity_base_webview;
}
@Override
protected void onInitData() {
Logger.e(getUrl());
wb.loadUrl(getUrl());
wb.setWebViewClient(new WebViewClient(){
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
normalDialog.show();
super.onPageStarted(view, url, favicon);
}
@Override
public void onPageFinished(WebView view, String url) {
normalDialog.dismiss();
super.onPageFinished(view, url);
}
});
webChromeClient=new WebChromeClient(){
@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
supActionBar.setTitle(title);
}
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
if (NEED_BACK.equals(getType())) {
if ("true".equals(message)) {
finish();
} else {
wb.loadUrl("javascript:htmlGoBack()");
}
result.confirm();
return true;
}
return super.onJsAlert(view, url, message, result);//其实默认就是返回false 不拦截事件,跟手势那个有点像,继续执行
}
};
wb.setWebChromeClient(webChromeClient);
}
public abstract String getUrl();
@Override
protected void onDestroy() {
super.destoryWebview(wb);
super.onDestroy();
}
}
public class TestTubeHelperActivity extends BaseWebviewActivity implements SupActionBar.OnLeftActionListener {
@Override
public String getUrl() {
return "xxxxxx";
}
@Override
public void onBackPressed() {
/*if (wb.canGoBack()) {
wb.goBack();//返回上一页面
} else {
// gWebView.reload();//这个可以防止关闭后视频的问题,忘记是白屏还是声音的问题了
super.onBackPressed();
}*/
onLeftAction();
}
@Override
protected String getType() {
return NEED_BACK;
}
@Override
public void onLeftAction() {
// onBackPressed();
wb.loadUrl("javascript:alert(isIndex())");
}
}
原生webview就可以控制返回顺序,但是为了测试这个方法有没有问题,所以试了下。
然后找了下相关分析比如这篇http://www.jianshu.com/p/a8ed39a17f3f
但是发现source下没有chromium这个目录了