From fdaff5d4e40b5b98230f6016c25c9f9888dfb389 Mon Sep 17 00:00:00 2001 From: SuperMonster003 Date: Tue, 27 Jan 2026 15:26:04 +0800 Subject: [PATCH] =?UTF-8?q?6.7.0=20-=20Alpha18=20-=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E9=9D=A2=E6=9D=BF=E5=88=97=E8=A1=A8=E9=A1=B9?= =?UTF-8?q?=E9=A2=91=E7=B9=81=E5=8F=98=E5=8A=A8=E6=97=B6=E5=8F=AF=E8=83=BD?= =?UTF-8?q?=E5=AF=BC=E8=87=B4=E5=BA=94=E7=94=A8=E5=B4=A9=E6=BA=83=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .changelog/lang_zh-Hans.json | 1 + .../ui/main/task/TaskListRecyclerView.java | 246 ++++++++++++++---- version.properties | 6 +- 3 files changed, 202 insertions(+), 51 deletions(-) diff --git a/.changelog/lang_zh-Hans.json b/.changelog/lang_zh-Hans.json index 4fe39c5b..d5e0d3ec 100644 --- a/.changelog/lang_zh-Hans.json +++ b/.changelog/lang_zh-Hans.json @@ -94,6 +94,7 @@ "崩溃报告页面复制详细信息功能失效的问题", "Android 16+ 自定义返回逻辑失效导致返回功能异常的问题", "文件管理器删除项目文件夹后 UI 未能自动刷新的问题", + "任务面板列表项频繁变动时可能导致应用崩溃的问题", "使用 \"三按钮\" 手势导航时导航栏前景色可能与夜间模式关联异常的问题", "APK 文件类型信息对话框可能无法获取应用名称及 SDK 信息的问题", "客户端模式连接后, 短时间断开连接并再次连接时, 出现握手超时且难以再次建立连接的问题", diff --git a/app/src/main/java/org/autojs/autojs/ui/main/task/TaskListRecyclerView.java b/app/src/main/java/org/autojs/autojs/ui/main/task/TaskListRecyclerView.java index fa178977..30358bf3 100644 --- a/app/src/main/java/org/autojs/autojs/ui/main/task/TaskListRecyclerView.java +++ b/app/src/main/java/org/autojs/autojs/ui/main/task/TaskListRecyclerView.java @@ -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 { public Adapter(@NonNull List parentList) { @@ -259,13 +406,14 @@ public class TaskListRecyclerView extends ThemeColorRecyclerView { } } - class TaskViewHolder extends ChildViewHolder { + private class TaskViewHolder extends ChildViewHolder { @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()); diff --git a/version.properties b/version.properties index 7b78c198..a8504e4e 100644 --- a/version.properties +++ b/version.properties @@ -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