在android開發中,經常需要使用數據分頁,比如要實現一個新聞列表的顯示,或者博文列表的顯示,不可能第一次加載就展示出全部,這就需要使用分頁的方法來加載數據,在android中Handler經常用來在耗時的工作中,它接收子線程發送的數據,並使用數據配合更新UI,AsyncTask是在一個線程中執行耗時操作然后把結果傳給UI線程,不需要你親自去管理線程和句柄。
一、使用Handler+線程方法
1、基礎知識
Handler在android系統中,主要負責發送和接收消息,它的用途主要有以下兩種:
(1)按照計划來處理一個消息(sendMessage(Message)方法)或者執行某個runnable實例(post(Runnable)方法)
(2)把其他的線程對象放入消息隊列中,避免線程沖突。
消息的發送通過post(Runnable), postAtTime(Runnable, long), postDelayed(Runnable, long), sendEmptyMessage(int),sendMessage(Message), sendMessageAtTime(Message, long)和 sendMessageDelayed(Message, long) 方法完成。對於postXXX方法通過Runnable對象給消息隊列,並在消息隊列到達后被調用。對於sendMessageXXX方法,則傳遞一個包含message對象,該對象可以被Handler類的handlerMessage(Message)方法處理。
2、主要代碼
1 public class HandlerDemo extends Activity implements OnScrollListener { 2 3 private ListView mListView; 4 LinearLayout loadingLayout; 5 private Thread mThread; 6 private ListViewAdapter adapter; 7 8 private int startIndex = 1;// 從第1條開始 9 private int size = 10;// 每次下載十條數據 10 private List<News> newsList; 11 List<Map<String, String>> data ; 12 13 /* 14 * 設置布局顯示屬性 15 */ 16 private LayoutParams mLayoutParams = new LayoutParams( 17 LinearLayout.LayoutParams.WRAP_CONTENT, 18 LinearLayout.LayoutParams.WRAP_CONTENT); 19 20 private LayoutParams ffLayoutParams = new LayoutParams( 21 LinearLayout.LayoutParams.FILL_PARENT, 22 LinearLayout.LayoutParams.FILL_PARENT); 23 24 private ProgressBar progressBar; 25 26 @Override 27 protected void onCreate(Bundle savedInstanceState) { 28 // TODO Auto-generated method stub 29 super.onCreate(savedInstanceState); 30 setContentView(R.layout.news_main); 31 data=new ArrayList<Map<String, String>>(); 32 addView(); 33 } 34 35 private void addView() { 36 if (startIndex == 1) { 37 newsList = new ArrayList<News>(); 38 newsList = getNewsList(); 39 } 40 getdata(newsList); 41 LinearLayout layout = new LinearLayout(this); 42 layout.setOrientation(LinearLayout.HORIZONTAL); 43 progressBar = new ProgressBar(this); 44 layout.addView(progressBar, mLayoutParams); 45 TextView textView = new TextView(this); 46 textView.setText("加載中..."); 47 textView.setGravity(Gravity.CENTER_VERTICAL); 48 layout.addView(textView, ffLayoutParams); 49 layout.setGravity(Gravity.CENTER); 50 loadingLayout = new LinearLayout(this); 51 loadingLayout.addView(layout, mLayoutParams); 52 loadingLayout.setGravity(Gravity.CENTER); 53 54 // 得到一個ListView用來顯示條目 55 mListView = (ListView) findViewById(R.id.listView); 56 mListView.addFooterView(loadingLayout); 57 adapter = new ListViewAdapter(); 58 mListView.setAdapter(adapter); 59 mListView.setOnScrollListener(this); 60 mListView.setTextFilterEnabled(true); 61 } 62 63 @Override 64 public void onScroll(AbsListView view, int firstVisibleItem, 65 int visibleItemCount, int totalItemCount) { 66 // TODO Auto-generated method stub 67 if (firstVisibleItem + visibleItemCount == totalItemCount) { 68 if (mThread == null || !mThread.isAlive()) { 69 mThread = new Thread() { 70 71 @Override 72 public void run() { 73 newsList = new ArrayList<News>(); 74 newsList = getNewsList(); 75 getdata(newsList); 76 Message msg = new Message(); 77 msg.what = 1; 78 handler.sendMessage(msg); 79 } 80 }; 81 mThread.run(); 82 } 83 } 84 } 85 86 Handler handler = new Handler() { 87 88 @Override 89 public void handleMessage(Message msg) { 90 // TODO Auto-generated method stub 91 if (msg.what == 1) { 92 startIndex = startIndex + size; 93 Log.v("startindex", startIndex + ""); 94 mListView.removeFooterView(loadingLayout); 95 mThread.stop(); 96 adapter.count += size; 97 adapter.notifyDataSetChanged(); 98 return; 99 } 100 } 101 }; 102 103 class ListViewAdapter extends BaseAdapter { 104 int count = 10; 105 106 @Override 107 public int getCount() { 108 // TODO Auto-generated method stub 109 return count; 110 } 111 112 @Override 113 public Object getItem(int position) { 114 // TODO Auto-generated method stub 115 return position; 116 } 117 118 @Override 119 public long getItemId(int position) { 120 // TODO Auto-generated method stub 121 return position; 122 } 123 124 @Override 125 public View getView(int position, View convertView, ViewGroup parent) { 126 // TODO Auto-generated method stub 127 convertView = LayoutInflater.from(getApplicationContext()).inflate( 128 R.layout.news_item, null); 129 TextView textView = (TextView) convertView 130 .findViewById(R.id.textNewsTitle); 131 textView.setText((data.get(position)).get("title")); 132 return convertView; 133 } 134 } 135 136 @Override 137 public void onScrollStateChanged(AbsListView view, int scrollState) { 138 // TODO Auto-generated method stub 139 140 } 141 142 private List<Map<String, String>> getdata(List<News> list) { 143 144 if (list == null) 145 return null; 146 for (News news : list) { 147 Map<String, String> map = new HashMap<String, String>(); 148 map.put("title", news.getTitle()); 149 data.add(map); 150 } 151 return data; 152 } 153 154 /* 155 * 獲取網絡數據 注:我是訪問本機的一個新聞服務,使用asp.net技術來實現的 156 * 這個是項目是一個基於android的資訊播報軟件 157 */ 158 private List<News> getNewsList() { 159 String path = "http://10.0.2.2/getNewsList.aspx"; 160 String xmlStr = "<?xml version='1.0' encoding='utf-8'?><source><categoryIds>1,3,7</categoryIds><startIndex>" 161 + startIndex 162 + "</startIndex><detail>2</detail><count>" 163 + size 164 + "</count></source>"; 165 NewsConnector newsConnector = new NewsConnector(); 166 List<News> list = new ArrayList<News>(); 167 list = newsConnector.getNewsList(path, xmlStr); 168 return list; 169 } 170 }
3、小結
ListView使用Handler+線程方式來動態加載數據的步驟如下:
1.先初始化頁面(如:加載第一頁數據)
2.在接收某個事件的消息之后(以上代碼是onScroll事件),啟動線程(線程完成下載數據,並發送消息給handler)
3.handler接收到消息后更新界面,顯示數據。
二、使用AsyncTask方法
1、基礎知識
AsyncTask也是android提供的一個為了不能阻塞主線程的一個類,AsyncTask定義了三種泛型類型Params、Progress和Result,Params啟動任務執行輸入參數,比如http請求的url和參數,Progress后台執行任務的百分比,后台執行最終返回的結果。
AsyncTask的執行分為四個步驟,每一步都對應都對應一個回調方法,開發者需要實現一個或者幾個方法,在任務的執行過程中,這些方法會自動調用。
onPreExecute(),在執行后台耗時操作前被調用,可以在執行此方法中做一些ui操作,比如顯示一個進度條等
doInBackground(Params...),這個方法在執行onPreExecute()后執行,這個方法完成耗時工作,比如下載等。
onProgressUpdate(Progress...),UI線程通過此方法獲取任務的完成的情況,比如完成的任務的百分比。
onPostExecute(Result),這個方法在耗時工作完成后被調用。UI線程調用此方法獲取結果。
注意:在使用AsyncTask類,有幾條准則需要遵守
(1)、Task的實例必須在UI線程中創建
(2)、execute方法必須在UI線程中調用
(3)、不要手動調用以上四個方法
(4)、這個任務只執行一次(如果執行第二次將會拋出異常)
2、主要代碼
1 @Override 2 public void onScroll(AbsListView arg0, int arg1, int arg2, int arg3) { 3 // TODO Auto-generated method stub 4 if(arg1+arg2==arg3) 5 { 6 if(!isloading) 7 { 8 new myAsyncTask().execute(null); 9 } 10 else 11 { 12 mListView.removeFooterView(loadingLayout); 13 } 14 } 15 } 16 17 @Override 18 public void onScrollStateChanged(AbsListView arg0, int arg1) { 19 // TODO Auto-generated method stub 20 21 } 22 23 private class myAsyncTask extends AsyncTask<Void, Void, Void> 24 { 25 26 @Override 27 protected Void doInBackground(Void... params) { 28 // TODO Auto-generated method stub 29 30 newsList = new ArrayList<News>(); 31 newsList = getNewsList(); 32 getdata(newsList); 33 return null; 34 35 } 36 37 @Override 38 protected void onPostExecute(Void result) { 39 // TODO Auto-generated method stub 40 super.onPostExecute(result); 41 adapter.count+=size; 42 adapter.notifyDataSetChanged(); 43 startIndex+=size; 44 isloading=false; 45 } 46 47 @Override 48 protected void onPreExecute() { 49 // TODO Auto-generated method stub 50 super.onPreExecute(); 51 isloading=true; 52 } 53 54 }
注:以上僅是和使用Handler+線程方法不同的代碼,建議下載源碼,了解詳細代碼
3、小結
ListView使用AsyncTask方法動態加載數據的方法如下:
1.和handler一樣初始化頁面(如:加載第一頁)
2.在接收某個事件的消息之后(以上代碼是onScroll事件),創建一個新的異步任務,並開始執行
3.耗時工作完成后,開始更新UI
三、總結
使用Handler+線程和使用AsyncTask方法進行ListView動態加載的比較
Handler+線程方式:
在使用Handler方式時,它涉及Handler、Thread、Message、Looper四個對象,在執行的流程如下:主線程啟動一個Thread,這個Thread執行耗時操作,耗時操作完成后,生成一個Message,Looper讀取Message並傳遞給Hander,Handler接收Message並更新響應的UI。因為Looper在一個message處理完,才會讀下一條,如果發生多個Message就會形成一個消息隊列,所以它對多個后台操作比較清晰,明朗。但對於單個message來講顯得代碼比較多,過於復雜。
AsyncTask方式:
AsyncTask繼承自Object,是android提供的輕量級的異步類。並提供了一個方法來獲取任務的執行進度(可以根據它來更新UI),最后會把結果返回在主線程。這個方式的比較簡單,而且可以清楚的看到耗時任務執行的進度。但是對於多個異步操作同時進行,並更新UI變得比較復雜。