Why does Android OS 8 WebVew with HTML select tag

2019-03-18 09:15发布

问题:

I've got a hybrid Cordova Android app, and the app crashes when user tap on a drop-down box in my WebView running on Android OS 8. I've created a simple page with a <select> tag and the issue is reproducible. I've got a workaround which is to do my own pop up alert to select, but just wondering if this is happening to anyone else and whether this is an OS8 WebView bug.

Below is a simple page with <select> tag

https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_select

Below is my crash log

11:04:58.643 3208-3208/com.****.****E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.****.****, PID: 3208 android.content.res.Resources$NotFoundException: Resource ID #0x0
    at android.content.res.ResourcesImpl.getValue(ResourcesImpl.java:195)
    at android.content.res.Resources.loadXmlResourceParser(Resources.java:2133)
    at android.content.res.Resources.getLayout(Resources.java:1142)
    at android.view.LayoutInflater.inflate(LayoutInflater.java:421)
    at android.widget.ArrayAdapter.createViewFromResource(ArrayAdapter.java:416)
    at android.widget.ArrayAdapter.getView(ArrayAdapter.java:407)
    at org.chromium.content.browser.input.SelectPopupAdapter.getView(SelectPopupAdapter.java:53)
    at android.widget.AbsListView.obtainView(AbsListView.java:2372)
    at android.widget.ListView.measureHeightOfChildren(ListView.java:1408)
    at android.widget.ListView.onMeasure(ListView.java:1315)
    at android.view.View.measure(View.java:21998)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6580)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
    at android.view.View.measure(View.java:21998)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6580)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
    at android.view.View.measure(View.java:21998)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6580)
    at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1514)
    at android.widget.LinearLayout.measureVertical(LinearLayout.java:806)
    at android.widget.LinearLayout.onMeasure(LinearLayout.java:685)
    at android.view.View.measure(View.java:21998)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6580)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
    at android.view.View.measure(View.java:21998)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6580)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
    at android.view.View.measure(View.java:21998)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6580)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
    at com.android.internal.policy.DecorView.onMeasure(DecorView.java:721)
    at android.view.View.measure(View.java:21998)
    at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2410)
    at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1471)
    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1751)
    at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1386)
    at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6733)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:911)
    at android.view.Choreographer.doCallbacks(Choreographer.java:723)
    at android.view.Choreographer.doFrame(Choreographer.java:658)
    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:897)
    at android.os.Handler.handleCallback(Handler.java:789)
    at android.os.Handler.dispatchMessage(Handler.java:98)
    at android.os.Looper.loop(Looper.java:164)
    at android.app.ActivityThread.main(ActivityThread.java:6541)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)

My issue is not the same as this

Trying to open SELECT tag in Android WebView crashes the application

UPDATE on 9th Jan 2018: I haven't got a solution yet, my temporary workaround is remove the tag and just use an input. When user select this element, we pass the event to native code to pop up a dialog for selection and update the input once user made a selection.

UPDATE on 23rd March 2018: After some more investigation, I noticed that it only crashes if the WebView is in a Fragment, but not in an Activity.

I found below comments from this post:

Trying to open SELECT tag in Android WebView crashes the application

"When a SELECT tag is clicked, Android internally displays its options using a native AlertDialog. Webview must be created with an Activity context because AlertDialog instance needs an Activity context."

I believe this is a bug in Android, probably not handling Context properly for Fragment.

UPDATE 17th of April 2018:

As kenyee pointed out, from here https://issuetracker.google.com/issues/77246450, Google says

You mustn't subclass the resources object - this was never supported and was only possible by accident (which is why it's now marked deprecated). The framework needs to know about all the resources objects so that it can update them when webview is loaded (since webview adds additional paths to the asset manager).

Edit 29th November 2018 Seems this issue has bothered a lot of people.

  • The solution I've tried and tested to work is to not subclassing the Resource.
  • Updating compile sdk and supported library version worked for some people.
  • Adding a wrapper class to Resource may work, I've tried this approach during my initial investigation, it solved the Select crashing issue, still crashes when you click and hold on a text view to pop up the "COPY", "PASTE" options.

回答1:

Looks like this bug: https://issuetracker.google.com/issues/77246450

Don't subclass resources...won't be fixed apparently.



回答2:

I have same issue on Android 8.0 . finally i solve it. Try to update your compileSdkVersion to 26, and update your com.android.support:appcompat-v7 to 26.



回答3:

If anyone is still having this issue, I found that it wasn't even my code that was subclassing the Resources class but rather the Google support library version that I was using. Updated the support library version and it worked like a charm!



回答4:

I digged into crash logs. Though this answer does not solve your problem, you might get some useful insights

  • Webiview uses this layout file select_dialog_singlechoice.xml for inflating Spinner item using ArrayAdaper
  • You are getting Resource not found Exception from here
  • It means that Your Webview is not able to locate this resource file when you click on select tag

I am not sure why it's happening on Android 8.0,i was not able to reproduce this on Android 8.0 emulator though



回答5:

After some investigation, I've isolated the issue to WebView inside Fragment on OS8 only. My workaround is to use Activity instead of Fragment for that particular flow. It seems to me an Android defect in Fragment.



回答6:

Maybe you use custom ContextWrapper in your Activity class. In my case, I override attachBaseContext method. Check this method and use super.attachBaseContext(newBase).



回答7:

Actually found a workaround for this a little while ago that allowed us to continue subclassing Resources and not crash our WebViews. The caveat is we can't let our WebView see or interact with our resources subclass AND we must create the WebView programmaticaly. The first step is exposing method in the Resources subclass to pull the original resources back out.

Lets say our resources subclass is called CustomResourcesWrapper and our ContextWrapper subclass is called CustomContextWrapper.

First we update the CustomResourcesWrapper so we can access the original Resources object

public class CustomResourcesWrapper {

    public static Resources findOriginalResources(Context context) {
        if (context.getResources() instanceof CustomResourcesWrapper) {
            return ((CustomResourcesWrapper) context.getResources()).getOriginalResources();
        }
        if (context instanceof ContextWrapper) {
            return findOriginalResources(((ContextWrapper) context).getBaseContext());
        }
        return context.getResources();
    }

    private Resources getOriginalResources() {
        return originalResources;
    }
}

We're also assuming the CustomContextWrapper looks something like this...

public class CustomContextWrapper extends ContextWrapper {

    private final Resources wrappedResources;

    public CustomContextWrapper(Context base, Resources resources) {
        super(base);
        this.wrappedResources = resources;
    }

    @Override
    public Resources getResources() {
        return wrappedResources;
    }
}

Then we create a static helper method to "unwrap" our custom resources and hide them

// the method name is a bit of a misnomer, 
// we're actually unwrapping, then re-wrapping
public static Context unwrapCustomContext(Context wrapped) {
    Resources originalResources = CustomResourcesWrapper.findOriginalResources(wrapped);
    Context customUnwrappedContext = new CustomContextWrapper(wrapped, originalResources);
    return new ContextThemeWrapper(customUnwrappedContext, android.support.v7.appcompat.R.style.Theme_AppCompat_Light);
}

When it comes time to create a WebView, do it ensure the Context we pass to it runs through the above method i.e. WebView webView = new WebView(unwrapCustomContext(context)). I'm not 100% sure why, but the ContextThemeWrapper is a required part of this hack.



回答8:

Its look like you are setting Integer value to the Textview. try using String.valueOf(value) to set value in Textview.