Android開發--異步加載

由於移動端軟件開發思惟模式或者說是開發的架構實際上是不分平臺和編程語言的,就拿安卓和IOS來講,他們都是移動前端app開發展現數據和用戶交互數據的數據終端,移動架構的幾個大模塊:UI界面展現、本地數據可持續化存儲、網絡數據請求、性能優化等等,安卓和IOS開發都要考慮這些架構的模塊。因此,熟悉IOS的開發的人,再去學習一下安卓的開發以及安卓的開發模式,你會發現不少技術和思想安卓和IOS是同樣的,只是可能說法不同,因爲編程語言好比OC和Java略微的差別性,編碼習慣和細節不同以外,其餘都是同樣的。前端

 

本人開始對安卓略有興趣,開始對安卓粗淺的學習,一方面也會拿IOS和安卓進行對比闡述,若是你會IOS,再學習安卓的,閱讀本人的博客也許會對你有很大的幫助。java

(可是對於安卓開發的語言基礎Java、以及android studio的使用,本人不會詳細闡述,做爲有情懷有獨立能力的程序員,這些基礎應該不會難道大家的吧,更況且本人對android studio的使用一直經過google和百度來學習相關的setting和快捷鍵)android

 

在Android中,異步加載最經常使用的兩種方式:git

  一、多線程\線程池程序員

  二、AsyncTaskgithub

固然,AsyncTask底層是基於線程池實現的。因此以上兩種方法是殊途同歸。算法

 

1、首先,按照在IOS中用UITableView加載數據的思路同樣,咱們先要建立出自定義的Cell以及Cell的佈局:編程

新建item_layout.xml文件:json

源碼:api

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:layout_width="match_parent"
 4     android:layout_height="wrap_content"
 5     android:padding="4dp"
 6     android:orientation="horizontal"
 7     >
 8     <ImageView
 9         android:id="@+id/iv_icon"
10         android:layout_width="64dp"
11         android:layout_height="64dp"
12         android:src="@mipmap/ic_launcher"/>
13 
14 
15     <LinearLayout
16         android:layout_width="match_parent"
17         android:layout_height="match_parent"
18         android:padding="4dp"
19         android:gravity="center"
20         android:orientation="vertical">
21 
22         <TextView
23             android:id="@+id/tv_title"
24             android:layout_width="match_parent"
25             android:layout_height="wrap_content"
26             android:textSize="15sp"
27             android:maxLines="1"
28             android:text="標題標題標題"/>
29 
30 
31         <TextView
32             android:id="@+id/tv_content"
33             android:layout_width="match_parent"
34             android:layout_height="wrap_content"
35             android:textSize="10sp"
36             android:maxLines="3"
37             android:text="內容內容內容"/>
38 
39     </LinearLayout>
40 
41 
42 </LinearLayout>

就這樣,一個自定義的item就建立好了,就比如咱們IOS中xib或者storyboard上的UITableViewCell建立好了。

而後接着在activity_main.xml中建立一個ListView,這個ListView就比如咱們IOS的UITableView。

源碼:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     xmlns:tools="http://schemas.android.com/tools"
 4     android:id="@+id/activity_main"
 5     android:layout_width="match_parent"
 6     android:layout_height="match_parent"
 7     android:paddingBottom="@dimen/activity_vertical_margin"
 8     android:paddingLeft="@dimen/activity_horizontal_margin"
 9     android:paddingRight="@dimen/activity_horizontal_margin"
10     android:paddingTop="@dimen/activity_vertical_margin"
11     tools:context="com.example.heyang.myapplication.MainActivity">
12 
13     <ListView
14         android:id="@+id/lv_main"
15         android:layout_width="match_parent"
16         android:layout_height="match_parent"
17         />
18 
19 
20 </RelativeLayout>

2、由於每個item或者類比IOS的每個Cell都須要一個圖片地址、標題Title、內容Content三個數據,因此咱們就須要一個模型對象來一一映射對應到item,在安卓或者Java中的說法叫建立一個Bean對象,其實能夠類比理解爲IOS的model模型對象。

源碼:

 1 package com.example.heyang.myapplication;
 2 
 3 /**
 4  * Created by HeYang on 16/10/5.
 5  */
 6 
 7 public class NewsBean {
 8     // 包含三個屬性:一、標題二、內容三、圖片的網址
 9     public String newsIconURL;
10     public String newsTitle;
11     public String newsContent;
12 }

 

3、接着就是要數據源了,http://www.imooc.com/api/teacher?type=4&num=30,點擊這個URL打開網頁會看到一大堆數據,而後你能夠經過json格式轉換工具就能夠看到json的數據格式。

那麼接下來就很容易明白了,模型Bean對象中的三個屬性就能夠來自這個URL下的json數據中的name、picSmall、discription。

那麼接下來就是IOS中所謂的字典轉模型的步驟。只不過在這以前,先要經過網絡請求獲取到這些數據才行,這裏網絡請求獲取json數據的作法有點和IOS的不一樣了。

感受IOS的網絡請求,無論是蘋果的NSURLSession仍是第三方的AFN框架都已是封裝好的網絡請求框架。而安卓的獲取json數據的作法好像更接近底層,採用了輸入輸出流來獲取URL的網頁數據:

  1 package com.example.heyang.myapplication;
  2 
  3 import android.os.AsyncTask;
  4 import android.support.v7.app.AppCompatActivity;
  5 import android.os.Bundle;
  6 import android.util.Log;
  7 import android.widget.ListView;
  8 
  9 import org.json.JSONArray;
 10 import org.json.JSONException;
 11 import org.json.JSONObject;
 12 
 13 import java.io.BufferedReader;
 14 import java.io.IOException;
 15 import java.io.InputStream;
 16 import java.io.InputStreamReader;
 17 import java.io.UnsupportedEncodingException;
 18 import java.net.URL;
 19 import java.util.ArrayList;
 20 import java.util.List;
 21 
 22 public class MainActivity extends AppCompatActivity {
 23 
 24     private ListView mListView;
 25 
 26     private static String URL = "http://www.imooc.com/api/teacher?type=4&num=30";
 27 
 28     @Override
 29     protected void onCreate(Bundle savedInstanceState) {
 30         super.onCreate(savedInstanceState);
 31         setContentView(R.layout.activity_main);
 32 
 33         // 獲取xml上的ListView對象
 34         mListView = (ListView) findViewById(R.id.lv_main);
 35 
 36         new NewsAsyncTask().execute(URL);
 37     }
 38 
 39     // 經過輸入輸出流獲取整個網頁格式的字符串數據
 40     private String readStream(InputStream is){
 41         InputStreamReader isr;
 42         String result = "";
 43 
 44         try {
 45             String line = "";
 46             // 一、輸入流對象 二、輸入流讀取對象 三、字節讀取對象
 47             isr = new InputStreamReader(is,"utf-8");
 48             BufferedReader br = new BufferedReader(isr);
 49             while ((line = br.readLine()) != null){
 50                 result += line;
 51             }
 52         } catch(UnsupportedEncodingException e){
 53             e.printStackTrace();
 54         } catch (IOException e) {
 55             e.printStackTrace();
 56         }
 57         return result;
 58     }
 59 
 60 
 61     private List<NewsBean> getJsonData(String url){
 62         // 建立存儲NewsBean的集合對象
 63         List<NewsBean> newsBeanList = new ArrayList<>();
 64         try {
 65             // 取出網絡的json字符串的格式以後
 66             String jsonStr = readStream(new URL(url).openStream());
 67             // 就要用JSONObject對象進行解析
 68             JSONObject jsonObject;
 69             // 而後須要NewsBean,其實至關於IOS的模型對象
 70             NewsBean newsBean;
 71             try {
 72                 // jsonObject的對象,建立該對象的同時傳入json字符串格式的對象
 73                 jsonObject = new JSONObject(jsonStr);
 74                 // 拿到jsonObject對象以後,就須要經過key值來拿到數組
 75                 JSONArray jsonArray = jsonObject.getJSONArray("data");
 76                 // 而後開始遍歷數組,獲取模型數組
 77                 for (int i = 0;i<jsonArray.length();i++){
 78                     // 數組裏每個元素又是jsonObject
 79                     jsonObject = jsonArray.getJSONObject(i);
 80 
 81                     // 開始建立模型對象
 82                     newsBean = new NewsBean();
 83                     newsBean.newsIconURL = jsonObject.getString("picSmall");
 84                     newsBean.newsTitle = jsonObject.getString("name");
 85                     newsBean.newsContent = jsonObject.getString("description");
 86                     
 87                     // 建立的一個模型對象,就要添加到集合當中去
 88                     newsBeanList.add(newsBean);
 89                 }
 90 
 91 
 92             } catch (JSONException e) {
 93                 e.printStackTrace();
 94             }
 95 
 96             Log.d("heyang",jsonStr);
 97         } catch (IOException e) {
 98             e.printStackTrace();
 99         }
100 
101         return newsBeanList;
102     }
103 
104     // 建立一個內部類來實現 ,在實現下面內部類以前,須要自定義的Bean對象來封裝處理Josn格式的數據
105     class  NewsAsyncTask extends AsyncTask<String,Void,List<NewsBean>>{
106         @Override
107         protected List<NewsBean> doInBackground(String... strings) {
108             return getJsonData(strings[0]);
109         }
110     }
111 }

按照以上的源碼,就能順利的獲取到了ListView的數據源數組。

可是在網絡請求方面,別忘了要給項目增長Internet訪問權限:

<!--增長 網絡訪問權限 -->
<uses-permission android:name="android.permission.INTERNET"/>

 

4、建立繼承BaseAdapter自定義的Adapter,注意其中利用了匿名內部類來優化處理ListView的每個view的循環利用:

這裏要補充一下,安卓加載ListView用到了適配器模式,因此須要下面自定義的Adapter對象,這個和咱們IOS開發的加載UITableView用到的一些列代理方法的代理模式仍是有區別的。不過,若是讀者學習了適配器模式,將會對安卓底層用到的適配器模式就會有所理解了。

 1 package com.example.heyang.myapplication;
 2 
 3 import android.content.Context;
 4 import android.view.LayoutInflater;
 5 import android.view.View;
 6 import android.view.ViewGroup;
 7 import android.widget.BaseAdapter;
 8 import android.widget.ImageView;
 9 import android.widget.TextView;
10 
11 import java.util.List;
12 
13 /**
14  * Created by HeYang on 16/10/6.
15  */
16 
17 public class NewsAdapter extends BaseAdapter {
18 
19     // 適配器對象須要傳入Bean數據集合對象,相似IOS的模型數組集合
20     private List<NewsBean> beanList;
21     // 而後要傳入LayoutInflater對象,用來獲取xml文件的視圖控件
22     private LayoutInflater layoutInflater;
23 
24     // 建立構造方法
25     public NewsAdapter(MainActivity context, List<NewsBean> data){
26         beanList = data;
27         layoutInflater = LayoutInflater.from(context);// 這個context對象就是Activity對象
28     }
29 
30     @Override
31     public int getCount() {
32         return beanList.size();
33     }
34 
35     @Override
36     public Object getItem(int i) {
37         // 由於beanList是數組,經過get訪問對應index的元素
38         return beanList.get(i);
39     }
40 
41     @Override
42     public long getItemId(int i) {
43         return i;
44     }
45 
46     @Override
47     public View getView(int i, View view, ViewGroup viewGroup) {
48         ViewHolder viewHolder = null;
49         if (view == null){
50             viewHolder = new ViewHolder();
51             // 每個View都要和layout關聯
52             view = layoutInflater.inflate(R.layout.item_layout,null);
53             // 在R.layout.item_layout中有三個控件對象
54             // 如今所有傳遞給view對象了
55             viewHolder.tvTitle = (TextView) view.findViewById(R.id.tv_title);
56             viewHolder.tvContent = (TextView) view.findViewById(R.id.tv_content);
57             viewHolder.ivIcon = (ImageView) view.findViewById(R.id.iv_icon);
58             view.setTag(viewHolder);
59 
60         }else {
61 
62             // 重複利用,可是因爲裏面的View展現的數據顯然須要從新賦值
63             viewHolder = (ViewHolder) view.getTag();
64         }
65         viewHolder.tvTitle.setText(beanList.get(i).newsTitle);
66         viewHolder.tvContent.setText(beanList.get(i).newsContent);
67         // 先默認加載系統圖片
68         viewHolder.ivIcon.setImageResource(R.mipmap.ic_launcher);
69 
70         return view;
71     }
72 
73     // 最後須要一個匿名內部類來建立一個臨時緩存View的對象
74     class ViewHolder{
75         public TextView tvContent,tvTitle;
76         public ImageView ivIcon;
77     }
78 }

5、最後優化和完善MainActivity對象

  1 package com.example.heyang.myapplication;
  2 
  3 import android.os.AsyncTask;
  4 import android.support.v7.app.AppCompatActivity;
  5 import android.os.Bundle;
  6 import android.util.Log;
  7 import android.widget.ListView;
  8 
  9 import org.json.JSONArray;
 10 import org.json.JSONException;
 11 import org.json.JSONObject;
 12 
 13 import java.io.BufferedReader;
 14 import java.io.IOException;
 15 import java.io.InputStream;
 16 import java.io.InputStreamReader;
 17 import java.io.UnsupportedEncodingException;
 18 import java.net.URL;
 19 import java.util.ArrayList;
 20 import java.util.List;
 21 
 22 public class MainActivity extends AppCompatActivity {
 23 
 24     private ListView mListView;
 25 
 26     private static String URL = "http://www.imooc.com/api/teacher?type=4&num=30";
 27 
 28     @Override
 29     protected void onCreate(Bundle savedInstanceState) {
 30         super.onCreate(savedInstanceState);
 31         setContentView(R.layout.activity_main);
 32 
 33         // 獲取xml上的ListView對象
 34         mListView = (ListView) findViewById(R.id.lv_main);
 35 
 36         new NewsAsyncTask().execute(URL);
 37     }
 38 
 39     // 經過輸入輸出流獲取整個網頁格式的字符串數據
 40     private String readStream(InputStream is){
 41         InputStreamReader isr;
 42         String result = "";
 43 
 44         try {
 45             String line = "";
 46             // 一、輸入流對象 二、輸入流讀取對象 三、字節讀取對象
 47             isr = new InputStreamReader(is,"utf-8");
 48             BufferedReader br = new BufferedReader(isr);
 49             while ((line = br.readLine()) != null){
 50                 result += line;
 51             }
 52         } catch(UnsupportedEncodingException e){
 53             e.printStackTrace();
 54         } catch (IOException e) {
 55             e.printStackTrace();
 56         }
 57         return result;
 58     }
 59 
 60 
 61     private List<NewsBean> getJsonData(String url){
 62         // 建立存儲NewsBean的集合對象
 63         List<NewsBean> newsBeanList = new ArrayList<>();
 64         try {
 65             // 取出網絡的json字符串的格式以後
 66             String jsonStr = readStream(new URL(url).openStream());
 67             // 就要用JSONObject對象進行解析
 68             JSONObject jsonObject;
 69             // 而後須要NewsBean,其實至關於IOS的模型對象
 70             NewsBean newsBean;
 71             try {
 72                 // jsonObject的對象,建立該對象的同時傳入json字符串格式的對象
 73                 jsonObject = new JSONObject(jsonStr);
 74                 // 拿到jsonObject對象以後,就須要經過key值來拿到數組
 75                 JSONArray jsonArray = jsonObject.getJSONArray("data");
 76                 // 而後開始遍歷數組,獲取模型數組
 77                 for (int i = 0;i<jsonArray.length();i++){
 78                     // 數組裏每個元素又是jsonObject
 79                     jsonObject = jsonArray.getJSONObject(i);
 80 
 81                     // 開始建立模型對象
 82                     newsBean = new NewsBean();
 83                     newsBean.newsIconURL = jsonObject.getString("picSmall");
 84                     newsBean.newsTitle = jsonObject.getString("name");
 85                     newsBean.newsContent = jsonObject.getString("description");
 86 
 87                     // 建立的一個模型對象,就要添加到集合當中去
 88                     newsBeanList.add(newsBean);
 89                 }
 90 
 91 
 92             } catch (JSONException e) {
 93                 e.printStackTrace();
 94             }
 95 
 96             Log.d("heyang",jsonStr);
 97         } catch (IOException e) {
 98             e.printStackTrace();
 99         }
100 
101         return newsBeanList;
102     }
103 
104     // 建立一個內部類來實現 ,在實現下面內部類以前,須要自定義的Bean對象來封裝處理Josn格式的數據
105     class  NewsAsyncTask extends AsyncTask<String,Void,List<NewsBean>>{
106         @Override
107         protected List<NewsBean> doInBackground(String... strings) {
108             return getJsonData(strings[0]);
109         }
110 
111         @Override
112         protected void onPostExecute(List<NewsBean> newsBeen) {
113             super.onPostExecute(newsBeen);
114             NewsAdapter newsAdapter = new NewsAdapter(MainActivity.this,newsBeen);
115             mListView.setAdapter(newsAdapter);
116 
117         }
118     }
119 }

 好,運行一下模擬器看看結果:

6、下面採用兩種方法進行加載圖片:①多線程加載圖片 ②AsyncTask

①多線程加載圖片:

建立一個普通的Class類:ImageLoader,在內部使用線程run運行執行ImageView加載網絡圖片的邏輯

 1 package com.example.heyang.myapplication;
 2 
 3 import android.graphics.Bitmap;
 4 import android.graphics.BitmapFactory;
 5 import android.os.Handler;
 6 import android.os.Message;
 7 import android.widget.ImageView;
 8 
 9 import java.io.BufferedInputStream;
10 import java.io.IOException;
11 import java.io.InputStream;
12 import java.net.HttpURLConnection;
13 import java.net.MalformedURLException;
14 import java.net.URL;
15 
16 /**
17  * Created by HeYang on 16/10/6.
18  */
19 
20 public class ImageLoader {
21 
22     private ImageView mImageView;
23     private String mURLStr;
24 
25     private Handler handler = new Handler(){
26         @Override
27         public void handleMessage(Message msg) {
28             super.handleMessage(msg);
29 
30             if (mImageView.getTag().equals(mURLStr)){
31                 mImageView.setImageBitmap((Bitmap) msg.obj);
32             }
33 
34 
35         }
36     };
37 
38     public void showImageByThread(ImageView imageView, final String urlString){
39 
40         mImageView = imageView;
41         mURLStr = urlString;
42 
43         new Thread(){
44             @Override
45             public void run() {
46                 super.run();
47                 Bitmap bitmap = getBitmapFromURL(urlString);
48                 // 當前線程是子線程,並非UI主線程
49                 // 不是Message message = new Message();
50                 Message message = Message.obtain();
51                 message.obj = bitmap;
52                 handler.sendMessage(message);
53 
54             }
55         }.start();
56     }
57 
58     public Bitmap getBitmapFromURL(String urlString){
59         Bitmap bitmap = null;
60         InputStream is = null;
61 
62         try {
63             URL url = new URL(urlString);
64             HttpURLConnection connection = (HttpURLConnection) url.openConnection();
65             is = new BufferedInputStream(connection.getInputStream());
66             bitmap = BitmapFactory.decodeStream(is);
67             // 最後要關閉http鏈接
68             connection.disconnect();
69             Thread.sleep(1000);// 睡眠1秒
70         } catch (IOException e) {
71             e.printStackTrace();
72         } catch (InterruptedException e) {
73             e.printStackTrace();
74         } finally {
75 
76             try {
77                 is.close();
78             } catch (IOException e) {
79                 e.printStackTrace();
80             }
81         }
82 
83         return bitmap;
84     }
85 }

而後修改一下以前的NewsAdapter的代碼:

 1 package com.example.heyang.myapplication;
 2 
 3 import android.content.Context;
 4 import android.view.LayoutInflater;
 5 import android.view.View;
 6 import android.view.ViewGroup;
 7 import android.widget.BaseAdapter;
 8 import android.widget.ImageView;
 9 import android.widget.TextView;
10 
11 import java.util.List;
12 
13 /**
14  * Created by HeYang on 16/10/6.
15  */
16 
17 public class NewsAdapter extends BaseAdapter {
18 
19     // 適配器對象須要傳入Bean數據集合對象,相似IOS的模型數組集合
20     private List<NewsBean> beanList;
21     // 而後要傳入LayoutInflater對象,用來獲取xml文件的視圖控件
22     private LayoutInflater layoutInflater;
23 
24     // 建立構造方法
25     public NewsAdapter(MainActivity context, List<NewsBean> data){
26         beanList = data;
27         layoutInflater = LayoutInflater.from(context);// 這個context對象就是Activity對象
28     }
29 
30     @Override
31     public int getCount() {
32         return beanList.size();
33     }
34 
35     @Override
36     public Object getItem(int i) {
37         // 由於beanList是數組,經過get訪問對應index的元素
38         return beanList.get(i);
39     }
40 
41     @Override
42     public long getItemId(int i) {
43         return i;
44     }
45 
46     @Override
47     public View getView(int i, View view, ViewGroup viewGroup) {
48         ViewHolder viewHolder = null;
49         if (view == null){
50             viewHolder = new ViewHolder();
51             // 每個View都要和layout關聯
52             view = layoutInflater.inflate(R.layout.item_layout,null);
53             // 在R.layout.item_layout中有三個控件對象
54             // 如今所有傳遞給view對象了
55             viewHolder.tvTitle = (TextView) view.findViewById(R.id.tv_title);
56             viewHolder.tvContent = (TextView) view.findViewById(R.id.tv_content);
57             viewHolder.ivIcon = (ImageView) view.findViewById(R.id.iv_icon);
58 
59             view.setTag(viewHolder);
60 
61         }else {
62 
63             // 重複利用,可是因爲裏面的View展現的數據顯然須要從新賦值
64             viewHolder = (ViewHolder) view.getTag();
65         }
66         viewHolder.tvTitle.setText(beanList.get(i).newsTitle);
67         viewHolder.tvContent.setText(beanList.get(i).newsContent);
68         // 先默認加載系統圖片
69         viewHolder.ivIcon.setImageResource(R.mipmap.ic_launcher);  // 相似加載佔位圖片
70         viewHolder.ivIcon.setTag(beanList.get(i).newsIconURL);
71         // 將ImageView對象和URLSting對象傳入進去
72         new ImageLoader().showImageByThread(viewHolder.ivIcon,beanList.get(i).newsIconURL);
73 
74         return view;
75     }
76 
77     // 最後須要一個匿名內部類來建立一個臨時緩存View的對象
78     class ViewHolder{
79         public TextView tvContent,tvTitle;
80         public ImageView ivIcon;
81     }
82 }

注意,其中使用了viewHolder.ivIcon.setTag(beanList.get(i).newsIconURL);做爲惟一標示傳遞給ImageLoader內部作判斷,這樣加載ListView不會出現圖片加載和緩存衝突了現象。(源碼中保留了Thread.sleep(1000)能夠查看到效果)。

②AsyncTask

接着,使用AsyncTask異步加載,在imageLoader對象的基礎上繼續:

完整源碼:

  1 package com.example.heyang.myapplication;
  2 
  3 import android.graphics.Bitmap;
  4 import android.graphics.BitmapFactory;
  5 import android.os.AsyncTask;
  6 import android.os.Handler;
  7 import android.os.Message;
  8 import android.widget.ImageView;
  9 
 10 import java.io.BufferedInputStream;
 11 import java.io.IOException;
 12 import java.io.InputStream;
 13 import java.net.HttpURLConnection;
 14 import java.net.MalformedURLException;
 15 import java.net.URL;
 16 
 17 /**
 18  * Created by HeYang on 16/10/6.
 19  */
 20 
 21 public class ImageLoader {
 22 
 23     private ImageView mImageView;
 24     private String mURLStr;
 25 
 26     private Handler handler = new Handler(){
 27         @Override
 28         public void handleMessage(Message msg) {
 29             super.handleMessage(msg);
 30 
 31             if (mImageView.getTag().equals(mURLStr)){
 32                 mImageView.setImageBitmap((Bitmap) msg.obj);
 33             }
 34 
 35 
 36         }
 37     };
 38 
 39     public void showImageByThread(ImageView imageView, final String urlString){
 40 
 41         mImageView = imageView;
 42         mURLStr = urlString;
 43 
 44         new Thread(){
 45             @Override
 46             public void run() {
 47                 super.run();
 48                 Bitmap bitmap = getBitmapFromURL(urlString);
 49                 // 當前線程是子線程,並非UI主線程
 50                 // 不是Message message = new Message();
 51                 Message message = Message.obtain();
 52                 message.obj = bitmap;
 53                 handler.sendMessage(message);
 54 
 55             }
 56         }.start();
 57     }
 58 
 59     public Bitmap getBitmapFromURL(String urlString){
 60         Bitmap bitmap = null;
 61         InputStream is = null;
 62 
 63         try {
 64             URL url = new URL(urlString);
 65             HttpURLConnection connection = (HttpURLConnection) url.openConnection();
 66             is = new BufferedInputStream(connection.getInputStream());
 67             bitmap = BitmapFactory.decodeStream(is);
 68             // 最後要關閉http鏈接
 69             connection.disconnect();
 70             Thread.sleep(1000);// 睡眠1秒
 71         } catch (IOException e) {
 72             e.printStackTrace();
 73         } catch (InterruptedException e) {
 74             e.printStackTrace();
 75         } finally {
 76 
 77             try {
 78                 is.close();
 79             } catch (IOException e) {
 80                 e.printStackTrace();
 81             }
 82         }
 83 
 84         return bitmap;
 85     }
 86 
 87 
 88     // ==================使用AsyncTask====================
 89     public void showImageByAsyncTask(ImageView imageView, final String urlString){
 90         new NewsAsyncTask(imageView, (String) imageView.getTag()).execute(urlString);// 這兩個參數分別傳遞的目的地能夠理解一下
 91     }
 92     
 93     private class NewsAsyncTask extends AsyncTask<String,Void,Bitmap>{
 94 
 95         // 須要私有的ImageView對象和構造方法來傳遞ImageView對象
 96         private ImageView mImageView;
 97         private String urlString;
 98 
 99         public NewsAsyncTask(ImageView imageView,String urlString){
100             mImageView = imageView;
101             urlString = urlString;
102         }
103 
104         @Override
105         protected Bitmap doInBackground(String... strings) {
106             // 在這個方法中,完成異步下載的任務
107             getBitmapFromURL(strings[0]);
108             return null;
109         }
110 
111 
112         @Override
113         protected void onPostExecute(Bitmap bitmap) {
114             super.onPostExecute(bitmap);
115             // 而後在這個方法中設置imageViewd
116             if (mImageView.getTag().equals(urlString)){
117                 mImageView.setImageBitmap(bitmap);
118             }
119 
120         }
121     }
122 
123 }

而後在NewsAdapter裏修改一下:

 而後運行效果和上面多線程的效果同樣,這裏就再也不重複展現了。

 

7、LruCache緩存處理

爲了提升用戶體驗,因此須要使用緩存機制。這裏安卓提供了Lru算法處理緩存。

Lru:Least Recently Used 近期最少使用算法。

因此,咱們須要在ImageLoader中建立LruCache對象:

完整源碼:

  1 package com.example.heyang.myapplication;
  2 
  3 import android.annotation.TargetApi;
  4 import android.graphics.Bitmap;
  5 import android.graphics.BitmapFactory;
  6 import android.os.AsyncTask;
  7 import android.os.Build;
  8 import android.os.Handler;
  9 import android.os.Message;
 10 import android.support.annotation.RequiresApi;
 11 import android.util.Log;
 12 import android.util.LruCache;
 13 import android.widget.ImageView;
 14 
 15 import java.io.BufferedInputStream;
 16 import java.io.IOException;
 17 import java.io.InputStream;
 18 import java.net.HttpURLConnection;
 19 import java.net.MalformedURLException;
 20 import java.net.URL;
 21 
 22 /**
 23  * Created by HeYang on 16/10/6.
 24  */
 25 
 26 public class ImageLoader {
 27 
 28     private ImageView mImageView;
 29     private String mURLStr;
 30     // 建立緩存對象
 31     // 第一個參數是須要緩存對象的名字或者ID,這裏咱們傳輸url做爲惟一名字便可
 32     // 第二個參數是Bitmap對象
 33     private LruCache<String,Bitmap> lruCache;
 34 
 35     // 而後咱們須要在構造方法中初始化這個緩存對象
 36     // 另外,咱們不可能把全部的緩存空間拿來用
 37     @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB_MR1)
 38     public ImageLoader(){
 39         
 40         // 獲取最大可用內存
 41         int maxMemory = (int) Runtime.getRuntime().maxMemory();
 42         int cachaSize =  maxMemory / 4;
 43         // 建立LruCache對象,同時用匿名內部類的方式重寫方法
 44         lruCache = new LruCache<String,Bitmap>(cachaSize){
 45             @Override
 46             protected int sizeOf(String key, Bitmap value) {
 47                 // 咱們須要直接返回Bitmap value的實際大小
 48                 //return super.sizeOf(key, value);
 49                 // 在每次存入緩存的時候調用
 50                 return value.getByteCount();
 51             }
 52         };
 53     }
 54 
 55     // 而後咱們要寫倆個方法:一、將bitmap存入緩存中 二、從緩存中取出bitmap
 56 
 57     @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
 58     public void addBitmapToCache(String url, Bitmap bitmap){
 59         if (getBitMapFromCache(url) == null){
 60             lruCache.put(url,bitmap);
 61         }
 62     }
 63 
 64     @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
 65     public Bitmap getBitMapFromCache(String url){
 66         return lruCache.get(url);
 67     }
 68 
 69 
 70     private Handler handler = new Handler(){
 71         @Override
 72         public void handleMessage(Message msg) {
 73             super.handleMessage(msg);
 74 
 75             if (mImageView.getTag().equals(mURLStr)){
 76                 mImageView.setImageBitmap((Bitmap) msg.obj);
 77             }
 78 
 79 
 80         }
 81     };
 82 
 83     public void showImageByThread(ImageView imageView, final String urlString){
 84 
 85         mImageView = imageView;
 86         mURLStr = urlString;
 87 
 88         new Thread(){
 89             @Override
 90             public void run() {
 91                 super.run();
 92                 Bitmap bitmap = getBitmapFromURL(urlString);
 93                 // 當前線程是子線程,並非UI主線程
 94                 // 不是Message message = new Message();
 95                 Message message = Message.obtain();
 96                 message.obj = bitmap;
 97                 handler.sendMessage(message);
 98 
 99             }
100         }.start();
101     }
102 
103     public Bitmap getBitmapFromURL(String urlString){
104         Bitmap bitmap = null;
105         InputStream is = null;
106 
107         try {
108             URL url = new URL(urlString);
109             HttpURLConnection connection = (HttpURLConnection) url.openConnection();
110             is = new BufferedInputStream(connection.getInputStream());
111             bitmap = BitmapFactory.decodeStream(is);
112             // 最後要關閉http鏈接
113             connection.disconnect();
114 //            Thread.sleep(1000);// 睡眠1秒
115         } catch (IOException e) {
116             e.printStackTrace();
117         }
118 //        catch (InterruptedException e) {
119 //            e.printStackTrace();
120 //        }
121         finally {
122 
123             try {
124                 is.close();
125             } catch (IOException e) {
126                 e.printStackTrace();
127             }
128         }
129 
130         return bitmap;
131     }
132 
133 
134     // ==================使用AsyncTask====================
135     public void showImageByAsyncTask(ImageView imageView, final String urlString){
136         // 在異步請求以前,先判斷緩存中是否有,有的話就取出直接加載
137         Bitmap bitmap = getBitMapFromCache(urlString);
138         if (bitmap == null){
139             // 若是沒有,就異步任務加劇
140             new NewsAsyncTask(imageView, (String) imageView.getTag()).execute(urlString);// 這兩個參數分別傳遞的目的地能夠理解一下
141         }else{
142             imageView.setImageBitmap(bitmap);
143         }
144 
145     }
146 
147     private class NewsAsyncTask extends AsyncTask<String,Void,Bitmap>{
148 
149         // 須要私有的ImageView對象和構造方法來傳遞ImageView對象
150         private ImageView mImageView;
151         private String mURlString;
152 
153         public NewsAsyncTask(ImageView imageView,String urlString){
154             mImageView = imageView;
155             mURlString = urlString;
156         }
157 
158         @Override
159         protected Bitmap doInBackground(String... strings) {
160             // 在這個方法中,完成異步下載的任務
161             String url = strings[0];
162             // 從網絡中獲取圖片
163             Bitmap bitmap = getBitmapFromURL(strings[0]);
164             if (bitmap != null){
165                 // 將網絡加載出來的圖片存儲緩存
166                 addBitmapToCache(url,bitmap);
167             }
168             return bitmap;
169         }
170 
171 
172         @Override
173         protected void onPostExecute(Bitmap bitmap) {
174             super.onPostExecute(bitmap);
175             // 而後在這個方法中設置imageViewd
176 
177             if (mImageView.getTag().equals(mURlString)){
178                 mImageView.setImageBitmap(bitmap);
179             }
180 
181         }
182     }
183 }

還須要在NewsAdapter類裏作一個小小的優化,由於適配器的getView是個不斷被調用的方法,就比如UITableView建立Cell的代理方法,會被不斷的被調用。而在getView方法中,ImageLoader的構造方法會被不斷的被調用,這樣的話,會形成ImageLoader構造方法中的LruCache對象不斷的被建立,這樣顯然是很差的。因此咱們須要用一個引用,指向一開始就建立好的ImageLoader對象,而後屢次使用。

而後

完整源碼:

 1 package com.example.heyang.myapplication;
 2 
 3 import android.annotation.TargetApi;
 4 import android.content.Context;
 5 import android.os.Build;
 6 import android.support.annotation.RequiresApi;
 7 import android.view.LayoutInflater;
 8 import android.view.View;
 9 import android.view.ViewGroup;
10 import android.widget.BaseAdapter;
11 import android.widget.ImageView;
12 import android.widget.TextView;
13 
14 import java.util.List;
15 
16 /**
17  * Created by HeYang on 16/10/6.
18  */
19 
20 public class NewsAdapter extends BaseAdapter {
21 
22     // 適配器對象須要傳入Bean數據集合對象,相似IOS的模型數組集合
23     private List<NewsBean> beanList;
24     // 而後要傳入LayoutInflater對象,用來獲取xml文件的視圖控件
25     private LayoutInflater layoutInflater;
26 
27     private ImageLoader imageLoader;
28 
29     // 建立構造方法
30 
31     @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
32     public NewsAdapter(MainActivity context, List<NewsBean> data){
33         beanList = data;
34         layoutInflater = LayoutInflater.from(context);// 這個context對象就是Activity對象
35         imageLoader = new ImageLoader();
36     }
37 
38     @Override
39     public int getCount() {
40         return beanList.size();
41     }
42 
43     @Override
44     public Object getItem(int i) {
45         // 由於beanList是數組,經過get訪問對應index的元素
46         return beanList.get(i);
47     }
48 
49     @Override
50     public long getItemId(int i) {
51         return i;
52     }
53 
54 
55     @Override
56     public View getView(int i, View view, ViewGroup viewGroup) {
57         ViewHolder viewHolder = null;
58         if (view == null){
59             viewHolder = new ViewHolder();
60             // 每個View都要和layout關聯
61             view = layoutInflater.inflate(R.layout.item_layout,null);
62             // 在R.layout.item_layout中有三個控件對象
63             // 如今所有傳遞給view對象了
64             viewHolder.tvTitle = (TextView) view.findViewById(R.id.tv_title);
65             viewHolder.tvContent = (TextView) view.findViewById(R.id.tv_content);
66             viewHolder.ivIcon = (ImageView) view.findViewById(R.id.iv_icon);
67 
68             view.setTag(viewHolder);
69 
70         }else {
71 
72             // 重複利用,可是因爲裏面的View展現的數據顯然須要從新賦值
73             viewHolder = (ViewHolder) view.getTag();
74         }
75         viewHolder.tvTitle.setText(beanList.get(i).newsTitle);
76         viewHolder.tvContent.setText(beanList.get(i).newsContent);
77         // 先默認加載系統圖片
78         viewHolder.ivIcon.setImageResource(R.mipmap.ic_launcher);  // 相似加載佔位圖片
79         viewHolder.ivIcon.setTag(beanList.get(i).newsIconURL);
80         // 將ImageView對象和URLSting對象傳入進去
81 //        new ImageLoader().showImageByThread(viewHolder.ivIcon,beanList.get(i).newsIconURL);
82         imageLoader.showImageByAsyncTask(viewHolder.ivIcon,beanList.get(i).newsIconURL);
83         return view;
84     }
85 
86     // 最後須要一個匿名內部類來建立一個臨時緩存View的對象
87     class ViewHolder{
88         public TextView tvContent,tvTitle;
89         public ImageView ivIcon;
90     }
91 }

 

8、滾動時的高效優化

前面的代碼,雖然順利的實現了異步加載的過程,可是在實際項目開發中,ListView的每個item項多是很複雜的,若是按照前面的代碼實現,用戶在滾動的過程就可能會出現卡頓的現象。

雖然從網絡加載的數據是在子線程中完成,可是更新UI的過程只能在主線程中更新,若是item項很複雜的話,滾動ListView出現卡頓現象也是能夠理解的。

可是咱們能夠優化這個卡頓的問題:

  ① ListView滑動中止後才加載可見項

  ② ListView滑動時,取消全部加載項

優化的緣由是:用戶在滑動的時候,由於ListView在滑動,因此不必作更新主線程UI的操做,而更新主線程UI的操做能夠放在滑動結束的時候執行,這樣也是符合用戶交互習慣的,同時也優化了卡頓的問題。

既然咱們須要監聽滾動的事件,能夠直接使用監聽滾動的接口:

並實現接口的方法:

由於咱們要實如今結束滾動的時候加載可見項,說的再明白點就是好比有100項須要加載,手機屏幕最多顯示8項,用戶滾動到第20項範圍內結束,這時候就要加載這地20項附近的8項便可。

因此要實現這樣的邏輯,就須要滾動結束以前,就要獲取可見項的起止位置,還要獲取這起止位置之間對應的全部數據。

接下來在前面代碼進行修改的細節就比較多了,這裏就直接上修改的源碼,整體思路就是以前的一邊滾動一邊執行適配器對象的getView對象去加載每個item,而接下來就是改爲

在滾動結束的時候,才加載可見項的圖片,固然以前的getView方法里加載String字符串的邏輯仍舊能夠保留,由於這個和經過網絡加載圖片的業務邏輯本質是不一樣的,讀者本身領會,不難。

在NewsAdapter類中:

  1 package com.example.heyang.myapplication;
  2 
  3 import android.annotation.TargetApi;
  4 import android.content.Context;
  5 import android.os.Build;
  6 import android.support.annotation.RequiresApi;
  7 import android.view.LayoutInflater;
  8 import android.view.View;
  9 import android.view.ViewGroup;
 10 import android.widget.AbsListView;
 11 import android.widget.BaseAdapter;
 12 import android.widget.ImageView;
 13 import android.widget.ListView;
 14 import android.widget.TextView;
 15 
 16 import java.util.List;
 17 
 18 /**
 19  * Created by HeYang on 16/10/6.
 20  */
 21 
 22 public class NewsAdapter extends BaseAdapter implements AbsListView.OnScrollListener{
 23 
 24     // 適配器對象須要傳入Bean數據集合對象,相似IOS的模型數組集合
 25     private List<NewsBean> beanList;
 26     // 而後要傳入LayoutInflater對象,用來獲取xml文件的視圖控件
 27     private LayoutInflater layoutInflater;
 28 
 29     private ImageLoader imageLoader;
 30 
 31     // 可見項的起止index
 32     private int mStart,mEnd;
 33     // 由於咱們須要存儲起止項全部的url地址
 34     public static String[] URLS;
 35 
 36     // 建立構造方法
 37     @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
 38     public NewsAdapter(MainActivity context, List<NewsBean> data, ListView listView){
 39         beanList = data;
 40         layoutInflater = LayoutInflater.from(context);// 這個context對象就是Activity對象
 41         imageLoader = new ImageLoader(listView);
 42 
 43         // 將模型數組中的url字符串單獨存儲在靜態數組中
 44         int dataSize = data.size();
 45         URLS = new String[dataSize];
 46         for (int i = 0; i < dataSize; i++) {
 47             URLS[i] = data.get(i).newsIconURL;
 48         }
 49 
 50         // 已經要記得註冊
 51         listView.setOnScrollListener(this);
 52     }
 53 
 54     @Override
 55     public int getCount() {
 56         return beanList.size();
 57     }
 58 
 59     @Override
 60     public Object getItem(int i) {
 61         // 由於beanList是數組,經過get訪問對應index的元素
 62         return beanList.get(i);
 63     }
 64 
 65     @Override
 66     public long getItemId(int i) {
 67         return i;
 68     }
 69 
 70 
 71     @Override
 72     public View getView(int i, View view, ViewGroup viewGroup) {
 73         ViewHolder viewHolder = null;
 74         if (view == null){
 75             viewHolder = new ViewHolder();
 76             // 每個View都要和layout關聯
 77             view = layoutInflater.inflate(R.layout.item_layout,null);
 78             // 在R.layout.item_layout中有三個控件對象
 79             // 如今所有傳遞給view對象了
 80             viewHolder.tvTitle = (TextView) view.findViewById(R.id.tv_title);
 81             viewHolder.tvContent = (TextView) view.findViewById(R.id.tv_content);
 82             viewHolder.ivIcon = (ImageView) view.findViewById(R.id.iv_icon);
 83 
 84             view.setTag(viewHolder);
 85 
 86         }else {
 87 
 88             // 重複利用,可是因爲裏面的View展現的數據顯然須要從新賦值
 89             viewHolder = (ViewHolder) view.getTag();
 90         }
 91         viewHolder.tvTitle.setText(beanList.get(i).newsTitle);
 92         viewHolder.tvContent.setText(beanList.get(i).newsContent);
 93         // 先默認加載系統圖片
 94         viewHolder.ivIcon.setImageResource(R.mipmap.ic_launcher);  // 相似加載佔位圖片
 95         viewHolder.ivIcon.setTag(beanList.get(i).newsIconURL);
 96         // 將ImageView對象和URLSting對象傳入進去
 97 //        new ImageLoader().showImageByThread(viewHolder.ivIcon,beanList.get(i).newsIconURL);
 98         imageLoader.showImageByAsyncTask(viewHolder.ivIcon,beanList.get(i).newsIconURL);
 99         return view;
100     }
101 
102     @Override
103     public void onScrollStateChanged(AbsListView absListView, int i) {
104         // SCROLL_STATE_IDLE :  滾動結束
105         if (i == SCROLL_STATE_IDLE){
106             // 加載可見項
107             imageLoader.loadImages(mStart,mEnd);
108         }else{
109             // 中止全部任務
110             imageLoader.cancelAllTask();
111         }
112     }
113 
114     @Override
115     public void onScroll(AbsListView absListView, int i, int i1, int i2) {
116         // i是第一個可見元素 i1是當前可見元素的長度
117         mStart = i;
118         mEnd = i + i1;
119     }
120 
121     // 最後須要一個匿名內部類來建立一個臨時緩存View的對象
122     class ViewHolder{
123         public TextView tvContent,tvTitle;
124         public ImageView ivIcon;
125     }
126 }

在ImageLoader類中:

  1 package com.example.heyang.myapplication;
  2 
  3 import android.annotation.TargetApi;
  4 import android.graphics.Bitmap;
  5 import android.graphics.BitmapFactory;
  6 import android.os.AsyncTask;
  7 import android.os.Build;
  8 import android.os.Handler;
  9 import android.os.Message;
 10 import android.support.annotation.RequiresApi;
 11 import android.util.Log;
 12 import android.util.LruCache;
 13 import android.widget.ImageView;
 14 import android.widget.ListView;
 15 
 16 import java.io.BufferedInputStream;
 17 import java.io.IOException;
 18 import java.io.InputStream;
 19 import java.net.HttpURLConnection;
 20 import java.net.MalformedURLException;
 21 import java.net.URL;
 22 import java.util.HashSet;
 23 import java.util.Set;
 24 
 25 /**
 26  * Created by HeYang on 16/10/6.
 27  */
 28 
 29 public class ImageLoader {
 30 
 31     private ImageView mImageView;
 32     private String mURLStr;
 33     // 建立緩存對象
 34     // 第一個參數是須要緩存對象的名字或者ID,這裏咱們傳輸url做爲惟一名字便可
 35     // 第二個參數是Bitmap對象
 36     private LruCache<String,Bitmap> lruCache;
 37 
 38     private ListView mListView;
 39     private Set<NewsAsyncTask> mTasks;
 40 
 41     // 而後咱們須要在構造方法中初始化這個緩存對象
 42     // 另外,咱們不可能把全部的緩存空間拿來用
 43     @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB_MR1)
 44     public ImageLoader(ListView listView){
 45 
 46         mListView = listView;
 47         mTasks = new HashSet<>();
 48 
 49         // 獲取最大可用內存
 50         int maxMemory = (int) Runtime.getRuntime().maxMemory();
 51         int cachaSize =  maxMemory / 4;
 52         // 建立LruCache對象,同時用匿名內部類的方式重寫方法
 53         lruCache = new LruCache<String,Bitmap>(cachaSize){
 54             @Override
 55             protected int sizeOf(String key, Bitmap value) {
 56                 // 咱們須要直接返回Bitmap value的實際大小
 57                 //return super.sizeOf(key, value);
 58                 // 在每次存入緩存的時候調用
 59                 return value.getByteCount();
 60             }
 61         };
 62     }
 63 
 64     // 而後咱們要寫倆個方法:一、將bitmap存入緩存中 二、從緩存中取出bitmap
 65 
 66     @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
 67     public void addBitmapToCache(String url, Bitmap bitmap){
 68         if (getBitMapFromCache(url) == null){
 69             lruCache.put(url,bitmap);
 70         }
 71     }
 72 
 73     @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
 74     public Bitmap getBitMapFromCache(String url){
 75         return lruCache.get(url);
 76     }
 77 
 78 
 79     private Handler handler = new Handler(){
 80         @Override
 81         public void handleMessage(Message msg) {
 82             super.handleMessage(msg);
 83 
 84             if (mImageView.getTag().equals(mURLStr)){
 85                 mImageView.setImageBitmap((Bitmap) msg.obj);
 86             }
 87 
 88 
 89         }
 90     };
 91 
 92     // 根據可見項起止位置加載可見項
 93     public void loadImages(int startIndex,int endIndex){
 94         for (int i = startIndex; i < endIndex; i++) {
 95             String url = NewsAdapter.URLS[i];
 96             // 在異步請求以前,先判斷緩存中是否有,有的話就取出直接加載
 97             Bitmap bitmap = getBitMapFromCache(url);
 98             if (bitmap == null){
 99                 // 若是沒有,就異步任務加劇
100 //                new NewsAsyncTask(imageView, (String) imageView.getTag()).execute(urlString);// 這兩個參數分別傳遞的目的地能夠理解一下
101                 NewsAsyncTask task = new NewsAsyncTask(url);
102                 task.execute(url);
103                 mTasks.add(task);
104 
105 
106 
107             }else{
108                 ImageView loadImageView = (ImageView) mListView.findViewWithTag(url);
109                 loadImageView.setImageBitmap(bitmap);
110             }
111         }
112     }
113 
114     public void cancelAllTask(){
115         if (mTasks != null){
116             for (NewsAsyncTask newsTask: mTasks) {
117                 newsTask.cancel(false);
118             }
119         }
120     }
121 
122 
123 
124 
125 
126     public Bitmap getBitmapFromURL(String urlString){
127         Bitmap bitmap = null;
128         InputStream is = null;
129 
130         try {
131             URL url = new URL(urlString);
132             HttpURLConnection connection = (HttpURLConnection) url.openConnection();
133             is = new BufferedInputStream(connection.getInputStream());
134             bitmap = BitmapFactory.decodeStream(is);
135             // 最後要關閉http鏈接
136             connection.disconnect();
137 //            Thread.sleep(1000);// 睡眠1秒
138         } catch (IOException e) {
139             e.printStackTrace();
140         }
141 //        catch (InterruptedException e) {
142 //            e.printStackTrace();
143 //        }
144         finally {
145 
146             try {
147                 is.close();
148             } catch (IOException e) {
149                 e.printStackTrace();
150             }
151         }
152 
153         return bitmap;
154     }
155 
156 
157     // ==================使用AsyncTask====================
158     public void showImageByAsyncTask(ImageView imageView, final String urlString){
159         // 在異步請求以前,先判斷緩存中是否有,有的話就取出直接加載
160         Bitmap bitmap = getBitMapFromCache(urlString);
161         if (bitmap == null){
162             // 若是沒有,就異步任務加劇
163 //            new NewsAsyncTask( urlString).execute(urlString);// 這兩個參數分別傳遞的目的地能夠理解一下
164             imageView.setImageResource(R.mipmap.ic_launcher);
165         }else{
166             imageView.setImageBitmap(bitmap);
167         }
168 
169     }
170 
171     private class NewsAsyncTask extends AsyncTask<String,Void,Bitmap>{
172 
173         // 須要私有的ImageView對象和構造方法來傳遞ImageView對象
174 //        private ImageView mImageView;
175         private String mURlString;
176 
177 //        public NewsAsyncTask(ImageView imageView,String urlString){
178 //            mImageView = imageView;
179 //            mURlString = urlString;
180 //        }
181 
182         public NewsAsyncTask(String urlString){
183             mURlString = urlString;
184         }
185 
186         @Override
187         protected Bitmap doInBackground(String... strings) {
188             // 在這個方法中,完成異步下載的任務
189             String url = strings[0];
190             // 從網絡中獲取圖片
191             Bitmap bitmap = getBitmapFromURL(strings[0]);
192             if (bitmap != null){
193                 // 將網絡加載出來的圖片存儲緩存
194                 addBitmapToCache(url,bitmap);
195             }
196             return bitmap;
197         }
198 
199 
200         @Override
201         protected void onPostExecute(Bitmap bitmap) {
202             super.onPostExecute(bitmap);
203             // 而後在這個方法中設置imageViewd
204 
205             ImageView executeImageView = (ImageView) mListView.findViewWithTag(mURlString);
206             if (bitmap != null && executeImageView != null){
207                 executeImageView.setImageBitmap(bitmap);
208             }
209             // 執行完當前任務,天然要把當前Task任務remove
210             mTasks.remove(this);
211 
212 //            if (mImageView.getTag().equals(mURlString)){
213 //                mImageView.setImageBitmap(bitmap);
214 //            }
215 
216         }
217     }
218 
219     // ==================使用多線程====================
220     public void showImageByThread(ImageView imageView, final String urlString){
221 
222         mImageView = imageView;
223         mURLStr = urlString;
224 
225         new Thread(){
226             @Override
227             public void run() {
228                 super.run();
229                 Bitmap bitmap = getBitmapFromURL(urlString);
230                 // 當前線程是子線程,並非UI主線程
231                 // 不是Message message = new Message();
232                 Message message = Message.obtain();
233                 message.obj = bitmap;
234                 handler.sendMessage(message);
235 
236             }
237         }.start();
238     }
239 }

 可是,以上的代碼還有一個不足之處,就是最開始啓動的時候,由於沒有滾動ScorllView,因此默認是沒有加載圖片的,可是咱們能夠作一個預加載:

完整源碼:

  1 package com.example.heyang.myapplication;
  2 
  3 import android.annotation.TargetApi;
  4 import android.content.Context;
  5 import android.os.Build;
  6 import android.support.annotation.RequiresApi;
  7 import android.view.LayoutInflater;
  8 import android.view.View;
  9 import android.view.ViewGroup;
 10 import android.widget.AbsListView;
 11 import android.widget.BaseAdapter;
 12 import android.widget.ImageView;
 13 import android.widget.ListView;
 14 import android.widget.TextView;
 15 
 16 import java.util.List;
 17 
 18 /**
 19  * Created by HeYang on 16/10/6.
 20  */
 21 
 22 public class NewsAdapter extends BaseAdapter implements AbsListView.OnScrollListener{
 23 
 24     // 適配器對象須要傳入Bean數據集合對象,相似IOS的模型數組集合
 25     private List<NewsBean> beanList;
 26     // 而後要傳入LayoutInflater對象,用來獲取xml文件的視圖控件
 27     private LayoutInflater layoutInflater;
 28 
 29     private ImageLoader imageLoader;
 30 
 31     private boolean isFirstLoadImage;
 32 
 33     // 可見項的起止index
 34     private int mStart,mEnd;
 35     // 由於咱們須要存儲起止項全部的url地址
 36     public static String[] URLS;
 37 
 38     // 建立構造方法
 39     @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
 40     public NewsAdapter(MainActivity context, List<NewsBean> data, ListView listView){
 41 
 42         isFirstLoadImage = true;
 43 
 44         beanList = data;
 45         layoutInflater = LayoutInflater.from(context);// 這個context對象就是Activity對象
 46         imageLoader = new ImageLoader(listView);
 47 
 48         // 將模型數組中的url字符串單獨存儲在靜態數組中
 49         int dataSize = data.size();
 50         URLS = new String[dataSize];
 51         for (int i = 0; i < dataSize; i++) {
 52             URLS[i] = data.get(i).newsIconURL;
 53         }
 54 
 55         // 已經要記得註冊
 56         listView.setOnScrollListener(this);
 57     }
 58 
 59     @Override
 60     public int getCount() {
 61         return beanList.size();
 62     }
 63 
 64     @Override
 65     public Object getItem(int i) {
 66         // 由於beanList是數組,經過get訪問對應index的元素
 67         return beanList.get(i);
 68     }
 69 
 70     @Override
 71     public long getItemId(int i) {
 72         return i;
 73     }
 74 
 75 
 76     @Override
 77     public View getView(int i, View view, ViewGroup viewGroup) {
 78         ViewHolder viewHolder = null;
 79         if (view == null){
 80             viewHolder = new ViewHolder();
 81             // 每個View都要和layout關聯
 82             view = layoutInflater.inflate(R.layout.item_layout,null);
 83             // 在R.layout.item_layout中有三個控件對象
 84             // 如今所有傳遞給view對象了
 85             viewHolder.tvTitle = (TextView) view.findViewById(R.id.tv_title);
 86             viewHolder.tvContent = (TextView) view.findViewById(R.id.tv_content);
 87             viewHolder.ivIcon = (ImageView) view.findViewById(R.id.iv_icon);
 88 
 89             view.setTag(viewHolder);
 90 
 91         }else {
 92 
 93             // 重複利用,可是因爲裏面的View展現的數據顯然須要從新賦值
 94             viewHolder = (ViewHolder) view.getTag();
 95         }
 96         viewHolder.tvTitle.setText(beanList.get(i).newsTitle);
 97         viewHolder.tvContent.setText(beanList.get(i).newsContent);
 98         // 先默認加載系統圖片
 99         viewHolder.ivIcon.setImageResource(R.mipmap.ic_launcher);  // 相似加載佔位圖片
100         viewHolder.ivIcon.setTag(beanList.get(i).newsIconURL);
101         // 將ImageView對象和URLSting對象傳入進去
102 //        new ImageLoader().showImageByThread(viewHolder.ivIcon,beanList.get(i).newsIconURL);
103         imageLoader.showImageByAsyncTask(viewHolder.ivIcon,beanList.get(i).newsIconURL);
104         return view;
105     }
106 
107     @Override
108     public void onScrollStateChanged(AbsListView absListView, int i) {
109         // SCROLL_STATE_IDLE :  滾動結束
110         if (i == SCROLL_STATE_IDLE){
111             // 加載可見項
112             imageLoader.loadImages(mStart,mEnd);
113         }else{
114             // 中止全部任務
115             imageLoader.cancelAllTask();
116         }
117     }
118 
119     @Override
120     public void onScroll(AbsListView absListView, int i, int i1, int i2) {
121         // i是第一個可見元素 i1是當前可見元素的長度
122         mStart = i;
123         mEnd = i + i1;
124         // 第一次預加載可見項
125         if (isFirstLoadImage && i1 > 0){
126             imageLoader.loadImages(mStart,mEnd);
127             isFirstLoadImage = false;
128         }
129     }
130 
131     // 最後須要一個匿名內部類來建立一個臨時緩存View的對象
132     class ViewHolder{
133         public TextView tvContent,tvTitle;
134         public ImageView ivIcon;
135     }
136 }

就這樣完成了異步加載的完整最優化的功能:

完整源碼下載地址:

 https://github.com/HeYang123456789/Android-ListView-AsyncLoader-Demo

相關文章
相關標籤/搜索