【问题标题】:Get ANR dialog when open links from WebView in Chrome custom tabs. How do I debug this?在 Chrome 自定义选项卡中从 WebView 打开链接时获取 ANR 对话框。我该如何调试?
【发布时间】:2017-12-28 20:13:19
【问题描述】:

我想使用 Chrome 自定义标签来正确处理域外的 URL。 这是代码

webView.setWebViewClient(new WebViewClient() {
    public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request){
        String url = request.getUrl().toString();
        if(url.startWith("http://my.domain.name"))
          return false;
        else{
            CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
            builder.setToolbarColor(getResources().getColor(R.color.colorPrimary));
            builder.setStartAnimations(getActivity(), R.anim.slide_in_right, R.anim.slide_out_left);
            builder.setExitAnimations(getActivity(), R.anim.slide_in_left, R.anim.slide_out_right);
            Intent actionIntent = new Intent(
                                getApplicationContext(), ActionBroadcastReceiver.class);
            actionIntent.setData(Uri.parse(url));
            PendingIntent menuItemPendingIntent =
                                PendingIntent.getBroadcast(getApplicationContext(), 0, actionIntent, 0);
            builder.addMenuItem(getString(R.string.action_share), menuItemPendingIntent);
            CustomTabsIntent customTabsIntent = builder.build();
            customTabsIntent.launchUrl(getActivity(), Uri.parse(url));

            return true;
        }
    }
});

但是,当我单击域外 URL 时,偶尔会出现 ANR 对话框并且应用程序的 UI 冻结。

我尝试调试 anr 跟踪,但没有发现可疑线程。 ANR 跟踪文件很长,所以我把它贴在这里: https://gist.github.com/hoavt-54/42f1109c0619eed81e82a9a8d1128a6d

如果您对如何调试应用程序或如何理解跟踪文件有任何建议,我将不胜感激。

【问题讨论】:

  • 在 ui 线程上运行?
  • @TWL:我很确定不是,但可能是
  • 即使 shouldOverrideUrlLoading 在 UI 线程上运行,触发 ANR 对话框的时间也不应超过 5 秒。
  • Hoa:ANR 仅发生在主 (ui) 线程上。永远不要在 UI 线程上进行任何网络调用,绝对没有。

标签: android android-anr-dialog chrome-custom-tabs


【解决方案1】:

根据您的代码,您将覆盖 Webview url 以移动到自定义 chrome 选项卡。

根据我对 webview 的理解,它在 chrome 线程中提供回调,因此理想情况下,您应该使用回调和带有活动主线程的处理程序在活动中运行代码。

   @Override
        public void retainOldWebView() {
            if (mmtWebViewPop != null) {
                mHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        if (mmtWebViewPop != null) {
                            webViewFrameLayout.removeView(mmtWebViewPop);
                            mmtWebViewPop = null;
                        }
  //TODO: write your own implementation here
                    }
                }, 100);
            }
        }

要调试 webview,我建议使用 chromium 标签验证日志,或者在进行此类实验时启用远程调试

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    WebView.setWebContentsDebuggingEnabled(true);
}

作为一名开发人员,我不希望在一个活动中混合两者的实现,即使我愿意为 customChromeTabs 和 WebView 创建单独的片段,并在必要时替换片段。因为它们都作为一个单独的实体工作,不应混淆两者的实现。

请分享代码或实现如下内容:-

  1. WebViewActivity 拦截请求,如果 url 模式不匹配,则提供回调并使用 Intent 包中传递的 url 启动新的 ChromeTabActivity。
  2. ChromeTabActivity 您的 customChromeTab 代码应该存在并且彼此独立工作并完成以从您离开的位置恢复 webview。
  3. 但我从来没有在类似的情况下使用过 ChromeTabActivity,我有一个用例,其中 fb 和 google 登录对我不起作用,所以我在 frameLayout 的另一个 web 视图中打开了相同的用例,这对我的用例来说很好用。一旦页面加载完成,我就会调用这个函数。

            if (null != mmtWebView) {
                mmtWebView.setWebChromeClient(new WebChromeClientImpl(getApplicationContext()));
            }
    

类 WebChromeClientImpl 扩展 WebChromeClient {

private final Context appContext;

public WebChromeClientImpl(Context appContext) {
    this.appContext = appContext;
}

@Override
public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
    mmtWebViewPop = new WebView(view.getContext());
    mmtWebViewPop.setVerticalScrollBarEnabled(false);
    mmtWebViewPop.setHorizontalScrollBarEnabled(false);
    mmtWebViewPop.setWebViewClient(new WebViewClientImpl(appContext, null));
    mmtWebViewPop.getSettings().setJavaScriptEnabled(true);
    mmtWebViewPop.setLayoutParams(new FrameLayout.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT,
            ViewGroup.LayoutParams.MATCH_PARENT));
    webViewFrameLayout.addView(mmtWebViewPop);
    WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
    transport.setWebView(mmtWebViewPop);
    resultMsg.sendToTarget();
    return true;
}

@Override
public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
    return super.onConsoleMessage(consoleMessage);
}

@Override
public void onCloseWindow(WebView window) {
    super.onCloseWindow(window);
    retainOldWebView();
}

}

【讨论】:

    【解决方案2】:

    我建议您至少在调试模式下利用StrictMode。使用下面的代码来获取在主线程上减慢您的应用程序的任何问题的日志。

    StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                .detectAll()
                .penaltyLog()
                .build());
    

    你可以设置不同的惩罚 -

    penaltyLog() // to print log
    penaltyDeath() // This will crash you App(so costly penalty)
    penaltyDialog() // Show alert when something went lazy on Main thread
    

    关于https://developer.android.com/reference/android/os/StrictMode.html的内容太多了

    【讨论】:

      【解决方案3】:

      5 秒规则无关紧要 - 即使 1 秒阻塞主线程也会在用户面前显示为卡住的应用程序。

      系统一直在后台和前台为您的应用程序所做的工作量是巨大的,而且都是在主线程上完成的。所以当你没有从任何回调中立即返回时(onXyz())——整个世界都在等待你,什么都不会被绘制,触摸事件也不会到达,等等。

      所以永远不要在主线程上进行任何网络调用。至少在某些时候,它总是会在某些设备上导致 ANR,例如当他们的网络关闭时。

      永远不要在主线程上做任何这些:

      • 读/写本地文件系统,包括属性和数据库
      • 繁重的计算
      • 网络
      • 任何类型的长时间运行/忙碌循环

      以上所有都会导致用户随机ANR。

      【讨论】:

        猜你喜欢
        • 2021-11-16
        • 1970-01-01
        • 2023-02-22
        • 1970-01-01
        • 1970-01-01
        • 2017-12-16
        • 1970-01-01
        • 2015-12-31
        • 2014-07-29
        相关资源
        最近更新 更多