1:网络的底层环境 采用apache 的httpClient 链接池框架

2:图片缓存采用基于LRU 的算法

3:网络接口采用监听者模式

4 包含图片的OOM 处理(及时回收处理技术的应用)

import java.io.FilterInputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.lang.ref.SoftReference; 
import java.util.HashMap; 
import java.util.concurrent.RejectedExecutionException; 
 
 
import org.apache.http.HttpEntity; 
import org.apache.http.HttpResponse; 
import org.apache.http.HttpStatus; 
import org.apache.http.client.methods.HttpGet; 
 
 
import xiaogang.enif.utils.HttpManager; 
import xiaogang.enif.utils.IOUtils; 
import xiaogang.enif.utils.LogUtils; 
import xiaogang.enif.utils.LruCache; 
import android.app.ActivityManager; 
import android.content.Context; 
import android.graphics.Bitmap; 
import android.graphics.BitmapFactory; 
import android.graphics.BitmapFactory.Options; 
import android.graphics.Canvas; 
import android.graphics.drawable.BitmapDrawable; 
import android.os.AsyncTask; 
import android.text.TextUtils; 
import android.util.AttributeSet; 
import android.widget.ImageView; 
 
 
public class CacheView extends ImageView { 
    private static final int DEFAULT_RES_ID = 0; 
    private int mDefaultImage = DEFAULT_RES_ID; 
 
 
    private static LruCache<String, Bitmap> mLruCache; 
    private static HashMap<Integer, SoftReference<Bitmap>> mResImage; 
 
 
    private Context mContext; 
    private LogUtils mLog = LogUtils.getLog(CacheView.class); 
 
 
    public CacheView(Context context, AttributeSet attrs, int defStyle) { 
        super(context, attrs, defStyle); 
        init(context); 
    } 
 
 
    public CacheView(Context context, AttributeSet attrs) { 
        super(context, attrs); 
        init(context); 
 
 
    } 
 
 
    public CacheView(Context context) { 
        super(context); 
        init(context); 
    } 
 
 
    private void init(Context context) { 
        mContext = context; 
 
 
        if (mLruCache == null) { 
            final int cacheSize = getCacheSize(context); 
            mLruCache = new LruCache<String, Bitmap>(cacheSize) { 
                @Override 
                protected int sizeOf(String key, Bitmap bitmap) { 
                    // The cache size will be measured in bytes rather than 
                    // number of items. 
                    return bitmap.getRowBytes() * bitmap.getHeight(); 
                } 
 
 
                @Override 
                protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, 
                        Bitmap newValue) { 
                    if (evicted && oldValue != null && !oldValue.isRecycled()) { 
                        oldValue.recycle(); 
                        oldValue = null; 
                    } 
                } 
            }; 
        } 
 
 
        if (mResImage == null) { 
            mResImage = new HashMap<Integer, SoftReference<Bitmap>>(); 
        } 
    } 
 
 
    @Override 
    protected void onDraw(Canvas canvas) { 
        BitmapDrawable drawable = (BitmapDrawable)getDrawable(); 
        if (drawable == null) { 
            setDefaultImage(); 
        } else { 
            if (drawable.getBitmap() == null || drawable.getBitmap().isRecycled()) { 
                setDefaultImage(); 
            } 
        } 
        try { 
            super.onDraw(canvas); 
        } catch(RuntimeException ex) { 
 
 
        } 
    } 
 
 
    public void setImageUrl(String url, int resId) { 
        setTag(url); 
        Bitmap bitmap = getBitmapFromCache(url); 
        if (bitmap == null || bitmap.isRecycled()) { 
            mDefaultImage = resId; 
            setDefaultImage(); 
            try { 
                new DownloadTask().execute(url); 
            } catch (RejectedExecutionException e) { 
                // do nothing, just keep not crash 
            } 
        } else { 
            setImageBitmap(bitmap); 
        } 
    } 
 
 
    private void setDefaultImage() { 
        if (mDefaultImage != DEFAULT_RES_ID) { 
            setImageBitmap(getDefaultBitmap(mContext)); 
        } 
    } 
 
 
    private Bitmap getDefaultBitmap(Context context) { 
        SoftReference<Bitmap> loading = mResImage.get(mDefaultImage); 
        if (loading == null || loading.get() == null || loading.get().isRecycled()) { 
            loading = new SoftReference<Bitmap>(BitmapFactory.decodeResource( 
                    context.getResources(), mDefaultImage)); 
            mResImage.put(mDefaultImage, loading); 
        } 
        return loading.get(); 
    } 
 
 
    private class DownloadTask extends AsyncTask<String, Void, Bitmap> { 
        private String mParams; 
 
 
        @Override 
        public Bitmap doInBackground(String... params) { 
            mParams = params[0]; 
            final Bitmap bm = download(mParams); 
            addBitmapToCache(mParams, bm); 
            return bm; 
        } 
 
 
        @Override 
        public void onPostExecute(Bitmap bitmap) { 
            String tag = (String)getTag(); 
            if (!TextUtils.isEmpty(tag) && tag.equals(mParams)) { 
                if (bitmap != null) { 
                    setImageBitmap(bitmap); 
                } 
            } 
        } 
    }; 
 
 
    /* 
     * An InputStream that skips the exact number of bytes provided, unless it 
     * reaches EOF. 
     */ 
    static class FlushedInputStream extends FilterInputStream { 
        public FlushedInputStream(InputStream inputStream) { 
            super(inputStream); 
        } 
 
 
        @Override 
        public long skip(long n) throws IOException { 
            long totalBytesSkipped = 0L; 
            while (totalBytesSkipped < n) { 
                long bytesSkipped = in.skip(n - totalBytesSkipped); 
                if (bytesSkipped == 0L) { 
                    int b = read(); 
                    if (b < 0) { 
                        break; // we reached EOF 
                    } else { 
                        bytesSkipped = 1; // we read one byte 
                    } 
                } 
                totalBytesSkipped += bytesSkipped; 
            } 
            return totalBytesSkipped; 
        } 
    } 
 
 
    private Bitmap download(String url) { 
        InputStream in = null; 
        HttpEntity entity = null; 
        Bitmap bmp = null; 
        try { 
            final HttpGet get = new HttpGet(url); 
            final HttpResponse response = HttpManager.execute(mContext, get); 
            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { 
                entity = response.getEntity(); 
                in = entity.getContent(); 
                try { 
                    bmp = getDecodeBitmap(in, url); 
                } catch (OutOfMemoryError err) { 
                    Runtime.getRuntime().gc(); 
                    bmp = getDecodeBitmap(in, url); 
                } 
            } else { 
                get.abort(); 
                return bmp; 
            } 
            addBitmapToCache(url, bmp); 
        } catch (IOException e) { 
            return bmp; 
        } finally { 
            IOUtils.closeStream(in); 
        } 
        return bmp; 
    } 
 
 
    private final Bitmap getDecodeBitmap(InputStream in, String url) { 
        Options options = new Options(); 
        options.inPurgeable = true; 
        options.inInputShareable = true; 
        return BitmapFactory.decodeStream(new FlushedInputStream(in), null, options); 
    } 
 
 
    private final void addBitmapToCache(String url, Bitmap bitmap) { 
        if (bitmap != null) { 
            mLruCache.put(url, bitmap); 
            Runtime.getRuntime().gc(); 
        } 
    } 
 
 
    private final Bitmap getBitmapFromCache(String url) { 
        return mLruCache.get(url); 
    } 
 
 
    private int getCacheSize(Context context) { 
        // According to the phone memory, set a proper cache size for LRU cache 
        // dynamically. 
        final ActivityManager am = (ActivityManager)context 
                .getSystemService(Context.ACTIVITY_SERVICE); 
        final int memClass = am.getMemoryClass(); 
 
 
        int cacheSize; 
        if (memClass <= 24) { 
            cacheSize = (memClass << 20) / 24; 
        } else if (memClass <= 36) { 
            cacheSize = (memClass << 20) / 18; 
        } else if (memClass <= 48) { 
            cacheSize = (memClass << 20) / 12; 
        } else { 
            cacheSize = (memClass << 20) >> 3; 
        } 
        mLog.debug("cacheSize == "+cacheSize); 
        System.out.println("cacheSize == "+cacheSize); 
        return cacheSize; 
    } 
 
 
    public static void recycle() { 
        if (mLruCache != null && !mLruCache.isEmpty()) { 
            mLruCache.evictAll(); 
            mLruCache = null; 
        } 
 
 
        if (mResImage != null) { 
            for (SoftReference<Bitmap> reference : mResImage.values()) { 
                Bitmap bitmap = reference.get(); 
                if (bitmap != null && !bitmap.isRecycled()) { 
                    bitmap.recycle(); 
                    bitmap = null; 
                } 
            } 
            mResImage = null; 
        } 
    } 
} 
 
 
 
 
说明: 
 
1)entryRemoved 在做Bitmap recyle 的时候的三个条件缺一不可 
 
2)onDraw 里面判断图片是否被回收,如果回收  需要设置默认的图片 
 
3)add bitmap 到cache 的时候 Runtime.getRuntime().gc 的调用

发布评论

分享到:

IT虾米网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!

Android读取选择联系人的模版代码实现详解
你是第一个吃螃蟹的人
发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。