6.7.0 - Alpha18 - 修复任务面板列表项频繁变动时可能导致应用崩溃的问题

This commit is contained in:
SuperMonster003
2026-01-27 15:26:04 +08:00
parent 6086521c2d
commit fdaff5d4e4
3 changed files with 202 additions and 51 deletions

View File

@@ -94,6 +94,7 @@
"崩溃报告页面复制详细信息功能失效的问题",
"Android 16+ 自定义返回逻辑失效导致返回功能异常的问题",
"文件管理器删除项目文件夹后 UI 未能自动刷新的问题",
"任务面板列表项频繁变动时可能导致应用崩溃的问题",
"使用 \"三按钮\" 手势导航时导航栏前景色可能与夜间模式关联异常的问题",
"APK 文件类型信息对话框可能无法获取应用名称及 SDK 信息的问题",
"客户端模式连接后, 短时间断开连接并再次连接时, 出现握手超时且难以再次建立连接的问题",

View File

@@ -38,11 +38,13 @@ import org.autojs.autojs6.databinding.ExplorerFirstCharIconBinding;
import org.autojs.autojs6.databinding.TaskListRecyclerViewItemBinding;
import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.List;
/**
* Created by Stardust on Mar 24, 2017.
* Modified by SuperMonster003 as of Jan 7, 2026.
* Modified by JetBrains AI Assistant (GPT-5.2) as of Jan 27, 2026.
* Modified by SuperMonster003 as of Jan 27, 2026.
*/
public class TaskListRecyclerView extends ThemeColorRecyclerView {
@@ -58,17 +60,33 @@ public class TaskListRecyclerView extends ThemeColorRecyclerView {
@Override
public void onStart(final ScriptExecution execution) {
try {
int i = mRunningTaskGroup.addTask(execution);
if (i != -1) {
final Adapter adapter = mAdapter;
post(() -> {
if (adapter != mAdapter) {
return;
}
adapter.notifyChildInserted(0, i);
notifyParentChangedSafe(adapter, 0);
});
}
final Adapter adapter = mAdapter;
post(() -> {
// Constrain list mutation to main thread to avoid ConcurrentModificationException in adapter iteration.
// zh-CN: 将列表修改约束在主线程执行, 避免适配器迭代时触发 ConcurrentModificationException.
if (adapter != mAdapter) {
return;
}
int insertedPos = mRunningTaskGroup.addTask(execution);
if (insertedPos == -1) {
return;
}
// Recompute childPosition at execution time to avoid stale index caused by frequent mutations.
// zh-CN: 在执行通知时重新计算 childPosition, 避免频繁变动导致的索引过期.
int childPosition = mRunningTaskGroup.indexOf(execution);
if (childPosition < 0) {
// Task disappeared before UI update; fallback to refresh to recover consistency.
// zh-CN: UI 更新前任务已消失; 回退到 refresh 以恢复一致性.
refresh();
return;
}
notifyChildInsertedSafe(adapter, 0, childPosition);
notifyParentChangedSafe(adapter, 0);
});
} catch (Exception e) {
e.printStackTrace();
}
@@ -92,7 +110,7 @@ public class TaskListRecyclerView extends ThemeColorRecyclerView {
}
final int i = mRunningTaskGroup.removeTask(execution);
if (i >= 0) {
adapter.notifyChildRemoved(0, i);
notifyChildRemovedSafe(adapter, 0, i);
notifyParentChangedSafe(adapter, 0);
} else {
refresh();
@@ -101,33 +119,6 @@ public class TaskListRecyclerView extends ThemeColorRecyclerView {
}
};
private void notifyParentChangedSafe(@NonNull Adapter adapter, int parentPosition) {
// Avoid notifying an adapter that has been replaced asynchronously.
// zh-CN: 避免对已经被异步替换的适配器发送通知.
if (adapter != mAdapter) {
return;
}
// Avoid out-of-range parentPosition caused by transient state mismatch.
// zh-CN: 避免由于瞬时状态不一致导致的 parentPosition 越界.
if (parentPosition < 0 || parentPosition >= mTaskGroups.size()) {
return;
}
// RecyclerView may be computing layout/dispatching updates; postpone to next loop.
// zh-CN: RecyclerView 可能正在计算布局/分发更新, 将通知延迟到下一次消息循环.
if (isComputingLayout()) {
post(() -> notifyParentChangedSafe(adapter, parentPosition));
return;
}
try {
adapter.notifyParentChanged(parentPosition);
} catch (IndexOutOfBoundsException e) {
/* Ignored. */
}
}
public TaskListRecyclerView(Context context) {
super(context);
init();
@@ -204,15 +195,15 @@ public class TaskListRecyclerView extends ThemeColorRecyclerView {
mIntentTaskChangeDisposable.dispose();
}
void onTaskChange(ModelChange<?> taskChange) {
private void onTaskChange(ModelChange<?> taskChange) {
final Adapter adapter = mAdapter;
if (taskChange.getAction() == ModelChange.INSERT) {
adapter.notifyChildInserted(1, mPendingTaskGroup.addTask(taskChange.getData()));
notifyChildInsertedSafe(adapter, 1, mPendingTaskGroup.addTask(taskChange.getData()));
notifyParentChangedSafe(adapter, 1);
} else if (taskChange.getAction() == ModelChange.DELETE) {
final int i = mPendingTaskGroup.removeTask(taskChange.getData());
if (i >= 0) {
adapter.notifyChildRemoved(1, i);
notifyChildRemovedSafe(adapter, 1, i);
notifyParentChangedSafe(adapter, 1);
} else {
Log.w(LOG_TAG, "data inconsistent on change: " + taskChange);
@@ -221,13 +212,169 @@ public class TaskListRecyclerView extends ThemeColorRecyclerView {
} else if (taskChange.getAction() == ModelChange.UPDATE) {
final int i = mPendingTaskGroup.updateTask(taskChange.getData());
if (i >= 0) {
adapter.notifyChildChanged(1, i);
notifyChildChangedSafe(adapter, 1, i);
} else {
refresh();
}
}
}
private void notifyParentChangedSafe(@NonNull Adapter adapter, int parentPosition) {
// Avoid notifying an adapter that has been replaced asynchronously.
// zh-CN: 避免对已经被异步替换的适配器发送通知.
if (adapter != mAdapter) {
return;
}
// Avoid out-of-range parentPosition caused by transient state mismatch.
// zh-CN: 避免由于瞬时状态不一致导致的 parentPosition 越界.
if (parentPosition < 0 || parentPosition >= mTaskGroups.size()) {
return;
}
// RecyclerView may be computing layout/dispatching updates; postpone to next loop.
// zh-CN: RecyclerView 可能正在计算布局/分发更新, 将通知延迟到下一次消息循环.
if (isComputingLayout()) {
post(() -> notifyParentChangedSafe(adapter, parentPosition));
return;
}
try {
adapter.notifyParentChanged(parentPosition);
} catch (IndexOutOfBoundsException e) {
/* Ignored. */
}
}
private void notifyChildInsertedSafe(@NonNull Adapter adapter, int parentPosition, int childPosition) {
// Avoid notifying an adapter that has been replaced asynchronously.
// zh-CN: 避免对已经被异步替换的适配器发送通知.
if (adapter != mAdapter) {
return;
}
// Avoid out-of-range parentPosition caused by transient state mismatch.
// zh-CN: 避免由于瞬时状态不一致导致的 parentPosition 越界.
if (parentPosition < 0 || parentPosition >= mTaskGroups.size()) {
return;
}
// Avoid negative childPosition caused by transient state mismatch.
// zh-CN: 避免由于瞬时状态不一致导致的 childPosition 为负数.
if (childPosition < 0) {
return;
}
// RecyclerView may be computing layout/dispatching updates; postpone to next loop.
// zh-CN: RecyclerView 可能正在计算布局/分发更新, 将通知延迟到下一次消息循环.
if (isComputingLayout()) {
post(() -> notifyChildInsertedSafe(adapter, parentPosition, childPosition));
return;
}
try {
adapter.notifyChildInserted(parentPosition, childPosition);
} catch (IndexOutOfBoundsException e) {
// Self-heal on transient inconsistency to avoid crashes during frequent list mutations.
// zh-CN: 在频繁列表变动导致的瞬时不一致场景下自愈, 避免崩溃.
Log.w(LOG_TAG, "notifyChildInsertedSafe: inconsistent state, fallback to refresh(). parentPosition="
+ parentPosition + ", childPosition=" + childPosition, e);
refresh();
} catch (ConcurrentModificationException e) {
// Self-heal when child list is modified during adapter iteration.
// zh-CN: 当适配器迭代 child 列表期间发生修改时自愈.
Log.w(LOG_TAG, "notifyChildInsertedSafe: CME, fallback to refresh(). parentPosition="
+ parentPosition + ", childPosition=" + childPosition, e);
refresh();
}
}
private void notifyChildRemovedSafe(@NonNull Adapter adapter, int parentPosition, int childPosition) {
// Avoid notifying an adapter that has been replaced asynchronously.
// zh-CN: 避免对已经被异步替换的适配器发送通知.
if (adapter != mAdapter) {
return;
}
// Avoid out-of-range parentPosition caused by transient state mismatch.
// zh-CN: 避免由于瞬时状态不一致导致的 parentPosition 越界.
if (parentPosition < 0 || parentPosition >= mTaskGroups.size()) {
return;
}
// Avoid negative childPosition caused by transient state mismatch.
// zh-CN: 避免由于瞬时状态不一致导致的 childPosition 为负数.
if (childPosition < 0) {
return;
}
// RecyclerView may be computing layout/dispatching updates; postpone to next loop.
// zh-CN: RecyclerView 可能正在计算布局/分发更新, 将通知延迟到下一次消息循环.
if (isComputingLayout()) {
post(() -> notifyChildRemovedSafe(adapter, parentPosition, childPosition));
return;
}
try {
adapter.notifyChildRemoved(parentPosition, childPosition);
} catch (IndexOutOfBoundsException e) {
// Self-heal on transient inconsistency to avoid crashes during frequent list mutations.
// zh-CN: 在频繁列表变动导致的瞬时不一致场景下自愈, 避免崩溃.
Log.w(LOG_TAG, "notifyChildRemovedSafe: inconsistent state, fallback to refresh(). parentPosition="
+ parentPosition + ", childPosition=" + childPosition, e);
refresh();
} catch (ConcurrentModificationException e) {
// Self-heal when child list is modified during adapter iteration.
// zh-CN: 当适配器迭代 child 列表期间发生修改时自愈.
Log.w(LOG_TAG, "notifyChildRemovedSafe: CME, fallback to refresh(). parentPosition="
+ parentPosition + ", childPosition=" + childPosition, e);
refresh();
}
}
private void notifyChildChangedSafe(@NonNull Adapter adapter, int parentPosition, int childPosition) {
// Avoid notifying an adapter that has been replaced asynchronously.
// zh-CN: 避免对已经被异步替换的适配器发送通知.
if (adapter != mAdapter) {
return;
}
// Avoid out-of-range parentPosition caused by transient state mismatch.
// zh-CN: 避免由于瞬时状态不一致导致的 parentPosition 越界.
if (parentPosition < 0 || parentPosition >= mTaskGroups.size()) {
return;
}
// Avoid negative childPosition caused by transient state mismatch.
// zh-CN: 避免由于瞬时状态不一致导致的 childPosition 为负数.
if (childPosition < 0) {
return;
}
// RecyclerView may be computing layout/dispatching updates; postpone to next loop.
// zh-CN: RecyclerView 可能正在计算布局/分发更新, 将通知延迟到下一次消息循环.
if (isComputingLayout()) {
post(() -> notifyChildChangedSafe(adapter, parentPosition, childPosition));
return;
}
try {
adapter.notifyChildChanged(parentPosition, childPosition);
} catch (IndexOutOfBoundsException e) {
// Self-heal on transient inconsistency to avoid crashes during frequent list mutations.
// zh-CN: 在频繁列表变动导致的瞬时不一致场景下自愈, 避免崩溃.
Log.w(LOG_TAG, "notifyChildChangedSafe: inconsistent state, fallback to refresh(). parentPosition="
+ parentPosition + ", childPosition=" + childPosition, e);
refresh();
} catch (ConcurrentModificationException e) {
// Self-heal when child list is modified during adapter iteration.
// zh-CN: 当适配器迭代 child 列表期间发生修改时自愈.
Log.w(LOG_TAG, "notifyChildChangedSafe: CME, fallback to refresh(). parentPosition="
+ parentPosition + ", childPosition=" + childPosition, e);
refresh();
}
}
private class Adapter extends ExpandableRecyclerAdapter<TaskGroup, Task, TaskGroupViewHolder, TaskViewHolder> {
public Adapter(@NonNull List<TaskGroup> parentList) {
@@ -259,13 +406,14 @@ public class TaskListRecyclerView extends ThemeColorRecyclerView {
}
}
class TaskViewHolder extends ChildViewHolder<Task> {
private class TaskViewHolder extends ChildViewHolder<Task> {
@NonNull
private final ExplorerFirstCharIconBinding firstCharIconBinding;
@NonNull
private final TaskListRecyclerViewItemBinding itemBinding;
private final int mItemIconThemeColorForContrast;
private Task mTask;
@@ -274,11 +422,13 @@ public class TaskListRecyclerView extends ThemeColorRecyclerView {
itemView.setOnClickListener(this::onItemClick);
itemBinding = TaskListRecyclerViewItemBinding.bind(itemView);
firstCharIconBinding = ExplorerFirstCharIconBinding.bind(itemView);
mItemIconThemeColorForContrast = ColorUtils.adjustColorForContrast(
getContext().getColor(R.color.window_background),
ThemeColorManagerCompat.getColorPrimary(), 2.3);
}
public void bind(Task task) {
mTask = task;
int itemIconThemeColorForContrast = ColorUtils.adjustColorForContrast(getContext().getColor(R.color.window_background), ThemeColorManagerCompat.getColorPrimary(), 2.3);
FileUtils.TYPE.Icon icon;
if (JavaScriptFileSource.ENGINE.equals(mTask.getEngineName())) {
icon = FileUtils.TYPE.JAVASCRIPT.icon;
@@ -289,8 +439,8 @@ public class TaskListRecyclerView extends ThemeColorRecyclerView {
}
firstCharIconBinding.firstChar
.setIcon(icon)
.setIconTextColor(itemIconThemeColorForContrast)
.setStrokeColor(itemIconThemeColorForContrast)
.setIconTextColor(mItemIconThemeColorForContrast)
.setStrokeColor(mItemIconThemeColorForContrast)
.setFillTransparent();
itemBinding.name.setText(task.getName());
itemBinding.taskListFilePath.setText(task.getDesc());

View File

@@ -1,5 +1,5 @@
#Mon Jan 26 20:19:51 CST 2026
BUILD_TIME=1769429991091
#Tue Jan 27 15:22:39 CST 2026
BUILD_TIME=1769498559481
COMPILE_SDK_VERSION=36
IMAGE_QUANT_CMAKE_VERSION=3.22.1
IMAGE_QUANT_NDK_VERSION=26.1.10909125
@@ -27,6 +27,6 @@ RAPID_OCR_OPENCV_MOBILE_LABEL_VERSION=13
RAPID_OCR_OPENCV_MOBILE_VERSION=4.5.3
TARGET_SDK_VERSION=36
TARGET_SDK_VERSION_INRT=29
VERSION_BUILD=3658
VERSION_BUILD=3660
VERSION_NAME=6.7.0 Alpha18
VSCODE_EXT_REQUIRED_VERSION=1.0.13