android異步下載照片墻

android異步下載照片墻


版權(quán)聲明:本文出自ShengFQ的博客.
轉(zhuǎn)載請(qǐng)注明出處:http://www.reibang.com/p/1709ea24ebbb

我的問(wèn)題

我要實(shí)現(xiàn)從服務(wù)器端下載一個(gè)圖片地址列表捌袜,并異步下載圖片展示在imageview亮瓷,實(shí)現(xiàn)緩存和壓縮幢泼。如果要下載壓縮后的圖片款侵,應(yīng)該是由服務(wù)器端先壓縮后存儲(chǔ)肝集,這里只說(shuō)客戶端范圍,不做討論盾计。

需要預(yù)備的知識(shí)點(diǎn):
1.AsyncTask異步調(diào)用方法下載json數(shù)據(jù)
2.listview钙皮,viewadapter的高性能寫法
3.HandlerThread,Hander,Message異步消息機(jī)制
4.LurCache緩存
5.BitmapFactory的壓縮圖片

1.AsyncTask異步調(diào)用方法下載json數(shù)據(jù)
一個(gè)AsyncTask和一個(gè)Adapter組合曼追,從服務(wù)器端拉取包含圖片地址的json格式數(shù)據(jù)窍仰。
2.listview,viewadapter的高性能寫法


viewadapter.java 部分實(shí)現(xiàn)
Object mlock;//異步操作list時(shí)礼殊,需要對(duì)list進(jìn)行同步鎖定
Context mContext;//調(diào)用上下文
List<Model> datalist;//list數(shù)據(jù)源
int itemLayoutId;//adapterview的個(gè)性化顯示布局id
getDataList();
reload(List models);
add(model model);
addAll(List models);
render(Model model,View view,int position);//重復(fù)利用ViewHolder提升getView方法的性能
getCount();
getItem(int position);
getItemId(int position);
class ViewHolder{}
getView(int position,View convertView,ViewGroup parent);

//將一特定的數(shù)據(jù)格式添加到view中
protected void render(LinkModel linkmodel,View view,int position){
     final LinkModel item=linkmodel;
     ViewHolder viewHolder=(ViewHolder)view.getTag();
     //重復(fù)利用視圖組件驹吮,將視圖組件存儲(chǔ)到裝載對(duì)象中
     if(viewHolder==null){
         viewHolder=new ViewHolder();
        viewHolder.app_layout= (FrameLayout)view.findViewById(R.id.iv_app_layout);
        viewHolder.app_icon_layout=(LinearLayout)view.findViewById(R.id.app_icon_layout);
        viewHolder.app_log_iv=(ImageView)view.findViewById(R.id.ic_app_ico);
        viewHolder.app_name_tv=(TextView)view.findViewById(R.id.ic_app_name);
        view.setTag(viewHolder);
     }else{
         viewHolder=(ViewHolder)view.getTag();
     }
     if(item!=null){
         viewHolder.app_log_iv.setImageResource(R.drawable.app_img_app_normal);
         viewHolder.app_name_tv.setText(item.name);
         if (ImageCache.getInstance(mCacheSize).isCache(item.photoUrl)) {
             viewHolder.app_log_iv.setImageBitmap(ImageCache.getInstance(mCacheSize).getBitmapFromMemCache(item.photoUrl));
            }else{
        //后臺(tái)線程異步下載圖片,Token在這里指定了imageview的實(shí)例
         mThumbnailThread.queueThumbnail(viewHolder.app_log_iv, item.photoUrl);
            }
    }
}

在AsyncTask中將后臺(tái)的json數(shù)據(jù)封裝到實(shí)體對(duì)象,通過(guò)上述的adapter.addAll(List models)注入到adapter中.

3.HandlerThread,Hander,Message異步消息機(jī)制
定義ThunbnailDownloader類,用來(lái)通過(guò)發(fā)送下載請(qǐng)求消息,從另一個(gè)線程去后臺(tái)下載并通過(guò)與前臺(tái)UI線程交互加載.

public class ThumbnailDownLoader<Token> extends HandlerThread {
    private static final String TAG="ThumbnailDownLoader";
    private static final int MESSAGE_DOWNLOAD=0;
    private Handler mHandler;
    private String requestToken;
    private int mCacheSize;
    Map<Token,String> requestMap= Collections.synchronizedMap(new HashMap<Token,String>());
    /**
     * 主線程中的Handler對(duì)象
     * */
    Handler mRespoonseHandler;
    //回調(diào)接口變量
    Listener<Token> mListener;
    
    /**
     * 用于通信的監(jiān)聽(tīng)器接口
     * 當(dāng)下載完要執(zhí)行的事情:將圖片加載到UI線程的ImageView
     * */
    public interface Listener<Token>{
        /**
         * 后臺(tái)線程的輸出,將下載的圖片指定給前臺(tái)的ImageView
         * @param token 存放圖片的容器
         * @param thumbnail 圖片格式
         * */
        void onThumbnailDownloaded(Token token,Bitmap thumbnail);
    }

    public void setListener(Listener<Token> listener){
        mListener=listener;
    }

    public ThumbnailDownLoader(String requesttoken){
        super(TAG);
        this.requestToken=requesttoken;
    }

    /**
     * 主線程傳遞的Handler
     * @param responseHandler 前臺(tái)UI線程handler
     * @param requesttoken 遠(yuǎn)程下載請(qǐng)求的token
     * @param cacheSize 緩存大小
     * */
    public ThumbnailDownLoader(Handler responseHandler,String requesttoken,int cacheSize){
        super(TAG);
        mRespoonseHandler=responseHandler;
        this.requestToken=requesttoken;
        this.mCacheSize=cacheSize;
    }
/**
 * 該方法的調(diào)用發(fā)生在Looper第一次檢查消息隊(duì)列之前
 * */

 @SuppressLint("handlerLeak")
 @Override
 protected void onLooperPrepared(){
    mHandler=new Handler(){
    //looper取得消息隊(duì)列中的特定消息,回調(diào)方法根據(jù)消息what屬性進(jìn)行處理
      public void handleMessage(Message msg){
        if(msg.what==MESSAGE_DOWNLOAD){
            @SuppressWarnings("unchecked")
            Token token=(Token)msg.obj;//Handler.obtainMessage(msg,obj);通過(guò)Handler發(fā)送Message傳遞了message.obj,這里處理消息時(shí),可以獲取obj,屬于約定內(nèi)容晶伦。
            Log.i(TAG,"Got a request for url:"+requestMap.get(token));
            handleRequest(token,requestToken);
        }
      }
    };
 }
 /**
  * 發(fā)送message請(qǐng)求下載圖片,將URL和Token傳遞到同步hashMap中
  * 在調(diào)用getView()的時(shí)候請(qǐng)求下載
  * @param token 前臺(tái)交互的UI控件
  * @param url 前臺(tái)指定的下載地址
  * */
    public void queueThumbnail(Token token,String url){
        Log.i(TAG,"Got to URL:"+url);
        requestMap.put(token,url);//調(diào)用getview()的時(shí)候調(diào)用
        Message message=mHandler.obtainMessage(MESSAGE_DOWNLOAD,token);//創(chuàng)建信息并傳入消息字段,自動(dòng)完成目標(biāo)handler的設(shè)置
        message.sendToTarget();//將消息壓入消息隊(duì)列
    }

    /**
     * 遠(yuǎn)程下載圖片碟狞,將后臺(tái)下載的圖片加載到前臺(tái)UI的ImageView中
     * @param token 泛型參數(shù),這里指前臺(tái)的ImageView
     * */
    private void handleRequest(final Token token,final String requestToken){
        try{
            final String url=requestMap.get(token);
            if(url==null)
            return;
            byte[] bitmapBytes=new ImageLoaderUtils().getUrlBytes(url,requestToken);
           // final Bitmap bitmap= BitmapFactory.decodeByteArray(bitmapBytes,0,bitmapBytes.length);
            final Bitmap bitmap=  ImageCompress.decodeSampleBitmapFromBytes(bitmapBytes, 54, 54);//圖片壓縮
            ImageCache.getInstance(mCacheSize).addBitmapToMemoryCache(url, bitmap);
            Log.i(TAG,"Bitmap created");
            //此處定義了主線程在后臺(tái)線程交互操作的UI處理
            mRespoonseHandler.post(new Runnable(){
                public void run(){
                    if(requestMap.get(token)!=url) return;
                    requestMap.remove(token);//
                    Bitmap cachebitmap =ImageCache.getInstance(mCacheSize).getBitmapFromMemCache(url);
                    if(cachebitmap!=null)
                        mListener.onThumbnailDownloaded(token,cachebitmap);
                    else{
                          mListener.onThumbnailDownloaded(token,bitmap);
                    }
                }
            });
            
        }catch(IOException ioe){
            Log.e(TAG,"Error downloading image",ioe);
        }
    }

    public void clearQueue(){
        mHandler.removeMessages(MESSAGE_DOWNLOAD);
        requestMap.clear();
    }
}

說(shuō)明:HandlerThread是一個(gè)消息通知處理線程類
onLooperPrepared();//初始化Handler的地方,因?yàn)榇颂幵贖andlerThread初始化后,進(jìn)入Loop()之前就會(huì)調(diào)用的方法,在這里初始化Handler有助于在消息發(fā)送之前handler已經(jīng)初始化.

Handler
handlleMessage(Message msg);//根據(jù)消息的類別和消息附帶的參數(shù)進(jìn)行異步下載
handler.obtainMessage(String msg,object) //構(gòu)建一個(gè)Message對(duì)象
message.sendToTarget();//將消息發(fā)送到消息隊(duì)列
handler.removeMessages(String msg);//清除該消息,釋放資源

如何與前臺(tái)UI線程交互

已經(jīng)將image下載下來(lái)了,要填充到UI線程的Imageview中,這就需要與前臺(tái)交互,沒(méi)錯(cuò),前臺(tái)UI線程也有Handler,而且只有一個(gè),將前臺(tái)的handler傳遞到后臺(tái)共享

/**
 * 主線程中的Handler對(duì)象
 * 
Handler mRespoonseHandler;
/**
     * 主線程傳遞的Handler
     * @param responseHandler 前臺(tái)UI線程handler
     * @param requesttoken 遠(yuǎn)程下載請(qǐng)求的token
     * @param cacheSize 緩存大小
     * */
public ThumbnailDownLoader(Handler responseHandler,String requesttoken,int cacheSize){
    super(TAG);
    mRespoonseHandler=responseHandler;
    this.requestToken=requesttoken;
    this.mCacheSize=cacheSize;
}

在下載完圖片后,通過(guò)handler.post();方法與前臺(tái)UI交互


//此處定義了主線程在后臺(tái)線程交互操作的UI處理
    mRespoonseHandler.post(new Runnable(){
        public void run(){
            if(requestMap.get(token)!=url) return;
            requestMap.remove(token);//
            Bitmap cachebitmap =ImageCache.getInstance(mCacheSize).getBitmapFromMemCache(url);
            if(cachebitmap!=null)
                mListener.onThumbnailDownloaded(token,cachebitmap);//回調(diào)接口方法
            else{
                  mListener.onThumbnailDownloaded(token,bitmap);
            }
        }
    });
    

Listener<Token> mListener;//回調(diào)接口對(duì)象,下載圖片后,定義行為將圖片設(shè)置到imageview

/**
 * 用于通信的監(jiān)聽(tīng)器接口
 * 當(dāng)下載完要執(zhí)行的事情:將圖片加載到UI線程的ImageView
 * */
public interface Listener<Token>{
    /**
     * 后臺(tái)線程的輸出,將下載的圖片指定給前臺(tái)的ImageView
     * @param token 存放圖片的容器
     * @param thumbnail 圖片格式
     * */
    void onThumbnailDownloaded(Token token,Bitmap thumbnail);
}

public void setListener(Listener<Token> listener){
    mListener=listener;
}

回到主線程UI

因此在UI線程中,做兩件事,初始化HandlerThread,實(shí)現(xiàn)回調(diào)接口

  //初始化后臺(tái)線程下載圖片
mThumbnailThread = new ThumbnailDownLoader<ImageView>(new Handler(),token,mCacheSize);//與主線程Looper綁定的Handler
mThumbnailThread.setListener(new ThumbnailDownLoader.Listener<ImageView>() {
    public void onThumbnailDownloaded(ImageView imageView, Bitmap thumbnail) {
        if (true) {
            //給ImageView設(shè)置圖片Bitmap
            imageView.setImageBitmap(thumbnail);
        }
    }
});
mThumbnailThread.start();
mThumbnailThread.getLooper();

如何觸發(fā)異步下載

我們要實(shí)現(xiàn)的效果是,下拉gridview時(shí),自動(dòng)下載圖片并加載到gridview里的imageview視圖,所以我們將焦點(diǎn)回到adapter.getView()方法里,要實(shí)現(xiàn)這個(gè)功能,需要兩個(gè)重要參數(shù)傳遞到HandlerThread中
當(dāng)前的Imageview實(shí)例和需要下載的圖片URL

mThumbnailThread.queueThumbnail(viewHolder.app_log_iv, item.photoUrl);

4.LurCache緩存
主要使用了LruCache<K,V> 類,該類的使用方法如下
一般緩存大小設(shè)置為:

UI線程中指定
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);  
        // 使用最大可用內(nèi)存值的1/8作為緩存的大小坝辫。  
        mCacheSize = maxMemory / 8;  
/**
 * 圖片緩存
 * */
public class ImageCache {
    private static ImageCache instance;
    
    /**
     * 實(shí)例化篷就,緩存大小
     */
    public static ImageCache getInstance(int cacheSize) {
        if (instance == null) {
            synchronized (ImageCache.class) {
                if (instance == null) {
                    instance = new ImageCache(cacheSize);
                }
            }
        }
        return instance;
    }
    private LruCache<String,Bitmap> mMemoryCache;
    private ImageCache(int cacheSize){
        mMemoryCache=new LruCache<String,Bitmap>(cacheSize){
            protected int sizeOf(String key,Bitmap bitmap){
                return bitmap.getByteCount()/1024;
            }
        };
    }
    
    /**
     * 加載到緩存
     * */
    public void addBitmapToMemoryCache(String key,Bitmap bitmap){
        if(getBitmapFromMemCache(key) ==null){
            mMemoryCache.put(key, bitmap);
        }
    }
    /**
     * 從緩存中讀取
     * */
    public Bitmap getBitmapFromMemCache(String key){
        return mMemoryCache.get(key);
    }
    /**
     * 檢查是否緩存
     * */
    public boolean isCache(String key){
        Bitmap map=mMemoryCache.get(key);
        return map!=null;
    }
}

5.BitmapFactory的壓縮圖片

ImageCompress.java
/**
     * 
     * 加載輸入流圖片 網(wǎng)絡(luò)*/
public static Bitmap decodeSampleBitmapFromBytes(byte[] data,int reqWidth,int reqHeight){
    final BitmapFactory.Options options=new BitmapFactory.Options();
    options.inJustDecodeBounds=true;
    //BitmapFactory.decodeResource(resource, resId, options);
    BitmapFactory.decodeByteArray(data, 0, data.length, options);
    options.inSampleSize=calculateInSampleSize(options, reqWidth, reqHeight);
    options.inJustDecodeBounds=false;
    return BitmapFactory.decodeByteArray(data, 0, data.length, options);
}

//怎么壓縮呢射亏,按照縮放比例inSamplesize,這個(gè)值是可要計(jì)算出來(lái)的
public static int calculateInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight){
    //
    final int height=options.outHeight;
    final int width=options.outWidth;
    int inSampleSize=1;
    if(height>reqHeight || width>reqWidth){
        final int heightRatio=Math.round((float)height/(float)reqHeight);
        final int widthRatio=Math.round((float)width/(float)reqWidth);
        inSampleSize=heightRatio<widthRatio ?heightRatio:widthRatio;
    }
    return inSampleSize;
}


參考資料

1.http://blog.csdn.net/guolin_blog/article/details/9526203 Android照片墻應(yīng)用實(shí)現(xiàn)近忙,再多的圖片也不怕崩潰
2 http://blog.csdn.net/guolin_blog/article/details/9316683 Android高效加載大圖竭业、多圖解決方案,有效避免程序OOM
3.android開(kāi)發(fā)編程權(quán)威指南
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末及舍,一起剝皮案震驚了整個(gè)濱河市未辆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌锯玛,老刑警劉巖咐柜,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異攘残,居然都是意外死亡拙友,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門歼郭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)遗契,“玉大人,你說(shuō)我怎么就攤上這事病曾‰狗洌” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵泰涂,是天一觀的道長(zhǎng)鲫竞。 經(jīng)常有香客問(wèn)我,道長(zhǎng)逼蒙,這世上最難降的妖魔是什么从绘? 我笑而不...
    開(kāi)封第一講書人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮其做,結(jié)果婚禮上顶考,老公的妹妹穿的比我還像新娘。我一直安慰自己妖泄,他們只是感情好驹沿,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著蹈胡,像睡著了一般渊季。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上罚渐,一...
    開(kāi)封第一講書人閱讀 51,718評(píng)論 1 305
  • 那天却汉,我揣著相機(jī)與錄音,去河邊找鬼荷并。 笑死合砂,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的源织。 我是一名探鬼主播翩伪,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼微猖,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了缘屹?” 一聲冷哼從身側(cè)響起凛剥,我...
    開(kāi)封第一講書人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎轻姿,沒(méi)想到半個(gè)月后犁珠,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡互亮,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年犁享,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片豹休。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡饼疙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出慕爬,到底是詐尸還是另有隱情窑眯,我是刑警寧澤,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布医窿,位于F島的核電站磅甩,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏姥卢。R本人自食惡果不足惜卷要,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望独榴。 院中可真熱鬧僧叉,春花似錦、人聲如沸棺榔。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)症歇。三九已至郎笆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間忘晤,已是汗流浹背宛蚓。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留设塔,地道東北人凄吏。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親痕钢。 傳聞我的和親對(duì)象是個(gè)殘疾皇子表谊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容