Резюме
Сначала я опишу контекст , т.е. что я пытаюсь сделать
Во-вторых, я объясняю вам, в каких случаях я сталкиваюсь с ошибкой и что это такое (это текущее поведение )
В-третьих, я объясняю вам ожидаемое поведение
Затем я объясню вам , как воспроизвести ошибку
Наконец я покажу вам минимальный и исполняемый код , который может привести к ошибке
Context
Я использую виджет AutoScrollTextView
, доступный между другими в API https://github.com/ronghao/AutoScrollTextView. AutoScrollTextView
- это TextView
, который может автоматически выполнять вертикальную прокрутку и, более того, если текст слишком длинный, по горизонтали. Таким образом, AutoScrollTextView
может содержать хотя бы один текст для автоматической прокрутки (см. Иллюстрацию в репозитории GitHub).
Текущее поведение: ошибка
- Если я использую один
AutoScrollTextView
в основной деятельности я не вижу ошибок. Если я использую его во фрагменте основного действия, я думаю, что это также будет иметь место (я не проверял его). - Если бы я использовал один
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)
Ошибка, однако, трудно вызвать:
- Иногда он срабатывает, как только начинается фрагмент основного действия
- Иногда происходит случайная задержка перед тем, как он запускается во фрагменте основного действия
- Иногда, возможно, это не сработало (или я закрываю приложение раньше, чем оно будет)
Как вы можете видеть, нет никаких признаков линии, которая вызывает этот cra sh.
Что я сделал для отладки
Я взял библиотеку и выделил минимальную ее часть, которая привела к этой ошибке.
Я установил минимальное исполняемое приложение с только основным действием, ViewPager
и фрагментом (2 экземпляра этого же фрагмента Класс Ent используется для Viewpager
).
Я установил это минимальное исполняемое приложение с изолированной частью библиотеки. Вы найдете источники ниже (даже если имеется «много» файлов, каждый из них был минимизирован и содержит только строгие минимальные строки, чтобы вы могли воспроизвести ошибку). Кроме того: с источниками, которые я вам даю, совершенно нормально, что вы не видите текстовую прокрутку (и текст вообще). Это потому, что я полностью изолировал часть API, которая вызывает ошибку, и текст прокрутки не был связан с этой ошибкой, поэтому он не является частью моих изолированных источников.
Я попытался использовать Android Timer
вместо Handler
, но ошибка все еще возникает.
MarqueeTextView::scrollTo(currentScrollPos, 0);
, кажется, вызывает эту ошибку.
Ожидаемое поведение
Обычно приложение, конечно, не должно создавать sh. Другими словами, не должно быть сработавшей ошибки (особенно той, которую я цитировал выше). С источниками, которые я вам даю, совершенно нормально, что вы не видите текстовую прокрутку (и текст вообще). Это потому, что я полностью изолировал часть API, которая вызывает ошибку, и текст прокрутки не был связан с этой ошибкой, поэтому он не является частью моих изолированных источников.
Как воспроизвести эту ошибку
Ниже я предоставлю вам все минимальные и исполняемые источники, которые вам нужны (даже если имеется «много» файлов, каждый из них был минимизирован и содержит только строгие минимальные строки, чтобы вы могли воспроизвести ошибку ). Создав соответствующие файлы и скопировав / вставив в них эти источники, запустите это минимальное и исполняемое приложение несколько раз. Даже если ошибка будет отображаться или не отображаться для каждого прогона, обычно вы увидите ее как минимум один раз после 5 прогонов макс. Вы можете использовать свой смартфон для тестирования этого приложения или, возможно, эмулятор Android Studio (но я не тестировал на эмуляторе).
NB: с источниками, которые я вам предоставляю совершенно нормально, что вы не видите текстовую прокрутку (и текст вообще). Это потому, что я полностью изолировал часть API, которая вызывает ошибку, и текст прокрутки не был связан с этой ошибкой, поэтому он не является частью моих изолированных источников.
Мой вопрос
Не могли бы вы сказать мне, почему эта ошибка возникает (я не смог ее отладить) и почему она кажется такой случайной? Как я могу это исправить?
Минимальные и исполняемые источники приложения
Краткое представление
Сначала я дам вам источники изолированной части библиотека
Затем я дам вам источники приложения с ViewPager
, et c.
Даже если Есть «много» файлов, каждый из которых был минимизирован и содержит только строгие минимальные строки, чтобы вы могли воспроизвести ошибку. С источниками, которые я вам даю, совершенно нормально, что вы не видите текстовую прокрутку (и текст вообще). Это потому, что я полностью изолировал часть 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>