Android 编辑器中Meizu设备上的NullPointerException.updateCursorPositionMz

Android 编辑器中Meizu设备上的NullPointerException.updateCursorPositionMz,android,textview,meizu,Android,Textview,Meizu,最近,我的Android应用程序出现了崩溃,仅在魅族设备上出现了(M5c、M5s、M5注)。安卓版本: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.Edito

最近,我的Android应用程序出现了崩溃,仅在魅族设备上出现了(M5c、M5s、M5注)。安卓版本: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:6964)
   at android.widget.Editor.updateCursorsPositions(Editor.java:1760)
   at android.widget.TextView.getUpdatedHighlightPath(TextView.java:5689)
   at android.widget.TextView.onDraw(TextView.java:5882)
   at android.view.View.draw(View.java:16539)
   at android.view.View.updateDisplayListIfDirty(View.java:15492)
   at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3719)
   at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3699)
   at android.view.View.updateDisplayListIfDirty(View.java:15443)
   at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3719)
   at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3699)
   at android.view.View.updateDisplayListIfDirty(View.java:15443)
   at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3719)
   at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3699)
   at android.view.View.updateDisplayListIfDirty(View.java:15443)
   at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3719)
   at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3699)
   at android.view.View.updateDisplayListIfDirty(View.java:15443)
   at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3719)
   at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3699)
   at android.view.View.updateDisplayListIfDirty(View.java:15443)
   at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3719)
   at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3699)
   at android.view.View.updateDisplayListIfDirty(View.java:15443)
   at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3719)
   at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3699)
   at android.view.View.updateDisplayListIfDirty(View.java:15443)
   at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3719)
   at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3699)
   at android.view.View.updateDisplayListIfDirty(View.java:15443)
   at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3719)
   at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3699)
   at android.view.View.updateDisplayListIfDirty(View.java:15443)
   at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3719)
   at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3699)
   at android.view.View.updateDisplayListIfDirty(View.java:15443)
   at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3719)
   at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3699)
   at android.view.View.updateDisplayListIfDirty(View.java:15443)
   at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3719)
   at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3699)
   at android.view.View.updateDisplayListIfDirty(View.java:15443)
   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:3051)
   at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2855)
   at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2464)
   at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1337)
   at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6819)
   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:5969)
   at java.lang.reflect.Method.invoke(Method.java)
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:830)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:720)
这与我的代码没有直接关系(即使是在其他线程的stracktraces中)。我只知道它每次都发生在有文本视图的片段中。当文本视图获得焦点时可能会发生这种情况,但我无法确定。当然我不能复制这个虫子,除非我买了一个魅族

另外,由于top方法名为
updatecorsorpositionmz
,在我看来,这可能是魅族FlymeOS的内部问题(“Mz”=“魅族”?)

是否有人已经出现此问题,知道原因以及如何解决

谢谢。

更新(2019年8月8日) 正如@andreas wenger、@waseefakhtar和@vadim kotov所提到的,修复现在包括从现在开始

旧答案 最后我有机会找到了一个魅族。正如我所想,每当用户点击某个字段以获得焦点时,崩溃就会发生

在我的例子中,我有一些
android.support.design.widget.textinputtext
内置
TextInputLayout
s。只需将这些
textinputtext
s替换为
appcompatitedtext
s即可解决此问题,如下所示:

<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>

行为保持不变(因为
textinputtext
扩展了
appcompatitedtext
)。但是,我仍然没有找到问题的根本原因。

从xml中删除提示:从文本输入布局或文本输入文本

用于材料组件

<com.google.android.material.textfield.TextInputLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

    <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/text_input_edit_text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
</com.google.android.material.textfield.TextInputLayout>

用于设计支持

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

    <android.support.design.widget.TextInputEditText
            android:id="@+id/text_input_edit_text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
</android.support.design.widget.TextInputLayout>

在您的代码中,以编程方式设置提示:

val myTextInputEditText = findViewById<TextInputEditText>(R.id.text_input_edit_text)
myTextInputEditText.hint = "Your hint"
val mytextinputtext=findviewbyd(R.id.text\u input\u edit\u text)
mytextinputtext.hint=“您的提示”

在我的例子中,我验证了使用
AppCompatitedText
而不是
TextInputItemText
确实可以防止崩溃,但我们无法使用此解决方案。我们使用的sdk视图扩展了
textinputtext
,因此切换到
AppCompatEditText
需要将相当多的sdk代码复制/修改到我们的项目中

我尝试在
textinputtext
textinputtelayout
上设置提示,但最终看到了双重提示(比如模糊的文本,我肯定我没有喝太多)

我看了一下由@Andrew链接的GitHub问题:

在那期杂志中,他们解释说根本原因是魅族的问题,因为
textinputettext.getHint()
textinputettext.mHint
不同

textinputtext
位于
TextInputLayout
内部,并且在
textinputtext
上以xml指定提示时,支持库基本上会将提示“移动”到包含
TextInputLayout
的位置:它在容器上设置提示,然后在编辑文本中将其设置为null

执行此操作的源位于:

//如果没有有效的提示,请尝试从EditText中检索它(如果已启用)
如果(暗示){
if(TextUtils.isEmpty(提示)){
//保存提示,以便在DispatchProviderAutoFillStructure()上还原它;
originalHint=this.editText.getHint();
setHint(原始提示);
//清除EditText的提示,因为我们将自己显示它
this.editText.setHint(null);
}
然后,当您调用
textinputtext.getHint()
时,它将返回容器的提示

getHint()
(提示值)和
mHint
(null)之间的这种不一致似乎给魅族设备带来了问题

我找到了另一种方法来避免这个问题

在魅族设备上,我:

1) 以编程方式将
textinputtext
的提示重置回它最初从xml设置的值(通过调用其重写的
getHint()
,返回容器的提示)

2) 将
textinputtext
的提示颜色设置为透明,以避免双重/模糊提示效果:

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());
        }
    }
}

TextInputLayout
textInputInputText
上添加提示为我修复了崩溃:

    <android.support.design.widget.TextInputLayout
        android:id="@+id/text_input_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/login"
        app:hintAnimationEnabled="false">

        <android.support.design.widget.TextInputEditText
            android:id="@+id/text_input_edit_text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/login" />
        </android.support.design.widget.TextInputLayout>

已在带有Android 6.0的Meizu MX6上验证,我的解决方案基于中提到的
fixedtimeditText

首先,我创建了一个固定的
textinputtext
实例:

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

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

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

    @Override
    public CharSequence getHint() {
        try {
            return getMeizuHintHack();
        } catch (Exception e) {
            return super.getHint();
        }
    }

    private CharSequence getMeizuHintHack() throws NoSuchFieldException, IllegalAccessException {
        Field textView = TextView.class.getDeclaredField("mHint");
        textView.setAccessible(true);
        return (CharSequence) textView.get(this);
    }
}
<>但是我必须用代码< MeuTutExtPueTeTimeTe> < /Cord>取代我所有的<代码> TcTyPuxTeTimeTe> <代码>用法。这不是在一个更大的代码库中容易做的事情。也在创建未来视图时,你总是需要考虑使用<代码> MeZututExpIdTuttTime<代码>来代替“坏的”。忘记它。很容易再次引入生产问题

因此,最终的修复程序由自定义视图类组成,再加上ViewPump库(),我们可以很容易地做到这一点

public class TextInputEditTextInterceptor implements Interceptor {
    @Override
    public InflateResult intercept(Chain chain) {
        InflateRequest request = chain.request();
        View view = inflateView(request.name(), request.context(), request.attrs());

        if (view != null) {
            return InflateResult.builder()
                    .view(view)
                    .name(view.getClass().getName())
                    .context(request.context())
                    .attrs(request.attrs())
                    .build();
        } else {
            return chain.proceed(request);
        }
    }

    @Nullable
    private View inflateView(String name, Context context, AttributeSet attrs) {
        if (name.endsWith("TextInputEditText")) {
            return new MeizuTextInputEditText(context, attrs);
        }
        return null;
    }
}
注册该自定义拦截器的操作与文档中的操作相同,方法是在活动的onCreate上设置一个ViewPump:

@Override
@CallSuper
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ViewPump.Builder viewPumpBuilder = ViewPump.builder();
    if (isMeizuDevice()) {
        viewPumpBuilder.addInterceptor(new TextInputEditTextInterceptor());
    }
    ViewPump.init(viewPumpBuilder.build());
}

如您所见,我仅在检测到Meizu设备的情况下才会膨胀
MeizuTextInputItemText
。这样,不需要它的设备不会触发反射。此外,此方法是我拥有的基本活动类,我的项目中的所有其他活动都从该类扩展,因此我的项目中启动的所有活动以及设备是一个魅族将自动修复!

我正在使用Kotlin和片段,我只是递归修复onViewCreated中的所有文本输入

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    fixTextInputEditText(view) // call this in onViewCreated
}

private fun fixTextInputEditText(view: View) {
    val manufacturer = Build.MANUFACTURER.toUpperCase(Locale.US)
    if ("MEIZU" in manufacturer) {
        val views = getAllTextInputs(view)
        views.forEach(::hackFixHintsForMeizu)
    }
}

private fun getAllTextInputs(v: View): List<TextInputEditText> {
    if (v !is ViewGroup) {
        val editTexts = mutableListOf<TextInputEditText>()
        (v as? TextInputEditText)?.let {
            editTexts += it
        }
        return editTexts
    }

    val result = mutableListOf<TextInputEditText>()
    for (i in 0 until v.childCount) {
        val child = v.getChildAt(i)
        result += getAllTextInputs(child)
    }
    return result
}

private fun hackFixHintsForMeizu(editText: TextInputEditText) {
    if (editText.hint != null) {
        editText.setHintTextColor(Color.TRANSPARENT)
        editText.hint = editText.hint
    }
}
覆盖已创建的视图(视图:视图,sa
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    fixTextInputEditText(view) // call this in onViewCreated
}

private fun fixTextInputEditText(view: View) {
    val manufacturer = Build.MANUFACTURER.toUpperCase(Locale.US)
    if ("MEIZU" in manufacturer) {
        val views = getAllTextInputs(view)
        views.forEach(::hackFixHintsForMeizu)
    }
}

private fun getAllTextInputs(v: View): List<TextInputEditText> {
    if (v !is ViewGroup) {
        val editTexts = mutableListOf<TextInputEditText>()
        (v as? TextInputEditText)?.let {
            editTexts += it
        }
        return editTexts
    }

    val result = mutableListOf<TextInputEditText>()
    for (i in 0 until v.childCount) {
        val child = v.getChildAt(i)
        result += getAllTextInputs(child)
    }
    return result
}

private fun hackFixHintsForMeizu(editText: TextInputEditText) {
    if (editText.hint != null) {
        editText.setHintTextColor(Color.TRANSPARENT)
        editText.hint = editText.hint
    }
}
import android.os.Build
import java.util.*
import android.content.Context
import android.support.design.widget.TextInputEditText
import android.util.AttributeSet
import android.widget.TextView
import android.support.design.widget.TextInputLayout
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputConnection
import java.lang.reflect.Field
import java.lang.reflect.Method
import android.support.design.R

class MyInputEditText(context: Context?, attrs: AttributeSet?,defStyleAttr:Int) : TextInputEditText(context, attrs,defStyleAttr){

    constructor(context: Context?, attrs: AttributeSet?):this(context,attrs,R.attr.editTextStyle)
    constructor(context: Context?):this(context,null,R.attr.editTextStyle)


    private val buggyMeizu = ("meizu") in Build.MANUFACTURER.toLowerCase(Locale.US)

    private lateinit var getTextInputLayoutMethod:Method
    private lateinit var providesHintMethod:Method
    private lateinit var mHintField:Field

    init {
        if (buggyMeizu) {
            getTextInputLayoutMethod=TextInputEditText::class.java.getDeclaredMethod("getTextInputLayout")
            getTextInputLayoutMethod.isAccessible=true

            providesHintMethod=TextInputLayout::class.java.getDeclaredMethod("isProvidingHint")
            providesHintMethod.isAccessible=true

            mHintField=TextView::class.java.getDeclaredField("mHint")
            mHintField.isAccessible=true
        }
    }


    private fun getTILProvidesHint():Boolean {
        val layout=getTIL()
        if (layout!=null) {
            val result=providesHintMethod.invoke(layout) as Boolean
            return result;
        } else {
            return false
        }
    }

    private fun getTIL():TextInputLayout? = getTextInputLayoutMethod.invoke(this) as TextInputLayout?

    private fun getBaseHint():CharSequence? = mHintField.get(this) as CharSequence?

    override fun getHint(): CharSequence? {
        if (!buggyMeizu) {
            return super.getHint()
        } else {
            val layout=getTIL()
            return if (layout != null && (getTILProvidesHint()) ) 
                layout.hint
            else 
                provideHintWrapped()
        }
    }


    override fun onCreateInputConnection(outAttrs: EditorInfo): InputConnection? {
        val needHint=(outAttrs.hintText==null)
        val ic = super.onCreateInputConnection(outAttrs)
        if (buggyMeizu) {
            if (ic != null && needHint) {
                outAttrs.hintText = this.provideHintWrapped()
            }
        }
        return ic
    }

    private fun provideHintWrapped():CharSequence? {

        val hintFromLayout=getHintFromLayoutMine()
        if (hintFromLayout!=null) {
            return hintFromLayout
        } else {
            val baseHint=getBaseHint()
            if (baseHint!=null) {
                return baseHint
            } else {
                return null
            }
        }

    }
    private fun getHintFromLayoutMine(): CharSequence? {
        val layout = getTIL()
        return layout?.hint
    }

    override fun onAttachedToWindow() {

        if (buggyMeizu) {

            val baseHint=getBaseHint()

            if (getTIL() != null
                    && getTILProvidesHint()
                    && baseHint == null) {
                this.hint=""
            }
        }

        super.onAttachedToWindow()
    }
}