michael@0: /* michael@0: * Copyright (C) 2013 Square, Inc. michael@0: * michael@0: * Licensed under the Apache License, Version 2.0 (the "License"); michael@0: * you may not use this file except in compliance with the License. michael@0: * You may obtain a copy of the License at michael@0: * michael@0: * http://www.apache.org/licenses/LICENSE-2.0 michael@0: * michael@0: * Unless required by applicable law or agreed to in writing, software michael@0: * distributed under the License is distributed on an "AS IS" BASIS, michael@0: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. michael@0: * See the License for the specific language governing permissions and michael@0: * limitations under the License. michael@0: */ michael@0: package com.squareup.picasso; michael@0: michael@0: import android.graphics.Bitmap; michael@0: import android.os.Handler; michael@0: import android.os.HandlerThread; michael@0: import android.os.Looper; michael@0: import android.os.Message; michael@0: michael@0: import static android.os.Process.THREAD_PRIORITY_BACKGROUND; michael@0: michael@0: class Stats { michael@0: private static final int CACHE_HIT = 0; michael@0: private static final int CACHE_MISS = 1; michael@0: private static final int BITMAP_DECODE_FINISHED = 2; michael@0: private static final int BITMAP_TRANSFORMED_FINISHED = 3; michael@0: michael@0: private static final String STATS_THREAD_NAME = Utils.THREAD_PREFIX + "Stats"; michael@0: michael@0: final HandlerThread statsThread; michael@0: final Cache cache; michael@0: final Handler handler; michael@0: michael@0: long cacheHits; michael@0: long cacheMisses; michael@0: long totalOriginalBitmapSize; michael@0: long totalTransformedBitmapSize; michael@0: long averageOriginalBitmapSize; michael@0: long averageTransformedBitmapSize; michael@0: int originalBitmapCount; michael@0: int transformedBitmapCount; michael@0: michael@0: Stats(Cache cache) { michael@0: this.cache = cache; michael@0: this.statsThread = new HandlerThread(STATS_THREAD_NAME, THREAD_PRIORITY_BACKGROUND); michael@0: this.statsThread.start(); michael@0: this.handler = new StatsHandler(statsThread.getLooper(), this); michael@0: } michael@0: michael@0: void dispatchBitmapDecoded(Bitmap bitmap) { michael@0: processBitmap(bitmap, BITMAP_DECODE_FINISHED); michael@0: } michael@0: michael@0: void dispatchBitmapTransformed(Bitmap bitmap) { michael@0: processBitmap(bitmap, BITMAP_TRANSFORMED_FINISHED); michael@0: } michael@0: michael@0: void dispatchCacheHit() { michael@0: handler.sendEmptyMessage(CACHE_HIT); michael@0: } michael@0: michael@0: void dispatchCacheMiss() { michael@0: handler.sendEmptyMessage(CACHE_MISS); michael@0: } michael@0: michael@0: void shutdown() { michael@0: statsThread.quit(); michael@0: } michael@0: michael@0: void performCacheHit() { michael@0: cacheHits++; michael@0: } michael@0: michael@0: void performCacheMiss() { michael@0: cacheMisses++; michael@0: } michael@0: michael@0: void performBitmapDecoded(long size) { michael@0: originalBitmapCount++; michael@0: totalOriginalBitmapSize += size; michael@0: averageOriginalBitmapSize = getAverage(originalBitmapCount, totalOriginalBitmapSize); michael@0: } michael@0: michael@0: void performBitmapTransformed(long size) { michael@0: transformedBitmapCount++; michael@0: totalTransformedBitmapSize += size; michael@0: averageTransformedBitmapSize = getAverage(originalBitmapCount, totalTransformedBitmapSize); michael@0: } michael@0: michael@0: synchronized StatsSnapshot createSnapshot() { michael@0: return new StatsSnapshot(cache.maxSize(), cache.size(), cacheHits, cacheMisses, michael@0: totalOriginalBitmapSize, totalTransformedBitmapSize, averageOriginalBitmapSize, michael@0: averageTransformedBitmapSize, originalBitmapCount, transformedBitmapCount, michael@0: System.currentTimeMillis()); michael@0: } michael@0: michael@0: private void processBitmap(Bitmap bitmap, int what) { michael@0: // Never send bitmaps to the handler as they could be recycled before we process them. michael@0: int bitmapSize = Utils.getBitmapBytes(bitmap); michael@0: handler.sendMessage(handler.obtainMessage(what, bitmapSize, 0)); michael@0: } michael@0: michael@0: private static long getAverage(int count, long totalSize) { michael@0: return totalSize / count; michael@0: } michael@0: michael@0: private static class StatsHandler extends Handler { michael@0: michael@0: private final Stats stats; michael@0: michael@0: public StatsHandler(Looper looper, Stats stats) { michael@0: super(looper); michael@0: this.stats = stats; michael@0: } michael@0: michael@0: @Override public void handleMessage(final Message msg) { michael@0: switch (msg.what) { michael@0: case CACHE_HIT: michael@0: stats.performCacheHit(); michael@0: break; michael@0: case CACHE_MISS: michael@0: stats.performCacheMiss(); michael@0: break; michael@0: case BITMAP_DECODE_FINISHED: michael@0: stats.performBitmapDecoded(msg.arg1); michael@0: break; michael@0: case BITMAP_TRANSFORMED_FINISHED: michael@0: stats.performBitmapTransformed(msg.arg1); michael@0: break; michael@0: default: michael@0: Picasso.HANDLER.post(new Runnable() { michael@0: @Override public void run() { michael@0: throw new AssertionError("Unhandled stats message." + msg.what); michael@0: } michael@0: }); michael@0: } michael@0: } michael@0: } michael@0: }