fix(ncm): 启动卡顿+大歌单+No tracks 三连修 (agent-209) — ①home/playlist 加载改后台 worker ②大歌单 detail n=1(tracks瘦身)+响应cap 2MB ③song_detail c数组引号转义 ④P0: MIPS32 long=32位歌曲id溢出→int64 ⑤eapi批缓冲2048→4096
ncm→f6bd765a 已部署; 真机验证(风控合规): 小歌单✓ 大歌单256首✓ 启动不卡✓; 已知: 大歌单首屏~30s(二期改分页端点)
This commit is contained in:
1 parent
a651df0e9b
commit
4cec476dd9
16 files changed
+344
-36
No files matched your search
@@ -80,6 +80,8 @@ static void app_on_tick(void)
|
||||
uint64_t now = meow_ui_time_ms();
|
||||
if (login_is_active()) { login_tick(now); return; }
|
||||
if (player_is_active()) { player_tick(now); return; }
|
||||
if (home_is_active()) { home_tick(now); }
|
||||
if (playlist_is_active()) { playlist_tick(now); }
|
||||
chrome_tick();
|
||||
}
|
||||
|
||||
|
||||
@@ -120,7 +120,8 @@ int appstate_load_playlists(void)
|
||||
int appstate_load_playlist_songs(int idx)
|
||||
{
|
||||
if (idx < 0 || idx >= g_st.playlist_count) return NCM_ERR_ARG;
|
||||
g_st.active_playlist = idx;
|
||||
/* 不在此处写 g_st.active_playlist:调用方(playlist_open/UI)已在 UI 线程设定;
|
||||
* 若后台 worker 在此改写,会覆盖用户已切换到的新歌单,造成“打开 B 却显示 A”。 */
|
||||
const ncm_playlist_t *pl = &g_st.playlists[idx];
|
||||
|
||||
int idn = 0;
|
||||
|
||||
@@ -103,8 +103,8 @@ typedef struct ncm_app_state {
|
||||
int playlist_count;
|
||||
int active_playlist; /* 当前打开歌单的下标;-1=无 */
|
||||
|
||||
/* 当前歌单曲目 */
|
||||
long song_ids[NCM_SONG_MAX];
|
||||
/* 当前歌单曲目(id 用 int64_t:网易云歌曲 id 可 >2^31,MIPS32 long 溢出) */
|
||||
int64_t song_ids[NCM_SONG_MAX];
|
||||
ncm_song_t songs[NCM_SONG_MAX];
|
||||
int song_count;
|
||||
|
||||
@@ -159,7 +159,8 @@ int home_on_tap(int x, int y);
|
||||
void home_swipe(int dir, int dist);
|
||||
void home_touch_down(int x, int y);
|
||||
void home_touch_move(int x, int y);
|
||||
void home_reload(void); /* 刷新我的歌单 */
|
||||
void home_reload(void); /* 刷新我的歌单(后台 worker,非阻塞) */
|
||||
void home_tick(uint64_t now); /* UI tick:检查歌单 worker 是否完成并刷新 */
|
||||
int home_go_up(void); /* 左缘返回:到底则退出 */
|
||||
|
||||
/* playlist */
|
||||
@@ -171,6 +172,7 @@ void playlist_touch_down(int x, int y);
|
||||
void playlist_touch_move(int x, int y);
|
||||
void playlist_open(int idx); /* 打开第 idx 个歌单(下拉进歌单详情) */
|
||||
void playlist_refresh(void);
|
||||
void playlist_tick(uint64_t now);/* UI tick:检查歌单 worker 是否完成并刷新 */
|
||||
|
||||
/* player */
|
||||
meow_view *player_view_create(void);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
|
||||
typedef struct { meow_view *view; int active; } home_ctrl;
|
||||
static home_ctrl g_hc;
|
||||
@@ -18,9 +19,19 @@ static meow_widget *g_list;
|
||||
static ncm_rect_t r_cont, r_list;
|
||||
static int g_empty_id, g_footer_id;
|
||||
static char g_title[128];
|
||||
static int g_load_err; /* 0=ok;负=错误码 */
|
||||
static volatile int g_load_err; /* 0=ok;负=错误码(worker 写,UI draw 读) */
|
||||
static int g_press_row, g_press_delta, g_dragging, g_drag_last_y;
|
||||
|
||||
/* 歌单加载后台 worker:网络(ensure_uid + user_playlist)从 UI 线程挪出,
|
||||
* 使冷启动/进 home 立即出界面,再异步填列表。gen 代际作“完成”信号(对齐 cover.c)。
|
||||
* 单核 MIPS,gen 在数据写完后置位,UI 见 gen 变化才重建列表,避免半成品。 */
|
||||
static pthread_mutex_t g_home_mu = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_t g_home_thr;
|
||||
static int g_home_thr_on; /* 1=worker 在跑(防重复 spawn) */
|
||||
static volatile int g_home_gen; /* worker 完成代数 */
|
||||
static int g_home_acked; /* UI 已消费的代数 */
|
||||
static volatile int g_home_loading; /* 1=加载中(draw 显示 LOADING) */
|
||||
|
||||
static void home_drag_reset(void)
|
||||
{
|
||||
g_press_row = -1;
|
||||
@@ -52,6 +63,10 @@ static void home_draw_empty(void *ctx)
|
||||
(void)ctx;
|
||||
if (meow_ui_activity_active() != ACT_HOME) return;
|
||||
if (g_load_err == 0 && g_st.playlist_count != 0) return;
|
||||
if (g_home_loading) {
|
||||
ncm_draw_empty(r_list, "LOADING", "fetching playlists...");
|
||||
return;
|
||||
}
|
||||
if (g_st.logged_in == 0)
|
||||
ncm_draw_empty(r_list, "NOT SIGNED IN", "sign in first");
|
||||
else if (g_load_err == NCM_ERR_AUTH)
|
||||
@@ -76,16 +91,9 @@ static void home_draw_footer(void *ctx)
|
||||
ncm_draw_footer(r_cont, foot);
|
||||
}
|
||||
|
||||
void home_reload(void)
|
||||
/* 把当前 playlist_count/昵称刷进列表 + 标题,并整帧重绘(UI 线程)。 */
|
||||
static void home_apply(void)
|
||||
{
|
||||
g_load_err = 0;
|
||||
int rc = 0;
|
||||
if (g_host_mock) { /* 宿主/测试:跳过网络,用预置数据 */
|
||||
rc = 0;
|
||||
} else if (!g_st.logged_in) rc = NCM_ERR_AUTH;
|
||||
else rc = appstate_load_playlists();
|
||||
g_load_err = rc;
|
||||
if (g_load_err != 0) g_st.playlist_count = 0;
|
||||
if (g_list) {
|
||||
meow_listview_set_count(g_list, g_st.playlist_count);
|
||||
meow_listview_set_scroll(g_list, 0);
|
||||
@@ -99,6 +107,54 @@ void home_reload(void)
|
||||
meow_ui_paint_full();
|
||||
}
|
||||
|
||||
/* 后台 worker:唯一走网络的地方(ensure_uid + user_playlist),UI 线程不阻塞。 */
|
||||
static void *home_worker(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
int rc;
|
||||
if (!g_st.logged_in) rc = NCM_ERR_AUTH;
|
||||
else rc = appstate_load_playlists();
|
||||
pthread_mutex_lock(&g_home_mu);
|
||||
g_load_err = rc;
|
||||
g_home_loading = 0;
|
||||
g_home_gen++;
|
||||
pthread_mutex_unlock(&g_home_mu);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void home_reload(void)
|
||||
{
|
||||
if (g_host_mock) { /* 宿主/测试:跳过网络,用预置数据(同步) */
|
||||
g_load_err = 0;
|
||||
if (g_load_err != 0) g_st.playlist_count = 0;
|
||||
home_apply();
|
||||
return;
|
||||
}
|
||||
if (g_home_thr_on) return; /* 已有 worker 在跑,忽略重复触发 */
|
||||
g_load_err = 0;
|
||||
g_st.playlist_count = 0; /* 清旧数据,先画 LOADING,worker 完成后回填 */
|
||||
g_home_loading = 1;
|
||||
g_home_thr_on = 1;
|
||||
if (pthread_create(&g_home_thr, NULL, home_worker, NULL) != 0) {
|
||||
g_home_thr_on = 0;
|
||||
g_home_loading = 0;
|
||||
g_load_err = NCM_ERR_IO;
|
||||
}
|
||||
home_apply(); /* 先画(LOADING/空态),不等网络 */
|
||||
}
|
||||
|
||||
/* UI tick:worker 完成(gen 变化)后重建列表并重绘。 */
|
||||
void home_tick(uint64_t now)
|
||||
{
|
||||
(void)now;
|
||||
if (!g_home_thr_on) return;
|
||||
if (g_home_gen == g_home_acked) return;
|
||||
g_home_acked = g_home_gen; /* 消费本次完成 */
|
||||
g_home_thr_on = 0; /* worker 已结束,可再次 reload */
|
||||
g_home_loading = 0;
|
||||
home_apply();
|
||||
}
|
||||
|
||||
int home_is_active(void) { return g_hc.active; }
|
||||
|
||||
int home_on_tap(int x, int y)
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
|
||||
typedef struct { meow_view *view; int active; } playlist_ctrl;
|
||||
static playlist_ctrl g_pc;
|
||||
@@ -17,9 +18,19 @@ static meow_widget *g_list;
|
||||
static ncm_rect_t r_cont, r_list;
|
||||
static int g_empty_id, g_footer_id;
|
||||
static char g_title[256];
|
||||
static int g_load_err;
|
||||
static volatile int g_load_err;
|
||||
static int g_press_row, g_press_delta, g_dragging, g_drag_last_y;
|
||||
|
||||
/* 歌单详情加载后台 worker:大歌单 detail+song_detail 在设备上可达数十秒(trackIds 全量 900KB+),
|
||||
* 不能占 UI 线程。worker 记录它装载的歌单下标,tick 判变;若用户已切到别的歌单则丢弃并重新加载。 */
|
||||
static pthread_mutex_t g_plmu = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_t g_plthr;
|
||||
static int g_plthr_on; /* 1=worker 在跑 */
|
||||
static volatile int g_plgen; /* worker 完成代数 */
|
||||
static int g_placked; /* UI 已消费代数 */
|
||||
static volatile int g_plloading; /* 1=加载中(draw 显示 LOADING) */
|
||||
static volatile int g_plloaded_idx;/* worker 实际装载的歌单下标 */
|
||||
|
||||
static void pl_drag_reset(void)
|
||||
{
|
||||
g_press_row = -1; g_press_delta = 0; g_dragging = 0; g_drag_last_y = 0;
|
||||
@@ -45,6 +56,10 @@ static void pl_draw_empty(void *ctx)
|
||||
(void)ctx;
|
||||
if (meow_ui_activity_active() != ACT_PLAYLIST) return;
|
||||
if (g_load_err == 0 && g_st.song_count != 0) return;
|
||||
if (g_plloading) {
|
||||
ncm_draw_empty(r_list, "LOADING", "fetching tracks...");
|
||||
return;
|
||||
}
|
||||
if (g_load_err == NCM_ERR_AUTH)
|
||||
ncm_draw_empty(r_list, "SESSION EXPIRED", "please sign in again");
|
||||
else if (g_load_err)
|
||||
@@ -66,16 +81,9 @@ static void pl_draw_footer(void *ctx)
|
||||
ncm_draw_footer(r_cont, foot);
|
||||
}
|
||||
|
||||
void playlist_refresh(void)
|
||||
/* 把当前 song_count 刷进列表 + 标题,并整帧重绘(UI 线程)。 */
|
||||
static void pl_apply(void)
|
||||
{
|
||||
g_load_err = 0;
|
||||
int rc = NCM_ERR_ARG;
|
||||
if (!g_host_mock && g_st.active_playlist >= 0)
|
||||
rc = appstate_load_playlist_songs(g_st.active_playlist);
|
||||
else if (g_host_mock)
|
||||
rc = 0;
|
||||
g_load_err = rc;
|
||||
if (g_load_err != 0) g_st.song_count = 0;
|
||||
if (g_list) {
|
||||
meow_listview_set_count(g_list, g_st.song_count);
|
||||
meow_listview_set_scroll(g_list, 0);
|
||||
@@ -90,6 +98,62 @@ void playlist_refresh(void)
|
||||
meow_ui_paint_full();
|
||||
}
|
||||
|
||||
/* 后台 worker:detail + song_detail 全在独立线程,UI 不卡(大歌单可数十秒)。 */
|
||||
static void *playlist_worker(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
int my_idx = g_st.active_playlist; /* 装载目标(UI 已设定) */
|
||||
int rc;
|
||||
if (g_host_mock) rc = 0;
|
||||
else if (my_idx < 0) rc = NCM_ERR_ARG;
|
||||
else rc = appstate_load_playlist_songs(my_idx);
|
||||
pthread_mutex_lock(&g_plmu);
|
||||
g_load_err = rc;
|
||||
g_plloading = 0;
|
||||
g_plloaded_idx = my_idx;
|
||||
g_plgen++;
|
||||
pthread_mutex_unlock(&g_plmu);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void playlist_refresh(void)
|
||||
{
|
||||
if (g_host_mock) { /* 宿主/测试:预置数据,同步 */
|
||||
g_load_err = 0;
|
||||
if (g_load_err != 0) g_st.song_count = 0;
|
||||
pl_apply();
|
||||
return;
|
||||
}
|
||||
if (g_plthr_on) return; /* 已有 worker 在跑 */
|
||||
g_load_err = 0;
|
||||
g_st.song_count = 0; /* 清旧曲目,先画 LOADING,worker 完成后回填 */
|
||||
g_plloading = 1;
|
||||
g_plthr_on = 1;
|
||||
if (pthread_create(&g_plthr, NULL, playlist_worker, NULL) != 0) {
|
||||
g_plthr_on = 0;
|
||||
g_plloading = 0;
|
||||
g_load_err = NCM_ERR_IO;
|
||||
}
|
||||
pl_apply(); /* 先画(LOADING/空态),不等网络 */
|
||||
}
|
||||
|
||||
/* UI tick:worker 完成(gen 变化)后重建列表;若装载目标已切换则丢弃并重新加载。 */
|
||||
void playlist_tick(uint64_t now)
|
||||
{
|
||||
(void)now;
|
||||
if (!g_plthr_on) return;
|
||||
if (g_plgen == g_placked) return;
|
||||
g_placked = g_plgen;
|
||||
g_plthr_on = 0;
|
||||
g_plloading = 0;
|
||||
if (g_plloaded_idx != g_st.active_playlist) {
|
||||
playlist_refresh(); /* 已切别的歌单:丢弃并重新加载当前目标 */
|
||||
return;
|
||||
}
|
||||
if (g_load_err != 0) g_st.song_count = 0;
|
||||
pl_apply();
|
||||
}
|
||||
|
||||
void playlist_open(int idx)
|
||||
{
|
||||
if (idx < 0 || idx >= g_st.playlist_count) return;
|
||||
|
||||
Reference in new issue
Block a user