android-in-china / Compatibility

用于反馈和跟踪国内 Android 设备/ROM 兼容性问题的公益项目
835 stars 28 forks source link

魅族设备 TextInputLayout 崩溃 #11

Open Qixingchen opened 6 years ago

Qixingchen commented 6 years ago

MX6 6.0

Fatal Exception: java.lang.NullPointerException: Attempt to invoke virtual method 'int android.text.Layout.getLineForOffset(int)' on a null object reference
       at android.widget.Editor.updateCursorPositionMz(Editor.java:6968)
       at android.widget.Editor.updateCursorsPositions(Editor.java:1759)
       at android.widget.TextView.getUpdatedHighlightPath(TextView.java:5703)
       at android.widget.TextView.onDraw(TextView.java:5896)
       at android.view.View.draw(View.java:16532)
       at android.view.View.updateDisplayListIfDirty(View.java:15485)
       at android.view.View.draw(View.java:16286)
       at android.view.ViewGroup.drawChild(ViewGroup.java:3735)
       at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3525)
       at android.view.View.updateDisplayListIfDirty(View.java:15477)
       at android.view.View.draw(View.java:16286)
       at android.view.ViewGroup.drawChild(ViewGroup.java:3735)
       at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3525)
       at android.view.View.draw(View.java:16544)
       at android.support.design.widget.TextInputLayout.draw(SourceFile:1577)
       at android.view.View.updateDisplayListIfDirty(View.java:15485)
       at android.view.View.draw(View.java:16286)
       at android.view.ViewGroup.drawChild(ViewGroup.java:3735)
       at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3525)
       at android.view.View.updateDisplayListIfDirty(View.java:15477)
       at android.view.View.draw(View.java:16286)
       at android.view.ViewGroup.drawChild(ViewGroup.java:3735)
       at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3525)
       at android.view.View.updateDisplayListIfDirty(View.java:15477)
       at android.view.View.draw(View.java:16286)
       at android.view.ViewGroup.drawChild(ViewGroup.java:3735)
       at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3525)
       at android.view.View.updateDisplayListIfDirty(View.java:15477)
       at android.view.View.draw(View.java:16286)
       at android.view.ViewGroup.drawChild(ViewGroup.java:3735)
       at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3525)
       at android.view.View.updateDisplayListIfDirty(View.java:15477)
       at android.view.View.draw(View.java:16286)
       at android.view.ViewGroup.drawChild(ViewGroup.java:3735)
       at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3525)
       at android.view.View.updateDisplayListIfDirty(View.java:15477)
       at android.view.View.draw(View.java:16286)
       at android.view.ViewGroup.drawChild(ViewGroup.java:3735)
       at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3525)
       at android.view.View.updateDisplayListIfDirty(View.java:15477)
       at android.view.View.draw(View.java:16286)
       at android.view.ViewGroup.drawChild(ViewGroup.java:3735)
       at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3525)
       at android.view.View.updateDisplayListIfDirty(View.java:15477)
       at android.view.View.draw(View.java:16286)
       at android.view.ViewGroup.drawChild(ViewGroup.java:3735)
       at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3525)
       at com.android.internal.policy.PhoneWindow$DecorView.dispatchDraw(PhoneWindow.java:2829)
       at android.view.View.draw(View.java:16544)
       at com.android.internal.policy.PhoneWindow$DecorView.draw(PhoneWindow.java:2810)
       at android.view.View.updateDisplayListIfDirty(View.java:15485)
       at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:286)
       at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:292)
       at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:327)
       at android.view.ViewRootImpl.draw(ViewRootImpl.java:3041)
       at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2845)
       at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2454)
       at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1340)
       at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6809)
       at android.view.Choreographer$CallbackRecord.run(Choreographer.java:894)
       at android.view.Choreographer.doCallbacks(Choreographer.java:696)
       at android.view.Choreographer.doFrame(Choreographer.java:631)
       at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:880)
       at android.os.Handler.handleCallback(Handler.java:815)
       at android.os.Handler.dispatchMessage(Handler.java:104)
       at android.os.Looper.loop(Looper.java:207)
       at android.app.ActivityThread.main(ActivityThread.java:5985)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:939)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:800)

android.support.design.widget.TextInputEditText 更换为 android.support.v7.widget.AppCompatEditText 不再闪退 相关 so 链接: https://stackoverflow.com/questions/51891415/nullpointerexception-on-meizu-devices-in-editor-updatecursorpositionmz/52001305

drakeet commented 6 years ago

同样遇到这个问题。

Context

image 这些 TextInputLayout 的第一层崩溃都在一个魅族自己添加的以 Mz 后缀的方法上。

这个事情最终确定为:只要使用 TextInputLayout 包裹 TextInputEditText (谷歌推荐的组合,能够更好地适配 extra UI),在魅族部分系统上就会闪退。据说 TextInputLayout + AppCompatEditText。

我这边遇到更多魅族设备存在这个问题,包括 M5s。

魅族员工答复

大意就是说,魅族已经修复这个问题了,但由于固件下沉很慢,实际上效果很不尽人意。

image

按照现在的状况,大概率最终 28.0.0 正式版也会有这个问题,因为这事根本没法和谷歌反馈,总不能说:你们去买台魅族旧设备来试试,它们不兼容你们最新的 support 库 😥。

如果魅族的开发同学能够一起来写一个 workaround 提交给谷歌 support library,或许可以避免这个问题之后更大范围的影响。因为大家都是升级后遇到线上崩溃才后知后觉,而且还要探究折腾一番才知道是源于魅族系统的修改所致。

mariotaku commented 6 years ago

我这里有一个(可能的)解决方案,同时不会有log警告和功能不全: 问题是出在getHint()这里。魅族可能如果mHintgetHint()不一致就会引起错误,所以我们在旧版本上面用反射获取mHint的值。

package android.support.design.widget

import android.content.Context
import android.os.Build
import android.util.AttributeSet
import android.widget.TextView

class FixedTextInputEditText(context: Context, attrs: AttributeSet?) : TextInputEditText(context, attrs) {

    override fun getHint(): CharSequence? {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            return super.getHint()
        }
        return try {
            getSuperHintHack()
        } catch (e: Exception) {
            super.getHint()
        }
    }

    private fun getSuperHintHack(): CharSequence? {
        val f = TextView::class.java.getDeclaredField("mHint")
        f.isAccessible = true
        return f.get(this) as? CharSequence
    }

}
GreyLabsDev commented 6 years ago

我这里有一个(可能的)解决方案,同时不会有log警告和功能不全: 问题是出在getHint()这里。魅族可能如果mHintgetHint()不一致就会引起错误,所以我们在旧版本上面用反射获取mHint的值。

package android.support.design.widget

import android.content.Context
import android.os.Build
import android.util.AttributeSet
import android.widget.TextView

class FixedTextInputEditText(context: Context, attrs: AttributeSet?) : TextInputEditText(context, attrs) {

    override fun getHint(): CharSequence? {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            return super.getHint()
        }
        return try {
            getSuperHintHack()
        } catch (e: Exception) {
            super.getHint()
        }
    }

    private fun getSuperHintHack(): CharSequence? {
        val f = TextView::class.java.getDeclaredField("mHint")
        f.isAccessible = true
        return f.get(this) as? CharSequence
    }

}

Greetings from Russian developers =) Thank you for this solution, it really works. I`am using MX6 as my main device and found that issue while testing new project. Only your type of workaround helped me.

udenfox commented 6 years ago

我这里有一个(可能的)解决方案,同时不会有log警告和功能不全: 问题是出在getHint()这里。魅族可能如果mHintgetHint()不一致就会引起错误,所以我们在旧版本上面用反射获取mHint的值。

package android.support.design.widget

import android.content.Context
import android.os.Build
import android.util.AttributeSet
import android.widget.TextView

class FixedTextInputEditText(context: Context, attrs: AttributeSet?) : TextInputEditText(context, attrs) {

    override fun getHint(): CharSequence? {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            return super.getHint()
        }
        return try {
            getSuperHintHack()
        } catch (e: Exception) {
            super.getHint()
        }
    }

    private fun getSuperHintHack(): CharSequence? {
        val f = TextView::class.java.getDeclaredField("mHint")
        f.isAccessible = true
        return f.get(this) as? CharSequence
    }

}

And another thanks from Ukrainian developers :) Can confirm that it works. Great workaround!

gugact commented 6 years ago
package android.support.design.widget

import android.content.Context;
import android.os.Build;
import android.support.design.widget.TextInputEditText;
import android.util.AttributeSet;
import android.widget.TextView;

import java.lang.reflect.Field;

public class FixedTextInputEditText extends TextInputEditText {
    public FixedTextInputEditText(Context context) {
        super(context);
    }

    public FixedTextInputEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public FixedTextInputEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public CharSequence getHint() {
        String manufacturer = Build.MANUFACTURER.toUpperCase();
        if (!manufacturer.contains("MEIZU") || Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            return super.getHint();
        }else{
            try {
                return getSuperHintHack();
            }catch (Exception e){
                return super.getHint();
            }
        }
    }

    private CharSequence getSuperHintHack() throws NoSuchFieldException, IllegalAccessException {
        Field hintField = TextView.class.getDeclaredField("mHint");
        hintField.setAccessible(true);
        return (CharSequence) hintField.get(this);
    }
}

Java version of the code from @mariotaku , code specific for Meizu devices only, I think it's safer this way, for the problems the java reflection can bring.

Good job @mariotaku

calvarez-ov commented 6 years ago

Thanks for the explanations and workarounds!

In my own project, I've tried out a different workaround, which doesn't use reflection. It's still a bit hacky though :smile:

private void hackFixHintsForMeizu(TextInputEditText... editTexts) {
    String manufacturer = Build.MANUFACTURER.toUpperCase(Locale.US);
    if (manufacturer.contains("MEIZU")) {
        for (TextInputEditText editText : editTexts) {
            editText.setHintTextColor(Color.TRANSPARENT);
            editText.setHint(editText.getHint());
        }
    }
}
liudongmiao commented 5 years ago

https://stackoverflow.com/a/53583732

linroid commented 5 years ago

TextInputEditTexts 换成 AppCompatEditText 可以避免这个问题

<android.support.design.widget.TextInputLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="...">

    <android.support.v7.widget.AppCompatEditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</android.support.design.widget.TextInputLayout>

https://stackoverflow.com/questions/51891415/nullpointerexception-on-meizu-devices-in-editor-updatecursorpositionmz

awenger commented 5 years ago

This was just fixed in the material components for Android lib, see: https://github.com/material-components/material-components-android/pull/358

huaxunhuang commented 5 years ago

这个issue是不是已经被修复了?我在mx6上重现不出来