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

View File

@@ -38,11 +38,13 @@ import org.autojs.autojs6.databinding.ExplorerFirstCharIconBinding;
import org.autojs.autojs6.databinding.TaskListRecyclerViewItemBinding; import org.autojs.autojs6.databinding.TaskListRecyclerViewItemBinding;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.List; import java.util.List;
/** /**
* Created by Stardust on Mar 24, 2017. * 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 { public class TaskListRecyclerView extends ThemeColorRecyclerView {
@@ -58,17 +60,33 @@ public class TaskListRecyclerView extends ThemeColorRecyclerView {
@Override @Override
public void onStart(final ScriptExecution execution) { public void onStart(final ScriptExecution execution) {
try { try {
int i = mRunningTaskGroup.addTask(execution); final Adapter adapter = mAdapter;
if (i != -1) { post(() -> {
final Adapter adapter = mAdapter; // Constrain list mutation to main thread to avoid ConcurrentModificationException in adapter iteration.
post(() -> { // zh-CN: 将列表修改约束在主线程执行, 避免适配器迭代时触发 ConcurrentModificationException.
if (adapter != mAdapter) {
return; if (adapter != mAdapter) {
} return;
adapter.notifyChildInserted(0, i); }
notifyParentChangedSafe(adapter, 0);
}); 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) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
@@ -92,7 +110,7 @@ public class TaskListRecyclerView extends ThemeColorRecyclerView {
} }
final int i = mRunningTaskGroup.removeTask(execution); final int i = mRunningTaskGroup.removeTask(execution);
if (i >= 0) { if (i >= 0) {
adapter.notifyChildRemoved(0, i); notifyChildRemovedSafe(adapter, 0, i);
notifyParentChangedSafe(adapter, 0); notifyParentChangedSafe(adapter, 0);
} else { } else {
refresh(); 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) { public TaskListRecyclerView(Context context) {
super(context); super(context);
init(); init();
@@ -204,15 +195,15 @@ public class TaskListRecyclerView extends ThemeColorRecyclerView {
mIntentTaskChangeDisposable.dispose(); mIntentTaskChangeDisposable.dispose();
} }
void onTaskChange(ModelChange<?> taskChange) { private void onTaskChange(ModelChange<?> taskChange) {
final Adapter adapter = mAdapter; final Adapter adapter = mAdapter;
if (taskChange.getAction() == ModelChange.INSERT) { if (taskChange.getAction() == ModelChange.INSERT) {
adapter.notifyChildInserted(1, mPendingTaskGroup.addTask(taskChange.getData())); notifyChildInsertedSafe(adapter, 1, mPendingTaskGroup.addTask(taskChange.getData()));
notifyParentChangedSafe(adapter, 1); notifyParentChangedSafe(adapter, 1);
} else if (taskChange.getAction() == ModelChange.DELETE) { } else if (taskChange.getAction() == ModelChange.DELETE) {
final int i = mPendingTaskGroup.removeTask(taskChange.getData()); final int i = mPendingTaskGroup.removeTask(taskChange.getData());
if (i >= 0) { if (i >= 0) {
adapter.notifyChildRemoved(1, i); notifyChildRemovedSafe(adapter, 1, i);
notifyParentChangedSafe(adapter, 1); notifyParentChangedSafe(adapter, 1);
} else { } else {
Log.w(LOG_TAG, "data inconsistent on change: " + taskChange); Log.w(LOG_TAG, "data inconsistent on change: " + taskChange);
@@ -221,13 +212,169 @@ public class TaskListRecyclerView extends ThemeColorRecyclerView {
} else if (taskChange.getAction() == ModelChange.UPDATE) { } else if (taskChange.getAction() == ModelChange.UPDATE) {
final int i = mPendingTaskGroup.updateTask(taskChange.getData()); final int i = mPendingTaskGroup.updateTask(taskChange.getData());
if (i >= 0) { if (i >= 0) {
adapter.notifyChildChanged(1, i); notifyChildChangedSafe(adapter, 1, i);
} else { } else {
refresh(); 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> { private class Adapter extends ExpandableRecyclerAdapter<TaskGroup, Task, TaskGroupViewHolder, TaskViewHolder> {
public Adapter(@NonNull List<TaskGroup> parentList) { 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 @NonNull
private final ExplorerFirstCharIconBinding firstCharIconBinding; private final ExplorerFirstCharIconBinding firstCharIconBinding;
@NonNull @NonNull
private final TaskListRecyclerViewItemBinding itemBinding; private final TaskListRecyclerViewItemBinding itemBinding;
private final int mItemIconThemeColorForContrast;
private Task mTask; private Task mTask;
@@ -274,11 +422,13 @@ public class TaskListRecyclerView extends ThemeColorRecyclerView {
itemView.setOnClickListener(this::onItemClick); itemView.setOnClickListener(this::onItemClick);
itemBinding = TaskListRecyclerViewItemBinding.bind(itemView); itemBinding = TaskListRecyclerViewItemBinding.bind(itemView);
firstCharIconBinding = ExplorerFirstCharIconBinding.bind(itemView); firstCharIconBinding = ExplorerFirstCharIconBinding.bind(itemView);
mItemIconThemeColorForContrast = ColorUtils.adjustColorForContrast(
getContext().getColor(R.color.window_background),
ThemeColorManagerCompat.getColorPrimary(), 2.3);
} }
public void bind(Task task) { public void bind(Task task) {
mTask = task; mTask = task;
int itemIconThemeColorForContrast = ColorUtils.adjustColorForContrast(getContext().getColor(R.color.window_background), ThemeColorManagerCompat.getColorPrimary(), 2.3);
FileUtils.TYPE.Icon icon; FileUtils.TYPE.Icon icon;
if (JavaScriptFileSource.ENGINE.equals(mTask.getEngineName())) { if (JavaScriptFileSource.ENGINE.equals(mTask.getEngineName())) {
icon = FileUtils.TYPE.JAVASCRIPT.icon; icon = FileUtils.TYPE.JAVASCRIPT.icon;
@@ -289,8 +439,8 @@ public class TaskListRecyclerView extends ThemeColorRecyclerView {
} }
firstCharIconBinding.firstChar firstCharIconBinding.firstChar
.setIcon(icon) .setIcon(icon)
.setIconTextColor(itemIconThemeColorForContrast) .setIconTextColor(mItemIconThemeColorForContrast)
.setStrokeColor(itemIconThemeColorForContrast) .setStrokeColor(mItemIconThemeColorForContrast)
.setFillTransparent(); .setFillTransparent();
itemBinding.name.setText(task.getName()); itemBinding.name.setText(task.getName());
itemBinding.taskListFilePath.setText(task.getDesc()); itemBinding.taskListFilePath.setText(task.getDesc());

View File

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