TextView во фрагментах ViewPager случайным образом вылетает из приложения. NullPointerException: попытка вызвать виртуальный метод void View.sendAccessibilityEventUnchec - PullRequest
1 голос
/ 06 апреля 2020

Резюме

  1. Сначала я опишу контекст , т.е. что я пытаюсь сделать

  2. Во-вторых, я объясняю вам, в каких случаях я сталкиваюсь с ошибкой и что это такое (это текущее поведение )

  3. В-третьих, я объясняю вам ожидаемое поведение

  4. Затем я объясню вам , как воспроизвести ошибку

  5. Наконец я покажу вам минимальный и исполняемый код , который может привести к ошибке


Context

Я использую виджет AutoScrollTextView, доступный между другими в API https://github.com/ronghao/AutoScrollTextView. AutoScrollTextView - это TextView, который может автоматически выполнять вертикальную прокрутку и, более того, если текст слишком длинный, по горизонтали. Таким образом, AutoScrollTextView может содержать хотя бы один текст для автоматической прокрутки (см. Иллюстрацию в репозитории GitHub).

Текущее поведение: ошибка

  1. Если я использую один AutoScrollTextView в основной деятельности я не вижу ошибок. Если я использую его во фрагменте основного действия, я думаю, что это также будет иметь место (я не проверял его).
  2. Если бы я использовал один AutoScrollTextView в каждом из хотя бы двух фрагментов ViewPager (2 экземпляра этого же класса фрагментов используются для Viewpager), когда по крайней мере один текст автоматически прокручивается каждым AutoScrollTextView, приложение вылетает со следующей ошибкой ( NB: это ViewPager относится к основному виду деятельности):

E / AndroidRuntime: FATAL EXCEPTION: main Процесс: com.example.myapplication, PID: 2814 java .lang. NullPointerException: попытка вызвать виртуальный метод 'void android .view.View.sendAccessibilityEventUnchecked (android .view.accessibility.AccessibilityEvent)' для ссылки на нулевой объект в android .view.ViewRootImpl $ SendWindowContentChangedAccessibilityEot. java: 9304) в android .os.Handler.handleCallback (Обработчик. java: 789) в android .os.Handler.dispatchMessage (Обработчик. java: 98) в android .os .Looper.l oop (Looper. java: 164) в android .app. ActivityThread.main (ActivityThread. java: 6944) в java .lang.reflect.Method.invoke (собственный метод) в com. android .internal.os.Zygote $ MethodAndArgsCaller.run (Zygote. java) : 327) at com. android .internal.os.ZygoteInit.main (ZygoteInit. java: 1374)

Ошибка, однако, трудно вызвать:

  1. Иногда он срабатывает, как только начинается фрагмент основного действия
  2. Иногда происходит случайная задержка перед тем, как он запускается во фрагменте основного действия
  3. Иногда, возможно, это не сработало (или я закрываю приложение раньше, чем оно будет)

Как вы можете видеть, нет никаких признаков линии, которая вызывает этот cra sh.

Что я сделал для отладки

  1. Я взял библиотеку и выделил минимальную ее часть, которая привела к этой ошибке.

  2. Я установил минимальное исполняемое приложение с только основным действием, ViewPager и фрагментом (2 экземпляра этого же фрагмента Класс Ent используется для Viewpager).

  3. Я установил это минимальное исполняемое приложение с изолированной частью библиотеки. Вы найдете источники ниже (даже если имеется «много» файлов, каждый из них был минимизирован и содержит только строгие минимальные строки, чтобы вы могли воспроизвести ошибку). Кроме того: с источниками, которые я вам даю, совершенно нормально, что вы не видите текстовую прокрутку (и текст вообще). Это потому, что я полностью изолировал часть API, которая вызывает ошибку, и текст прокрутки не был связан с этой ошибкой, поэтому он не является частью моих изолированных источников.

  4. Я попытался использовать Android Timer вместо Handler, но ошибка все еще возникает.

  5. MarqueeTextView::scrollTo(currentScrollPos, 0);, кажется, вызывает эту ошибку.

Ожидаемое поведение

Обычно приложение, конечно, не должно создавать sh. Другими словами, не должно быть сработавшей ошибки (особенно той, которую я цитировал выше). С источниками, которые я вам даю, совершенно нормально, что вы не видите текстовую прокрутку (и текст вообще). Это потому, что я полностью изолировал часть API, которая вызывает ошибку, и текст прокрутки не был связан с этой ошибкой, поэтому он не является частью моих изолированных источников.

Как воспроизвести эту ошибку

Ниже я предоставлю вам все минимальные и исполняемые источники, которые вам нужны (даже если имеется «много» файлов, каждый из них был минимизирован и содержит только строгие минимальные строки, чтобы вы могли воспроизвести ошибку ). Создав соответствующие файлы и скопировав / вставив в них эти источники, запустите это минимальное и исполняемое приложение несколько раз. Даже если ошибка будет отображаться или не отображаться для каждого прогона, обычно вы увидите ее как минимум один раз после 5 прогонов макс. Вы можете использовать свой смартфон для тестирования этого приложения или, возможно, эмулятор Android Studio (но я не тестировал на эмуляторе).

NB: с источниками, которые я вам предоставляю совершенно нормально, что вы не видите текстовую прокрутку (и текст вообще). Это потому, что я полностью изолировал часть API, которая вызывает ошибку, и текст прокрутки не был связан с этой ошибкой, поэтому он не является частью моих изолированных источников.

Мой вопрос

Не могли бы вы сказать мне, почему эта ошибка возникает (я не смог ее отладить) и почему она кажется такой случайной? Как я могу это исправить?

Минимальные и исполняемые источники приложения

Краткое представление

  1. Сначала я дам вам источники изолированной части библиотека

  2. Затем я дам вам источники приложения с ViewPager, et c.

  3. Даже если Есть «много» файлов, каждый из которых был минимизирован и содержит только строгие минимальные строки, чтобы вы могли воспроизвести ошибку. С источниками, которые я вам даю, совершенно нормально, что вы не видите текстовую прокрутку (и текст вообще). Это потому, что я полностью изолировал часть API, которая вызывает ошибку, и текст прокрутки не был связан с этой ошибкой, поэтому он не является частью моих изолированных источников.

Изолированная часть библиотеки, которая вызывает ошибку . java package com.example.myapplication.libs.autoscrolling_text_view; import android.content.Context; import android.util.AttributeSet; import android.widget.RelativeLayout; import android.widget.ViewSwitcher; /** * MarqueeSwitcher {@link android.widget.TextSwitcher} t * * @author haohao on 2017/9/21 下午 03:57 * @version v1.0 */ public class MarqueeSwitcher extends ViewSwitcher { /** * Creates a new empty TextSwitcher for the given context and with the * specified set attributes. * * @param context the application environment * @param attrs a collection of attributes */ public MarqueeSwitcher(Context context, AttributeSet attrs) { super(context, attrs); } /** * Sets the text of the next view and switches to the next view. This can * be used to animate the old text out and animate the next text in. * */ public void setText() { final MarqueeTextView t = getNextView(); t.postStartScroll(1500); // BUG HERE (Cf. MarqueeTextView::postStartScroll) } public MarqueeTextView getCurrentView() { return (MarqueeTextView) ((RelativeLayout) super.getCurrentView()).getChildAt(0); } public MarqueeTextView getNextView() { return (MarqueeTextView) ((RelativeLayout) super.getNextView()).getChildAt(0); } } com.example.myapplication.libs.autoscrolling_text_view.BaseScrollTextView. java package com.example.myapplication.libs.autoscrolling_text_view; import android.content.Context; import android.os.Handler; import android.os.Message; import android.util.AttributeSet; import android.view.View; import android.widget.RelativeLayout; import android.widget.ViewSwitcher; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; /** * 父类:上下滚动 * * @author haohao on 2017/9/21 下午 02:28 * @version v1.0 */ public class BaseScrollTextView extends MarqueeSwitcher implements ViewSwitcher.ViewFactory { private static final int FLAG_START_AUTO_SCROLL = 1000; private ArrayList<String> textList; private Handler handler; public BaseScrollTextView(Context context, AttributeSet attrs) { super(context, attrs); init(); } private void init() { textList = new ArrayList<>(); handler = new MyHandler(this); setFactory(this); } /** * 设置数据源 */ public void setTextList(List<String> titles) { textList.addAll(titles); } /** * 开始轮播 */ public void startAutoScroll() { handler.sendEmptyMessage(FLAG_START_AUTO_SCROLL); } @Override public View makeView() { RelativeLayout layout = new RelativeLayout(getContext()); MarqueeTextView textView = new MarqueeTextView(getContext()); layout.addView(textView); return layout; } private static class MyHandler extends Handler { WeakReference<BaseScrollTextView> textViewWeakReference; private MyHandler(BaseScrollTextView autoScrollTextView) { textViewWeakReference = new WeakReference<>(autoScrollTextView); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (null != textViewWeakReference) { BaseScrollTextView autoScrollTextView = textViewWeakReference.get(); if (msg.what == FLAG_START_AUTO_SCROLL) { if (autoScrollTextView.textList.size() > 0) { autoScrollTextView.setText(); } } } } } } res.anim.push_up_in. xml <?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false" android:zAdjustment="top"> <translate android:duration="400" android:fromYDelta="100%" android:interpolator="@android:anim/accelerate_interpolator" android:toYDelta="0"/> <alpha android:duration="400" android:fromAlpha="0.0" android:toAlpha="1.0"/> </set> res.anim.push_up_out. xml <?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false" android:zAdjustment="bottom"> <translate android:duration="400" android:fromYDelta="0" android:interpolator="@android:anim/accelerate_interpolator" android:toYDelta="-100%"/> <alpha android:duration="400" android:fromAlpha="1.0" android:toAlpha="0.0"/> </set> Приложение (одно действие, один фрагмент использовался два раза в Viewpager действия; фрагмент использует AutoScrollTextView, который случайным образом срабатывает ошибка)

Gradle уровня приложения

apply plugin: 'com.android.application'

android {
    compileSdkVersion 28


    defaultConfig {
        applicationId "com.example.myapplication"
        minSdkVersion 22
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    implementation("com.google.guava:guava:28.2-android")
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

com.example.myapplication.MainActivity. java

package com.example.myapplication;

import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentStatePagerAdapter;
import androidx.viewpager.widget.ViewPager;

import com.google.common.collect.Lists;

import java.util.List;

public class MainActivity extends AppCompatActivity {
    private List<FragmentHomeSlide> slides;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final FragmentHomeSlide slide_1 = new FragmentHomeSlide();
        final FragmentHomeSlide slide_2 = new FragmentHomeSlide();
        slides = Lists.newArrayList(slide_1, slide_2);

        final ViewPager view_pager = findViewById(R.id.view_pager);
        assert getFragmentManager() != null;
        view_pager.setAdapter(new FragmentStatePagerAdapter(getSupportFragmentManager(), FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
            @Override
            @NonNull
            public Fragment getItem(final int position) {
                return slides.get(position);
            }

            @Override
            public int getCount() {
                return slides.size();
            }
        });
    }
}

com.example.myapplication.FragmentHomeSlide. java

package com.example.myapplication;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

import com.example.myapplication.libs.autoscrolling_text_view.BaseScrollTextView;

import java.util.ArrayList;
import java.util.Arrays;

public class FragmentHomeSlide extends Fragment {
    @Override
    public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {
        View inflated = inflater.inflate(R.layout.home_slide, container, false);
        setWidgets(inflated);
        return inflated;
    }

    private void setWidgets(View inflated) {
        String[] text_presentation = new String[1];
        text_presentation[0] = "Foo";

        BaseScrollTextView baseScrollTextView = inflated.findViewById(R.id.main_autoscroll_text1);
        baseScrollTextView.setTextList(new ArrayList<>(Arrays.asList(text_presentation)));
        baseScrollTextView.startAutoScroll();
    }
}

res.layout.activity_main. xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.viewpager.widget.ViewPager
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        />

</androidx.constraintlayout.widget.ConstraintLayout>

res.layout.home_slide. xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.example.myapplication.libs.autoscrolling_text_view.BaseScrollTextView
        android:id="@+id/main_autoscroll_text1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        />

</androidx.constraintlayout.widget.ConstraintLayout>

1 Ответ

0 голосов
/ 07 апреля 2020

Я думаю, что решил проблему, используя runOnUiThread в методе MarqueeTextView::TimerTask::run. С тех пор как я изменился, я не увидел никаких ошибок. Но я все еще не знаю, почему произошла эта ошибка, и почему эта модификация исправила ее (я знаю, что лучше использовать runOnUiThread в этой ситуации, поскольку используется scrollTo, но я не знаю, какова связь между этим вызов и ошибка). Если кто-то может прокомментировать мой ответ, объясните мне, пожалуйста :-).

Вы можете сравнить изменения (представленные ниже) с оригинальным файлом в моем вопросе. .

Поэтому единственные сделанные мной изменения содержатся в следующем файле ( com.example.myapplication.libs.autoscrolling_text_view.MarqueeTextView ):

package com.example.myapplication.libs.autoscrolling_text_view;

import android.content.Context;

import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.AppCompatTextView;

import java.util.TimerTask;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 *
 * @author haohao on 2017/9/21 下午 02:33
 * @version v1.0
 */
public class MarqueeTextView extends AppCompatTextView {

    private int currentScrollPos = 0;

    ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);

    final TimerTask task = new TimerTask() {
        @Override
        public void run() {
            ((AppCompatActivity) getContext()).runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    currentScrollPos += 1;
                    scrollTo(currentScrollPos, 0);
                }
            });
        }
    };

    public MarqueeTextView(Context context) {
        super(context);
    }

    public void postStartScroll(int delay) {  // Bug when reset AND then scheduleAtFixedRate are executed
        int speed = 6;
        pool.scheduleAtFixedRate(task, delay, speed, TimeUnit.MILLISECONDS);
    }
}
...