Android非同步載入全解析之使用多線程

來源:互聯網
上載者:User

標籤:android   非同步   多線程   圖片   緩衝   

非同步載入之使用多線程初次嘗試非同步、非同步,其實說白了就是多任務處理,也就是多線程執行,多線程那就會有各種問題,我們一步步來看,首先,我們建立一個class——ImageLoaderWithoutCaches,從命名上,大家也看出來,這個類,我們實現的是不帶緩衝的映像載入,不多說,我們再建立一個方法——showImageByThread,通過多線程來載入映像:
/** * Using Thread * @param imageView * @param url */public void showImageByThread(final ImageView imageView, final String url) {    mHandler = new Handler() {        @Override        public void handleMessage(Message msg) {            Bitmap bitmap = (Bitmap) msg.obj;            imageView.setImageBitmap(bitmap);        }    };    new Thread() {        @Override        public void run() {            Bitmap bitmap = getBitmapFromUrl(url);            Message message = Message.obtain();            message.obj = bitmap;            mHandler.sendMessage(message);        }    }.start();}

在這個方法中,我們開啟了一個線程,並在新線程中使用我們前面說的下載映像的方法進行下載,由於這時候已經不是主線程了,所以我們想怎麼下就可以怎麼下,天高皇帝遠。下載好了之後,通過Handler對象將訊息告知在主線程中等了一輩子的Handler,收到訊息之後,Handler操縱UI,修改imageView的映像。OK,跑起。

臥草,坑我啊,有的圖老閃,有的圖動都不動,滑一滑還不停的變,這是什麼鬼。這當然不是我們想要看見的結果。但是為什麼會這樣呢?我們先來分析下,首先,我們需要瞭解下ListView的Recycler機制。
ListView之Recycler機制
public View getView(int position, View convertView, ViewGroup parent)

這個就是ListView中的getView()方法,參數convertView就是我們的頭號嫌疑犯。假如我們有100條資料,但是頁面上只能顯示10條。Android還不會白癡到全部先建立出來。在初始建立的時候,顯示多少,convertView就建立了多少,當你滑動的時候,比如向上滑動一個Item,那麼Item1就會隱藏,Item11會從下面出來,那麼這時候,如果Android再去建立一個convertView,那它就要被人罵死了,因為它首先要回收Item1,再去建立Item11,有意思嗎?為瞭解決這樣一個問題,Android在ListView中提供了Recycler機制,說白了就是建立了一個Item的緩衝池,當Item1消失後,它並沒有被回收,而是進入了緩衝池,成了二手貨,當Item11顯示的時候,它會從緩衝池中取出Item1,把Item1的資料擦擦乾淨繼續用,所以這個時候,Item11和Item1顯示的內容會是相同的,因為它們在記憶體中的地址是一樣的,本質上是一個Item,但這個時候,Item1已經看不見了,所以它顯示成什麼,跟Item11沒一點關係。當它再顯示的時候,重新再寫上正確的資料就好了。這裡在網上盜了張圖,協助大家來理解這樣一個撿二手貨的概念:
當然,如果不是非同步作業,屁事都沒有,因為Android UI是單線程的,或者說你重用了convertView但每次都new一個,那也沒事,當然你也不會這樣做,太Low了。但是非同步就不一樣了,就拿我們這裡下載圖片來說,每個圖片都有自己的脾氣,大家下載的速度都是不一樣的,當初始顯示ListView時,Item1的圖片下載太慢,當你滑上去,顯示Item11了,Item11的圖顯示的飛起,馬上就下好了,OK,Item11的圖顯示好了,可馬上,Item1下的慢的圖也下好了,它去找Item1,但是它不知道這個時候的Item1已經變成Item11了,它還去給人家設定映像,這不就亂了嗎?OK,知道了原因,我們怎麼解決呢?現在的問題就是,下載的映像就好像一個冬眠的人,它睡了20年,起來發現,他大爺已經不是當年的那個大爺了,但它的記憶還停在20年前。所以,我們需要建立一個標誌,來讓冬眠的朋友回來看看清楚,現在的這個大爺是不是你以前的那個大爺。在Android中,我們可以非常方便的使用tag來對View進行標識,Item1顯示的時候,tag被設定為url1,然後它就去下載url1了,當滑動後,顯示Item11了,這時候tag被修改為url11,下載url11的圖了。而當url1的圖片下載好了之後,回來只要看看tag是不是url1就知道大爺還是不是那個大爺了,如果不是,就不顯示了,這樣就可以避免錯位的問題了。當然,前面的例子中還有一個坑,我就不埋大家了,下載完畢後,通過handler將圖片通知UI線程修改ImageView,但是這個時候,參數中的ImageView與這時候傳遞過來的映像可能並不是一一對應的關係,顯示肯定不正確了,所以我們還需要讓url和對應的ImageView進行下配對,最簡單的方法就是使用一個對象來進行儲存。多線程非同步下載正解在進行初次嘗試並分析了失敗原因後,我們將上面兩個坑填起來,修改後的方法如下:
/** * Using Thread * @param imageView * @param url */public void showImageByThread(final ImageView imageView, final String url) {    mHandler = new Handler() {        @Override        public void handleMessage(Message msg) {            ImgHolder holder = (ImgHolder) msg.obj;            if (holder.imageView.getTag().equals(holder.url)) {                holder.imageView.setImageBitmap(holder.bitmap);            }        }    };    new Thread() {        @Override        public void run() {            Bitmap bitmap = getBitmapFromUrl(url);            Message message = Message.obtain();            message.obj = new ImgHolder(imageView, bitmap, url);            mHandler.sendMessage(message);        }    }.start();}

private class ImgHolder {    public Bitmap bitmap;    public ImageView imageView;    public String url;    public ImgHolder(ImageView iv, Bitmap bm,String url) {        this.imageView = iv;        this.bitmap = bm;        this.url = url;    }}

修改後的方法,依然是使用Handler,這個沒有別的選擇,首先我們下載映像,然後將映像和ImageView通過一個對象來進行匹配儲存。在Handler拿到對象後,通過判斷當前的tag與url是否對應來決定是否載入。這裡就利用到了我們在Android非同步載入全解析之開篇瞎扯淡裡面所提到的:
viewHolder.imageView.setTag(url);

這裡就派上用場了。
最後,在Adapter中修改下代碼:
@Overridepublic View getView(int position, View convertView, ViewGroup parent) {    String url = mData.get(position);    ViewHolder viewHolder = null;    if (convertView == null) {        viewHolder = new ViewHolder();        convertView = mInflater.inflate(R.layout.listview_item, null);        viewHolder.imageView = (ImageView) convertView.findViewById(R.id.iv_lv_item);        convertView.setTag(viewHolder);    } else {        viewHolder = (ViewHolder) convertView.getTag();    }    viewHolder.imageView.setTag(url);    viewHolder.imageView.setImageResource(R.drawable.ic_launcher);    mImageLoader.showImageByThread(viewHolder.imageView, url);    return convertView;}

在getView的時候,非同步載入圖片。
這時候,我們再來運行程式,效果如下:

OK,已經可以正常顯示了。

我的Github
我的視訊 慕課網

Android非同步載入全解析之使用多線程

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.