0
点赞
收藏
分享

微信扫一扫

Android Tinker运行时错误上报流程原理深度剖析(20)


Android Tinker运行时错误上报流程原理深度剖析

一、引言

在Android应用开发中,热修复技术能够快速修复线上问题,提升用户体验。Tinker作为一款优秀的热修复框架,其运行时错误上报流程是保证框架稳定性和可维护性的关键。本文将从源码级别深入分析Tinker的运行时错误上报流程原理,详细阐述每一个步骤的具体实现和作用,帮助开发者更好地理解和使用Tinker框架。

二、运行时错误上报概述

2.1 运行时错误的定义

运行时错误是指应用在运行过程中出现的异常情况,包括未捕获的异常(Uncaught Exception)和ANR(Application Not Responding)等。这些错误会导致应用崩溃或无响应,严重影响用户体验。

2.2 错误上报的重要性

及时准确地收集和上报运行时错误信息,对于开发者来说至关重要。通过分析错误信息,开发者可以快速定位问题,及时修复,提高应用的稳定性和可靠性。

2.3 Tinker错误上报的特点

Tinker的错误上报机制具有以下特点:

  • 与热修复功能深度集成:能够区分正常错误和热修复相关错误
  • 轻量级:对应用性能影响小
  • 高度可定制:支持自定义错误处理和上报逻辑
  • 多维度数据收集:收集丰富的错误上下文信息,帮助开发者快速定位问题

三、错误捕获机制

3.1 全局异常处理器

Tinker通过注册全局异常处理器来捕获未被处理的异常。源码如下:

// TinkerUncaughtHandler.java
public class TinkerUncaughtHandler implements Thread.UncaughtExceptionHandler {
    private static final String TAG = "Tinker.UncaughtHandler";
    private final Thread.UncaughtExceptionHandler originalHandler;
    
    public TinkerUncaughtHandler() {
        // 获取原始的异常处理器
        this.originalHandler = Thread.getDefaultUncaughtExceptionHandler();
        // 设置当前实例为全局异常处理器
        Thread.setDefaultUncaughtExceptionHandler(this);
    }
    
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        // 处理异常
        handleException(e);
        
        // 如果原始处理器不为空,则调用原始处理器处理异常
        if (originalHandler != null) {
            originalHandler.uncaughtException(t, e);
        } else {
            // 否则终止当前线程
            android.os.Process.killProcess(android.os.Process.myPid());
            System.exit(10);
        }
    }
    
    private void handleException(Throwable e) {
        // 记录异常信息
        TinkerLog.e(TAG, "uncaughtException: " + e.getMessage(), e);
        
        // 检查是否是Tinker相关异常
        if (isTinkerRelatedException(e)) {
            // 处理Tinker相关异常
            handleTinkerException(e);
        }
        
        // 收集错误信息
        collectErrorInfo(e);
        
        // 上报错误信息
        reportErrorInfo();
    }
    
    // 其他辅助方法...
}

3.2 ANR监控机制

Tinker还实现了ANR监控机制,用于捕获应用无响应的情况。源码如下:

// AnrMonitor.java
public class AnrMonitor {
    private static final String TAG = "Tinker.AnrMonitor";
    private static final long ANR_TIMEOUT = 5000; // 5秒
    
    private final Handler mainHandler = new Handler(Looper.getMainLooper());
    private final Object lock = new Object();
    private boolean monitoring = false;
    private long lastCheckTime = 0;
    
    public void start() {
        if (monitoring) {
            return;
        }
        
        monitoring = true;
        
        // 启动定时检查任务
        mainHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                checkAnr();
                
                if (monitoring) {
                    mainHandler.postDelayed(this, ANR_TIMEOUT);
                }
            }
        }, ANR_TIMEOUT);
    }
    
    public void stop() {
        monitoring = false;
        mainHandler.removeCallbacksAndMessages(null);
    }
    
    private void checkAnr() {
        synchronized (lock) {
            long currentTime = System.currentTimeMillis();
            
            // 如果主线程处理时间过长,认为发生了ANR
            if (currentTime - lastCheckTime > ANR_TIMEOUT * 2) {
                TinkerLog.e(TAG, "ANR detected!");
                
                // 收集ANR信息
                collectAnrInfo();
                
                // 上报ANR信息
                reportAnrInfo();
            }
            
            lastCheckTime = currentTime;
        }
    }
    
    // 其他辅助方法...
}

四、错误信息收集

4.1 基本错误信息收集

Tinker会收集基本的错误信息,包括异常堆栈、错误类型等。源码如下:

// ErrorReporter.java
public class ErrorReporter {
    private static final String TAG = "Tinker.ErrorReporter";
    
    public static void collectErrorInfo(Throwable throwable) {
        if (throwable == null) {
            return;
        }
        
        // 创建错误信息对象
        ErrorInfo errorInfo = new ErrorInfo();
        
        // 设置错误类型
        errorInfo.setErrorType(throwable.getClass().getName());
        
        // 设置错误消息
        errorInfo.setErrorMessage(throwable.getMessage());
        
        // 设置错误堆栈
        errorInfo.setStackTrace(getStackTrace(throwable));
        
        // 设置时间戳
        errorInfo.setTimestamp(System.currentTimeMillis());
        
        // 保存错误信息
        saveErrorInfo(errorInfo);
    }
    
    private static String getStackTrace(Throwable throwable) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        
        try {
            // 打印异常堆栈
            throwable.printStackTrace(pw);
            
            // 获取嵌套异常堆栈
            Throwable cause = throwable.getCause();
            while (cause != null) {
                pw.print("\nCaused by: ");
                cause.printStackTrace(pw);
                cause = cause.getCause();
            }
            
            return sw.toString();
        } finally {
            pw.close();
        }
    }
    
    // 其他辅助方法...
}

4.2 环境信息收集

除了基本错误信息,Tinker还会收集应用运行的环境信息,帮助开发者更好地理解错误发生的上下文。源码如下:

// EnvironmentCollector.java
public class EnvironmentCollector {
    private static final String TAG = "Tinker.EnvCollector";
    
    public static EnvironmentInfo collectEnvironmentInfo(Context context) {
        if (context == null) {
            return null;
        }
        
        EnvironmentInfo envInfo = new EnvironmentInfo();
        
        try {
            // 收集设备信息
            collectDeviceInfo(envInfo);
            
            // 收集应用信息
            collectAppInfo(context, envInfo);
            
            // 收集Tinker信息
            collectTinkerInfo(context, envInfo);
            
            // 收集内存信息
            collectMemoryInfo(envInfo);
            
            // 收集CPU信息
            collectCpuInfo(envInfo);
            
            // 收集电池信息
            collectBatteryInfo(context, envInfo);
            
            // 收集网络信息
            collectNetworkInfo(context, envInfo);
        } catch (Exception e) {
            TinkerLog.e(TAG, "collectEnvironmentInfo: Exception", e);
        }
        
        return envInfo;
    }
    
    // 其他辅助方法...
}

4.3 Tinker相关信息收集

Tinker会特别收集与热修复相关的信息,包括补丁版本、补丁状态等。源码如下:

// TinkerInfoCollector.java
public class TinkerInfoCollector {
    private static final String TAG = "Tinker.InfoCollector";
    
    public static void collectTinkerInfo(Context context, EnvironmentInfo envInfo) {
        if (context == null || envInfo == null) {
            return;
        }
        
        try {
            // 获取Tinker实例
            Tinker tinker = Tinker.with(context);
            
            // 获取补丁信息
            PatchInfo patchInfo = tinker.getPatchInfo();
            
            if (patchInfo != null) {
                // 设置补丁版本
                envInfo.setPatchVersion(patchInfo.getNewVersion());
                
                // 设置补丁状态
                envInfo.setPatchState(patchInfo.isEnabled() ? "enabled" : "disabled");
                
                // 设置补丁路径
                envInfo.setPatchPath(patchInfo.getPatchPath());
                
                // 设置补丁加载时间
                envInfo.setPatchLoadTime(patchInfo.getLoadTime());
            }
            
            // 获取Tinker运行模式
            envInfo.setTinkerRunMode(tinker.isMainProcess() ? "main_process" : "other_process");
            
            // 获取Tinker版本
            envInfo.setTinkerVersion(TinkerInstaller.getTinkerVersion(context));
        } catch (Exception e) {
            TinkerLog.e(TAG, "collectTinkerInfo: Exception", e);
        }
    }
}

五、错误信息存储

5.1 错误信息序列化

Tinker会将收集到的错误信息序列化为JSON格式,以便存储和传输。源码如下:

// ErrorInfoSerializer.java
public class ErrorInfoSerializer {
    private static final String TAG = "Tinker.ErrorSerializer";
    
    public static String serialize(ErrorInfo errorInfo) {
        if (errorInfo == null) {
            return null;
        }
        
        try {
            // 创建JSON对象
            JSONObject jsonObject = new JSONObject();
            
            // 设置基本错误信息
            jsonObject.put("errorType", errorInfo.getErrorType());
            jsonObject.put("errorMessage", errorInfo.getErrorMessage());
            jsonObject.put("stackTrace", errorInfo.getStackTrace());
            jsonObject.put("timestamp", errorInfo.getTimestamp());
            
            // 设置环境信息
            if (errorInfo.getEnvironmentInfo() != null) {
                jsonObject.put("environmentInfo", serializeEnvironmentInfo(errorInfo.getEnvironmentInfo()));
            }
            
            return jsonObject.toString();
        } catch (JSONException e) {
            TinkerLog.e(TAG, "serialize: JSONException", e);
            return null;
        }
    }
    
    private static JSONObject serializeEnvironmentInfo(EnvironmentInfo envInfo) throws JSONException {
        JSONObject envJson = new JSONObject();
        
        // 设置设备信息
        envJson.put("deviceInfo", serializeDeviceInfo(envInfo.getDeviceInfo()));
        
        // 设置应用信息
        envJson.put("appInfo", serializeAppInfo(envInfo.getAppInfo()));
        
        // 设置Tinker信息
        envJson.put("tinkerInfo", serializeTinkerInfo(envInfo.getTinkerInfo()));
        
        // 设置系统信息
        envJson.put("systemInfo", serializeSystemInfo(envInfo.getSystemInfo()));
        
        // 设置性能信息
        envJson.put("performanceInfo", serializePerformanceInfo(envInfo.getPerformanceInfo()));
        
        return envJson;
    }
    
    // 其他辅助方法...
}

5.2 错误信息存储到文件

Tinker会将序列化后的错误信息存储到本地文件中。源码如下:

// ErrorStorage.java
public class ErrorStorage {
    private static final String TAG = "Tinker.ErrorStorage";
    private static final String ERROR_DIR = "tinker_error";
    private static final String ERROR_FILE_PREFIX = "error_";
    private static final String ERROR_FILE_SUFFIX = ".json";
    private static final int MAX_ERROR_FILES = 100; // 最多保存100个错误文件
    
    private final Context context;
    
    public ErrorStorage(Context context) {
        this.context = context.getApplicationContext();
    }
    
    public void saveErrorInfo(ErrorInfo errorInfo) {
        if (errorInfo == null) {
            return;
        }
        
        try {
            // 获取错误目录
            File errorDir = getErrorDirectory();
            
            // 创建错误文件
            File errorFile = createErrorFile(errorDir);
            
            // 序列化错误信息
            String errorJson = ErrorInfoSerializer.serialize(errorInfo);
            
            if (errorJson != null) {
                // 写入文件
                FileOutputStream fos = new FileOutputStream(errorFile);
                fos.write(errorJson.getBytes());
                fos.close();
                
                TinkerLog.i(TAG, "Error info saved to: " + errorFile.getAbsolutePath());
            }
            
            // 清理旧的错误文件
            cleanOldErrorFiles(errorDir);
        } catch (Exception e) {
            TinkerLog.e(TAG, "saveErrorInfo: Exception", e);
        }
    }
    
    private File getErrorDirectory() {
        File errorDir = new File(context.getFilesDir(), ERROR_DIR);
        
        if (!errorDir.exists()) {
            errorDir.mkdirs();
        }
        
        return errorDir;
    }
    
    // 其他辅助方法...
}

六、错误上报策略

6.1 上报条件判断

Tinker会根据一定的条件判断是否上报错误信息,避免过多的上报请求。源码如下:

// ErrorReportPolicy.java
public class ErrorReportPolicy {
    private static final String TAG = "Tinker.ReportPolicy";
    private static final int MAX_REPORTS_PER_DAY = 10; // 每天最多上报10次
    private static final int MIN_INTERVAL = 5 * 60 * 1000; // 两次上报之间的最小间隔为5分钟
    
    private final Context context;
    private final SharedPreferences preferences;
    
    public ErrorReportPolicy(Context context) {
        this.context = context.getApplicationContext();
        this.preferences = context.getSharedPreferences("tinker_error_policy", Context.MODE_PRIVATE);
    }
    
    public boolean shouldReport() {
        try {
            // 检查网络连接
            if (!isNetworkAvailable()) {
                TinkerLog.i(TAG, "Network not available, skip reporting");
                return false;
            }
            
            // 检查上报频率
            if (!checkReportFrequency()) {
                TinkerLog.i(TAG, "Report frequency exceeded, skip reporting");
                return false;
            }
            
            // 检查是否处于调试模式
            if (isDebugMode()) {
                TinkerLog.i(TAG, "App is in debug mode, skip reporting");
                return false;
            }
            
            return true;
        } catch (Exception e) {
            TinkerLog.e(TAG, "shouldReport: Exception", e);
            return false;
        }
    }
    
    private boolean isNetworkAvailable() {
        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = cm.getActiveNetworkInfo();
        return networkInfo != null && networkInfo.isConnected();
    }
    
    // 其他辅助方法...
}

6.2 上报时机选择

Tinker会选择合适的时机上报错误信息,避免影响用户体验。源码如下:

// ErrorReportScheduler.java
public class ErrorReportScheduler {
    private static final String TAG = "Tinker.ReportScheduler";
    private static final long INITIAL_DELAY = 10 * 1000; // 初始延迟10秒
    private static final long PERIOD = 30 * 60 * 1000; // 周期为30分钟
    
    private final Context context;
    private final ScheduledExecutorService executorService;
    private final ErrorReportPolicy reportPolicy;
    private final ErrorReporter errorReporter;
    
    public ErrorReportScheduler(Context context) {
        this.context = context.getApplicationContext();
        this.executorService = Executors.newSingleThreadScheduledExecutor();
        this.reportPolicy = new ErrorReportPolicy(context);
        this.errorReporter = new ErrorReporter(context);
    }
    
    public void start() {
        // 提交定时任务
        executorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    // 检查是否应该上报
                    if (reportPolicy.shouldReport()) {
                        // 执行上报
                        errorReporter.reportPendingErrors();
                    }
                } catch (Exception e) {
                    TinkerLog.e(TAG, "Report task error", e);
                }
            }
        }, INITIAL_DELAY, PERIOD, TimeUnit.MILLISECONDS);
    }
    
    public void stop() {
        executorService.shutdownNow();
    }
    
    // 其他辅助方法...
}

七、错误上报网络实现

7.1 网络请求封装

Tinker使用OkHttp封装网络请求,实现错误信息的上报。源码如下:

// NetworkClient.java
public class NetworkClient {
    private static final String TAG = "Tinker.NetworkClient";
    private static final String REPORT_URL = "https://example.com/tinker/report";
    
    private final OkHttpClient client;
    
    public NetworkClient() {
        // 配置OkHttp客户端
        client = new OkHttpClient.Builder()
                .connectTimeout(10, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS)
                .writeTimeout(30, TimeUnit.SECONDS)
                .build();
    }
    
    public boolean reportError(String errorJson) {
        if (errorJson == null || errorJson.isEmpty()) {
            return false;
        }
        
        try {
            // 创建请求体
            RequestBody requestBody = RequestBody.create(
                    MediaType.parse("application/json; charset=utf-8"),
                    errorJson
            );
            
            // 创建请求
            Request request = new Request.Builder()
                    .url(REPORT_URL)
                    .post(requestBody)
                    .build();
            
            // 执行请求
            Response response = client.newCall(request).execute();
            
            if (response.isSuccessful()) {
                TinkerLog.i(TAG, "Error reported successfully");
                return true;
            } else {
                TinkerLog.w(TAG, "Error report failed, code: " + response.code());
                return false;
            }
        } catch (Exception e) {
            TinkerLog.e(TAG, "reportError: Exception", e);
            return false;
        }
    }
    
    // 其他辅助方法...
}

7.2 批量上报实现

为了减少网络请求,Tinker会将多个错误信息批量上报。源码如下:

// ErrorReporter.java
public class ErrorReporter {
    private static final String TAG = "Tinker.ErrorReporter";
    private static final int BATCH_SIZE = 5; // 每次上报5个错误
    
    private final Context context;
    private final ErrorStorage errorStorage;
    private final NetworkClient networkClient;
    
    public ErrorReporter(Context context) {
        this.context = context.getApplicationContext();
        this.errorStorage = new ErrorStorage(context);
        this.networkClient = new NetworkClient();
    }
    
    public void reportPendingErrors() {
        try {
            // 获取待上报的错误文件
            List<File> errorFiles = errorStorage.getPendingErrorFiles();
            
            if (errorFiles == null || errorFiles.isEmpty()) {
                TinkerLog.i(TAG, "No pending errors to report");
                return;
            }
            
            // 分批上报
            int batchCount = (int) Math.ceil((double) errorFiles.size() / BATCH_SIZE);
            
            for (int i = 0; i < batchCount; i++) {
                int start = i * BATCH_SIZE;
                int end = Math.min(start + BATCH_SIZE, errorFiles.size());
                
                List<File> batchFiles = errorFiles.subList(start, end);
                
                // 合并错误信息
                String batchJson = mergeErrorFiles(batchFiles);
                
                if (batchJson != null) {
                    // 上报
                    boolean success = networkClient.reportError(batchJson);
                    
                    // 如果上报成功,删除已上报的错误文件
                    if (success) {
                        for (File file : batchFiles) {
                            file.delete();
                        }
                    }
                }
            }
        } catch (Exception e) {
            TinkerLog.e(TAG, "reportPendingErrors: Exception", e);
        }
    }
    
    // 其他辅助方法...
}

八、错误上报结果处理

8.1 上报成功处理

当错误信息上报成功后,Tinker会删除已上报的错误文件,并记录上报成功信息。源码如下:

// ErrorReporter.java
private void handleReportSuccess(List<File> reportedFiles) {
    if (reportedFiles == null || reportedFiles.isEmpty()) {
        return;
    }
    
    try {
        // 删除已上报的错误文件
        for (File file : reportedFiles) {
            if (file.exists()) {
                file.delete();
            }
        }
        
        // 记录上报成功信息
        int count = reportedFiles.size();
        TinkerLog.i(TAG, "Successfully reported " + count + " error(s)");
        
        // 更新上报统计信息
        updateReportStats(count);
    } catch (Exception e) {
        TinkerLog.e(TAG, "handleReportSuccess: Exception", e);
    }
}

8.2 上报失败处理

当错误信息上报失败时,Tinker会保留错误文件,并记录上报失败信息。源码如下:

// ErrorReporter.java
private void handleReportFailure(List<File> failedFiles) {
    if (failedFiles == null || failedFiles.isEmpty()) {
        return;
    }
    
    try {
        // 记录上报失败信息
        int count = failedFiles.size();
        TinkerLog.w(TAG, "Failed to report " + count + " error(s)");
        
        // 更新失败统计信息
        updateFailureStats(count);
        
        // 安排重试
        scheduleRetry();
    } catch (Exception e) {
        TinkerLog.e(TAG, "handleReportFailure: Exception", e);
    }
}

九、错误信息展示与分析

9.1 开发者后台展示

Tinker提供了开发者后台,用于展示和分析上报的错误信息。开发者可以在后台查看错误详情、统计数据等。

9.2 错误分类与聚合

Tinker会对上报的错误信息进行分类和聚合,方便开发者快速定位和解决问题。源码如下:

// ErrorAnalyzer.java
public class ErrorAnalyzer {
    private static final String TAG = "Tinker.ErrorAnalyzer";
    
    public static List<ErrorGroup> analyzeErrors(List<ErrorInfo> errorInfos) {
        if (errorInfos == null || errorInfos.isEmpty()) {
            return Collections.emptyList();
        }
        
        // 按错误类型分组
        Map<String, List<ErrorInfo>> errorGroups = new HashMap<>();
        
        for (ErrorInfo errorInfo : errorInfos) {
            String errorType = errorInfo.getErrorType();
            
            if (!errorGroups.containsKey(errorType)) {
                errorGroups.put(errorType, new ArrayList<>());
            }
            
            errorGroups.get(errorType).add(errorInfo);
        }
        
        // 转换为错误分组对象
        List<ErrorGroup> result = new ArrayList<>();
        
        for (Map.Entry<String, List<ErrorInfo>> entry : errorGroups.entrySet()) {
            ErrorGroup group = new ErrorGroup();
            group.setErrorType(entry.getKey());
            group.setErrorCount(entry.getValue().size());
            group.setErrorInfos(entry.getValue());
            
            // 计算最早和最晚时间
            long earliestTime = Long.MAX_VALUE;
            long latestTime = Long.MIN_VALUE;
            
            for (ErrorInfo errorInfo : entry.getValue()) {
                long timestamp = errorInfo.getTimestamp();
                earliestTime = Math.min(earliestTime, timestamp);
                latestTime = Math.max(latestTime, timestamp);
            }
            
            group.setFirstOccurrenceTime(earliestTime);
            group.setLastOccurrenceTime(latestTime);
            
            result.add(group);
        }
        
        // 按错误数量排序
        Collections.sort(result, (g1, g2) -> g2.getErrorCount() - g1.getErrorCount());
        
        return result;
    }
    
    // 其他辅助方法...
}

十、自定义错误上报

10.1 自定义错误处理器

开发者可以通过实现自定义错误处理器,扩展Tinker的错误上报功能。源码如下:

// CustomErrorHandler.java
public class CustomErrorHandler implements TinkerUncaughtHandler.ErrorHandler {
    private static final String TAG = "CustomErrorHandler";
    
    @Override
    public void handleError(Throwable throwable) {
        // 记录错误信息
        TinkerLog.e(TAG, "Custom error handling: " + throwable.getMessage(), throwable);
        
        // 收集额外的错误信息
        collectAdditionalErrorInfo(throwable);
        
        // 调用默认的错误处理逻辑
        TinkerUncaughtHandler.getDefaultHandler().handleError(throwable);
    }
    
    private void collectAdditionalErrorInfo(Throwable throwable) {
        // 收集额外的错误信息,如用户行为、应用状态等
        // ...
    }
    
    // 其他自定义方法...
}

10.2 注册自定义错误处理器

开发者可以在应用启动时注册自定义错误处理器。源码如下:

// MyApplication.java
public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        
        // 注册自定义错误处理器
        TinkerUncaughtHandler.registerErrorHandler(new CustomErrorHandler());
        
        // 初始化Tinker
        TinkerInstaller.install(this);
    }
}

十一、错误上报性能优化

11.1 异步处理

Tinker的错误上报采用异步处理方式,避免阻塞主线程。源码如下:

// AsyncErrorReporter.java
public class AsyncErrorReporter {
    private static final String TAG = "Tinker.AsyncReporter";
    private static final int CORE_POOL_SIZE = 1;
    private static final int MAX_POOL_SIZE = 3;
    private static final long KEEP_ALIVE_TIME = 60L;
    private static final TimeUnit TIME_UNIT = TimeUnit.SECONDS;
    
    private final ExecutorService executorService;
    private final ErrorReporter errorReporter;
    
    public AsyncErrorReporter(Context context) {
        // 创建线程池
        executorService = new ThreadPoolExecutor(
                CORE_POOL_SIZE,
                MAX_POOL_SIZE,
                KEEP_ALIVE_TIME,
                TIME_UNIT,
                new LinkedBlockingQueue<Runnable>(),
                new ThreadFactory() {
                    private final AtomicInteger threadNumber = new AtomicInteger(1);
                    
                    @Override
                    public Thread newThread(Runnable r) {
                        return new Thread(r, "Tinker-Error-Reporter-" + threadNumber.getAndIncrement());
                    }
                }
        );
        
        this.errorReporter = new ErrorReporter(context);
    }
    
    public void reportErrorAsync(final Throwable throwable) {
        if (throwable == null) {
            return;
        }
        
        // 提交任务到线程池
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    // 收集错误信息
                    ErrorInfo errorInfo = ErrorInfoCollector.collectErrorInfo(throwable);
                    
                    if (errorInfo != null) {
                        // 保存错误信息
                        errorReporter.saveErrorInfo(errorInfo);
                        
                        // 检查是否需要立即上报
                        if (shouldReportImmediately(throwable)) {
                            errorReporter.reportPendingErrors();
                        }
                    }
                } catch (Exception e) {
                    TinkerLog.e(TAG, "reportErrorAsync: Exception", e);
                }
            }
        });
    }
    
    // 其他辅助方法...
}

11.2 上报频率控制

Tinker通过控制上报频率,避免过多的上报请求影响应用性能。源码如下:

// ErrorReportPolicy.java
private boolean checkReportFrequency() {
    long currentTime = System.currentTimeMillis();
    long lastReportTime = preferences.getLong("last_report_time", 0);
    int todayReports = preferences.getInt("today_reports", 0);
    
    // 检查是否是新的一天
    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(currentTime);
    int currentDay = calendar.get(Calendar.DAY_OF_YEAR);
    
    calendar.setTimeInMillis(lastReportTime);
    int lastReportDay = calendar.get(Calendar.DAY_OF_YEAR);
    
    if (currentDay != lastReportDay) {
        // 新的一天,重置计数
        Editor editor = preferences.edit();
        editor.putInt("today_reports", 0);
        editor.apply();
        todayReports = 0;
    }
    
    // 检查上报次数是否超过限制
    if (todayReports >= MAX_REPORTS_PER_DAY) {
        return false;
    }
    
    // 检查上报间隔
    if (currentTime - lastReportTime < MIN_INTERVAL) {
        return false;
    }
    
    return true;
}


举报

相关推荐

0 条评论