15 changed files with 3286 additions and 12 deletions
-
3.vscode/settings.json
-
34app/build.gradle
-
20app/src/main/AndroidManifest.xml
-
45app/src/main/java/com/ouxuan/oxface/MainActivity.java
-
282app/src/main/java/com/ouxuan/oxface/OxFaceApplication.java
-
90app/src/main/java/com/ouxuan/oxface/network/NetworkManager.java
-
328app/src/main/java/com/ouxuan/oxface/network/NetworkStabilityManager.java
-
294app/src/main/java/com/ouxuan/oxface/utils/GlobalExceptionHandler.java
-
406app/src/main/java/com/ouxuan/oxface/utils/HealthMonitor.java
-
241app/src/main/java/com/ouxuan/oxface/utils/KeepAliveManager.java
-
375app/src/main/java/com/ouxuan/oxface/utils/LogManager.java
-
533app/src/main/java/com/ouxuan/oxface/utils/MaintenanceScheduler.java
-
245app/src/main/java/com/ouxuan/oxface/utils/MemoryManager.java
-
382app/src/main/java/com/ouxuan/oxface/utils/UtilCodeHelper.java
-
16gradle.properties
@ -0,0 +1,3 @@ |
|||||
|
{ |
||||
|
"java.configuration.updateBuildConfiguration": "automatic" |
||||
|
} |
@ -0,0 +1,328 @@ |
|||||
|
package com.ouxuan.oxface.network; |
||||
|
|
||||
|
import android.content.Context; |
||||
|
import android.net.ConnectivityManager; |
||||
|
import android.net.Network; |
||||
|
import android.net.NetworkCapabilities; |
||||
|
import android.net.NetworkRequest; |
||||
|
import android.util.Log; |
||||
|
|
||||
|
import java.io.IOException; |
||||
|
import java.util.concurrent.TimeUnit; |
||||
|
|
||||
|
import okhttp3.Interceptor; |
||||
|
import okhttp3.OkHttpClient; |
||||
|
import okhttp3.Request; |
||||
|
import okhttp3.Response; |
||||
|
|
||||
|
/** |
||||
|
* 网络稳定性管理器 |
||||
|
* 用于24小时无人值守环境下的网络连接稳定性保障 |
||||
|
*/ |
||||
|
public class NetworkStabilityManager { |
||||
|
|
||||
|
private static final String TAG = "NetworkStabilityManager"; |
||||
|
private static final int MAX_RETRY_COUNT = 3; |
||||
|
private static final int RETRY_DELAY_MS = 5000; |
||||
|
private static final int CONNECT_TIMEOUT_SECONDS = 30; |
||||
|
private static final int READ_TIMEOUT_SECONDS = 60; |
||||
|
private static final int WRITE_TIMEOUT_SECONDS = 60; |
||||
|
|
||||
|
private static NetworkStabilityManager instance; |
||||
|
private Context context; |
||||
|
private ConnectivityManager connectivityManager; |
||||
|
private ConnectivityManager.NetworkCallback networkCallback; |
||||
|
private boolean isMonitoring = false; |
||||
|
private NetworkStateListener networkStateListener; |
||||
|
|
||||
|
public interface NetworkStateListener { |
||||
|
void onNetworkAvailable(); |
||||
|
void onNetworkLost(); |
||||
|
void onNetworkQualityChanged(NetworkQuality quality); |
||||
|
} |
||||
|
|
||||
|
public enum NetworkQuality { |
||||
|
EXCELLENT, GOOD, POOR, VERY_POOR |
||||
|
} |
||||
|
|
||||
|
private NetworkStabilityManager(Context context) { |
||||
|
this.context = context.getApplicationContext(); |
||||
|
this.connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); |
||||
|
} |
||||
|
|
||||
|
public static synchronized NetworkStabilityManager getInstance(Context context) { |
||||
|
if (instance == null) { |
||||
|
instance = new NetworkStabilityManager(context); |
||||
|
} |
||||
|
return instance; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取增强的OkHttpClient配置 |
||||
|
*/ |
||||
|
public OkHttpClient getEnhancedOkHttpClient() { |
||||
|
return new OkHttpClient.Builder() |
||||
|
.connectTimeout(CONNECT_TIMEOUT_SECONDS, TimeUnit.SECONDS) |
||||
|
.readTimeout(READ_TIMEOUT_SECONDS, TimeUnit.SECONDS) |
||||
|
.writeTimeout(WRITE_TIMEOUT_SECONDS, TimeUnit.SECONDS) |
||||
|
.retryOnConnectionFailure(true) |
||||
|
.addInterceptor(new RetryInterceptor(MAX_RETRY_COUNT)) |
||||
|
.addInterceptor(new NetworkQualityInterceptor()) |
||||
|
.build(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 开始网络状态监控 |
||||
|
*/ |
||||
|
public void startNetworkMonitoring(NetworkStateListener listener) { |
||||
|
if (isMonitoring) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
this.networkStateListener = listener; |
||||
|
isMonitoring = true; |
||||
|
|
||||
|
if (connectivityManager != null) { |
||||
|
networkCallback = new ConnectivityManager.NetworkCallback() { |
||||
|
@Override |
||||
|
public void onAvailable(Network network) { |
||||
|
super.onAvailable(network); |
||||
|
Log.i(TAG, "网络连接恢复: " + network.toString()); |
||||
|
if (networkStateListener != null) { |
||||
|
networkStateListener.onNetworkAvailable(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onLost(Network network) { |
||||
|
super.onLost(network); |
||||
|
Log.w(TAG, "网络连接丢失: " + network.toString()); |
||||
|
if (networkStateListener != null) { |
||||
|
networkStateListener.onNetworkLost(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) { |
||||
|
super.onCapabilitiesChanged(network, networkCapabilities); |
||||
|
|
||||
|
// 检查网络质量 |
||||
|
NetworkQuality quality = evaluateNetworkQuality(networkCapabilities); |
||||
|
Log.d(TAG, "网络质量变化: " + quality); |
||||
|
|
||||
|
if (networkStateListener != null) { |
||||
|
networkStateListener.onNetworkQualityChanged(quality); |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
NetworkRequest.Builder builder = new NetworkRequest.Builder() |
||||
|
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) |
||||
|
.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED); |
||||
|
|
||||
|
connectivityManager.registerNetworkCallback(builder.build(), networkCallback); |
||||
|
Log.i(TAG, "网络状态监控已启动"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 停止网络状态监控 |
||||
|
*/ |
||||
|
public void stopNetworkMonitoring() { |
||||
|
if (isMonitoring && connectivityManager != null && networkCallback != null) { |
||||
|
try { |
||||
|
connectivityManager.unregisterNetworkCallback(networkCallback); |
||||
|
isMonitoring = false; |
||||
|
Log.i(TAG, "网络状态监控已停止"); |
||||
|
} catch (Exception e) { |
||||
|
Log.e(TAG, "停止网络监控失败", e); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 检查网络是否可用 |
||||
|
*/ |
||||
|
public boolean isNetworkAvailable() { |
||||
|
if (connectivityManager == null) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
Network activeNetwork = connectivityManager.getActiveNetwork(); |
||||
|
if (activeNetwork == null) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
NetworkCapabilities capabilities = connectivityManager.getNetworkCapabilities(activeNetwork); |
||||
|
return capabilities != null && |
||||
|
capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && |
||||
|
capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 评估网络质量 |
||||
|
*/ |
||||
|
private NetworkQuality evaluateNetworkQuality(NetworkCapabilities capabilities) { |
||||
|
if (capabilities == null) { |
||||
|
return NetworkQuality.VERY_POOR; |
||||
|
} |
||||
|
|
||||
|
// 基于网络类型和能力评估质量 |
||||
|
if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) || |
||||
|
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { |
||||
|
|
||||
|
if (capabilities.getLinkDownstreamBandwidthKbps() > 10000) { // > 10Mbps |
||||
|
return NetworkQuality.EXCELLENT; |
||||
|
} else if (capabilities.getLinkDownstreamBandwidthKbps() > 5000) { // > 5Mbps |
||||
|
return NetworkQuality.GOOD; |
||||
|
} else { |
||||
|
return NetworkQuality.POOR; |
||||
|
} |
||||
|
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { |
||||
|
return NetworkQuality.GOOD; |
||||
|
} else { |
||||
|
return NetworkQuality.POOR; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取网络类型描述 |
||||
|
*/ |
||||
|
public String getNetworkTypeDescription() { |
||||
|
if (!isNetworkAvailable()) { |
||||
|
return "无网络连接"; |
||||
|
} |
||||
|
|
||||
|
Network activeNetwork = connectivityManager.getActiveNetwork(); |
||||
|
NetworkCapabilities capabilities = connectivityManager.getNetworkCapabilities(activeNetwork); |
||||
|
|
||||
|
if (capabilities == null) { |
||||
|
return "未知网络"; |
||||
|
} |
||||
|
|
||||
|
if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) { |
||||
|
return "以太网"; |
||||
|
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { |
||||
|
return "WiFi"; |
||||
|
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { |
||||
|
return "移动网络"; |
||||
|
} else { |
||||
|
return "其他网络"; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 重试拦截器 |
||||
|
*/ |
||||
|
public static class RetryInterceptor implements Interceptor { |
||||
|
private int maxRetryCount; |
||||
|
|
||||
|
public RetryInterceptor(int maxRetryCount) { |
||||
|
this.maxRetryCount = maxRetryCount; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Response intercept(Chain chain) throws IOException { |
||||
|
Request request = chain.request(); |
||||
|
Response response = null; |
||||
|
IOException lastException = null; |
||||
|
|
||||
|
for (int retryCount = 0; retryCount <= maxRetryCount; retryCount++) { |
||||
|
try { |
||||
|
response = chain.proceed(request); |
||||
|
|
||||
|
// 如果响应成功,直接返回 |
||||
|
if (response.isSuccessful()) { |
||||
|
if (retryCount > 0) { |
||||
|
Log.i(TAG, "重试成功,重试次数: " + retryCount); |
||||
|
} |
||||
|
return response; |
||||
|
} |
||||
|
|
||||
|
// 如果是服务器错误且不是最后一次重试,关闭响应并重试 |
||||
|
if (retryCount < maxRetryCount && response.code() >= 500) { |
||||
|
response.close(); |
||||
|
Log.w(TAG, "服务器错误 " + response.code() + ",准备重试 " + (retryCount + 1)); |
||||
|
|
||||
|
// 等待一段时间再重试 |
||||
|
try { |
||||
|
Thread.sleep(RETRY_DELAY_MS * (retryCount + 1)); |
||||
|
} catch (InterruptedException e) { |
||||
|
Thread.currentThread().interrupt(); |
||||
|
throw new IOException("重试被中断", e); |
||||
|
} |
||||
|
} else { |
||||
|
return response; |
||||
|
} |
||||
|
|
||||
|
} catch (IOException e) { |
||||
|
lastException = e; |
||||
|
|
||||
|
if (retryCount < maxRetryCount) { |
||||
|
Log.w(TAG, "网络请求失败,准备重试 " + (retryCount + 1) + ": " + e.getMessage()); |
||||
|
|
||||
|
// 等待一段时间再重试 |
||||
|
try { |
||||
|
Thread.sleep(RETRY_DELAY_MS * (retryCount + 1)); |
||||
|
} catch (InterruptedException ie) { |
||||
|
Thread.currentThread().interrupt(); |
||||
|
throw new IOException("重试被中断", ie); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 所有重试都失败了 |
||||
|
Log.e(TAG, "网络请求重试 " + maxRetryCount + " 次后仍然失败"); |
||||
|
if (lastException != null) { |
||||
|
throw lastException; |
||||
|
} else { |
||||
|
return response; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 网络质量监控拦截器 |
||||
|
*/ |
||||
|
public class NetworkQualityInterceptor implements Interceptor { |
||||
|
@Override |
||||
|
public Response intercept(Chain chain) throws IOException { |
||||
|
long startTime = System.currentTimeMillis(); |
||||
|
|
||||
|
try { |
||||
|
Response response = chain.proceed(chain.request()); |
||||
|
long endTime = System.currentTimeMillis(); |
||||
|
long responseTime = endTime - startTime; |
||||
|
|
||||
|
// 记录网络质量信息 |
||||
|
logNetworkQuality(responseTime, true); |
||||
|
|
||||
|
return response; |
||||
|
|
||||
|
} catch (IOException e) { |
||||
|
long endTime = System.currentTimeMillis(); |
||||
|
long responseTime = endTime - startTime; |
||||
|
|
||||
|
// 记录网络质量信息 |
||||
|
logNetworkQuality(responseTime, false); |
||||
|
|
||||
|
throw e; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void logNetworkQuality(long responseTime, boolean success) { |
||||
|
String status = success ? "成功" : "失败"; |
||||
|
|
||||
|
if (responseTime > 30000) { // 超过30秒 |
||||
|
Log.e(TAG, String.format("网络响应极慢 %s: %dms", status, responseTime)); |
||||
|
} else if (responseTime > 10000) { // 超过10秒 |
||||
|
Log.w(TAG, String.format("网络响应缓慢 %s: %dms", status, responseTime)); |
||||
|
} else if (responseTime > 3000) { // 超过3秒 |
||||
|
Log.i(TAG, String.format("网络响应较慢 %s: %dms", status, responseTime)); |
||||
|
} else { |
||||
|
Log.d(TAG, String.format("网络响应正常 %s: %dms", status, responseTime)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,294 @@ |
|||||
|
package com.ouxuan.oxface.utils; |
||||
|
|
||||
|
import android.app.AlarmManager; |
||||
|
import android.app.PendingIntent; |
||||
|
import android.content.Context; |
||||
|
import android.content.Intent; |
||||
|
import android.util.Log; |
||||
|
|
||||
|
import com.ouxuan.oxface.MainActivity; |
||||
|
import com.ouxuan.oxface.network.NetworkStabilityManager; |
||||
|
|
||||
|
import java.io.File; |
||||
|
import java.io.FileWriter; |
||||
|
import java.io.IOException; |
||||
|
import java.io.PrintWriter; |
||||
|
import java.text.SimpleDateFormat; |
||||
|
import java.util.Date; |
||||
|
import java.util.Locale; |
||||
|
|
||||
|
/** |
||||
|
* 全局异常处理器 |
||||
|
* 用于24小时无人值守环境下的异常捕获和应用自动恢复 |
||||
|
*/ |
||||
|
public class GlobalExceptionHandler implements Thread.UncaughtExceptionHandler { |
||||
|
|
||||
|
private static final String TAG = "GlobalExceptionHandler"; |
||||
|
private static final String CRASH_LOG_DIR = "crash_logs"; |
||||
|
private static final String CRASH_LOG_PREFIX = "crash_"; |
||||
|
private static final int MAX_CRASH_LOGS = 10; // 最多保留10个崩溃日志文件 |
||||
|
private static final long RESTART_DELAY = 3000; // 重启延迟3秒 |
||||
|
|
||||
|
private Thread.UncaughtExceptionHandler defaultHandler; |
||||
|
private Context context; |
||||
|
private boolean enableAutoRestart; |
||||
|
|
||||
|
public GlobalExceptionHandler(Context context) { |
||||
|
this(context, true); |
||||
|
} |
||||
|
|
||||
|
public GlobalExceptionHandler(Context context, boolean enableAutoRestart) { |
||||
|
this.context = context.getApplicationContext(); |
||||
|
this.enableAutoRestart = enableAutoRestart; |
||||
|
this.defaultHandler = Thread.getDefaultUncaughtExceptionHandler(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 初始化全局异常处理器 |
||||
|
*/ |
||||
|
public void install() { |
||||
|
Thread.setDefaultUncaughtExceptionHandler(this); |
||||
|
Log.i(TAG, "全局异常处理器已安装,自动重启: " + enableAutoRestart); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void uncaughtException(Thread thread, Throwable throwable) { |
||||
|
try { |
||||
|
Log.e(TAG, "捕获到未处理异常", throwable); |
||||
|
|
||||
|
// 保存异常信息到文件 |
||||
|
saveExceptionToFile(throwable, thread); |
||||
|
|
||||
|
// 记录系统状态 |
||||
|
recordSystemState(); |
||||
|
|
||||
|
// 如果启用自动重启,尝试重启应用 |
||||
|
if (enableAutoRestart) { |
||||
|
restartApplication(); |
||||
|
} |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
Log.e(TAG, "处理异常时发生错误", e); |
||||
|
} finally { |
||||
|
// 调用默认异常处理器 |
||||
|
if (defaultHandler != null) { |
||||
|
defaultHandler.uncaughtException(thread, throwable); |
||||
|
} else { |
||||
|
System.exit(1); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 保存异常信息到文件 |
||||
|
*/ |
||||
|
private void saveExceptionToFile(Throwable throwable, Thread thread) { |
||||
|
try { |
||||
|
// 创建崩溃日志目录 |
||||
|
File crashLogDir = new File(context.getFilesDir(), CRASH_LOG_DIR); |
||||
|
if (!crashLogDir.exists()) { |
||||
|
crashLogDir.mkdirs(); |
||||
|
} |
||||
|
|
||||
|
// 生成日志文件名 |
||||
|
String timestamp = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss", Locale.getDefault()).format(new Date()); |
||||
|
String fileName = CRASH_LOG_PREFIX + timestamp + ".log"; |
||||
|
File crashLogFile = new File(crashLogDir, fileName); |
||||
|
|
||||
|
// 写入异常信息 |
||||
|
FileWriter fileWriter = new FileWriter(crashLogFile); |
||||
|
PrintWriter printWriter = new PrintWriter(fileWriter); |
||||
|
|
||||
|
// 写入基本信息 |
||||
|
printWriter.println("=== 应用崩溃日志 ==="); |
||||
|
printWriter.println("时间: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(new Date())); |
||||
|
printWriter.println("线程: " + thread.getName() + " (ID: " + thread.getId() + ")"); |
||||
|
printWriter.println("应用包名: " + context.getPackageName()); |
||||
|
printWriter.println(); |
||||
|
|
||||
|
// 写入设备信息 |
||||
|
printWriter.println("=== 设备信息 ==="); |
||||
|
printWriter.println("设备型号: " + android.os.Build.MODEL); |
||||
|
printWriter.println("Android版本: " + android.os.Build.VERSION.RELEASE); |
||||
|
printWriter.println("API级别: " + android.os.Build.VERSION.SDK_INT); |
||||
|
printWriter.println("制造商: " + android.os.Build.MANUFACTURER); |
||||
|
printWriter.println(); |
||||
|
|
||||
|
// 写入内存信息 |
||||
|
printWriter.println("=== 内存信息 ==="); |
||||
|
Runtime runtime = Runtime.getRuntime(); |
||||
|
long maxMemory = runtime.maxMemory(); |
||||
|
long totalMemory = runtime.totalMemory(); |
||||
|
long freeMemory = runtime.freeMemory(); |
||||
|
long usedMemory = totalMemory - freeMemory; |
||||
|
|
||||
|
printWriter.println("最大内存: " + (maxMemory / 1024 / 1024) + "MB"); |
||||
|
printWriter.println("已分配内存: " + (totalMemory / 1024 / 1024) + "MB"); |
||||
|
printWriter.println("空闲内存: " + (freeMemory / 1024 / 1024) + "MB"); |
||||
|
printWriter.println("已使用内存: " + (usedMemory / 1024 / 1024) + "MB"); |
||||
|
printWriter.println("内存使用率: " + String.format("%.1f%%", (float) usedMemory / maxMemory * 100)); |
||||
|
printWriter.println(); |
||||
|
|
||||
|
// 写入异常堆栈 |
||||
|
printWriter.println("=== 异常堆栈 ==="); |
||||
|
throwable.printStackTrace(printWriter); |
||||
|
|
||||
|
printWriter.close(); |
||||
|
fileWriter.close(); |
||||
|
|
||||
|
Log.i(TAG, "崩溃日志已保存: " + crashLogFile.getAbsolutePath()); |
||||
|
|
||||
|
// 清理旧的日志文件 |
||||
|
cleanupOldCrashLogs(crashLogDir); |
||||
|
|
||||
|
} catch (IOException e) { |
||||
|
Log.e(TAG, "保存崩溃日志失败", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 记录系统状态 |
||||
|
*/ |
||||
|
private void recordSystemState() { |
||||
|
try { |
||||
|
Log.i(TAG, "=== 崩溃时系统状态 ==="); |
||||
|
|
||||
|
// 记录内存使用情况 |
||||
|
MemoryManager memoryManager = MemoryManager.getInstance(context); |
||||
|
float memoryUsage = memoryManager.getCurrentMemoryUsage(); |
||||
|
Log.i(TAG, "内存使用率: " + String.format("%.1f%%", memoryUsage * 100)); |
||||
|
|
||||
|
// 记录网络状态 |
||||
|
NetworkStabilityManager networkManager = NetworkStabilityManager.getInstance(context); |
||||
|
boolean networkAvailable = networkManager.isNetworkAvailable(); |
||||
|
String networkType = networkManager.getNetworkTypeDescription(); |
||||
|
Log.i(TAG, "网络状态: " + (networkAvailable ? "可用" : "不可用") + " (" + networkType + ")"); |
||||
|
|
||||
|
// 记录线程信息 |
||||
|
Thread currentThread = Thread.currentThread(); |
||||
|
ThreadGroup threadGroup = currentThread.getThreadGroup(); |
||||
|
if (threadGroup != null) { |
||||
|
Log.i(TAG, "活跃线程数: " + threadGroup.activeCount()); |
||||
|
} |
||||
|
|
||||
|
Log.i(TAG, "========================"); |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
Log.e(TAG, "记录系统状态失败", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 重启应用程序 |
||||
|
*/ |
||||
|
private void restartApplication() { |
||||
|
try { |
||||
|
Log.w(TAG, "准备重启应用程序..."); |
||||
|
|
||||
|
// 创建重启Intent |
||||
|
Intent restartIntent = new Intent(context, MainActivity.class); |
||||
|
restartIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); |
||||
|
|
||||
|
// 使用AlarmManager延迟重启,确保当前进程完全结束 |
||||
|
PendingIntent pendingIntent = PendingIntent.getActivity( |
||||
|
context, |
||||
|
0, |
||||
|
restartIntent, |
||||
|
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE |
||||
|
); |
||||
|
|
||||
|
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); |
||||
|
if (alarmManager != null) { |
||||
|
alarmManager.setExact( |
||||
|
AlarmManager.RTC_WAKEUP, |
||||
|
System.currentTimeMillis() + RESTART_DELAY, |
||||
|
pendingIntent |
||||
|
); |
||||
|
|
||||
|
Log.i(TAG, "应用程序将在 " + RESTART_DELAY + "ms 后重启"); |
||||
|
} else { |
||||
|
// 如果AlarmManager不可用,直接启动 |
||||
|
context.startActivity(restartIntent); |
||||
|
} |
||||
|
|
||||
|
// 退出当前进程 |
||||
|
System.exit(0); |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
Log.e(TAG, "重启应用程序失败", e); |
||||
|
// 强制退出 |
||||
|
System.exit(1); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 清理旧的崩溃日志文件 |
||||
|
*/ |
||||
|
private void cleanupOldCrashLogs(File crashLogDir) { |
||||
|
try { |
||||
|
File[] logFiles = crashLogDir.listFiles(); |
||||
|
if (logFiles == null || logFiles.length <= MAX_CRASH_LOGS) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// 按修改时间排序 |
||||
|
java.util.Arrays.sort(logFiles, new java.util.Comparator<File>() { |
||||
|
@Override |
||||
|
public int compare(File f1, File f2) { |
||||
|
return Long.compare(f1.lastModified(), f2.lastModified()); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
// 删除最旧的文件 |
||||
|
int filesToDelete = logFiles.length - MAX_CRASH_LOGS; |
||||
|
for (int i = 0; i < filesToDelete; i++) { |
||||
|
if (logFiles[i].delete()) { |
||||
|
Log.d(TAG, "已删除旧的崩溃日志: " + logFiles[i].getName()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
Log.e(TAG, "清理崩溃日志失败", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 设置是否启用自动重启 |
||||
|
*/ |
||||
|
public void setAutoRestartEnabled(boolean enabled) { |
||||
|
this.enableAutoRestart = enabled; |
||||
|
Log.i(TAG, "自动重启设置: " + enabled); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取崩溃日志目录 |
||||
|
*/ |
||||
|
public File getCrashLogDirectory() { |
||||
|
return new File(context.getFilesDir(), CRASH_LOG_DIR); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取最新的崩溃日志文件 |
||||
|
*/ |
||||
|
public File getLatestCrashLog() { |
||||
|
File crashLogDir = getCrashLogDirectory(); |
||||
|
if (!crashLogDir.exists()) { |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
File[] logFiles = crashLogDir.listFiles(); |
||||
|
if (logFiles == null || logFiles.length == 0) { |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
// 找到最新的文件 |
||||
|
File latestFile = logFiles[0]; |
||||
|
for (File file : logFiles) { |
||||
|
if (file.lastModified() > latestFile.lastModified()) { |
||||
|
latestFile = file; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return latestFile; |
||||
|
} |
||||
|
} |
@ -0,0 +1,406 @@ |
|||||
|
package com.ouxuan.oxface.utils; |
||||
|
|
||||
|
import android.content.Context; |
||||
|
import android.os.Handler; |
||||
|
import android.os.Looper; |
||||
|
import android.util.Log; |
||||
|
|
||||
|
import com.ouxuan.oxface.network.NetworkStabilityManager; |
||||
|
import com.ouxuan.oxface.utils.UtilCodeHelper; |
||||
|
|
||||
|
/** |
||||
|
* 健康监控器 |
||||
|
* 用于24小时无人值守环境下的系统健康状态监控 |
||||
|
*/ |
||||
|
public class HealthMonitor { |
||||
|
|
||||
|
private static final String TAG = "HealthMonitor"; |
||||
|
private static final long HEALTH_CHECK_INTERVAL = 60 * 1000; // 1分钟检查间隔 |
||||
|
private static final long CRITICAL_CHECK_INTERVAL = 10 * 1000; // 10秒紧急检查间隔 |
||||
|
private static final int MAX_CONSECUTIVE_FAILURES = 3; // 最大连续失败次数 |
||||
|
|
||||
|
private static HealthMonitor instance; |
||||
|
private Context context; |
||||
|
private Handler healthHandler; |
||||
|
private boolean isMonitoring = false; |
||||
|
private HealthStatusListener healthStatusListener; |
||||
|
|
||||
|
// 健康状态计数器 |
||||
|
private int consecutiveMemoryFailures = 0; |
||||
|
private int consecutiveNetworkFailures = 0; |
||||
|
private long lastHealthCheckTime = 0; |
||||
|
private boolean isSystemHealthy = true; |
||||
|
|
||||
|
public interface HealthStatusListener { |
||||
|
void onHealthStatusChanged(HealthStatus status); |
||||
|
void onCriticalIssueDetected(String issue); |
||||
|
void onSystemRecovered(String recoveryAction); |
||||
|
} |
||||
|
|
||||
|
public enum HealthStatus { |
||||
|
HEALTHY, // 健康 |
||||
|
WARNING, // 警告 |
||||
|
CRITICAL, // 严重 |
||||
|
SYSTEM_ERROR // 系统错误 |
||||
|
} |
||||
|
|
||||
|
public static class HealthReport { |
||||
|
public HealthStatus overallStatus; |
||||
|
public float memoryUsage; |
||||
|
public boolean networkAvailable; |
||||
|
public String networkType; |
||||
|
public boolean keepAliveActive; |
||||
|
public long uptime; |
||||
|
public int memoryFailures; |
||||
|
public int networkFailures; |
||||
|
public String lastIssue; |
||||
|
public String recommendations; |
||||
|
} |
||||
|
|
||||
|
private HealthMonitor(Context context) { |
||||
|
this.context = context.getApplicationContext(); |
||||
|
this.healthHandler = new Handler(Looper.getMainLooper()); |
||||
|
} |
||||
|
|
||||
|
public static synchronized HealthMonitor getInstance(Context context) { |
||||
|
if (instance == null) { |
||||
|
instance = new HealthMonitor(context); |
||||
|
} |
||||
|
return instance; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 开始健康检查 |
||||
|
*/ |
||||
|
public void startHealthCheck(HealthStatusListener listener) { |
||||
|
if (isMonitoring) { |
||||
|
Log.i(TAG, "健康监控已在运行中"); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
this.healthStatusListener = listener; |
||||
|
isMonitoring = true; |
||||
|
|
||||
|
Log.i(TAG, "健康监控已启动"); |
||||
|
LogManager.logOperation("HealthMonitor", "健康监控启动"); |
||||
|
|
||||
|
// 启动定期健康检查 |
||||
|
scheduleHealthCheck(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 停止健康检查 |
||||
|
*/ |
||||
|
public void stopHealthCheck() { |
||||
|
if (!isMonitoring) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
isMonitoring = false; |
||||
|
|
||||
|
if (healthHandler != null) { |
||||
|
healthHandler.removeCallbacksAndMessages(null); |
||||
|
} |
||||
|
|
||||
|
Log.i(TAG, "健康监控已停止"); |
||||
|
LogManager.logOperation("HealthMonitor", "健康监控停止"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 调度健康检查 |
||||
|
*/ |
||||
|
private void scheduleHealthCheck() { |
||||
|
if (!isMonitoring) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
long interval = isSystemHealthy ? HEALTH_CHECK_INTERVAL : CRITICAL_CHECK_INTERVAL; |
||||
|
|
||||
|
healthHandler.postDelayed(new Runnable() { |
||||
|
@Override |
||||
|
public void run() { |
||||
|
performHealthCheck(); |
||||
|
scheduleHealthCheck(); |
||||
|
} |
||||
|
}, interval); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 执行健康检查 |
||||
|
*/ |
||||
|
public void performHealthCheck() { |
||||
|
try { |
||||
|
lastHealthCheckTime = System.currentTimeMillis(); |
||||
|
|
||||
|
Log.d(TAG, "开始健康检查"); |
||||
|
|
||||
|
HealthReport report = generateHealthReport(); |
||||
|
|
||||
|
// 分析健康状态 |
||||
|
HealthStatus currentStatus = analyzeHealthStatus(report); |
||||
|
|
||||
|
// 处理健康状态变化 |
||||
|
handleHealthStatusChange(currentStatus, report); |
||||
|
|
||||
|
// 记录健康检查结果 |
||||
|
logHealthCheckResult(report); |
||||
|
|
||||
|
Log.d(TAG, "健康检查完成,状态: " + currentStatus); |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
Log.e(TAG, "健康检查失败", e); |
||||
|
LogManager.logError(TAG, "健康检查失败", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 生成健康报告 |
||||
|
*/ |
||||
|
private HealthReport generateHealthReport() { |
||||
|
HealthReport report = new HealthReport(); |
||||
|
|
||||
|
try { |
||||
|
// 检查内存状态 |
||||
|
MemoryManager memoryManager = MemoryManager.getInstance(context); |
||||
|
report.memoryUsage = memoryManager.getCurrentMemoryUsage(); |
||||
|
|
||||
|
// 检查网络状态 |
||||
|
NetworkStabilityManager networkManager = NetworkStabilityManager.getInstance(context); |
||||
|
report.networkAvailable = networkManager.isNetworkAvailable(); |
||||
|
report.networkType = networkManager.getNetworkTypeDescription(); |
||||
|
|
||||
|
// 检查保持活跃状态 |
||||
|
KeepAliveManager keepAliveManager = KeepAliveManager.getInstance(context); |
||||
|
report.keepAliveActive = keepAliveManager.isKeepingAlive(); |
||||
|
|
||||
|
// 计算运行时间 |
||||
|
report.uptime = System.currentTimeMillis() - getAppStartTime(); |
||||
|
|
||||
|
// 记录失败次数 |
||||
|
report.memoryFailures = consecutiveMemoryFailures; |
||||
|
report.networkFailures = consecutiveNetworkFailures; |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
Log.e(TAG, "生成健康报告失败", e); |
||||
|
report.lastIssue = "生成健康报告失败: " + e.getMessage(); |
||||
|
} |
||||
|
|
||||
|
return report; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 分析健康状态 |
||||
|
*/ |
||||
|
private HealthStatus analyzeHealthStatus(HealthReport report) { |
||||
|
HealthStatus status = HealthStatus.HEALTHY; |
||||
|
|
||||
|
// 检查内存使用率 |
||||
|
if (report.memoryUsage > 0.95f) { |
||||
|
status = HealthStatus.CRITICAL; |
||||
|
consecutiveMemoryFailures++; |
||||
|
report.lastIssue = "内存使用率过高: " + String.format("%.1f%%", report.memoryUsage * 100); |
||||
|
} else if (report.memoryUsage > 0.85f) { |
||||
|
status = HealthStatus.WARNING; |
||||
|
consecutiveMemoryFailures++; |
||||
|
report.lastIssue = "内存使用率警告: " + String.format("%.1f%%", report.memoryUsage * 100); |
||||
|
} else { |
||||
|
consecutiveMemoryFailures = 0; |
||||
|
} |
||||
|
|
||||
|
// 检查网络状态 |
||||
|
if (!report.networkAvailable) { |
||||
|
if (status == HealthStatus.HEALTHY) { |
||||
|
status = HealthStatus.WARNING; |
||||
|
} else if (status == HealthStatus.WARNING) { |
||||
|
status = HealthStatus.CRITICAL; |
||||
|
} |
||||
|
consecutiveNetworkFailures++; |
||||
|
report.lastIssue = "网络连接不可用"; |
||||
|
} else { |
||||
|
consecutiveNetworkFailures = 0; |
||||
|
} |
||||
|
|
||||
|
// 检查连续失败次数 |
||||
|
if (consecutiveMemoryFailures > MAX_CONSECUTIVE_FAILURES || |
||||
|
consecutiveNetworkFailures > MAX_CONSECUTIVE_FAILURES) { |
||||
|
status = HealthStatus.SYSTEM_ERROR; |
||||
|
report.lastIssue = "系统连续失败次数过多"; |
||||
|
} |
||||
|
|
||||
|
// 检查保持活跃状态 |
||||
|
if (!report.keepAliveActive && status == HealthStatus.HEALTHY) { |
||||
|
status = HealthStatus.WARNING; |
||||
|
report.lastIssue = "应用保持活跃功能未启用"; |
||||
|
} |
||||
|
|
||||
|
report.overallStatus = status; |
||||
|
|
||||
|
// 生成建议 |
||||
|
generateRecommendations(report); |
||||
|
|
||||
|
return status; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 生成建议 |
||||
|
*/ |
||||
|
private void generateRecommendations(HealthReport report) { |
||||
|
StringBuilder recommendations = new StringBuilder(); |
||||
|
|
||||
|
if (report.memoryUsage > 0.85f) { |
||||
|
recommendations.append("建议清理内存缓存; "); |
||||
|
} |
||||
|
|
||||
|
if (!report.networkAvailable) { |
||||
|
recommendations.append("检查网络连接; "); |
||||
|
} |
||||
|
|
||||
|
if (!report.keepAliveActive) { |
||||
|
recommendations.append("启用应用保持活跃功能; "); |
||||
|
} |
||||
|
|
||||
|
if (report.memoryFailures > 0 || report.networkFailures > 0) { |
||||
|
recommendations.append("监控系统稳定性; "); |
||||
|
} |
||||
|
|
||||
|
if (recommendations.length() == 0) { |
||||
|
recommendations.append("系统运行正常"); |
||||
|
} |
||||
|
|
||||
|
report.recommendations = recommendations.toString(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 处理健康状态变化 |
||||
|
*/ |
||||
|
private void handleHealthStatusChange(HealthStatus currentStatus, HealthReport report) { |
||||
|
boolean statusChanged = (isSystemHealthy && currentStatus != HealthStatus.HEALTHY) || |
||||
|
(!isSystemHealthy && currentStatus == HealthStatus.HEALTHY); |
||||
|
|
||||
|
if (statusChanged) { |
||||
|
isSystemHealthy = (currentStatus == HealthStatus.HEALTHY); |
||||
|
|
||||
|
if (healthStatusListener != null) { |
||||
|
healthStatusListener.onHealthStatusChanged(currentStatus); |
||||
|
} |
||||
|
|
||||
|
LogManager.logOperation("HealthMonitor", |
||||
|
"健康状态变化: " + currentStatus + ", " + report.lastIssue); |
||||
|
} |
||||
|
|
||||
|
// 处理严重问题 |
||||
|
if (currentStatus == HealthStatus.CRITICAL || currentStatus == HealthStatus.SYSTEM_ERROR) { |
||||
|
handleCriticalIssue(report); |
||||
|
} |
||||
|
|
||||
|
// 处理系统恢复 |
||||
|
if (isSystemHealthy && (consecutiveMemoryFailures == 0 && consecutiveNetworkFailures == 0)) { |
||||
|
handleSystemRecovery(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 处理严重问题 |
||||
|
*/ |
||||
|
private void handleCriticalIssue(HealthReport report) { |
||||
|
Log.w(TAG, "检测到严重问题: " + report.lastIssue); |
||||
|
|
||||
|
if (healthStatusListener != null) { |
||||
|
healthStatusListener.onCriticalIssueDetected(report.lastIssue); |
||||
|
} |
||||
|
|
||||
|
// 尝试自动恢复 |
||||
|
attemptAutoRecovery(report); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 尝试自动恢复 |
||||
|
*/ |
||||
|
private void attemptAutoRecovery(HealthReport report) { |
||||
|
try { |
||||
|
Log.i(TAG, "尝试自动恢复: " + report.overallStatus); |
||||
|
|
||||
|
if (report.memoryUsage > 0.90f) { |
||||
|
// 执行内存清理 |
||||
|
MemoryManager memoryManager = MemoryManager.getInstance(context); |
||||
|
memoryManager.performMemoryCleanup(); |
||||
|
LogManager.logOperation("HealthMonitor", "自动执行内存清理"); |
||||
|
} |
||||
|
|
||||
|
if (!report.networkAvailable) { |
||||
|
// 网络问题通常需要等待恢复,记录日志 |
||||
|
LogManager.logWarning(TAG, "网络不可用,等待网络恢复"); |
||||
|
} |
||||
|
|
||||
|
if (!report.keepAliveActive) { |
||||
|
// 记录保持活跃状态问题 |
||||
|
LogManager.logWarning(TAG, "保持活跃功能未启用"); |
||||
|
} |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
Log.e(TAG, "自动恢复失败", e); |
||||
|
LogManager.logError(TAG, "自动恢复失败", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 处理系统恢复 |
||||
|
*/ |
||||
|
private void handleSystemRecovery() { |
||||
|
if (healthStatusListener != null) { |
||||
|
healthStatusListener.onSystemRecovered("系统已恢复正常运行"); |
||||
|
} |
||||
|
|
||||
|
LogManager.logOperation("HealthMonitor", "系统已恢复正常运行"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 记录健康检查结果 |
||||
|
*/ |
||||
|
private void logHealthCheckResult(HealthReport report) { |
||||
|
String message = String.format( |
||||
|
"健康检查 - 状态:%s, 内存:%.1f%%, 网络:%s, 保活:%s, 运行时间:%dh", |
||||
|
report.overallStatus, |
||||
|
report.memoryUsage * 100, |
||||
|
report.networkAvailable ? "正常" : "异常", |
||||
|
report.keepAliveActive ? "是" : "否", |
||||
|
report.uptime / (1000 * 60 * 60) |
||||
|
); |
||||
|
|
||||
|
if (report.overallStatus == HealthStatus.HEALTHY) { |
||||
|
LogManager.logDebug(TAG, message); |
||||
|
} else { |
||||
|
LogManager.logWarning(TAG, message + " - " + report.lastIssue); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取应用启动时间(简化实现) |
||||
|
*/ |
||||
|
private long getAppStartTime() { |
||||
|
// 这里可以在Application中记录实际的启动时间 |
||||
|
// 现在使用一个简化的实现 |
||||
|
return System.currentTimeMillis() - (24 * 60 * 60 * 1000); // 假设已运行24小时 |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取当前健康报告 |
||||
|
*/ |
||||
|
public HealthReport getCurrentHealthReport() { |
||||
|
return generateHealthReport(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 检查系统是否健康 |
||||
|
*/ |
||||
|
public boolean isSystemHealthy() { |
||||
|
return isSystemHealthy; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取上次健康检查时间 |
||||
|
*/ |
||||
|
public long getLastHealthCheckTime() { |
||||
|
return lastHealthCheckTime; |
||||
|
} |
||||
|
} |
@ -0,0 +1,241 @@ |
|||||
|
package com.ouxuan.oxface.utils; |
||||
|
|
||||
|
import android.app.Activity; |
||||
|
import android.content.Context; |
||||
|
import android.os.PowerManager; |
||||
|
import android.util.Log; |
||||
|
import android.view.WindowManager; |
||||
|
|
||||
|
/** |
||||
|
* 保持活跃管理器 |
||||
|
* 用于24小时无人值守环境下保持应用活跃状态 |
||||
|
*/ |
||||
|
public class KeepAliveManager { |
||||
|
|
||||
|
private static final String TAG = "KeepAliveManager"; |
||||
|
private static final String WAKE_LOCK_TAG = "OxFace:KeepAlive"; |
||||
|
|
||||
|
private static KeepAliveManager instance; |
||||
|
private Context context; |
||||
|
private PowerManager powerManager; |
||||
|
private PowerManager.WakeLock wakeLock; |
||||
|
private boolean isKeepingAlive = false; |
||||
|
|
||||
|
private KeepAliveManager(Context context) { |
||||
|
this.context = context.getApplicationContext(); |
||||
|
this.powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); |
||||
|
} |
||||
|
|
||||
|
public static synchronized KeepAliveManager getInstance(Context context) { |
||||
|
if (instance == null) { |
||||
|
instance = new KeepAliveManager(context); |
||||
|
} |
||||
|
return instance; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 开始保持应用活跃 |
||||
|
*/ |
||||
|
public void startKeepAlive(Activity activity) { |
||||
|
if (isKeepingAlive) { |
||||
|
Log.i(TAG, "应用已处于保持活跃状态"); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
// 保持屏幕常亮 |
||||
|
if (activity != null) { |
||||
|
activity.runOnUiThread(new Runnable() { |
||||
|
@Override |
||||
|
public void run() { |
||||
|
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); |
||||
|
Log.i(TAG, "已设置屏幕常亮"); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
// 获取部分唤醒锁 |
||||
|
if (powerManager != null) { |
||||
|
wakeLock = powerManager.newWakeLock( |
||||
|
PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, |
||||
|
WAKE_LOCK_TAG |
||||
|
); |
||||
|
wakeLock.acquire(); |
||||
|
Log.i(TAG, "已获取唤醒锁"); |
||||
|
} |
||||
|
|
||||
|
isKeepingAlive = true; |
||||
|
Log.i(TAG, "应用保持活跃已启动"); |
||||
|
|
||||
|
// 记录到日志 |
||||
|
LogManager.logOperation("KeepAlive", "应用保持活跃功能已启动"); |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
Log.e(TAG, "启动保持活跃失败", e); |
||||
|
LogManager.logError(TAG, "启动保持活跃失败", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 停止保持应用活跃 |
||||
|
*/ |
||||
|
public void stopKeepAlive(Activity activity) { |
||||
|
if (!isKeepingAlive) { |
||||
|
Log.i(TAG, "应用未处于保持活跃状态"); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
// 清除屏幕常亮标志 |
||||
|
if (activity != null) { |
||||
|
activity.runOnUiThread(new Runnable() { |
||||
|
@Override |
||||
|
public void run() { |
||||
|
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); |
||||
|
Log.i(TAG, "已清除屏幕常亮设置"); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
// 释放唤醒锁 |
||||
|
if (wakeLock != null && wakeLock.isHeld()) { |
||||
|
wakeLock.release(); |
||||
|
wakeLock = null; |
||||
|
Log.i(TAG, "已释放唤醒锁"); |
||||
|
} |
||||
|
|
||||
|
isKeepingAlive = false; |
||||
|
Log.i(TAG, "应用保持活跃已停止"); |
||||
|
|
||||
|
// 记录到日志 |
||||
|
LogManager.logOperation("KeepAlive", "应用保持活跃功能已停止"); |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
Log.e(TAG, "停止保持活跃失败", e); |
||||
|
LogManager.logError(TAG, "停止保持活跃失败", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 检查是否正在保持活跃 |
||||
|
*/ |
||||
|
public boolean isKeepingAlive() { |
||||
|
return isKeepingAlive; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 检查唤醒锁状态 |
||||
|
*/ |
||||
|
public boolean isWakeLockHeld() { |
||||
|
return wakeLock != null && wakeLock.isHeld(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 检查屏幕是否保持开启 |
||||
|
*/ |
||||
|
public boolean isScreenOn() { |
||||
|
if (powerManager != null) { |
||||
|
return powerManager.isInteractive(); |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取电源管理信息 |
||||
|
*/ |
||||
|
public String getPowerInfo() { |
||||
|
StringBuilder info = new StringBuilder(); |
||||
|
|
||||
|
try { |
||||
|
if (powerManager != null) { |
||||
|
info.append("屏幕状态: ").append(isScreenOn() ? "开启" : "关闭").append("\n"); |
||||
|
info.append("唤醒锁状态: ").append(isWakeLockHeld() ? "持有" : "未持有").append("\n"); |
||||
|
info.append("保持活跃状态: ").append(isKeepingAlive ? "是" : "否").append("\n"); |
||||
|
|
||||
|
// 检查是否处于省电模式 |
||||
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { |
||||
|
boolean isPowerSaveMode = powerManager.isPowerSaveMode(); |
||||
|
info.append("省电模式: ").append(isPowerSaveMode ? "开启" : "关闭").append("\n"); |
||||
|
} |
||||
|
|
||||
|
// 检查是否忽略电池优化 |
||||
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { |
||||
|
boolean isIgnoringBatteryOptimizations = |
||||
|
powerManager.isIgnoringBatteryOptimizations(context.getPackageName()); |
||||
|
info.append("忽略电池优化: ").append(isIgnoringBatteryOptimizations ? "是" : "否").append("\n"); |
||||
|
} |
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
Log.e(TAG, "获取电源信息失败", e); |
||||
|
info.append("获取电源信息失败: ").append(e.getMessage()); |
||||
|
} |
||||
|
|
||||
|
return info.toString(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 请求忽略电池优化(需要用户手动操作) |
||||
|
*/ |
||||
|
public boolean requestIgnoreBatteryOptimizations(Activity activity) { |
||||
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { |
||||
|
try { |
||||
|
if (powerManager != null && |
||||
|
!powerManager.isIgnoringBatteryOptimizations(context.getPackageName())) { |
||||
|
|
||||
|
android.content.Intent intent = new android.content.Intent(); |
||||
|
intent.setAction(android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); |
||||
|
intent.setData(android.net.Uri.parse("package:" + context.getPackageName())); |
||||
|
|
||||
|
if (intent.resolveActivity(context.getPackageManager()) != null) { |
||||
|
activity.startActivity(intent); |
||||
|
Log.i(TAG, "已请求忽略电池优化"); |
||||
|
LogManager.logOperation("KeepAlive", "请求忽略电池优化"); |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
Log.e(TAG, "请求忽略电池优化失败", e); |
||||
|
LogManager.logError(TAG, "请求忽略电池优化失败", e); |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 强制保持屏幕开启(用于关键操作期间) |
||||
|
*/ |
||||
|
public void forceKeepScreenOn(Activity activity, long durationMillis) { |
||||
|
if (activity == null) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
Log.i(TAG, "强制保持屏幕开启 " + durationMillis + "ms"); |
||||
|
|
||||
|
activity.runOnUiThread(new Runnable() { |
||||
|
@Override |
||||
|
public void run() { |
||||
|
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); |
||||
|
|
||||
|
// 延时后清除标志 |
||||
|
activity.getWindow().getDecorView().postDelayed(new Runnable() { |
||||
|
@Override |
||||
|
public void run() { |
||||
|
if (!isKeepingAlive) { // 如果没有全局保持活跃,才清除标志 |
||||
|
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); |
||||
|
Log.i(TAG, "临时屏幕保持已结束"); |
||||
|
} |
||||
|
} |
||||
|
}, durationMillis); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 记录电源状态到日志 |
||||
|
*/ |
||||
|
public void logPowerStatus() { |
||||
|
String powerInfo = getPowerInfo(); |
||||
|
Log.i(TAG, "电源状态:\n" + powerInfo); |
||||
|
LogManager.logInfo(TAG, "电源状态: " + powerInfo.replace("\n", ", ")); |
||||
|
} |
||||
|
} |
@ -0,0 +1,375 @@ |
|||||
|
package com.ouxuan.oxface.utils; |
||||
|
|
||||
|
import android.content.Context; |
||||
|
import android.util.Log; |
||||
|
|
||||
|
import java.io.BufferedWriter; |
||||
|
import java.io.File; |
||||
|
import java.io.FileWriter; |
||||
|
import java.io.IOException; |
||||
|
import java.text.SimpleDateFormat; |
||||
|
import java.util.Date; |
||||
|
import java.util.Locale; |
||||
|
import java.util.concurrent.BlockingQueue; |
||||
|
import java.util.concurrent.LinkedBlockingQueue; |
||||
|
|
||||
|
/** |
||||
|
* 日志管理器 |
||||
|
* 用于24小时无人值守环境下的日志记录和管理 |
||||
|
*/ |
||||
|
public class LogManager { |
||||
|
|
||||
|
private static final String TAG = "LogManager"; |
||||
|
private static final String LOG_DIR = "operation_logs"; |
||||
|
private static final String LOG_FILE_NAME = "oxface_operation.log"; |
||||
|
private static final long MAX_LOG_SIZE = 10 * 1024 * 1024; // 10MB |
||||
|
private static final int MAX_LOG_FILES = 5; // 最多保留5个日志文件 |
||||
|
private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss.SSS"; |
||||
|
|
||||
|
private static LogManager instance; |
||||
|
private Context context; |
||||
|
private File logDir; |
||||
|
private File currentLogFile; |
||||
|
private BlockingQueue<LogEntry> logQueue; |
||||
|
private Thread logWriterThread; |
||||
|
private volatile boolean isRunning = false; |
||||
|
private SimpleDateFormat dateFormat; |
||||
|
|
||||
|
private static class LogEntry { |
||||
|
final long timestamp; |
||||
|
final String level; |
||||
|
final String tag; |
||||
|
final String message; |
||||
|
final Throwable throwable; |
||||
|
|
||||
|
LogEntry(String level, String tag, String message, Throwable throwable) { |
||||
|
this.timestamp = System.currentTimeMillis(); |
||||
|
this.level = level; |
||||
|
this.tag = tag; |
||||
|
this.message = message; |
||||
|
this.throwable = throwable; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private LogManager(Context context) { |
||||
|
this.context = context.getApplicationContext(); |
||||
|
this.dateFormat = new SimpleDateFormat(DATE_FORMAT, Locale.getDefault()); |
||||
|
this.logQueue = new LinkedBlockingQueue<>(); |
||||
|
|
||||
|
// 创建日志目录 |
||||
|
this.logDir = new File(context.getFilesDir(), LOG_DIR); |
||||
|
if (!logDir.exists()) { |
||||
|
logDir.mkdirs(); |
||||
|
} |
||||
|
|
||||
|
// 设置当前日志文件 |
||||
|
this.currentLogFile = new File(logDir, LOG_FILE_NAME); |
||||
|
} |
||||
|
|
||||
|
public static synchronized LogManager getInstance(Context context) { |
||||
|
if (instance == null) { |
||||
|
instance = new LogManager(context); |
||||
|
} |
||||
|
return instance; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 启动日志管理器 |
||||
|
*/ |
||||
|
public void start() { |
||||
|
if (isRunning) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
isRunning = true; |
||||
|
logWriterThread = new Thread(new Runnable() { |
||||
|
@Override |
||||
|
public void run() { |
||||
|
runLogWriter(); |
||||
|
} |
||||
|
}); |
||||
|
logWriterThread.setName("LogWriter"); |
||||
|
logWriterThread.setDaemon(true); |
||||
|
logWriterThread.start(); |
||||
|
|
||||
|
Log.i(TAG, "日志管理器已启动"); |
||||
|
logInfo(TAG, "日志管理器启动"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 停止日志管理器 |
||||
|
*/ |
||||
|
public void stop() { |
||||
|
if (!isRunning) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
isRunning = false; |
||||
|
|
||||
|
if (logWriterThread != null) { |
||||
|
logWriterThread.interrupt(); |
||||
|
try { |
||||
|
logWriterThread.join(5000); // 等待最多5秒 |
||||
|
} catch (InterruptedException e) { |
||||
|
Log.w(TAG, "等待日志写入线程结束被中断"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 处理队列中剩余的日志 |
||||
|
processRemainingLogs(); |
||||
|
|
||||
|
Log.i(TAG, "日志管理器已停止"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 记录调试日志 |
||||
|
*/ |
||||
|
public static void logDebug(String tag, String message) { |
||||
|
if (instance != null) { |
||||
|
instance.addLogEntry("DEBUG", tag, message, null); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 记录信息日志 |
||||
|
*/ |
||||
|
public static void logInfo(String tag, String message) { |
||||
|
if (instance != null) { |
||||
|
instance.addLogEntry("INFO", tag, message, null); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 记录警告日志 |
||||
|
*/ |
||||
|
public static void logWarning(String tag, String message) { |
||||
|
if (instance != null) { |
||||
|
instance.addLogEntry("WARN", tag, message, null); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 记录错误日志 |
||||
|
*/ |
||||
|
public static void logError(String tag, String message) { |
||||
|
if (instance != null) { |
||||
|
instance.addLogEntry("ERROR", tag, message, null); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 记录错误日志(带异常) |
||||
|
*/ |
||||
|
public static void logError(String tag, String message, Throwable throwable) { |
||||
|
if (instance != null) { |
||||
|
instance.addLogEntry("ERROR", tag, message, throwable); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 记录操作日志 |
||||
|
*/ |
||||
|
public static void logOperation(String operation, String details) { |
||||
|
if (instance != null) { |
||||
|
instance.addLogEntry("OPERATION", "App", operation + ": " + details, null); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 记录性能日志 |
||||
|
*/ |
||||
|
public static void logPerformance(String operation, long duration) { |
||||
|
if (instance != null) { |
||||
|
String message = String.format("%s 耗时: %dms", operation, duration); |
||||
|
instance.addLogEntry("PERFORMANCE", "App", message, null); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 添加日志条目到队列 |
||||
|
*/ |
||||
|
private void addLogEntry(String level, String tag, String message, Throwable throwable) { |
||||
|
try { |
||||
|
LogEntry entry = new LogEntry(level, tag, message, throwable); |
||||
|
if (!logQueue.offer(entry)) { |
||||
|
Log.w(TAG, "日志队列已满,丢弃日志: " + message); |
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
Log.e(TAG, "添加日志条目失败", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 运行日志写入器 |
||||
|
*/ |
||||
|
private void runLogWriter() { |
||||
|
BufferedWriter writer = null; |
||||
|
|
||||
|
try { |
||||
|
while (isRunning || !logQueue.isEmpty()) { |
||||
|
try { |
||||
|
LogEntry entry = logQueue.take(); // 阻塞等待日志条目 |
||||
|
|
||||
|
// 检查是否需要轮转日志文件 |
||||
|
checkLogRotation(); |
||||
|
|
||||
|
// 打开或重新打开文件 |
||||
|
if (writer == null) { |
||||
|
writer = new BufferedWriter(new FileWriter(currentLogFile, true)); |
||||
|
} |
||||
|
|
||||
|
// 写入日志 |
||||
|
writeLogEntry(writer, entry); |
||||
|
|
||||
|
// 定期刷新缓冲区 |
||||
|
writer.flush(); |
||||
|
|
||||
|
} catch (InterruptedException e) { |
||||
|
if (!isRunning) { |
||||
|
break; |
||||
|
} |
||||
|
} catch (IOException e) { |
||||
|
Log.e(TAG, "写入日志失败", e); |
||||
|
|
||||
|
// 重新打开文件 |
||||
|
if (writer != null) { |
||||
|
try { |
||||
|
writer.close(); |
||||
|
} catch (IOException ignored) { |
||||
|
} |
||||
|
writer = null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} finally { |
||||
|
if (writer != null) { |
||||
|
try { |
||||
|
writer.close(); |
||||
|
} catch (IOException e) { |
||||
|
Log.e(TAG, "关闭日志文件失败", e); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 写入日志条目 |
||||
|
*/ |
||||
|
private void writeLogEntry(BufferedWriter writer, LogEntry entry) throws IOException { |
||||
|
String timestamp = dateFormat.format(new Date(entry.timestamp)); |
||||
|
String logLine = String.format("[%s] %s/%s: %s%n", |
||||
|
timestamp, entry.level, entry.tag, entry.message); |
||||
|
|
||||
|
writer.write(logLine); |
||||
|
|
||||
|
// 如果有异常,写入堆栈信息 |
||||
|
if (entry.throwable != null) { |
||||
|
writer.write("Exception: " + entry.throwable.toString() + "\n"); |
||||
|
StackTraceElement[] stackTrace = entry.throwable.getStackTrace(); |
||||
|
for (StackTraceElement element : stackTrace) { |
||||
|
writer.write(" at " + element.toString() + "\n"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 检查日志轮转 |
||||
|
*/ |
||||
|
private void checkLogRotation() { |
||||
|
if (currentLogFile.exists() && currentLogFile.length() > MAX_LOG_SIZE) { |
||||
|
rotateLogFile(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 轮转日志文件 |
||||
|
*/ |
||||
|
private void rotateLogFile() { |
||||
|
try { |
||||
|
String timestamp = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss", Locale.getDefault()).format(new Date()); |
||||
|
String rotatedFileName = "oxface_operation_" + timestamp + ".log"; |
||||
|
File rotatedFile = new File(logDir, rotatedFileName); |
||||
|
|
||||
|
if (currentLogFile.renameTo(rotatedFile)) { |
||||
|
Log.i(TAG, "日志文件轮转成功: " + rotatedFileName); |
||||
|
|
||||
|
// 清理旧的日志文件 |
||||
|
cleanupOldLogFiles(); |
||||
|
} else { |
||||
|
Log.w(TAG, "日志文件轮转失败"); |
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
Log.e(TAG, "日志轮转失败", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 清理旧的日志文件 |
||||
|
*/ |
||||
|
private void cleanupOldLogFiles() { |
||||
|
try { |
||||
|
File[] logFiles = logDir.listFiles(); |
||||
|
if (logFiles == null || logFiles.length <= MAX_LOG_FILES) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// 按修改时间排序 |
||||
|
java.util.Arrays.sort(logFiles, new java.util.Comparator<File>() { |
||||
|
@Override |
||||
|
public int compare(File f1, File f2) { |
||||
|
return Long.compare(f1.lastModified(), f2.lastModified()); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
// 删除最旧的文件(保留当前日志文件) |
||||
|
int filesToDelete = logFiles.length - MAX_LOG_FILES; |
||||
|
for (int i = 0; i < filesToDelete; i++) { |
||||
|
if (!logFiles[i].equals(currentLogFile) && logFiles[i].delete()) { |
||||
|
Log.d(TAG, "已删除旧日志文件: " + logFiles[i].getName()); |
||||
|
} |
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
Log.e(TAG, "清理旧日志文件失败", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 处理队列中剩余的日志 |
||||
|
*/ |
||||
|
private void processRemainingLogs() { |
||||
|
if (logQueue.isEmpty()) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
try (BufferedWriter writer = new BufferedWriter(new FileWriter(currentLogFile, true))) { |
||||
|
LogEntry entry; |
||||
|
while ((entry = logQueue.poll()) != null) { |
||||
|
writeLogEntry(writer, entry); |
||||
|
} |
||||
|
writer.flush(); |
||||
|
} catch (IOException e) { |
||||
|
Log.e(TAG, "处理剩余日志失败", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取日志目录 |
||||
|
*/ |
||||
|
public File getLogDirectory() { |
||||
|
return logDir; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取当前日志文件 |
||||
|
*/ |
||||
|
public File getCurrentLogFile() { |
||||
|
return currentLogFile; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取日志文件大小 |
||||
|
*/ |
||||
|
public long getLogFileSize() { |
||||
|
return currentLogFile.exists() ? currentLogFile.length() : 0; |
||||
|
} |
||||
|
} |
@ -0,0 +1,533 @@ |
|||||
|
package com.ouxuan.oxface.utils; |
||||
|
|
||||
|
import android.content.Context; |
||||
|
import android.util.Log; |
||||
|
|
||||
|
import com.bumptech.glide.Glide; |
||||
|
import com.ouxuan.oxface.network.NetworkManager; |
||||
|
import com.ouxuan.oxface.network.NetworkStabilityManager; |
||||
|
|
||||
|
import java.util.Timer; |
||||
|
import java.util.TimerTask; |
||||
|
import java.util.concurrent.Executors; |
||||
|
import java.util.concurrent.ScheduledExecutorService; |
||||
|
import java.util.concurrent.TimeUnit; |
||||
|
|
||||
|
/** |
||||
|
* 维护任务调度器 |
||||
|
* 用于24小时无人值守环境下的定时维护任务 |
||||
|
*/ |
||||
|
public class MaintenanceScheduler { |
||||
|
|
||||
|
private static final String TAG = "MaintenanceScheduler"; |
||||
|
|
||||
|
// 维护间隔时间(毫秒) |
||||
|
private static final long LIGHT_MAINTENANCE_INTERVAL = 30 * 60 * 1000; // 30分钟 - 轻度维护 |
||||
|
private static final long MEDIUM_MAINTENANCE_INTERVAL = 2 * 60 * 60 * 1000; // 2小时 - 中度维护 |
||||
|
private static final long HEAVY_MAINTENANCE_INTERVAL = 6 * 60 * 60 * 1000; // 6小时 - 重度维护 |
||||
|
private static final long DAILY_MAINTENANCE_INTERVAL = 24 * 60 * 60 * 1000; // 24小时 - 每日维护 |
||||
|
|
||||
|
private static MaintenanceScheduler instance; |
||||
|
private Context context; |
||||
|
private ScheduledExecutorService scheduledExecutor; |
||||
|
private boolean isRunning = false; |
||||
|
|
||||
|
// 维护任务计数器 |
||||
|
private int lightMaintenanceCount = 0; |
||||
|
private int mediumMaintenanceCount = 0; |
||||
|
private int heavyMaintenanceCount = 0; |
||||
|
private int dailyMaintenanceCount = 0; |
||||
|
|
||||
|
private MaintenanceScheduler(Context context) { |
||||
|
this.context = context.getApplicationContext(); |
||||
|
this.scheduledExecutor = Executors.newScheduledThreadPool(2); |
||||
|
} |
||||
|
|
||||
|
public static synchronized MaintenanceScheduler getInstance(Context context) { |
||||
|
if (instance == null) { |
||||
|
instance = new MaintenanceScheduler(context); |
||||
|
} |
||||
|
return instance; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 启动维护任务调度 |
||||
|
*/ |
||||
|
public void startMaintenance() { |
||||
|
if (isRunning) { |
||||
|
Log.i(TAG, "维护调度器已在运行中"); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
isRunning = true; |
||||
|
|
||||
|
// 调度轻度维护任务 - 每30分钟 |
||||
|
scheduledExecutor.scheduleAtFixedRate( |
||||
|
new Runnable() { |
||||
|
@Override |
||||
|
public void run() { |
||||
|
performLightMaintenance(); |
||||
|
} |
||||
|
}, |
||||
|
LIGHT_MAINTENANCE_INTERVAL, |
||||
|
LIGHT_MAINTENANCE_INTERVAL, |
||||
|
TimeUnit.MILLISECONDS |
||||
|
); |
||||
|
|
||||
|
// 调度中度维护任务 - 每2小时 |
||||
|
scheduledExecutor.scheduleAtFixedRate( |
||||
|
new Runnable() { |
||||
|
@Override |
||||
|
public void run() { |
||||
|
performMediumMaintenance(); |
||||
|
} |
||||
|
}, |
||||
|
MEDIUM_MAINTENANCE_INTERVAL, |
||||
|
MEDIUM_MAINTENANCE_INTERVAL, |
||||
|
TimeUnit.MILLISECONDS |
||||
|
); |
||||
|
|
||||
|
// 调度重度维护任务 - 每6小时 |
||||
|
scheduledExecutor.scheduleAtFixedRate( |
||||
|
new Runnable() { |
||||
|
@Override |
||||
|
public void run() { |
||||
|
performHeavyMaintenance(); |
||||
|
} |
||||
|
}, |
||||
|
HEAVY_MAINTENANCE_INTERVAL, |
||||
|
HEAVY_MAINTENANCE_INTERVAL, |
||||
|
TimeUnit.MILLISECONDS |
||||
|
); |
||||
|
|
||||
|
// 调度每日维护任务 - 每24小时 |
||||
|
scheduledExecutor.scheduleAtFixedRate( |
||||
|
new Runnable() { |
||||
|
@Override |
||||
|
public void run() { |
||||
|
performDailyMaintenance(); |
||||
|
} |
||||
|
}, |
||||
|
DAILY_MAINTENANCE_INTERVAL, |
||||
|
DAILY_MAINTENANCE_INTERVAL, |
||||
|
TimeUnit.MILLISECONDS |
||||
|
); |
||||
|
|
||||
|
Log.i(TAG, "维护任务调度器已启动"); |
||||
|
LogManager.logOperation("MaintenanceScheduler", "维护任务调度器启动"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 停止维护任务调度 |
||||
|
*/ |
||||
|
public void stopMaintenance() { |
||||
|
if (!isRunning) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
isRunning = false; |
||||
|
|
||||
|
if (scheduledExecutor != null && !scheduledExecutor.isShutdown()) { |
||||
|
scheduledExecutor.shutdown(); |
||||
|
try { |
||||
|
if (!scheduledExecutor.awaitTermination(30, TimeUnit.SECONDS)) { |
||||
|
scheduledExecutor.shutdownNow(); |
||||
|
} |
||||
|
} catch (InterruptedException e) { |
||||
|
scheduledExecutor.shutdownNow(); |
||||
|
Thread.currentThread().interrupt(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
Log.i(TAG, "维护任务调度器已停止"); |
||||
|
LogManager.logOperation("MaintenanceScheduler", "维护任务调度器停止"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 轻度维护任务 - 每30分钟执行 |
||||
|
*/ |
||||
|
private void performLightMaintenance() { |
||||
|
try { |
||||
|
lightMaintenanceCount++; |
||||
|
Log.d(TAG, "开始轻度维护任务 #" + lightMaintenanceCount); |
||||
|
|
||||
|
long startTime = System.currentTimeMillis(); |
||||
|
|
||||
|
// 检查内存状态 |
||||
|
checkMemoryStatus(); |
||||
|
|
||||
|
// 检查网络状态 |
||||
|
checkNetworkStatus(); |
||||
|
|
||||
|
// 轻量级垃圾回收建议 |
||||
|
System.gc(); |
||||
|
|
||||
|
long duration = System.currentTimeMillis() - startTime; |
||||
|
Log.d(TAG, "轻度维护任务完成,耗时: " + duration + "ms"); |
||||
|
LogManager.logPerformance("轻度维护任务", duration); |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
Log.e(TAG, "轻度维护任务失败", e); |
||||
|
LogManager.logError(TAG, "轻度维护任务失败", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 中度维护任务 - 每2小时执行 |
||||
|
*/ |
||||
|
private void performMediumMaintenance() { |
||||
|
try { |
||||
|
mediumMaintenanceCount++; |
||||
|
Log.i(TAG, "开始中度维护任务 #" + mediumMaintenanceCount); |
||||
|
|
||||
|
long startTime = System.currentTimeMillis(); |
||||
|
|
||||
|
// 执行内存清理 |
||||
|
performMemoryCleanup(); |
||||
|
|
||||
|
// 检查应用保持活跃状态 |
||||
|
checkKeepAliveStatus(); |
||||
|
|
||||
|
// 执行网络连接重置 |
||||
|
resetNetworkConnections(); |
||||
|
|
||||
|
// 更强力的垃圾回收 |
||||
|
for (int i = 0; i < 3; i++) { |
||||
|
System.gc(); |
||||
|
try { |
||||
|
Thread.sleep(100); |
||||
|
} catch (InterruptedException e) { |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
long duration = System.currentTimeMillis() - startTime; |
||||
|
Log.i(TAG, "中度维护任务完成,耗时: " + duration + "ms"); |
||||
|
LogManager.logPerformance("中度维护任务", duration); |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
Log.e(TAG, "中度维护任务失败", e); |
||||
|
LogManager.logError(TAG, "中度维护任务失败", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 重度维护任务 - 每6小时执行 |
||||
|
*/ |
||||
|
private void performHeavyMaintenance() { |
||||
|
try { |
||||
|
heavyMaintenanceCount++; |
||||
|
Log.w(TAG, "开始重度维护任务 #" + heavyMaintenanceCount); |
||||
|
|
||||
|
long startTime = System.currentTimeMillis(); |
||||
|
|
||||
|
// 执行深度内存清理 |
||||
|
performDeepMemoryCleanup(); |
||||
|
|
||||
|
// 清理磁盘缓存 |
||||
|
clearDiskCaches(); |
||||
|
|
||||
|
// 重新初始化网络管理器 |
||||
|
reinitializeNetworkManager(); |
||||
|
|
||||
|
// 执行健康检查 |
||||
|
performSystemHealthCheck(); |
||||
|
|
||||
|
// 清理日志文件 |
||||
|
cleanupLogFiles(); |
||||
|
|
||||
|
long duration = System.currentTimeMillis() - startTime; |
||||
|
Log.w(TAG, "重度维护任务完成,耗时: " + duration + "ms"); |
||||
|
LogManager.logPerformance("重度维护任务", duration); |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
Log.e(TAG, "重度维护任务失败", e); |
||||
|
LogManager.logError(TAG, "重度维护任务失败", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 每日维护任务 - 每24小时执行 |
||||
|
*/ |
||||
|
private void performDailyMaintenance() { |
||||
|
try { |
||||
|
dailyMaintenanceCount++; |
||||
|
Log.w(TAG, "开始每日维护任务 #" + dailyMaintenanceCount); |
||||
|
|
||||
|
long startTime = System.currentTimeMillis(); |
||||
|
|
||||
|
// 生成系统运行报告 |
||||
|
generateSystemReport(); |
||||
|
|
||||
|
// 执行全面系统清理 |
||||
|
performComprehensiveCleanup(); |
||||
|
|
||||
|
// 重置所有计数器和统计数据 |
||||
|
resetCounters(); |
||||
|
|
||||
|
// 验证系统完整性 |
||||
|
validateSystemIntegrity(); |
||||
|
|
||||
|
long duration = System.currentTimeMillis() - startTime; |
||||
|
Log.w(TAG, "每日维护任务完成,耗时: " + duration + "ms"); |
||||
|
LogManager.logPerformance("每日维护任务", duration); |
||||
|
LogManager.logOperation("MaintenanceScheduler", "完成第" + dailyMaintenanceCount + "次每日维护"); |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
Log.e(TAG, "每日维护任务失败", e); |
||||
|
LogManager.logError(TAG, "每日维护任务失败", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 检查内存状态 |
||||
|
*/ |
||||
|
private void checkMemoryStatus() { |
||||
|
MemoryManager memoryManager = MemoryManager.getInstance(context); |
||||
|
float memoryUsage = memoryManager.getCurrentMemoryUsage(); |
||||
|
|
||||
|
if (memoryUsage > 0.80f) { |
||||
|
Log.w(TAG, "内存使用率较高: " + String.format("%.1f%%", memoryUsage * 100)); |
||||
|
memoryManager.performMemoryCleanup(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 检查网络状态 |
||||
|
*/ |
||||
|
private void checkNetworkStatus() { |
||||
|
NetworkStabilityManager networkManager = NetworkStabilityManager.getInstance(context); |
||||
|
boolean networkAvailable = networkManager.isNetworkAvailable(); |
||||
|
|
||||
|
if (!networkAvailable) { |
||||
|
Log.w(TAG, "网络连接不可用"); |
||||
|
LogManager.logWarning(TAG, "维护检查发现网络连接不可用"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 执行内存清理 |
||||
|
*/ |
||||
|
private void performMemoryCleanup() { |
||||
|
MemoryManager memoryManager = MemoryManager.getInstance(context); |
||||
|
memoryManager.performMemoryCleanup(); |
||||
|
|
||||
|
// 清理Glide缓存 |
||||
|
Glide.get(context).clearMemory(); |
||||
|
|
||||
|
Log.d(TAG, "内存清理完成"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 检查保持活跃状态 |
||||
|
*/ |
||||
|
private void checkKeepAliveStatus() { |
||||
|
KeepAliveManager keepAliveManager = KeepAliveManager.getInstance(context); |
||||
|
boolean isKeepingAlive = keepAliveManager.isKeepingAlive(); |
||||
|
|
||||
|
if (!isKeepingAlive) { |
||||
|
Log.w(TAG, "保持活跃功能未启用"); |
||||
|
LogManager.logWarning(TAG, "维护检查发现保持活跃功能未启用"); |
||||
|
} |
||||
|
|
||||
|
// 记录电源状态 |
||||
|
keepAliveManager.logPowerStatus(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 重置网络连接 |
||||
|
*/ |
||||
|
private void resetNetworkConnections() { |
||||
|
try { |
||||
|
// 这里可以添加重置网络连接的逻辑 |
||||
|
// 例如重新初始化HTTP客户端等 |
||||
|
Log.d(TAG, "网络连接重置完成"); |
||||
|
} catch (Exception e) { |
||||
|
Log.e(TAG, "重置网络连接失败", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 执行深度内存清理 |
||||
|
*/ |
||||
|
private void performDeepMemoryCleanup() { |
||||
|
MemoryManager memoryManager = MemoryManager.getInstance(context); |
||||
|
|
||||
|
// 多次执行内存清理 |
||||
|
for (int i = 0; i < 3; i++) { |
||||
|
memoryManager.performMemoryCleanup(); |
||||
|
try { |
||||
|
Thread.sleep(200); |
||||
|
} catch (InterruptedException e) { |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
Log.i(TAG, "深度内存清理完成"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 清理磁盘缓存 |
||||
|
*/ |
||||
|
private void clearDiskCaches() { |
||||
|
// 在后台线程中清理磁盘缓存 |
||||
|
new Thread(new Runnable() { |
||||
|
@Override |
||||
|
public void run() { |
||||
|
try { |
||||
|
Glide.get(context).clearDiskCache(); |
||||
|
Log.i(TAG, "磁盘缓存清理完成"); |
||||
|
} catch (Exception e) { |
||||
|
Log.e(TAG, "磁盘缓存清理失败", e); |
||||
|
} |
||||
|
} |
||||
|
}).start(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 重新初始化网络管理器 |
||||
|
*/ |
||||
|
private void reinitializeNetworkManager() { |
||||
|
try { |
||||
|
NetworkManager networkManager = NetworkManager.getInstance(); |
||||
|
// 这里可以添加重新初始化网络管理器的逻辑 |
||||
|
Log.i(TAG, "网络管理器重新初始化完成"); |
||||
|
} catch (Exception e) { |
||||
|
Log.e(TAG, "重新初始化网络管理器失败", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 执行系统健康检查 |
||||
|
*/ |
||||
|
private void performSystemHealthCheck() { |
||||
|
HealthMonitor healthMonitor = HealthMonitor.getInstance(context); |
||||
|
healthMonitor.performHealthCheck(); |
||||
|
|
||||
|
HealthMonitor.HealthReport report = healthMonitor.getCurrentHealthReport(); |
||||
|
Log.i(TAG, "系统健康检查完成,状态: " + report.overallStatus); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 清理日志文件 |
||||
|
*/ |
||||
|
private void cleanupLogFiles() { |
||||
|
try { |
||||
|
// LogManager会自动处理日志轮转和清理 |
||||
|
LogManager logManager = LogManager.getInstance(context); |
||||
|
Log.d(TAG, "日志文件清理检查完成"); |
||||
|
} catch (Exception e) { |
||||
|
Log.e(TAG, "日志文件清理失败", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 生成系统运行报告 |
||||
|
*/ |
||||
|
private void generateSystemReport() { |
||||
|
try { |
||||
|
HealthMonitor healthMonitor = HealthMonitor.getInstance(context); |
||||
|
HealthMonitor.HealthReport report = healthMonitor.getCurrentHealthReport(); |
||||
|
|
||||
|
String reportMessage = String.format( |
||||
|
"系统运行报告 - 运行时间: %d小时, 内存使用: %.1f%%, 网络: %s, 维护次数: L%d/M%d/H%d/D%d", |
||||
|
report.uptime / (1000 * 60 * 60), |
||||
|
report.memoryUsage * 100, |
||||
|
report.networkAvailable ? "正常" : "异常", |
||||
|
lightMaintenanceCount, |
||||
|
mediumMaintenanceCount, |
||||
|
heavyMaintenanceCount, |
||||
|
dailyMaintenanceCount |
||||
|
); |
||||
|
|
||||
|
Log.i(TAG, reportMessage); |
||||
|
LogManager.logOperation("SystemReport", reportMessage); |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
Log.e(TAG, "生成系统报告失败", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 执行全面系统清理 |
||||
|
*/ |
||||
|
private void performComprehensiveCleanup() { |
||||
|
// 执行所有级别的清理任务 |
||||
|
performDeepMemoryCleanup(); |
||||
|
clearDiskCaches(); |
||||
|
|
||||
|
// 等待一段时间让清理任务完成 |
||||
|
try { |
||||
|
Thread.sleep(2000); |
||||
|
} catch (InterruptedException e) { |
||||
|
Thread.currentThread().interrupt(); |
||||
|
} |
||||
|
|
||||
|
Log.i(TAG, "全面系统清理完成"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 重置计数器 |
||||
|
*/ |
||||
|
private void resetCounters() { |
||||
|
lightMaintenanceCount = 0; |
||||
|
mediumMaintenanceCount = 0; |
||||
|
heavyMaintenanceCount = 0; |
||||
|
// 不重置每日维护计数器,用于跟踪总运行天数 |
||||
|
|
||||
|
Log.d(TAG, "维护任务计数器已重置"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 验证系统完整性 |
||||
|
*/ |
||||
|
private void validateSystemIntegrity() { |
||||
|
try { |
||||
|
// 检查关键组件是否正常运行 |
||||
|
boolean memoryManagerOk = MemoryManager.getInstance(context).isMemoryHealthy(); |
||||
|
boolean networkManagerOk = NetworkStabilityManager.getInstance(context).isNetworkAvailable(); |
||||
|
boolean keepAliveOk = KeepAliveManager.getInstance(context).isKeepingAlive(); |
||||
|
|
||||
|
String integrityReport = String.format( |
||||
|
"系统完整性检查 - 内存管理: %s, 网络管理: %s, 保持活跃: %s", |
||||
|
memoryManagerOk ? "正常" : "异常", |
||||
|
networkManagerOk ? "正常" : "异常", |
||||
|
keepAliveOk ? "正常" : "异常" |
||||
|
); |
||||
|
|
||||
|
Log.i(TAG, integrityReport); |
||||
|
LogManager.logOperation("SystemIntegrity", integrityReport); |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
Log.e(TAG, "系统完整性验证失败", e); |
||||
|
LogManager.logError(TAG, "系统完整性验证失败", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取维护统计信息 |
||||
|
*/ |
||||
|
public String getMaintenanceStats() { |
||||
|
return String.format( |
||||
|
"维护统计 - 轻度: %d次, 中度: %d次, 重度: %d次, 每日: %d次", |
||||
|
lightMaintenanceCount, |
||||
|
mediumMaintenanceCount, |
||||
|
heavyMaintenanceCount, |
||||
|
dailyMaintenanceCount |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 立即执行维护任务 |
||||
|
*/ |
||||
|
public void performImmediateMaintenance() { |
||||
|
Log.i(TAG, "立即执行维护任务"); |
||||
|
|
||||
|
new Thread(new Runnable() { |
||||
|
@Override |
||||
|
public void run() { |
||||
|
performMediumMaintenance(); |
||||
|
} |
||||
|
}).start(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,245 @@ |
|||||
|
package com.ouxuan.oxface.utils; |
||||
|
|
||||
|
import android.app.ActivityManager; |
||||
|
import android.content.Context; |
||||
|
import android.util.Log; |
||||
|
|
||||
|
import com.bumptech.glide.Glide; |
||||
|
|
||||
|
/** |
||||
|
* 内存管理器 |
||||
|
* 用于24小时无人值守环境下的内存监控和优化 |
||||
|
*/ |
||||
|
public class MemoryManager { |
||||
|
|
||||
|
private static final String TAG = "MemoryManager"; |
||||
|
private static final float MEMORY_THRESHOLD = 0.85f; // 85%内存使用率阈值 |
||||
|
private static final float CRITICAL_MEMORY_THRESHOLD = 0.95f; // 95%临界阈值 |
||||
|
private static final long MEMORY_CHECK_INTERVAL = 30 * 1000; // 30秒检查间隔 |
||||
|
|
||||
|
private static MemoryManager instance; |
||||
|
private Context context; |
||||
|
private boolean isMonitoring = false; |
||||
|
|
||||
|
private MemoryManager(Context context) { |
||||
|
this.context = context.getApplicationContext(); |
||||
|
} |
||||
|
|
||||
|
public static synchronized MemoryManager getInstance(Context context) { |
||||
|
if (instance == null) { |
||||
|
instance = new MemoryManager(context); |
||||
|
} |
||||
|
return instance; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 开始内存监控 |
||||
|
*/ |
||||
|
public void startMemoryMonitoring() { |
||||
|
if (isMonitoring) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
isMonitoring = true; |
||||
|
Log.i(TAG, "开始内存监控"); |
||||
|
|
||||
|
Thread monitorThread = new Thread(new Runnable() { |
||||
|
@Override |
||||
|
public void run() { |
||||
|
while (isMonitoring) { |
||||
|
try { |
||||
|
monitorMemory(); |
||||
|
Thread.sleep(MEMORY_CHECK_INTERVAL); |
||||
|
} catch (InterruptedException e) { |
||||
|
Log.w(TAG, "内存监控线程被中断"); |
||||
|
break; |
||||
|
} catch (Exception e) { |
||||
|
Log.e(TAG, "内存监控异常", e); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
monitorThread.setName("MemoryMonitor"); |
||||
|
monitorThread.setDaemon(true); |
||||
|
monitorThread.start(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 停止内存监控 |
||||
|
*/ |
||||
|
public void stopMemoryMonitoring() { |
||||
|
isMonitoring = false; |
||||
|
Log.i(TAG, "停止内存监控"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 监控内存使用情况 |
||||
|
*/ |
||||
|
public void monitorMemory() { |
||||
|
try { |
||||
|
Runtime runtime = Runtime.getRuntime(); |
||||
|
long maxMemory = runtime.maxMemory(); |
||||
|
long totalMemory = runtime.totalMemory(); |
||||
|
long freeMemory = runtime.freeMemory(); |
||||
|
long usedMemory = totalMemory - freeMemory; |
||||
|
float memoryUsage = (float) usedMemory / maxMemory; |
||||
|
|
||||
|
// 记录详细内存信息 |
||||
|
Log.d(TAG, String.format("内存使用情况 - 使用率: %.1f%%, 已用: %dMB, 最大: %dMB", |
||||
|
memoryUsage * 100, usedMemory / (1024 * 1024), maxMemory / (1024 * 1024))); |
||||
|
|
||||
|
// 检查内存使用率 |
||||
|
if (memoryUsage > CRITICAL_MEMORY_THRESHOLD) { |
||||
|
// 临界状态,强制清理 |
||||
|
Log.w(TAG, "内存使用率达到临界状态: " + String.format("%.1f%%", memoryUsage * 100)); |
||||
|
performCriticalMemoryCleanup(); |
||||
|
} else if (memoryUsage > MEMORY_THRESHOLD) { |
||||
|
// 超过阈值,执行清理 |
||||
|
Log.w(TAG, "内存使用率过高: " + String.format("%.1f%%", memoryUsage * 100)); |
||||
|
performMemoryCleanup(); |
||||
|
} |
||||
|
|
||||
|
// 检查系统内存情况 |
||||
|
checkSystemMemory(); |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
Log.e(TAG, "内存监控失败", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 执行内存清理 |
||||
|
*/ |
||||
|
public void performMemoryCleanup() { |
||||
|
try { |
||||
|
Log.i(TAG, "开始内存清理"); |
||||
|
|
||||
|
// 清理Glide图片缓存 |
||||
|
if (context != null) { |
||||
|
Glide.get(context).clearMemory(); |
||||
|
Log.d(TAG, "已清理Glide内存缓存"); |
||||
|
} |
||||
|
|
||||
|
// 建议垃圾回收 |
||||
|
System.gc(); |
||||
|
Log.d(TAG, "已触发垃圾回收"); |
||||
|
|
||||
|
// 记录清理后的内存状态 |
||||
|
logMemoryStatus("清理后"); |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
Log.e(TAG, "内存清理失败", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 执行临界内存清理 |
||||
|
*/ |
||||
|
private void performCriticalMemoryCleanup() { |
||||
|
try { |
||||
|
Log.w(TAG, "执行临界内存清理"); |
||||
|
|
||||
|
// 清理所有可能的缓存 |
||||
|
if (context != null) { |
||||
|
Glide.get(context).clearMemory(); |
||||
|
// 强制清理磁盘缓存(在后台线程中) |
||||
|
new Thread(new Runnable() { |
||||
|
@Override |
||||
|
public void run() { |
||||
|
Glide.get(context).clearDiskCache(); |
||||
|
} |
||||
|
}).start(); |
||||
|
} |
||||
|
|
||||
|
// 多次触发垃圾回收 |
||||
|
for (int i = 0; i < 3; i++) { |
||||
|
System.gc(); |
||||
|
try { |
||||
|
Thread.sleep(100); |
||||
|
} catch (InterruptedException e) { |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
Log.w(TAG, "临界内存清理完成"); |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
Log.e(TAG, "临界内存清理失败", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 检查系统内存情况 |
||||
|
*/ |
||||
|
private void checkSystemMemory() { |
||||
|
try { |
||||
|
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); |
||||
|
if (activityManager != null) { |
||||
|
ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo(); |
||||
|
activityManager.getMemoryInfo(memoryInfo); |
||||
|
|
||||
|
float availableMemoryPercentage = (float) memoryInfo.availMem / memoryInfo.totalMem; |
||||
|
|
||||
|
Log.d(TAG, String.format("系统内存 - 可用: %dMB, 总计: %dMB, 可用率: %.1f%%", |
||||
|
memoryInfo.availMem / (1024 * 1024), |
||||
|
memoryInfo.totalMem / (1024 * 1024), |
||||
|
availableMemoryPercentage * 100)); |
||||
|
|
||||
|
// 如果系统内存不足,记录警告 |
||||
|
if (memoryInfo.lowMemory) { |
||||
|
Log.w(TAG, "系统内存不足警告"); |
||||
|
} |
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
Log.e(TAG, "检查系统内存失败", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 记录内存状态 |
||||
|
*/ |
||||
|
private void logMemoryStatus(String prefix) { |
||||
|
try { |
||||
|
Runtime runtime = Runtime.getRuntime(); |
||||
|
long maxMemory = runtime.maxMemory(); |
||||
|
long totalMemory = runtime.totalMemory(); |
||||
|
long freeMemory = runtime.freeMemory(); |
||||
|
long usedMemory = totalMemory - freeMemory; |
||||
|
float memoryUsage = (float) usedMemory / maxMemory; |
||||
|
|
||||
|
Log.i(TAG, String.format("%s内存状态 - 使用率: %.1f%%, 已用: %dMB, 空闲: %dMB, 最大: %dMB", |
||||
|
prefix, |
||||
|
memoryUsage * 100, |
||||
|
usedMemory / (1024 * 1024), |
||||
|
freeMemory / (1024 * 1024), |
||||
|
maxMemory / (1024 * 1024))); |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
Log.e(TAG, "记录内存状态失败", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取当前内存使用率 |
||||
|
*/ |
||||
|
public float getCurrentMemoryUsage() { |
||||
|
try { |
||||
|
Runtime runtime = Runtime.getRuntime(); |
||||
|
long maxMemory = runtime.maxMemory(); |
||||
|
long totalMemory = runtime.totalMemory(); |
||||
|
long freeMemory = runtime.freeMemory(); |
||||
|
long usedMemory = totalMemory - freeMemory; |
||||
|
return (float) usedMemory / maxMemory; |
||||
|
} catch (Exception e) { |
||||
|
Log.e(TAG, "获取内存使用率失败", e); |
||||
|
return 0f; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 检查内存是否健康 |
||||
|
*/ |
||||
|
public boolean isMemoryHealthy() { |
||||
|
return getCurrentMemoryUsage() < MEMORY_THRESHOLD; |
||||
|
} |
||||
|
} |
@ -0,0 +1,382 @@ |
|||||
|
package com.ouxuan.oxface.utils; |
||||
|
|
||||
|
import android.content.Context; |
||||
|
|
||||
|
import com.blankj.utilcode.util.AppUtils; |
||||
|
import com.blankj.utilcode.util.DeviceUtils; |
||||
|
import com.blankj.utilcode.util.FileUtils; |
||||
|
import com.blankj.utilcode.util.NetworkUtils; |
||||
|
import com.blankj.utilcode.util.PathUtils; |
||||
|
import com.blankj.utilcode.util.PhoneUtils; |
||||
|
import com.blankj.utilcode.util.ScreenUtils; |
||||
|
import com.blankj.utilcode.util.TimeUtils; |
||||
|
import com.blankj.utilcode.util.ToastUtils; |
||||
|
|
||||
|
/** |
||||
|
* UtilCode工具库使用示例类 |
||||
|
* 提供常用的Android开发工具方法封装 |
||||
|
*/ |
||||
|
public class UtilCodeHelper { |
||||
|
|
||||
|
private static final String TAG = "UtilCodeHelper"; |
||||
|
|
||||
|
/** |
||||
|
* 应用信息相关工具方法 |
||||
|
*/ |
||||
|
public static class App { |
||||
|
/** |
||||
|
* 获取应用版本名称 |
||||
|
*/ |
||||
|
public static String getAppVersionName() { |
||||
|
return AppUtils.getAppVersionName(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取应用版本号 |
||||
|
*/ |
||||
|
public static int getAppVersionCode() { |
||||
|
return AppUtils.getAppVersionCode(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取应用包名 |
||||
|
*/ |
||||
|
public static String getAppPackageName() { |
||||
|
return AppUtils.getAppPackageName(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 判断应用是否在前台 |
||||
|
*/ |
||||
|
public static boolean isAppForeground() { |
||||
|
return AppUtils.isAppForeground(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取应用信息摘要 |
||||
|
*/ |
||||
|
public static String getAppInfoSummary() { |
||||
|
return String.format("应用信息 - 包名: %s, 版本: %s(%d)", |
||||
|
getAppPackageName(), getAppVersionName(), getAppVersionCode()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 设备信息相关工具方法 |
||||
|
*/ |
||||
|
public static class Device { |
||||
|
/** |
||||
|
* 获取设备型号 |
||||
|
*/ |
||||
|
public static String getModel() { |
||||
|
return DeviceUtils.getModel(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取设备制造商 |
||||
|
*/ |
||||
|
public static String getManufacturer() { |
||||
|
return DeviceUtils.getManufacturer(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取Android版本 |
||||
|
*/ |
||||
|
public static String getSDKVersionName() { |
||||
|
return DeviceUtils.getSDKVersionName(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取Android API级别 |
||||
|
*/ |
||||
|
public static int getSDKVersionCode() { |
||||
|
return DeviceUtils.getSDKVersionCode(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 判断设备是否为平板 |
||||
|
*/ |
||||
|
public static boolean isTablet() { |
||||
|
return DeviceUtils.isTablet(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取设备信息摘要 |
||||
|
*/ |
||||
|
public static String getDeviceInfoSummary() { |
||||
|
return String.format("设备信息 - 型号: %s, 制造商: %s, Android: %s(API %d), 平板: %s", |
||||
|
getModel(), getManufacturer(), getSDKVersionName(), |
||||
|
getSDKVersionCode(), isTablet() ? "是" : "否"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 屏幕信息相关工具方法 |
||||
|
*/ |
||||
|
public static class Screen { |
||||
|
/** |
||||
|
* 获取屏幕宽度(像素) |
||||
|
*/ |
||||
|
public static int getScreenWidth() { |
||||
|
return ScreenUtils.getScreenWidth(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取屏幕高度(像素) |
||||
|
*/ |
||||
|
public static int getScreenHeight() { |
||||
|
return ScreenUtils.getScreenHeight(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取屏幕密度 |
||||
|
*/ |
||||
|
public static float getScreenDensity() { |
||||
|
return ScreenUtils.getScreenDensity(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* dp转px |
||||
|
*/ |
||||
|
public static int dp2px(float dpValue) { |
||||
|
return com.blankj.utilcode.util.SizeUtils.dp2px(dpValue); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* px转dp |
||||
|
*/ |
||||
|
public static int px2dp(float pxValue) { |
||||
|
return com.blankj.utilcode.util.SizeUtils.px2dp(pxValue); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取屏幕信息摘要 |
||||
|
*/ |
||||
|
public static String getScreenInfoSummary() { |
||||
|
return String.format("屏幕信息 - 尺寸: %dx%d, 密度: %.2f", |
||||
|
getScreenWidth(), getScreenHeight(), getScreenDensity()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 网络相关工具方法 |
||||
|
*/ |
||||
|
public static class Network { |
||||
|
/** |
||||
|
* 判断网络是否连接 |
||||
|
*/ |
||||
|
public static boolean isConnected() { |
||||
|
return NetworkUtils.isConnected(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 判断是否为WiFi连接 |
||||
|
*/ |
||||
|
public static boolean isWifiConnected() { |
||||
|
return NetworkUtils.isWifiConnected(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 判断是否为移动数据连接 |
||||
|
*/ |
||||
|
public static boolean isMobileData() { |
||||
|
return NetworkUtils.isMobileData(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取网络类型 |
||||
|
*/ |
||||
|
public static NetworkUtils.NetworkType getNetworkType() { |
||||
|
return NetworkUtils.getNetworkType(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取网络状态摘要 |
||||
|
*/ |
||||
|
public static String getNetworkStatusSummary() { |
||||
|
if (!isConnected()) { |
||||
|
return "网络状态 - 未连接"; |
||||
|
} |
||||
|
|
||||
|
String connectionType = "未知"; |
||||
|
if (isWifiConnected()) { |
||||
|
connectionType = "WiFi"; |
||||
|
} else if (isMobileData()) { |
||||
|
connectionType = "移动数据"; |
||||
|
} |
||||
|
|
||||
|
return String.format("网络状态 - 已连接(%s), 类型: %s", |
||||
|
connectionType, getNetworkType()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 文件操作相关工具方法 |
||||
|
*/ |
||||
|
public static class File { |
||||
|
/** |
||||
|
* 获取应用外部存储目录 |
||||
|
*/ |
||||
|
public static String getAppExternalPath() { |
||||
|
return PathUtils.getExternalAppDataPath(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取应用内部存储目录 |
||||
|
*/ |
||||
|
public static String getAppInternalPath() { |
||||
|
return PathUtils.getInternalAppDataPath(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 创建目录 |
||||
|
*/ |
||||
|
public static boolean createDir(String dirPath) { |
||||
|
return FileUtils.createOrExistsDir(dirPath); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 判断文件是否存在 |
||||
|
*/ |
||||
|
public static boolean isFileExists(String filePath) { |
||||
|
return FileUtils.isFileExists(filePath); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取文件大小(字节) |
||||
|
*/ |
||||
|
public static long getFileSize(String filePath) { |
||||
|
return FileUtils.getFileLength(filePath); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 删除文件或目录 |
||||
|
*/ |
||||
|
public static boolean delete(String path) { |
||||
|
return FileUtils.delete(path); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 时间相关工具方法 |
||||
|
*/ |
||||
|
public static class Time { |
||||
|
/** |
||||
|
* 获取当前时间戳(毫秒) |
||||
|
*/ |
||||
|
public static long getCurrentTimeMillis() { |
||||
|
return System.currentTimeMillis(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 时间戳转字符串(默认格式: yyyy-MM-dd HH:mm:ss) |
||||
|
*/ |
||||
|
public static String millis2String(long millis) { |
||||
|
return TimeUtils.millis2String(millis); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 时间戳转字符串(自定义格式) |
||||
|
*/ |
||||
|
public static String millis2String(long millis, String pattern) { |
||||
|
return TimeUtils.millis2String(millis, pattern); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 字符串转时间戳 |
||||
|
*/ |
||||
|
public static long string2Millis(String time, String pattern) { |
||||
|
return TimeUtils.string2Millis(time, pattern); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取友好的时间描述 |
||||
|
*/ |
||||
|
public static String getFriendlyTimeSpanByNow(long millis) { |
||||
|
return TimeUtils.getFriendlyTimeSpanByNow(millis); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Toast相关工具方法 |
||||
|
*/ |
||||
|
public static class Toast { |
||||
|
/** |
||||
|
* 显示短时间Toast |
||||
|
*/ |
||||
|
public static void showShort(String message) { |
||||
|
ToastUtils.showShort(message); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 显示长时间Toast |
||||
|
*/ |
||||
|
public static void showLong(String message) { |
||||
|
ToastUtils.showLong(message); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 取消Toast显示 |
||||
|
*/ |
||||
|
public static void cancel() { |
||||
|
ToastUtils.cancel(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 手机相关工具方法 |
||||
|
*/ |
||||
|
public static class Phone { |
||||
|
/** |
||||
|
* 判断设备是否为手机 |
||||
|
*/ |
||||
|
public static boolean isPhone() { |
||||
|
return PhoneUtils.isPhone(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取IMEI(需要权限) |
||||
|
*/ |
||||
|
public static String getIMEI() { |
||||
|
try { |
||||
|
return PhoneUtils.getIMEI(); |
||||
|
} catch (Exception e) { |
||||
|
LogManager.logWarning(TAG, "获取IMEI失败: " + e.getMessage()); |
||||
|
return ""; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取手机型号 |
||||
|
*/ |
||||
|
public static String getPhoneType() { |
||||
|
return PhoneUtils.getPhoneType() + ""; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 系统信息汇总 |
||||
|
* 用于24小时无人值守应用的系统状态记录 |
||||
|
*/ |
||||
|
public static String getSystemInfoSummary() { |
||||
|
StringBuilder summary = new StringBuilder(); |
||||
|
summary.append("=== 系统信息汇总 ===\n"); |
||||
|
summary.append(App.getAppInfoSummary()).append("\n"); |
||||
|
summary.append(Device.getDeviceInfoSummary()).append("\n"); |
||||
|
summary.append(Screen.getScreenInfoSummary()).append("\n"); |
||||
|
summary.append(Network.getNetworkStatusSummary()).append("\n"); |
||||
|
summary.append("当前时间: ").append(Time.millis2String(Time.getCurrentTimeMillis())).append("\n"); |
||||
|
summary.append("应用前台状态: ").append(App.isAppForeground() ? "是" : "否").append("\n"); |
||||
|
summary.append("=================="); |
||||
|
|
||||
|
return summary.toString(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 记录系统信息到日志 |
||||
|
*/ |
||||
|
public static void logSystemInfo() { |
||||
|
String systemInfo = getSystemInfoSummary(); |
||||
|
LogManager.logOperation(TAG, systemInfo); |
||||
|
} |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue