Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" /> <!-- Android 14+ 前台服务类型 -->
<uses-permission android:name="android.permission.CAMERA" /> <!-- 用于获取wifi状态 -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <!-- 使用系统自带下载器 -->
<uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER" /> <!-- 自动安装apk -->
Expand Down
12 changes: 12 additions & 0 deletions app/src/main/java/com/hippo/ehviewer/EhApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
import javax.net.ssl.X509TrustManager;

import okhttp3.Cache;
import okhttp3.ConnectionPool;
import okhttp3.ConnectionSpec;
import okhttp3.OkHttpClient;
import okhttp3.Response;
Expand Down Expand Up @@ -377,13 +378,24 @@ public static OkHttpClient getOkHttpClient(@NonNull Context context) {
if (application.mOkHttpClient == null) {
// Dispatcher dispatcher = new Dispatcher();
// dispatcher.setMaxRequestsPerHost(4);

// 创建优化的连接池 - 针对后台下载优化
// 最多保持 10 个连接,每个连接保持 5 分钟,适合后台长时间下载
ConnectionPool connectionPool = new ConnectionPool(
10, // 最大空闲连接数
5, // 连接保活时间(分钟)
TimeUnit.MINUTES
);

OkHttpClient.Builder builder = new OkHttpClient.Builder()
.followRedirects(true)
.followSslRedirects(true)
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
// .callTimeout(10, TimeUnit.SECONDS)
.connectionPool(connectionPool) // 添加优化的连接池
.retryOnConnectionFailure(true) // 连接失败时重试
.cookieJar(getEhCookieStore(application))
.cache(getOkHttpCache(application))
// .hostnameVerifier((hostname, session) -> true)
Expand Down
189 changes: 182 additions & 7 deletions app/src/main/java/com/hippo/ehviewer/download/DownloadService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,18 @@ import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.Context
import android.content.Intent
import android.content.pm.ServiceInfo
import android.graphics.BitmapFactory
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import android.os.Build
import android.os.Bundle
import android.os.IBinder
import android.os.PowerManager
import android.os.SystemClock
import android.util.Log
import androidx.annotation.IntDef
Expand All @@ -37,6 +43,7 @@ import com.hippo.ehviewer.client.data.GalleryInfo
import com.hippo.ehviewer.dao.DownloadInfo
import com.hippo.ehviewer.ui.MainActivity
import com.hippo.ehviewer.ui.scene.download.DownloadsScene
import com.hippo.ehviewer.util.MiuiOptimizationHelper
import com.hippo.scene.StageActivity
import com.hippo.util.ReadableTime
import com.hippo.lib.yorozuya.FileUtils
Expand All @@ -56,28 +63,64 @@ class DownloadService : Service(), DownloadManager.DownloadListener {
private var mDownloadedDelay: NotificationDelay? = null
private var m509Delay: NotificationDelay? = null

// WakeLock 用于防止CPU被限制(针对后台下载优化)
private var mWakeLock: PowerManager.WakeLock? = null

// 网络回调用于监听网络状态(针对小米系统优化)
private var mNetworkCallback: ConnectivityManager.NetworkCallback? = null
private var mConnectivityManager: ConnectivityManager? = null

private var CHANNEL_ID: String? = null

override fun onCreate() {
super.onCreate()

// 记录设备信息用于调试
MiuiOptimizationHelper.logDeviceInfo()

CHANNEL_ID = "$packageName.download"
mNotifyManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager

// 根据设备类型动态调整通知优先级
val notificationImportance = MiuiOptimizationHelper.getRecommendedNotificationImportance()

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mNotifyManager!!.createNotificationChannel(
NotificationChannel(
CHANNEL_ID, getString(R.string.download_service),
NotificationManager.IMPORTANCE_LOW
)
)
}
val channel = NotificationChannel(
CHANNEL_ID,
getString(R.string.download_service),
notificationImportance
).apply {
// 针对高版本Android和小米系统的优化
if (MiuiOptimizationHelper.needsAggressiveOptimization()) {
setShowBadge(true)
enableVibration(false) // 避免频繁振动
setSound(null, null) // 避免频繁提示音
description = "后台下载服务 - 请勿限制后台运行"
}
}
mNotifyManager!!.createNotificationChannel(channel)

Log.i(TAG, "Created notification channel with importance: $notificationImportance")
}

// 初始化 WakeLock(用于防止CPU被限制)
initWakeLock()

// 初始化网络监听(针对小米系统优化)
initNetworkCallback()

mDownloadManager = EhApplication.getDownloadManager(applicationContext)
mDownloadManager!!.setDownloadListener(this)
}

override fun onDestroy() {
super.onDestroy()

// 释放 WakeLock
releaseWakeLock()

// 释放网络监听
releaseNetworkCallback()

mNotifyManager = null
if (mDownloadManager != null) {
Expand Down Expand Up @@ -276,6 +319,9 @@ class DownloadService : Service(), DownloadManager.DownloadListener {
if (mNotifyManager == null) {
return
}

// 获取 WakeLock 防止后台下载被限制
acquireWakeLock()

ensureDownloadingBuilder()

Expand Down Expand Up @@ -464,9 +510,137 @@ class DownloadService : Service(), DownloadManager.DownloadListener {
private fun checkStopSelf() {
if (mDownloadManager == null || mDownloadManager!!.isIdle) {
// stopForeground(true);
// 释放 WakeLock
releaseWakeLock()
stopSelf()
}
}

/**
* 初始化 WakeLock
* 用于防止后台下载时CPU被限制,特别是针对Android 14+和小米系统
*/
@SuppressLint("WakelockTimeout")
private fun initWakeLock() {
try {
val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager

// 使用 PARTIAL_WAKE_LOCK,允许CPU继续运行但屏幕可以关闭
mWakeLock = powerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK,
"EhViewer:DownloadWakeLock"
).apply {
// 设置为可计数,避免重复释放导致崩溃
setReferenceCounted(false)
}

Log.i(TAG, "WakeLock initialized successfully")
} catch (e: Exception) {
Log.e(TAG, "Failed to initialize WakeLock", e)
}
}

/**
* 获取 WakeLock
*/
@SuppressLint("WakelockTimeout")
private fun acquireWakeLock() {
try {
if (mWakeLock != null && !mWakeLock!!.isHeld) {
// 针对小米系统和Android 14+,使用WakeLock防止后台限制
if (MiuiOptimizationHelper.needsAggressiveOptimization()) {
mWakeLock!!.acquire()
Log.i(TAG, "WakeLock acquired (aggressive mode)")
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
// Android 14+ 也需要WakeLock
mWakeLock!!.acquire()
Log.i(TAG, "WakeLock acquired (Android 14+)")
}
}
} catch (e: Exception) {
Log.e(TAG, "Failed to acquire WakeLock", e)
}
}

/**
* 释放 WakeLock
*/
private fun releaseWakeLock() {
try {
if (mWakeLock != null && mWakeLock!!.isHeld) {
mWakeLock!!.release()
Log.i(TAG, "WakeLock released")
}
} catch (e: Exception) {
Log.e(TAG, "Failed to release WakeLock", e)
}
}

/**
* 初始化网络回调
* 用于监听网络状态,针对小米系统优化
*/
private fun initNetworkCallback() {
if (!MiuiOptimizationHelper.needsMiuiOptimization()) {
return
}

try {
mConnectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
mNetworkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
super.onAvailable(network)
Log.d(TAG, "Network available: $network")
}

override fun onLost(network: Network) {
super.onLost(network)
Log.d(TAG, "Network lost: $network")
}

override fun onCapabilitiesChanged(
network: Network,
networkCapabilities: NetworkCapabilities
) {
super.onCapabilitiesChanged(network, networkCapabilities)
// 监听网络能力变化
val isUnmetered = networkCapabilities.hasCapability(
NetworkCapabilities.NET_CAPABILITY_NOT_METERED
)
Log.d(TAG, "Network capabilities changed, unmetered: $isUnmetered")
}
}

val networkRequest = NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
.build()

mConnectivityManager?.registerNetworkCallback(networkRequest, mNetworkCallback!!)
Log.i(TAG, "Network callback registered (MIUI optimization)")
}
} catch (e: Exception) {
Log.e(TAG, "Failed to initialize network callback", e)
}
}

/**
* 释放网络回调
*/
private fun releaseNetworkCallback() {
try {
if (mNetworkCallback != null && mConnectivityManager != null) {
mConnectivityManager?.unregisterNetworkCallback(mNetworkCallback!!)
mNetworkCallback = null
mConnectivityManager = null
Log.i(TAG, "Network callback unregistered")
}
} catch (e: Exception) {
Log.e(TAG, "Failed to unregister network callback", e)
}
}

// TODO Include all notification in one delay
// Avoid frequent notification
Expand Down Expand Up @@ -596,6 +770,7 @@ class DownloadService : Service(), DownloadManager.DownloadListener {
const val KEY_GID: String = "gid"
const val KEY_GID_LIST: String = "gid_list"

private const val TAG = "DownloadService"
private const val ID_DOWNLOADING = 1
private const val ID_DOWNLOADED = 2
private const val ID_509 = 3
Expand Down
Loading
Loading