perf: refresh only active root tab data on realtime events

This commit is contained in:
kris
2026-04-05 08:02:14 +08:00
parent 28f692706b
commit 0bae3a78ec
2 changed files with 253 additions and 9 deletions

View File

@@ -360,7 +360,150 @@ public class MainActivity extends AppCompatActivity {
}
void refreshCurrentTab() {
refreshAllData(sessionData);
if ("devices".equals(activeTab)) {
refreshDevicesData();
return;
}
if ("me".equals(activeTab)) {
refreshMeData();
return;
}
refreshConversationsData();
}
void refreshConversationsData() {
startRefreshing(true);
executor.execute(() -> {
try {
JSONObject session = ensureActiveSession();
BossApiClient.ApiResponse conversations = null;
boolean conversationsOk = false;
try {
conversations = apiClient.getConversations();
conversationsOk = conversations.ok();
} catch (Exception ignored) {
conversationsOk = false;
}
if (!conversationsOk) {
try {
BossApiClient.ApiResponse fallbackConversations = apiClient.getConversationHome();
if (fallbackConversations.ok()) {
conversations = fallbackConversations;
conversationsOk = true;
}
} catch (Exception ignored) {
conversationsOk = false;
}
}
BossApiClient.ApiResponse finalConversations = conversations;
final boolean finalConversationsOk = conversationsOk;
runOnUiThread(() -> {
sessionData = session;
conversationsData = WechatSurfaceMapper.resolveRefreshValue(
conversationsData,
finalConversations == null ? null : finalConversations.json.optJSONArray("conversations"),
finalConversationsOk
);
maybeApplyPreferredEntry();
renderCurrentTab();
startRefreshing(false);
if (!finalConversationsOk) {
showMessage("刷新失败,请稍后重试");
}
});
} catch (Exception error) {
runOnUiThread(() -> handleSessionRefreshFailure());
}
});
}
void refreshDevicesData() {
startRefreshing(true);
executor.execute(() -> {
try {
JSONObject session = ensureActiveSession();
BossApiClient.ApiResponse devices = null;
boolean devicesOk = false;
try {
devices = apiClient.getDevices();
devicesOk = devices.ok();
} catch (Exception ignored) {
devicesOk = false;
}
BossApiClient.ApiResponse finalDevices = devices;
final boolean finalDevicesOk = devicesOk;
runOnUiThread(() -> {
sessionData = session;
devicesData = WechatSurfaceMapper.resolveRefreshValue(
devicesData,
finalDevices == null ? null : finalDevices.json.optJSONArray("devices"),
finalDevicesOk
);
renderCurrentTab();
startRefreshing(false);
if (!finalDevicesOk) {
showMessage("刷新失败,请稍后重试");
}
});
} catch (Exception error) {
runOnUiThread(() -> handleSessionRefreshFailure());
}
});
}
void refreshMeData() {
startRefreshing(true);
executor.execute(() -> {
try {
JSONObject session = ensureActiveSession();
BossApiClient.ApiResponse ota = null;
BossApiClient.ApiResponse settings = null;
boolean otaOk = false;
boolean settingsOk = false;
try {
ota = apiClient.getOtaStatus();
otaOk = ota.ok();
} catch (Exception ignored) {
otaOk = false;
}
try {
settings = apiClient.getSettings();
settingsOk = settings.ok();
} catch (Exception ignored) {
settingsOk = false;
}
BossApiClient.ApiResponse finalOta = ota;
BossApiClient.ApiResponse finalSettings = settings;
final boolean finalOtaOk = otaOk;
final boolean finalSettingsOk = settingsOk;
runOnUiThread(() -> {
sessionData = session;
otaData = WechatSurfaceMapper.resolveRefreshValue(
otaData,
finalOta == null ? null : finalOta.json,
finalOtaOk
);
JSONObject settingsPayload = finalSettings == null ? null : finalSettings.json.optJSONObject("settings");
JSONObject userPayload = finalSettings == null ? null : finalSettings.json.optJSONObject("user");
if (finalSettingsOk && settingsPayload != null) {
preferredEntryTab = settingsPayload.optString("preferredEntryPoint", "conversations");
}
if (finalSettingsOk) {
updateBoundDeviceState(userPayload);
}
renderCurrentTab();
startRefreshing(false);
if (!finalOtaOk && !finalSettingsOk) {
showMessage("刷新失败,请稍后重试");
}
});
} catch (Exception error) {
runOnUiThread(() -> handleSessionRefreshFailure());
}
});
}
void handleRealtimeEvent(BossRealtimeEvent event) {
@@ -569,6 +712,40 @@ public class MainActivity extends AppCompatActivity {
});
}
private JSONObject ensureActiveSession() throws IOException {
JSONObject session = sessionData;
if (session != null) {
return session;
}
try {
BossApiClient.ApiResponse sessionResponse = apiClient.getSession();
if (!sessionResponse.ok()) {
sessionResponse = apiClient.restoreSession();
}
if (!sessionResponse.ok()) {
throw new IOException("SESSION_UNAVAILABLE");
}
JSONObject restored = sessionResponse.json.optJSONObject("session");
if (restored == null) {
throw new IOException("SESSION_UNAVAILABLE");
}
return restored;
} catch (IOException error) {
throw error;
} catch (Exception error) {
throw new IOException("SESSION_UNAVAILABLE", error);
}
}
private void handleSessionRefreshFailure() {
startRefreshing(false);
sessionData = null;
conversationsData = null;
devicesData = null;
otaData = null;
showLogin("当前登录已失效或同步失败,请重新点击登录。");
}
private void showLogin(String hint) {
loginPanel.setVisibility(View.VISIBLE);
contentPanel.setVisibility(View.GONE);

View File

@@ -28,7 +28,9 @@ public class MainActivityRealtimeTest {
);
Shadows.shadowOf(activity.getMainLooper()).idle();
assertEquals(1, activity.refreshCount);
assertEquals(1, activity.conversationRefreshCount);
assertEquals(0, activity.deviceRefreshCount);
assertEquals(0, activity.meRefreshCount);
}
@Test
@@ -45,7 +47,8 @@ public class MainActivityRealtimeTest {
);
Shadows.shadowOf(activity.getMainLooper()).idle();
assertEquals(0, activity.refreshCount);
assertEquals(0, activity.conversationRefreshCount);
assertEquals(0, activity.deviceRefreshCount);
}
@Test
@@ -62,7 +65,7 @@ public class MainActivityRealtimeTest {
);
Shadows.shadowOf(activity.getMainLooper()).idle();
assertEquals(0, activity.refreshCount);
assertEquals(0, activity.conversationRefreshCount);
}
@Test
@@ -82,7 +85,8 @@ public class MainActivityRealtimeTest {
);
Shadows.shadowOf(activity.getMainLooper()).idle();
assertEquals(1, activity.refreshCount);
assertEquals(1, activity.conversationRefreshCount);
assertEquals(0, activity.deviceRefreshCount);
}
@Test
@@ -113,15 +117,78 @@ public class MainActivityRealtimeTest {
);
Shadows.shadowOf(activity.getMainLooper()).idle();
assertEquals(2, activity.refreshCount);
assertEquals(2, activity.conversationRefreshCount);
assertEquals(0, activity.deviceRefreshCount);
}
@Test
public void devicesRealtimeEventRefreshesVisibleDevicesTabOnly() throws Exception {
TestMainActivity activity = Robolectric.buildActivity(TestMainActivity.class).setup().resume().get();
ReflectionHelpers.callInstanceMethod(activity, "showContent");
ReflectionHelpers.callInstanceMethod(
activity,
"setActiveTab",
ReflectionHelpers.ClassParameter.from(String.class, "devices"),
ReflectionHelpers.ClassParameter.from(boolean.class, false)
);
ReflectionHelpers.callInstanceMethod(
activity,
"handleRealtimeEvent",
ReflectionHelpers.ClassParameter.from(
BossRealtimeEvent.class,
new BossRealtimeEvent("devices.updated", new JSONObject().put("deviceId", "mac-studio"))
)
);
Shadows.shadowOf(activity.getMainLooper()).idle();
assertEquals(0, activity.conversationRefreshCount);
assertEquals(1, activity.deviceRefreshCount);
assertEquals(0, activity.meRefreshCount);
}
@Test
public void otaRealtimeEventRefreshesVisibleMeTabOnly() throws Exception {
TestMainActivity activity = Robolectric.buildActivity(TestMainActivity.class).setup().resume().get();
ReflectionHelpers.callInstanceMethod(activity, "showContent");
ReflectionHelpers.callInstanceMethod(
activity,
"setActiveTab",
ReflectionHelpers.ClassParameter.from(String.class, "me"),
ReflectionHelpers.ClassParameter.from(boolean.class, false)
);
ReflectionHelpers.callInstanceMethod(
activity,
"handleRealtimeEvent",
ReflectionHelpers.ClassParameter.from(
BossRealtimeEvent.class,
new BossRealtimeEvent("ota.updated", new JSONObject())
)
);
Shadows.shadowOf(activity.getMainLooper()).idle();
assertEquals(0, activity.conversationRefreshCount);
assertEquals(0, activity.deviceRefreshCount);
assertEquals(1, activity.meRefreshCount);
}
public static class TestMainActivity extends MainActivity {
int refreshCount;
int conversationRefreshCount;
int deviceRefreshCount;
int meRefreshCount;
@Override
void refreshCurrentTab() {
refreshCount += 1;
void refreshConversationsData() {
conversationRefreshCount += 1;
}
@Override
void refreshDevicesData() {
deviceRefreshCount += 1;
}
@Override
void refreshMeData() {
meRefreshCount += 1;
}
}
}