-
Android異步加載 異步加載:用異步的方式加載數(shù)據(jù),原因無(wú)非兩個(gè):1.提高用戶體驗(yàn),用戶在加載數(shù)據(jù)的時(shí)候不會(huì)感覺(jué)到明顯的卡頓;2.安卓的要求。 異步加載最常用的兩種方式:多線程\線程池;AsyncTask。 其中AsyncTask的底層也是通過(guò)線程池實(shí)現(xiàn),所以二者異曲同工。查看全部
-
Android 異步加載的總結(jié) 1. 通過(guò)異步加載,避免阻塞UI線程,從而實(shí)現(xiàn)了網(wǎng)絡(luò)的異步訪問(wèn)。這是基礎(chǔ),也是 Android 要求我們這么做的。 2. 通過(guò) LruCache,將已經(jīng)下載的圖片放到內(nèi)存中,以一個(gè)緩存避免多次重復(fù)的下載,提高用戶的體驗(yàn)。當(dāng)然,這里只介紹了內(nèi)存緩存,我們還可以使用 DiskLruCache ,將內(nèi)存中的圖片保存到硬盤中,保存到存儲(chǔ)器中,做為一個(gè)持久化的緩存,這也稱作“二級(jí)緩存”。而內(nèi)存緩存便叫做“一級(jí)緩存”。 3. 通過(guò)判斷 ListView 的滑動(dòng)狀態(tài),決定什么時(shí)候該加載圖片,什么時(shí)候不該加載圖片,而讓ListView平滑地去滾動(dòng)。在這一點(diǎn)上我們是通過(guò)對(duì) ListView 的滑動(dòng)狀態(tài)的監(jiān)聽(tīng),去實(shí)現(xiàn)這樣的操作,將加載圖片的控制權(quán)由 getView() 轉(zhuǎn)移到我們的滑動(dòng)事件,從而讓復(fù)雜的 ListView 也能實(shí)現(xiàn)流暢的異步加載。 4. 不僅僅是 ListView ,任何控件都可以使用異步加載,這里以 ListView 作為例子的原因是因?yàn)樗容^常用。異步加載不僅僅用于獲取網(wǎng)絡(luò)圖片,所有的耗時(shí)操作我們都可以認(rèn)為是異步加載,通過(guò)異步的方式,去將這些耗時(shí)操作與主線程分開(kāi),從而提升用戶體驗(yàn),這也是異步加載的核心所在。查看全部
-
上節(jié)課中我們看到圖片最初并沒(méi)有加載,需要滑動(dòng)才加載,因?yàn)槲覀儼鸭虞d圖片的控制權(quán)交給了onScrollStateChanged ,也就是說(shuō)只有滑動(dòng)狀態(tài)改變,才觸發(fā)加載任務(wù)。那么預(yù)加載的問(wèn)題該如何解決呢?我們可以把啟動(dòng)時(shí)的圖片加載交給onScroll(因?yàn)樗恢北徽{(diào)用)去執(zhí)行。步驟: 1. 在NewsAdapter中設(shè)置一個(gè)布爾變量,判斷當(dāng)前是不是第一次啟動(dòng):private boolean mFirstIn; 在初始化的時(shí)候(就在構(gòu)造方法里面)把 mFirstIn設(shè)置為true。 2. 在 onScroll() 中進(jìn)行判斷: if( mFirstIn && visibleItemCount>0){ mImageLoader.loadImages(mStart,mEnd); mFirstIn = false; } 為什么要寫(xiě)visibleItemCount>0?因?yàn)?onScroll()會(huì)被多次調(diào)用,而初始化調(diào)用的時(shí)候visibleItemCount是等于0的,這時(shí)候 Item 還沒(méi)有被加載,所以我們要將這個(gè)過(guò)程跳過(guò)去。這就是為什么我們這里要使用兩個(gè)條件:當(dāng)前列表是第一次顯示、而且 ListView 的 Item 已經(jīng)畫(huà)出來(lái)了。查看全部
-
步驟: 1.在 NewsAdaper 中修改 onScrollStateChanged(): 如果停止?jié)L動(dòng)則加載可見(jiàn)項(xiàng):mImageLoader.loadImages(mStart,mEnd); 如果滾動(dòng),則取消所有加載任務(wù):mImageLoader.cancelAllTasks(); 2. 在ImageLoader中添加方法cancelAllTasks(): public void cancelAllTasks(){ if (mTask != null){ for (NewsAsyncTask task:mTask) { task.cancel(false);//任務(wù)取消 } } } 3. 修改 NewsAdapter 的有參構(gòu)造方法,添加參數(shù) ListView listView,并傳遞該參數(shù): mImageLoader = new ImageLoader(listView); 另外,別忘了注冊(cè)監(jiān)聽(tīng)事件:listView.setOnScrollListener(this); 4. 修改 MainActivity 中的 onPostExecute: NewsAdapter adapter = new NewsAdapter(MainActivity.this,newsBeen,mListView); 5. 修改 ImageLoader 的 onPostExecute,在最后添加:mTask.remove(this); //設(shè)置完bitmap,任務(wù)結(jié)束,所以要移除該任務(wù)(寫(xiě)在if之外) --------------------------- 核心:將顯示圖片的控制權(quán)由原來(lái)的 getView() 移交給 滑動(dòng)監(jiān)聽(tīng)事件。查看全部
-
4.修改onPostExecute,原有內(nèi)容注釋掉,改為: ImageView imageView = (ImageView) mListView.findViewWithTag(mUrl); if (imageView != null && bitmap !=null){ imageView.setImageBitmap(bitmap); } 在ImageView imageView=(ImageView) mListView.findViewWithTag(url); 我們不再使用getView()去觸發(fā)下載,而是使用ListView滾動(dòng)的時(shí)候去觸發(fā)下載任務(wù)。查看全部
-
異步加載——圖片加載優(yōu)化 1. 創(chuàng)建一個(gè)靜態(tài)String[]將所有要顯示的圖片的地址保存起來(lái) public static String[] URLStrings;//用來(lái)保存當(dāng)前所有需要顯示的圖片的url地址 public NewsAdapter(Context context, List<NewsBean> mListData) { super(); this.mList = mListData; mInflater = LayoutInflater.from(context); mImageLoader = new ImageLoader(); URLStrings = new String[mListData.size()]; for (int i = 0; i<mListData.size(); i++){ URLStrings[i] = mListData.get(i).imageUrl; }} 2. 創(chuàng)建一個(gè)方法,加載從start到end的所有圖片 public void loadImages(int start, int end){ for (int i = start; i < end; i++){ //因?yàn)閁RLStrings是靜態(tài)的,所以可以直接使用 String url = NewsAdapter.URLStrings[i]; //從緩存中獲取對(duì)應(yīng)的圖片并判斷 Bitmap bitmap = getBitmapFromCache(url); if(bitmap == null){ NewsAsyncTask task = new NewsAsyncTask(url); task.execute(url); mTasks.add(task); }else { //從緩存中直接獲取數(shù)據(jù)讓iamgeView顯示,這里imageView是從url的tag得來(lái)的 ImageView imageView = (ImageView) mListView.findViewWithTag(url); imageView.setImageBitmap(bitmap); }}} 3. 改寫(xiě)showImageByAsyncTask(),讓顯示圖片的操作交給scroll的監(jiān)聽(tīng)事件,當(dāng)bitmap不在緩存時(shí),也不去加載圖片查看全部
-
實(shí)際項(xiàng)目中的 Item 可能非常復(fù)雜,僅僅使用前面所學(xué)的異步加載過(guò)程,效果可能并不好。這是因?yàn)?ListView的滾動(dòng)對(duì)畫(huà)面流暢度的要求是非常高的,當(dāng)你做異步加載的時(shí)候,雖然加載是在新的線程中實(shí)行的,并沒(méi)有阻塞 UI 線程,但當(dāng)我們加載好之后,去更新 UI 線程,就會(huì)導(dǎo)致 UI線程發(fā)生一次“重繪”,而如果這次“重繪”正好發(fā)生在 ListView滾動(dòng)的時(shí)候,就會(huì)導(dǎo)致滾動(dòng)的“卡頓”,這種體驗(yàn)并不好。 那么,如何提高 ListView在滾動(dòng)時(shí)候的異步加載的效率問(wèn)題? 1. ListView滑動(dòng)停止后才加載可見(jiàn)項(xiàng); 2. ListView滑動(dòng)時(shí),取消所有加載項(xiàng)。 實(shí)際上,滾動(dòng)的時(shí)候加載可見(jiàn)項(xiàng)是沒(méi)有意義的。 ------------------ NewsAdapter 實(shí)現(xiàn)接口 AbsListView.OnScrollListener,重寫(xiě)如下兩個(gè)方法: onScrollStateChanged:當(dāng) ListView的滑動(dòng)狀態(tài)改變的時(shí)候會(huì)調(diào)用此方法。 onScroll:在整個(gè)滑動(dòng)狀態(tài)中都會(huì)去調(diào)用。 實(shí)現(xiàn)步驟: 判斷ListView當(dāng)前的滾動(dòng)狀態(tài),如果它處于滾動(dòng)過(guò)程中,則取消所有的正在加載的任務(wù),讓ListView安靜地滾動(dòng),當(dāng)它滾動(dòng)完畢之后,我們?cè)俑鶕?jù)當(dāng)前 ListView 所反映的第一項(xiàng)和最后一項(xiàng),去加載這之間所有的項(xiàng)目,也就是在ListView停止?jié)L動(dòng)之后加載所有可見(jiàn)項(xiàng)。查看全部
-
LruCache類似Map,底層也是通過(guò) HashMap實(shí)現(xiàn)的,所以也有 put與get 方法。 創(chuàng)建完兩個(gè)方法(//將數(shù)據(jù)存入緩存中,增加到緩存public void addBitmapToCache(String url,Bitmap bitmap){//判斷緩存是否存在 if (getBitmapFromCache(url)==null) {mCaches.put(url, bitmap);}} //從緩存中獲取數(shù)據(jù)public Bitmap getBitmapFromCache(String url){return mCaches.get(url);} )后,需要對(duì)之前的方法做一些修改: 1. 在showImageByAsyncTask()先判斷緩存中是否存在該對(duì)象,有則直接從緩存中取出,沒(méi)有才創(chuàng)建: Bitmap bitmap = getBitmapFromCache(url); if (bitmap==null) { new NewsAsyncTask(imageView, url).execute(url);//啟動(dòng)異步加載 }else { imageView.setImageBitmap(bitmap);//直接使用緩存中的bitmap,無(wú)需異步加載 } 2. 在doInBackground()中,原先只是下載圖片,現(xiàn)在要把下載的圖片緩存到 LruCache 里面: Bitmap bitmap = getBitmapFromURL(params[0]); if(bitmap!=null){ //bitmap非空表示確實(shí)下載好了,這時(shí)將其緩存起來(lái) addBitmapToCache(params[0],bitmap); } return bitmap; 3. 在 NewsAdapter 添加成員變量 ImageLoader mImageLoader; 并在構(gòu)造方法中初始化:mImageLoader = new ImageLoader(); 然后把getView()里面實(shí)現(xiàn)異步加載的這句代碼: new ImageLoader().showImageByAsyncTask(viewHolder.iv_icon,url); 改成 mImageLoader.show。。 這樣就保證只有一個(gè) LruCache。查看全部
-
使用緩存,將已經(jīng)加載的圖片緩存起來(lái),而不用每次都去加載 使用 LruCache: 1. 在我們定義的類 ImageLoader里面添加成員變量: private LruCache<String,Bitmap> mCaches; 這里第一個(gè)參數(shù)(Key)就是緩存的圖片的名字,選擇URL地址作為名字較好,所以類型寫(xiě)String,而第二個(gè)參數(shù)(Value)是我們要保存的對(duì)象,類型填Bitmap。 2. 在構(gòu)造方法中初始化 LruCache。在使用LruCache之前,我們需要設(shè)定一個(gè)可用范圍,畢竟不可能將所有內(nèi)存都作為緩存,這里要將一部分內(nèi)存轉(zhuǎn)化為我們所需要的緩存空間: 首先獲取當(dāng)前應(yīng)用可用的內(nèi)存大?。?int maxMemory = (int)Runtime.getRuntime().maxMemory(); 然后設(shè)定一個(gè)我們所需要的緩存的大?。?int cacheSize = maxMemory / 4; 接著初始化 LruCache,通過(guò)匿名內(nèi)部類的方式重寫(xiě)里面的sizeOf()方法,該方法是為了獲取每一個(gè)緩存對(duì)象的大小,我們必須重寫(xiě)這個(gè)方法,去加載正確的內(nèi)存大小,不然它默認(rèn)返回元素的個(gè)數(shù),這樣就不對(duì)了。sizeOf()在每次存入緩存的時(shí)候會(huì)調(diào)用,我們就需要在加入緩存的時(shí)候,給出一個(gè)準(zhǔn)確的大小,也就是Bitmap的大小,這里直接 return value.getByteCount();將bitmap的實(shí)際大小保存進(jìn)去: mCache = new LruCache<String, Bitmap>(cacheSize){ @Override protected int sizeOf(String key, Bitmap value) { //在每次存入緩存的時(shí)候調(diào)用 return value.getByteCount(); } }; 這樣,LruCache就初始化好了。 我們要如何使用呢?在使用之前需要?jiǎng)?chuàng)建兩個(gè)方法,分別是“將內(nèi)容保存到LruCache”和“從LruCache中獲取內(nèi)容”,也就是set/get方法。查看全部
-
使用asynctask實(shí)現(xiàn)異步加載圖片查看全部
-
在異步加載中,我們通常使用兩種方式去避免ListView的緩存所造成的ListView的Item的錯(cuò)亂: 1. 在BaseAdapter中設(shè)置對(duì)應(yīng)的Tag,將一個(gè) url 或者其他可以表明身份的信息與相應(yīng)的Item綁定,并在真正加載的時(shí)候,去判斷這樣一個(gè)身份信息是否正確,只有正確的情況下才會(huì)去設(shè)置圖片(if (mImageView.getTag().equals(mUrl)) )。 2. 使用成員變量(mImageView=iamgeView;mUrl=url;),將對(duì)應(yīng)的數(shù)據(jù)進(jìn)行緩存,從而避免由于網(wǎng)絡(luò)下載時(shí)間不確定所導(dǎo)致的時(shí)序上的混亂,這種方式與我們?cè)?BaseAdapter去使用ViewHolder這樣的一個(gè)模式去緩存View的方式是一樣的。我們通過(guò)成員變量將每次調(diào)用方法所傳進(jìn)來(lái)的參數(shù)緩存起來(lái),從而避免時(shí)序上的順序錯(cuò)誤。 這兩種方式在異步加載中是使用得非常廣泛的,大部分的異步加載錯(cuò)誤都可以通過(guò)這兩種方式去解決。 在子線程中將Bitmap以message的形式發(fā)送出去: Bitmap bitmap = getBitmapFromURL(url);//從 url中獲取 bitmap。 Message message = Message.obtain();//此方法使用現(xiàn)有的、已回收的Message,提高M(jìn)essage的使用效率。 message.obj = bitmap;//將信息的內(nèi)容(obj)設(shè)置為 bitmap。 handler.sendMessage(message);//調(diào)用Handler將載有bitmap的Message發(fā)送出去。 注意這里的 Handler 要選擇android.os.Handler的。查看全部
-
我們發(fā)現(xiàn)異步加載出現(xiàn)圖片錯(cuò)位的情況,圖片的排列順序有問(wèn)題。 圖片顯示錯(cuò)位的原因:ListView自身的緩存機(jī)制。正確的ListView沒(méi)有顯示正確的URL。 解決辦法:在適配器中給每一個(gè)圖片設(shè)置一個(gè)標(biāo)識(shí)Tag,Tag本身就是圖片對(duì)應(yīng)的url,也就是將圖片和 url 進(jìn)行了綁定。 如圖,只有當(dāng)Tag等于圖片自身的url的時(shí)候才加載圖片,否則保持原樣。避免了緩存圖片對(duì)正確圖片的影響。查看全部
-
將圖片的地址 urlString 轉(zhuǎn)變?yōu)?Bitmap。 基本上和網(wǎng)絡(luò)相關(guān)的操作都需要捕捉異常。 1. 將 urlString(字符串類型)轉(zhuǎn)變?yōu)?URL 類型對(duì)象url。 2. 通過(guò) url 獲取連接對(duì)象 connection。方法:url.openConnection()。 3. 通過(guò)連接對(duì)象獲得輸入流,并用BufferedInputStream包裝: is = new BufferedInputStream(connection.getInputStream()); 4. 將輸入流解析為Bitmap:bitmap = BitmapFactory.decodeStream(is); 5. 釋放資源:connection.disconnect(); is.close();查看全部
-
異步加載——實(shí)現(xiàn)ListView圖文混排邏輯總結(jié) 第一步 onCreate中new一個(gè)NewsAsyncTask 將url傳遞給getJsonData 實(shí)現(xiàn)異步訪問(wèn) 第二步 getJsonData將url轉(zhuǎn)換為NewsBean對(duì)象 第三步 onPostExecute講NewsBean的數(shù)據(jù)傳遞給NewsAdapter 第四步 NewsAdapter構(gòu)造一個(gè)ListView數(shù)據(jù)源 并將數(shù)據(jù)源設(shè)置給ListView 異步加載的第一層:通過(guò)AsyncTask訪問(wèn)網(wǎng)絡(luò),獲取json或者XML字符串,然后解析他們產(chǎn)生若干object,將每個(gè)object放入到ListVIew中(adapter需要使用view holder pattern去寫(xiě)),AsyncTask中的訪問(wèn)網(wǎng)絡(luò)獲取json或者XML字符串,并且產(chǎn)生若干個(gè)object的工作就是在doInBackground()方法中進(jìn)行的,所以這個(gè)方法總的來(lái)說(shuō)就是用來(lái)準(zhǔn)備數(shù)據(jù)源的。查看全部
-
本節(jié)課目標(biāo):文藝方式重現(xiàn)BaseAdpter 1、自定義Adpter繼承BaseAdpter; 2、定義變量:List<NewsBean>;LayoutInflater; 3、重寫(xiě)構(gòu)造函數(shù)NewsAdpter(Context context, List<NewsBean> data)。 4、文藝方式重寫(xiě)getView()方法。 4.1、自定義類ViewHolder,映射相關(guān)的view對(duì)象 最后在 onPostExecute()方法中將生成的 newsBean添加到 mListView:通過(guò)適配器加載: NewsAdapter adapter = new NewsAdapter(MainActivity.this,newsBeen); mListView.setAdapter(adapter);查看全部
舉報(bào)
0/150
提交
取消