漸進(jìn)式加載初嘗試

前言
前面我們講解了漸進(jìn)式加載的基礎(chǔ),接下來我們就講一下在app中具體如何實(shí)現(xiàn)漸進(jìn)式加載

1.基礎(chǔ)
關(guān)于漸進(jìn)式加載的基礎(chǔ)知識(shí),我們已經(jīng)在前面講解過了,這里就不重復(fù)講解了,但是不代表不重要.

2.背景
Android圖片加載主要有四大框架,但是只有Fresco框架是可以支持漸進(jìn)式加載(需要設(shè)置)
筆者是由于項(xiàng)目已經(jīng)使用Glide,且跟換框架成本太大,所以才嘗試用Glide實(shí)現(xiàn)漸進(jìn)式加載,但是Glide本身框架目前是不適合做漸進(jìn)式加載,里面的請求是將整個(gè)InputStream轉(zhuǎn)化為bitmap后才通過回調(diào)設(shè)置給ImageView,這個(gè)過程只能走一次.筆者做了很多嘗試來讓Glide實(shí)現(xiàn)漸進(jìn)式加載,雖然最后實(shí)現(xiàn)了這個(gè)功能,但是會(huì)破壞Glide的結(jié)構(gòu),影響內(nèi)部的一些優(yōu)化和設(shè)置,比如緩存和防止錯(cuò)位等,所以這里就不介紹這個(gè)實(shí)現(xiàn)過程了,但是在這個(gè)過程中,使用到一些實(shí)現(xiàn)漸進(jìn)式加載的操作,可以介紹一下.
3.實(shí)現(xiàn)
首先我們定義一個(gè)類,來通過網(wǎng)絡(luò)請求來獲取InputStream(HttpUrlFetcher是glide里面的一個(gè)實(shí)現(xiàn)類,這里將里面的代碼簡化一下,拿來使用)
//HttpUrlFetcher.javapublic class HttpUrlFetcher { private static final String TAG = "HttpUrlFetcher"; private HttpURLConnection urlConnection; private int size; public int getSize() { return size; } public InputStream loadData(URL url) throws IOException { urlConnection = (HttpURLConnection) url.openConnection(); urlConnection.setConnectTimeout(2500); urlConnection.setReadTimeout(2500); urlConnection.setUseCaches(false); urlConnection.setDoInput(true); size = urlConnection.getContentLength(); final int statusCode = urlConnection.getResponseCode(); if (statusCode / 100 == 2) { return urlConnection.getInputStream(); } return null; }}

找到控件和設(shè)置圖片鏈接(此鏈接的圖片是支持漸進(jìn)式加載的JPEG圖片)
//MainActivity url = "http://www.reasoft.com/tutorials/web/img/progress.jpg"; iv1 = (ImageView) findViewById(R.id.iv1);

在子線程中請求網(wǎng)絡(luò)并實(shí)現(xiàn)漸進(jìn)式加載
//MainActivity.javapublic void show(View v) { isCancel = false; new Thread(new Runnable() { @Override public void run() { try { //網(wǎng)絡(luò)請求 InputStream inputStream = httpUrl.loadData(new URL(url)); //創(chuàng)建一個(gè)和總數(shù)據(jù)一樣大的數(shù)組來當(dāng)容器 byte[] bytes = new byte[httpUrl.getSize()]; byte lastOne = 0; byte lastTwo = 0; int offest = 0; while (!isCancel) { //本次讀取的字節(jié) byte[] get = getBytes(inputStream); //放入本次讀取的數(shù)據(jù) System.arraycopy(get, 0, bytes, offest, get.length); offest = offest + get.length; //記錄最后兩位字符 lastOne = bytes[offest - 1]; lastTwo = bytes[offest - 2]; //替換掉最后兩個(gè)字節(jié)為FFD9,否則無法轉(zhuǎn)化成bitmap bytes[offest - 2] = -1; bytes[offest - 1] = -39; //生成bitmap Bitmap result = BitmapFactory.decodeByteArray(bytes, 0, offest); //還原最后兩個(gè)字節(jié) bytes[offest - 2] = lastTwo; bytes[offest - 1] = lastOne; Message obtain = Message.obtain(); Bundle bundle = new Bundle(); bundle.putParcelable(BITMAP, result); obtain.setData(bundle); handler.sendMessage(obtain); } } catch (IOException e) { e.printStackTrace(); } } }).start();}

//MainActivity.javaprivate Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); Bitmap bitmap = msg.getData().getParcelable(BITMAP); if (bitmap != null) { iv1.setImageBitmap(bitmap); } }};

//MainActivity.javapublic static byte[] getBytes(InputStream is) throws IOException { ByteArrayOutputStream outstream = new ByteArrayOutputStream(); //這里設(shè)置每次讀取的數(shù)量,設(shè)置小一點(diǎn)是為了讓效果更明顯 byte[] buffer = new byte[10]; // 用數(shù)據(jù)裝 int len = -1; //要實(shí)現(xiàn)比較理想的漸進(jìn)式加載效果,其實(shí)不應(yīng)該寫死每次讀取量,應(yīng)該是根據(jù)FFDA來判斷讀到第幾幀了,然后決定是否需要顯示了 if ((len = is.read(buffer)) != -1) { outstream.write(buffer, 0, len); } else { is.close(); } outstream.close(); // 關(guān)閉流一定要記得。 return outstream.toByteArray();}

這里有幾個(gè)地方要注意的 :
1.BitmapFactory.decodeByteArray方法讀取的字節(jié)數(shù)組,jpg文件開頭得是FFD8,結(jié)尾得是FFD9,不然的話會(huì)返回null,所以這邊在每次解析的時(shí)候都將最后兩位臨時(shí)換成FFD9
2.這里 FF D9是十六進(jìn)制的,需要轉(zhuǎn)換為二進(jìn)制,并且取補(bǔ)碼,最后的字節(jié)是 -1,-39
3.記得增加網(wǎng)絡(luò)權(quán)限

4.其他
本節(jié)代碼很多地方寫得不嚴(yán)禁,效率也不高,目的主要是介紹如何實(shí)現(xiàn)漸進(jìn)式加載.但是筆者認(rèn)為Java在處理這類情況時(shí),其實(shí)本身效率并不高,在實(shí)際項(xiàng)目使用的中,如果對性能要求比較高的話,這類操作還是用C語言等底層來寫,Android只是通過JNI來調(diào)用,這樣更加合理和高效.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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