package me.xiaopan.sketchsample; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Rect; import android.os.Build; import android.os.Environment; import android.text.format.Formatter; import com.tencent.bugly.crashreport.CrashReport; import java.io.File; import java.util.Arrays; import java.util.List; import java.util.Locale; import me.xiaopan.sketch.SketchMonitor; import me.xiaopan.sketch.drawable.RefDrawable; import me.xiaopan.sketch.feature.large.Tile; import me.xiaopan.sketch.process.ImageProcessor; import me.xiaopan.sketch.request.DisplayRequest; import me.xiaopan.sketch.request.DownloadRequest; import me.xiaopan.sketch.request.LoadRequest; import me.xiaopan.sketch.request.UriScheme; import me.xiaopan.sketch.util.SketchUtils; import me.xiaopan.sketch.util.UnableCreateDirException; import me.xiaopan.sketch.util.UnableCreateFileException; class SampleSketchMonitor extends SketchMonitor { private static final int INSTALL_FAILED_RETRY_TIME_INTERVAL = 30 * 60 * 1000; private Context context; private long lastUploadInstallFailedTime; private long lastUploadDecodeNormalImageFailedTime; private long lastUploadDecodeGifImageFailedTime; private long lastUploadProcessImageFailedTime; private boolean uploadDecodeGifImageFailed; public SampleSketchMonitor(Context context) { super(context); this.context = context.getApplicationContext(); logName = "SampleSketchMonitor"; } @Override public void onDecodeGifImageError(Throwable throwable, LoadRequest request, int outWidth, int outHeight, String outMimeType) { super.onDecodeGifImageError(throwable, request, outWidth, outHeight, outMimeType); boolean notFoundSoFile = throwable instanceof UnsatisfiedLinkError || throwable instanceof ExceptionInInitializerError; if (notFoundSoFile) { // 如果是找不到so文件异常,那么每次运行只上报一次 if (uploadDecodeGifImageFailed) { return; } uploadDecodeGifImageFailed = true; } else { // 其它异常每半小时上报一次 long currentTime = System.currentTimeMillis(); if (currentTime - lastUploadDecodeGifImageFailedTime < INSTALL_FAILED_RETRY_TIME_INTERVAL) { return; } lastUploadDecodeGifImageFailedTime = currentTime; } StringBuilder builder = new StringBuilder(); builder.append("Sketch") .append(" - ").append("DecodeGifImageFailed") .append(" - ").append(throwable.getClass().getSimpleName()) .append(" - ").append(decodeUri(context, request.getUri())); builder.append("\n").append("exceptionMessage: ").append(throwable.getMessage()); if (notFoundSoFile) { if (Build.VERSION.SDK_INT >= 21) { builder.append("\n").append("abiInfo: ").append(Arrays.toString(Build.SUPPORTED_ABIS)); } else { builder.append("\n").append("abiInfo: ").append("abi1=").append(Build.CPU_ABI).append(", abi2=").append(Build.CPU_ABI2); } } if (throwable instanceof OutOfMemoryError) { long maxMemory = Runtime.getRuntime().maxMemory(); long freeMemory = Runtime.getRuntime().freeMemory(); long totalMemory = Runtime.getRuntime().totalMemory(); String maxMemoryFormatted = Formatter.formatFileSize(this.context, maxMemory); String freeMemoryFormatted = Formatter.formatFileSize(this.context, freeMemory); String totalMemoryFormatted = Formatter.formatFileSize(this.context, totalMemory); builder.append("\n") .append("memoryInfo: ") .append("maxMemory=").append(maxMemoryFormatted) .append(", freeMemory=").append(freeMemoryFormatted) .append(", totalMemory=").append(totalMemoryFormatted); } builder.append("\n") .append("imageInfo: ") .append("outWidth=").append(outWidth) .append(", outHeight=").append(outHeight) .append(", outMimeType=").append(outMimeType); CrashReport.postCatchedException(new Exception(builder.toString(), throwable)); } @Override public void onDecodeNormalImageError(Throwable throwable, LoadRequest request, int outWidth, int outHeight, String outMimeType) { super.onDecodeNormalImageError(throwable, request, outWidth, outHeight, outMimeType); // 每半小时上报一次 long currentTime = System.currentTimeMillis(); if (currentTime - lastUploadDecodeNormalImageFailedTime < INSTALL_FAILED_RETRY_TIME_INTERVAL) { return; } lastUploadDecodeNormalImageFailedTime = currentTime; StringBuilder builder = new StringBuilder(); builder.append("Sketch") .append(" - ").append("DecodeNormalImageFailed") .append(" - ").append(throwable.getClass().getSimpleName()) .append(" - ").append(decodeUri(context, request.getUri())); builder.append("\n").append("exceptionMessage: ").append(throwable.getMessage()); if (throwable instanceof OutOfMemoryError) { long maxMemory = Runtime.getRuntime().maxMemory(); long freeMemory = Runtime.getRuntime().freeMemory(); long totalMemory = Runtime.getRuntime().totalMemory(); String maxMemoryFormatted = Formatter.formatFileSize(this.context, maxMemory); String freeMemoryFormatted = Formatter.formatFileSize(this.context, freeMemory); String totalMemoryFormatted = Formatter.formatFileSize(this.context, totalMemory); builder.append("\n").append("memoryInfo: ") .append("maxMemory=").append(maxMemoryFormatted) .append(", freeMemory=").append(freeMemoryFormatted) .append(", totalMemory=").append(totalMemoryFormatted); } builder.append("\n").append("imageInfo: ") .append("outWidth=").append(outWidth) .append(", outHeight=").append(outHeight) .append(", outMimeType=").append(outMimeType); CrashReport.postCatchedException(new Exception(builder.toString(), throwable)); } @Override public void onInstallDiskCacheError(Exception e, File cacheDir) { super.onInstallDiskCacheError(e, cacheDir); // 每半小时上传一次 long currentTime = System.currentTimeMillis(); if (currentTime - lastUploadInstallFailedTime < INSTALL_FAILED_RETRY_TIME_INTERVAL) { return; } lastUploadInstallFailedTime = currentTime; StringBuilder builder = new StringBuilder(); builder.append("Sketch") .append(" - ").append("InstallDiskCacheFailed"); if (e instanceof UnableCreateDirException) { builder.append(" - ").append("UnableCreateDirException"); } else if (e instanceof UnableCreateFileException) { builder.append(" - ").append("UnableCreateFileException"); } else { builder.append(" - ").append(e.getClass().getSimpleName()); } builder.append(" - ").append(cacheDir.getPath()); builder.append("\n").append("exceptionMessage: ").append(e.getMessage()); String sdcardState = Environment.getExternalStorageState(); builder.append("\n").append("sdcardState: ").append(sdcardState); if (Environment.MEDIA_MOUNTED.equals(sdcardState)) { File sdcardDir = Environment.getExternalStorageDirectory(); long totalBytes = SketchUtils.getTotalBytes(sdcardDir); long availableBytes = SketchUtils.getAvailableBytes(sdcardDir); builder.append("\n") .append("sdcardSize: ") .append(Formatter.formatFileSize(context, availableBytes)) .append("/") .append(Formatter.formatFileSize(context, totalBytes)); } CrashReport.postCatchedException(new Exception(builder.toString(), e)); } @Override public void onProcessImageError(Throwable e, String imageUri, ImageProcessor processor) { super.onProcessImageError(e, imageUri, processor); // 每半小时上报一次 long currentTime = System.currentTimeMillis(); if (currentTime - lastUploadProcessImageFailedTime < INSTALL_FAILED_RETRY_TIME_INTERVAL) { return; } lastUploadProcessImageFailedTime = currentTime; String outOfMemoryInfo = e instanceof OutOfMemoryError ? String.format("\nmemoryState: %s", getSystemState()) : ""; String log = String.format("Sketch - %s - %s" + "\nexceptionMessage: %s" + "%s", processor.getKey(), decodeUri(context, imageUri), e.getMessage(), outOfMemoryInfo); CrashReport.postCatchedException(new Exception(log, e)); } @Override public void onDownloadError(final DownloadRequest request, Throwable throwable) { super.onDownloadError(request, throwable); } @Override public void onTileSortError(IllegalArgumentException e, List<Tile> tileList, boolean useLegacyMergeSort) { super.onTileSortError(e, tileList, useLegacyMergeSort); String log = String.format("Sketch - TileSortError - %s " + "\ntiles: %s", useLegacyMergeSort ? "useLegacyMergeSort. " : "", SketchUtils.tileListToString(tileList)); CrashReport.postCatchedException(new Exception(log, e)); } @Override public void onBitmapRecycledOnDisplay(DisplayRequest request, RefDrawable refDrawable) { super.onBitmapRecycledOnDisplay(request, refDrawable); String log = String.format("Sketch - BitmapRecycledOnDisplay - %s " + "\ndrawable: %s", decodeUri(context, request.getUri()), refDrawable.getInfo()); CrashReport.postCatchedException(new Exception(log)); } @Override public void onInBitmapExceptionForRegionDecoder(String imageUri, int imageWidth, int imageHeight, Rect srcRect, int inSampleSize, Bitmap inBitmap) { super.onInBitmapExceptionForRegionDecoder(imageUri, imageWidth, imageHeight, srcRect, inSampleSize, inBitmap); String log = String.format("Sketch - InBitmapExceptionForRegionDecoder - %s" + "\nimageSize:%dx%d" + "\nsrcRect:%s" + "\ninSampleSize:%d" + "\ninBitmap:%dx%d, %d, %s" + "\nsystemState:%s", decodeUri(context, imageUri), imageWidth, imageHeight, srcRect.toString(), inSampleSize, inBitmap.getWidth(), inBitmap.getHeight(), SketchUtils.getByteCount(inBitmap), inBitmap.getConfig(), getSystemState()); CrashReport.postCatchedException(new Exception(log)); } @Override public void onInBitmapException(String imageUri, int imageWidth, int imageHeight, int inSampleSize, Bitmap inBitmap) { super.onInBitmapException(imageUri, imageWidth, imageHeight, inSampleSize, inBitmap); String log = String.format("Sketch - InBitmapException - %s" + "\nimageSize:%dx%d" + "\ninSampleSize:%d" + "\ninBitmap:%dx%d, %d, %s" + "\nsystemState:%s", decodeUri(context, imageUri), imageWidth, imageHeight, inSampleSize, inBitmap.getWidth(), inBitmap.getHeight(), SketchUtils.getByteCount(inBitmap), inBitmap.getConfig(), getSystemState()); CrashReport.postCatchedException(new Exception(log)); } private String decodeUri(Context context, String imageUri) { UriScheme scheme = UriScheme.valueOfUri(imageUri); if (scheme != null && scheme == UriScheme.DRAWABLE) { try { int resId = Integer.parseInt(UriScheme.DRAWABLE.crop(imageUri)); return context.getResources().getResourceName(resId); } catch (Exception e) { e.printStackTrace(); } } return imageUri; } private String getSystemInfo() { return String.format(Locale.getDefault(), "%s, %d", Build.MODEL, Build.VERSION.SDK_INT); } private String getMemoryInfo() { String freeMemory = Formatter.formatFileSize(context, Runtime.getRuntime().freeMemory()); String maxMemory = Formatter.formatFileSize(context, Runtime.getRuntime().maxMemory()); return String.format("%s/%s", freeMemory, maxMemory); } private String getSystemState() { return String.format(Locale.getDefault(), "%s, %s", getSystemInfo(), getMemoryInfo()); } }