perf: coalesce root tab realtime refresh bursts

This commit is contained in:
kris
2026-04-05 08:26:39 +08:00
parent 71aa1a7143
commit 52f7d08b9e
2 changed files with 93 additions and 3 deletions

View File

@@ -104,6 +104,8 @@ public class MainActivity extends AppCompatActivity {
private boolean conversationQuickActionsVisible = false;
private boolean conversationAutoRefreshArmed = false;
private boolean conversationAutoRefreshEnabled = false;
private boolean rootTabRefreshInFlight = false;
private boolean pendingRootTabRefresh = false;
private final java.util.HashMap<String, Long> recentRealtimeEventTimestamps = new java.util.HashMap<>();
private final Set<String> selectedConversationProjectIds = new LinkedHashSet<>();
private @Nullable RootPagerAdapter rootPagerAdapter;
@@ -362,6 +364,11 @@ public class MainActivity extends AppCompatActivity {
}
void refreshCurrentTab() {
if (rootTabRefreshInFlight) {
pendingRootTabRefresh = true;
return;
}
rootTabRefreshInFlight = true;
if ("devices".equals(activeTab)) {
refreshDevicesData();
return;
@@ -413,9 +420,13 @@ public class MainActivity extends AppCompatActivity {
if (!finalConversationsOk) {
showMessage("刷新失败,请稍后重试");
}
completeRealtimeTabRefresh();
});
} catch (Exception error) {
runOnUiThread(() -> handleSessionRefreshFailure());
runOnUiThread(() -> {
handleSessionRefreshFailure();
completeRealtimeTabRefresh();
});
}
});
}
@@ -448,9 +459,13 @@ public class MainActivity extends AppCompatActivity {
if (!finalDevicesOk) {
showMessage("刷新失败,请稍后重试");
}
completeRealtimeTabRefresh();
});
} catch (Exception error) {
runOnUiThread(() -> handleSessionRefreshFailure());
runOnUiThread(() -> {
handleSessionRefreshFailure();
completeRealtimeTabRefresh();
});
}
});
}
@@ -501,13 +516,26 @@ public class MainActivity extends AppCompatActivity {
if (!finalOtaOk && !finalSettingsOk) {
showMessage("刷新失败,请稍后重试");
}
completeRealtimeTabRefresh();
});
} catch (Exception error) {
runOnUiThread(() -> handleSessionRefreshFailure());
runOnUiThread(() -> {
handleSessionRefreshFailure();
completeRealtimeTabRefresh();
});
}
});
}
void completeRealtimeTabRefresh() {
rootTabRefreshInFlight = false;
if (!pendingRootTabRefresh) {
return;
}
pendingRootTabRefresh = false;
refreshCurrentTab();
}
void handleRealtimeEvent(BossRealtimeEvent event) {
if (event == null || event.eventName.isEmpty()) {
return;

View File

@@ -2,6 +2,7 @@ package com.hyzq.boss;
import static org.junit.Assert.assertEquals;
import android.os.Looper;
import org.json.JSONObject;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -11,6 +12,8 @@ import org.robolectric.Shadows;
import org.robolectric.annotation.Config;
import org.robolectric.util.ReflectionHelpers;
import java.util.function.BooleanSupplier;
@RunWith(RobolectricTestRunner.class)
@Config(sdk = 34)
public class MainActivityRealtimeTest {
@@ -171,6 +174,62 @@ public class MainActivityRealtimeTest {
assertEquals(1, activity.meRefreshCount);
}
@Test
public void burstConversationRealtimeEventsCoalesceIntoSingleFollowUpRefresh() throws Exception {
TestMainActivity activity = Robolectric.buildActivity(TestMainActivity.class).setup().resume().get();
ReflectionHelpers.callInstanceMethod(activity, "showContent");
ReflectionHelpers.setField(activity, "rootTabRefreshInFlight", true);
ReflectionHelpers.callInstanceMethod(
activity,
"handleRealtimeEvent",
ReflectionHelpers.ClassParameter.from(
BossRealtimeEvent.class,
new BossRealtimeEvent("conversation.updated", new JSONObject().put("projectId", "project-1"))
)
);
Shadows.shadowOf(activity.getMainLooper()).idle();
ReflectionHelpers.callInstanceMethod(
activity,
"handleRealtimeEvent",
ReflectionHelpers.ClassParameter.from(
BossRealtimeEvent.class,
new BossRealtimeEvent("project.messages.updated", new JSONObject().put("projectId", "project-1"))
)
);
ReflectionHelpers.callInstanceMethod(
activity,
"handleRealtimeEvent",
ReflectionHelpers.ClassParameter.from(
BossRealtimeEvent.class,
new BossRealtimeEvent("conversation.context_indicator.updated", new JSONObject().put("projectId", "project-1"))
)
);
Shadows.shadowOf(activity.getMainLooper()).idle();
assertEquals(0, activity.conversationRefreshCount);
activity.completeRealtimeTabRefresh();
waitFor(() -> activity.conversationRefreshCount == 1);
assertEquals(1, activity.conversationRefreshCount);
assertEquals(0, activity.deviceRefreshCount);
assertEquals(0, activity.meRefreshCount);
}
private static void waitFor(BooleanSupplier condition) throws Exception {
long deadlineAt = System.currentTimeMillis() + 2_000L;
while (System.currentTimeMillis() < deadlineAt) {
Shadows.shadowOf(Looper.getMainLooper()).idle();
if (condition.getAsBoolean()) {
return;
}
Thread.sleep(20L);
}
throw new AssertionError("condition not met before timeout");
}
public static class TestMainActivity extends MainActivity {
int conversationRefreshCount;
int deviceRefreshCount;
@@ -179,16 +238,19 @@ public class MainActivityRealtimeTest {
@Override
void refreshConversationsData() {
conversationRefreshCount += 1;
completeRealtimeTabRefresh();
}
@Override
void refreshDevicesData() {
deviceRefreshCount += 1;
completeRealtimeTabRefresh();
}
@Override
void refreshMeData() {
meRefreshCount += 1;
completeRealtimeTabRefresh();
}
}
}