diff --git a/android-app/.gradle/8.14/checksums/checksums.lock b/android-app/.gradle/8.14/checksums/checksums.lock index 9ae74ae9..d4942611 100644 Binary files a/android-app/.gradle/8.14/checksums/checksums.lock and b/android-app/.gradle/8.14/checksums/checksums.lock differ diff --git a/android-app/.gradle/8.14/checksums/md5-checksums.bin b/android-app/.gradle/8.14/checksums/md5-checksums.bin index 8b127a29..c43983c5 100644 Binary files a/android-app/.gradle/8.14/checksums/md5-checksums.bin and b/android-app/.gradle/8.14/checksums/md5-checksums.bin differ diff --git a/android-app/.gradle/8.14/checksums/sha1-checksums.bin b/android-app/.gradle/8.14/checksums/sha1-checksums.bin index 81eca621..79caeaed 100644 Binary files a/android-app/.gradle/8.14/checksums/sha1-checksums.bin and b/android-app/.gradle/8.14/checksums/sha1-checksums.bin differ diff --git a/android-app/.gradle/8.14/executionHistory/executionHistory.bin b/android-app/.gradle/8.14/executionHistory/executionHistory.bin index b64fc610..ae86bea0 100644 Binary files a/android-app/.gradle/8.14/executionHistory/executionHistory.bin and b/android-app/.gradle/8.14/executionHistory/executionHistory.bin differ diff --git a/android-app/.gradle/8.14/executionHistory/executionHistory.lock b/android-app/.gradle/8.14/executionHistory/executionHistory.lock index f4974e8d..afb7326f 100644 Binary files a/android-app/.gradle/8.14/executionHistory/executionHistory.lock and b/android-app/.gradle/8.14/executionHistory/executionHistory.lock differ diff --git a/android-app/.gradle/8.14/fileHashes/fileHashes.bin b/android-app/.gradle/8.14/fileHashes/fileHashes.bin index 5805bed4..e886a182 100644 Binary files a/android-app/.gradle/8.14/fileHashes/fileHashes.bin and b/android-app/.gradle/8.14/fileHashes/fileHashes.bin differ diff --git a/android-app/.gradle/8.14/fileHashes/fileHashes.lock b/android-app/.gradle/8.14/fileHashes/fileHashes.lock index 34c27fb8..1bd25408 100644 Binary files a/android-app/.gradle/8.14/fileHashes/fileHashes.lock and b/android-app/.gradle/8.14/fileHashes/fileHashes.lock differ diff --git a/android-app/.gradle/8.14/fileHashes/resourceHashesCache.bin b/android-app/.gradle/8.14/fileHashes/resourceHashesCache.bin index 8b858a16..cf457e3a 100644 Binary files a/android-app/.gradle/8.14/fileHashes/resourceHashesCache.bin and b/android-app/.gradle/8.14/fileHashes/resourceHashesCache.bin differ diff --git a/android-app/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/android-app/.gradle/buildOutputCleanup/buildOutputCleanup.lock index 9793642f..a8e4a610 100644 Binary files a/android-app/.gradle/buildOutputCleanup/buildOutputCleanup.lock and b/android-app/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/android-app/.gradle/buildOutputCleanup/outputFiles.bin b/android-app/.gradle/buildOutputCleanup/outputFiles.bin index ebbd7cc8..b3650f40 100644 Binary files a/android-app/.gradle/buildOutputCleanup/outputFiles.bin and b/android-app/.gradle/buildOutputCleanup/outputFiles.bin differ diff --git a/android-app/.gradle/file-system.probe b/android-app/.gradle/file-system.probe index b3285246..2ea50c31 100644 Binary files a/android-app/.gradle/file-system.probe and b/android-app/.gradle/file-system.probe differ diff --git a/android-app/app/build.gradle.kts b/android-app/app/build.gradle.kts index 2499ba47..979b7d01 100644 --- a/android-app/app/build.gradle.kts +++ b/android-app/app/build.gradle.kts @@ -17,14 +17,27 @@ android { } buildTypes { - release { + debug { + // 调试版本优化 isMinifyEnabled = false + isDebuggable = true + } + release { + // 发布版本优化 + isMinifyEnabled = true + isShrinkResources = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) } } + + // 编译优化 + dexOptions { + javaMaxHeapSize = "2g" + preDexLibraries = true + } compileOptions { sourceCompatibility = JavaVersion.VERSION_17 diff --git a/android-app/app/src/main/AndroidManifest.xml b/android-app/app/src/main/AndroidManifest.xml index 1a521b97..07e63db3 100644 --- a/android-app/app/src/main/AndroidManifest.xml +++ b/android-app/app/src/main/AndroidManifest.xml @@ -9,7 +9,9 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.LiveStreaming" - android:usesCleartextTraffic="true"> + android:usesCleartextTraffic="true" + android:hardwareAccelerated="true" + android:largeHeap="true"> + android:exported="true" + android:launchMode="singleTop" + android:screenOrientation="portrait"> diff --git a/android-app/app/src/main/java/com/example/livestreaming/MainActivity.java b/android-app/app/src/main/java/com/example/livestreaming/MainActivity.java index e631bc3d..b0a1bd34 100644 --- a/android-app/app/src/main/java/com/example/livestreaming/MainActivity.java +++ b/android-app/app/src/main/java/com/example/livestreaming/MainActivity.java @@ -53,6 +53,15 @@ public class MainActivity extends AppCompatActivity { binding = ActivityMainBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); + // 立即显示缓存数据,提升启动速度 + setupRecyclerView(); + setupUI(); + + // 异步加载资源文件,避免阻塞主线程 + loadCoverAssetsAsync(); + } + + private void setupRecyclerView() { adapter = new RoomsAdapter(room -> { if (room == null) return; Intent intent = new Intent(MainActivity.this, RoomDetailActivity.class); @@ -64,9 +73,12 @@ public class MainActivity extends AppCompatActivity { glm.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS); binding.roomsRecyclerView.setLayoutManager(glm); binding.roomsRecyclerView.setAdapter(adapter); + + // 立即显示演示数据,提升用户体验 + adapter.submitList(buildDemoRooms(6)); + } - loadCoverAssets(); - + private void setupUI() { binding.swipeRefresh.setOnRefreshListener(this::fetchRooms); binding.roomsRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @@ -95,6 +107,9 @@ public class MainActivity extends AppCompatActivity { } }); + // 设置添加直播按钮点击事件 + binding.fabAddLive.setOnClickListener(v -> showCreateRoomDialog()); + BottomNavigationView bottomNavigation = binding.bottomNavInclude.bottomNavigation; bottomNavigation.setSelectedItemId(R.id.nav_home); bottomNavigation.setOnItemSelectedListener(item -> { @@ -151,9 +166,11 @@ public class MainActivity extends AppCompatActivity { stopPolling(); pollRunnable = () -> { fetchRooms(); - handler.postDelayed(pollRunnable, 5000); + // 增加轮询间隔,减少网络请求频率 + handler.postDelayed(pollRunnable, 10000); }; - handler.post(pollRunnable); + // 延迟首次请求,让界面先显示 + handler.postDelayed(pollRunnable, 1000); } private void stopPolling() { @@ -210,9 +227,13 @@ public class MainActivity extends AppCompatActivity { dialog.dismiss(); fetchRooms(); - Intent intent = new Intent(MainActivity.this, RoomDetailActivity.class); - intent.putExtra(RoomDetailActivity.EXTRA_ROOM_ID, room.getId()); - startActivity(intent); + // 显示推流信息 + showStreamInfo(room); + + // 可选:跳转到房间详情 + // Intent intent = new Intent(MainActivity.this, RoomDetailActivity.class); + // intent.putExtra(RoomDetailActivity.EXTRA_ROOM_ID, room.getId()); + // startActivity(intent); } @Override @@ -321,22 +342,31 @@ public class MainActivity extends AppCompatActivity { return list; } - private void loadCoverAssets() { - AssetManager am = getAssets(); - List files = new ArrayList<>(); - try { - String[] list = am.list("img"); - if (list != null) { - for (String f : list) { - if (f == null) continue; - String lower = f.toLowerCase(); - if (lower.endsWith(".png") || lower.endsWith(".jpg") || lower.endsWith(".jpeg") || lower.endsWith(".webp") || lower.endsWith(".gif")) { - files.add(f); + private void loadCoverAssetsAsync() { + // 在后台线程加载资源文件,避免阻塞UI + new Thread(() -> { + AssetManager am = getAssets(); + List files = new ArrayList<>(); + try { + String[] list = am.list("img"); + if (list != null) { + for (String f : list) { + if (f == null) continue; + String lower = f.toLowerCase(); + if (lower.endsWith(".png") || lower.endsWith(".jpg") || lower.endsWith(".jpeg") || lower.endsWith(".webp") || lower.endsWith(".gif")) { + files.add(f); + } } } + } catch (IOException ignored) { } - } catch (IOException ignored) { - } - adapter.setCoverAssetFiles(files); + + // 回到主线程更新UI + runOnUiThread(() -> { + if (adapter != null) { + adapter.setCoverAssetFiles(files); + } + }); + }).start(); } } diff --git a/android-app/app/src/main/java/com/example/livestreaming/PlayerActivity.java b/android-app/app/src/main/java/com/example/livestreaming/PlayerActivity.java index b5381907..1ed97574 100644 --- a/android-app/app/src/main/java/com/example/livestreaming/PlayerActivity.java +++ b/android-app/app/src/main/java/com/example/livestreaming/PlayerActivity.java @@ -38,8 +38,25 @@ public class PlayerActivity extends AppCompatActivity { triedAltUrl = false; - ExoPlayer exo = new ExoPlayer.Builder(this).build(); + // 创建低延迟播放器配置 + ExoPlayer exo = new ExoPlayer.Builder(this) + .setLoadControl(new androidx.media3.exoplayer.DefaultLoadControl.Builder() + // 减少缓冲区大小,降低延迟 + .setBufferDurationsMs( + 1000, // 最小缓冲时长 1秒 + 3000, // 最大缓冲时长 3秒 + 500, // 播放缓冲时长 0.5秒 + 1000 // 播放后缓冲时长 1秒 + ) + .build()) + .build(); + + // 设置播放器视图 binding.playerView.setPlayer(exo); + + // 启用低延迟模式 + binding.playerView.setUseController(true); + binding.playerView.setControllerAutoShow(false); String altUrl = getAltHlsUrl(url); exo.addListener(new Player.Listener() { diff --git a/android-app/app/src/main/java/com/example/livestreaming/net/ApiClient.java b/android-app/app/src/main/java/com/example/livestreaming/net/ApiClient.java index f9373573..3b8c3da9 100644 --- a/android-app/app/src/main/java/com/example/livestreaming/net/ApiClient.java +++ b/android-app/app/src/main/java/com/example/livestreaming/net/ApiClient.java @@ -25,6 +25,9 @@ public final class ApiClient { OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(logging) + .connectTimeout(5, java.util.concurrent.TimeUnit.SECONDS) + .readTimeout(10, java.util.concurrent.TimeUnit.SECONDS) + .writeTimeout(10, java.util.concurrent.TimeUnit.SECONDS) .build(); retrofit = new Retrofit.Builder() diff --git a/android-app/app/src/main/java/com/example/livestreaming/net/Room.java b/android-app/app/src/main/java/com/example/livestreaming/net/Room.java index e8817447..e7f48058 100644 --- a/android-app/app/src/main/java/com/example/livestreaming/net/Room.java +++ b/android-app/app/src/main/java/com/example/livestreaming/net/Room.java @@ -21,6 +21,9 @@ public class Room { @SerializedName("isLive") private boolean isLive; + @SerializedName("viewerCount") + private int viewerCount; + @SerializedName("streamUrls") private StreamUrls streamUrls; @@ -58,6 +61,10 @@ public class Room { this.streamUrls = streamUrls; } + public void setViewerCount(int viewerCount) { + this.viewerCount = viewerCount; + } + public String getId() { return id; } @@ -82,12 +89,17 @@ public class Room { return streamUrls; } + public int getViewerCount() { + return viewerCount; + } + @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Room)) return false; Room room = (Room) o; return isLive == room.isLive + && viewerCount == room.viewerCount && Objects.equals(id, room.id) && Objects.equals(title, room.title) && Objects.equals(streamerName, room.streamerName) @@ -97,6 +109,6 @@ public class Room { @Override public int hashCode() { - return Objects.hash(id, title, streamerName, streamKey, isLive, streamUrls); + return Objects.hash(id, title, streamerName, streamKey, isLive, viewerCount, streamUrls); } } diff --git a/android-app/app/src/main/res/layout/activity_main.xml b/android-app/app/src/main/res/layout/activity_main.xml index 3a6a56c6..585ab011 100644 --- a/android-app/app/src/main/res/layout/activity_main.xml +++ b/android-app/app/src/main/res/layout/activity_main.xml @@ -212,6 +212,17 @@ android:visibility="gone" /> + +