編輯:關於android開發
Android中使用網絡訪問來加載網上的內容,並將其解析出來加載到控件中,是一種很常見的操作。但是Android的UI線程(也就是主線程)中是不允許進行耗時操作的,因為耗時操作會阻塞主線程,影響用戶體驗。而訪問網絡同樣是一個耗時操作,並且Android3.0以後是不允許在主線程中訪問網絡的,所以我們這裡用Android封裝好的AsyncTask類來完成這些耗時操作。
項目的目錄結構如下:

AsyncTask是一個抽象類,實際上他是封裝好的一個類,底層也是用handler和thread來實現的,我們首先應該定義一個類來繼承它。AsyncTask的繼承是包含三個泛型參數的,這點官方文檔上有詳細說明,第一個參數是要操作的數據的類型,我們一般傳入一個String字符串來表示網址;第二個參數是想要展示進度條的時候,進度條的參數類型,一般指定為Integer;第三個參數是doInBackground()方法操作返回的數據類型,這裡根據你想操作什麼樣的數據類型,就返回什麼樣的數據類型。注意:這三個參數不是一定要設置,用到了哪個設置哪個,不需要的參數可以設置為Void。
然後我們來看一下AsyncTask中的四個重要方法,基本上使用的時候我們都要重寫這幾個方法:
大概看完了這四個方法,下面我們開始看看這次的Demo:
因為是想要從網絡上獲取json數據,所以要先准備一個接口,我的接口是時光網的:
http://api.m.mtime.cn/News/NewsList.api?pageIndex=1
為了得到這個接口中的Json格式的數據,我們先定義了一個HttpUtils類,在其中先定義了一個測試網絡是否聯通的類isNetConn(Context context)如下所示:
1 /**
2 * 獲取網絡狀態
3 *
4 * @param context 上下文
5 * @return 聯通狀態
6 */
7 public static boolean isNetConn(Context context) {
8 //獲取網絡連接管理對象
9 ConnectivityManager manager = (ConnectivityManager) context.getSystemService(context.CONNECTIVITY_SERVICE);
10 //獲取活躍狀態的網絡信息對象
11 NetworkInfo info = manager.getActiveNetworkInfo();
12 if (info != null) {
13 return info.isConnected(); //返回是否鏈接
14 } else {
15 return false;
16 }
17
18 }
又定義了一個返回byte數組downloadFromNet()方法,來獲取數據的byte[]數組:
1 /**
2 * 獲取網絡上下載下來的數據的byte數組
3 *
4 * @param urlPath 網絡URL路徑
5 * @return 網絡上獲取的json字符串的byte數組形式
6 */
7 public static byte[] downloadFromNet(String urlPath) {
8 ByteArrayOutputStream baos = new ByteArrayOutputStream();
9 URL url = null;
10 try {
11 url = new URL(urlPath);
12 HttpURLConnection conn = (HttpURLConnection) url.openConnection();
13 conn.setRequestMethod("GET");
14 conn.setConnectTimeout(5000);
15 conn.setReadTimeout(5000);
16 conn.setDoInput(true);
17 conn.connect();
18 if (conn.getResponseCode() == 200) {
19 InputStream is = conn.getInputStream();
20 int len;
21 byte b[] = new byte[1024];
22 //注意這裡:is.read(b) 中的b數組一定要寫,不然讀取的數據不對
23 while ((len = is.read(b)) != -1) {
24 baos.write(b, 0, len);
25 baos.flush();
26 }
27 return baos.toByteArray();
28 }
29 return baos.toByteArray();
30 } catch (IOException e) {
31 e.printStackTrace();
32 }
33 return baos.toByteArray();
34 }
之所以返回byte[]數組類型,是方便我們下載其他東西的時候也可以使用。
獲取到了json字符串,下一步就是將其解析出來,定義一個ParserJson方法,json字符串的解析相信大家應該都是了解的,因為這是Android中非常重要的一部分知識,這裡就不再贅述,直接上代碼:
json串對應的實體類:
1 package com.yztc.lx.asynctasklistview.com.yztc.lx.bean;
2
3 import java.util.List;
4
5 /**
6 * 外層JsonObject對象
7 * Created by Lx on 2016/8/10.
8 */
9
10 public class ShiGuang {
11
12 private int totalCount;
13 private int pageCount;
14 private List<News> newsList;
15 }
1 package com.yztc.lx.asynctasklistview.com.yztc.lx.bean;
2
3 /**
4 * Created by Lx on 2016/8/10.
5 */
6
7 public class News {
8 private int id;
9 private int type;
10 private String image;
11 private String title;
12 private String title2;
13 private String summary;
14 private String summaryInfo;
15 private String tag;
16 private int commentCount;
17
18 @Override
19 public String toString() {
20 return "News{" +
21 "id=" + id +
22 ", type=" + type +
23 ", image='" + image + '\'' +
24 ", title='" + title + '\'' +
25 ", title2='" + title2 + '\'' +
26 ", summary='" + summary + '\'' +
27 ", summaryInfo='" + summaryInfo + '\'' +
28 ", tag='" + tag + '\'' +
29 ", commmentCount=" + commentCount +
30 '}';
31 }
32
33 public int getId() {
34 return id;
35 }
36
37 public void setId(int id) {
38 this.id = id;
39 }
40
41 public int getType() {
42 return type;
43 }
44
45 public void setType(int type) {
46 this.type = type;
47 }
48
49 public String getTitle() {
50 return title;
51 }
52
53 public void setTitle(String title) {
54 this.title = title;
55 }
56
57 public String getImage() {
58 return image;
59 }
60
61 public void setImage(String image) {
62 this.image = image;
63 }
64
65 public String getTitle2() {
66 return title2;
67 }
68
69 public void setTitle2(String title2) {
70 this.title2 = title2;
71 }
72
73 public String getSummary() {
74 return summary;
75 }
76
77 public void setSummary(String summary) {
78 this.summary = summary;
79 }
80
81 public String getSummaryInfo() {
82 return summaryInfo;
83 }
84
85 public void setSummaryInfo(String summaryInfo) {
86 this.summaryInfo = summaryInfo;
87 }
88
89 public String getTag() {
90 return tag;
91 }
92
93 public void setTag(String tag) {
94 this.tag = tag;
95 }
96
97 public int getCommmentCount() {
98 return commentCount;
99 }
100
101 public void setCommmentCount(int commmentCount) {
102 this.commentCount = commmentCount;
103 }
104 }
下面是ParserJson類:
1 package com.yztc.lx.asynctasklistview.com.yztc.lx.utils;
2
3 import com.yztc.lx.asynctasklistview.com.yztc.lx.bean.News;
4
5 import org.json.JSONArray;
6 import org.json.JSONException;
7 import org.json.JSONObject;
8
9 import java.util.ArrayList;
10 import java.util.List;
11
12 /**
13 * Created by Lx on 2016/8/10.
14 */
15
16 public class ParserJson {
17 public static List<News> parserJsonToNews(String jsonString){
18 List<News> list=null;
19 try {
20 list=new ArrayList<>();
21 JSONObject obj=new JSONObject(jsonString);
22 JSONArray arr=obj.getJSONArray("newsList");
23 for(int i=0;i<arr.length();i++){
24 JSONObject obj1=arr.getJSONObject(i);
25 News news=new News();
26 news.setId(obj1.getInt("id"));
27 news.setTitle(obj1.getString("title"));
28 news.setSummary(obj1.getString("summary"));
29 list.add(news);
30 }
31 } catch (JSONException e) {
32 e.printStackTrace();
33 }
34 return list;
35 }
36 }
json串格式化後的一部分如下所示:

本Demo中只解析了newsList中的部分內容,包括id,title,summary這三部分,但是實體類中基本上都定義了。
定義完了這些工具類之後,我們在主頁面的布局中加入一個ListView控件,再定義一個item.xml用來作為ListView的自布局,主界面就不上代碼了,下面看一下item的布局:
1 <?xml version="1.0" encoding="utf-8"?> 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="wrap_content"> 5 6 <TextView 7 android:id="@+id/tv_title" 8 android:layout_width="wrap_content" 9 android:layout_height="wrap_content" 10 android:padding="10dp" 11 android:text="Title" 12 android:textSize="20sp" /> 13 14 <TextView 15 android:id="@+id/tv_summary" 16 android:layout_width="wrap_content" 17 android:layout_height="wrap_content" 18 android:layout_below="@+id/tv_title" 19 android:padding="10dp" 20 android:text="Summary" 21 android:textSize="12sp" /> 22 23 <TextView 24 android:id="@+id/tv_id" 25 android:layout_width="wrap_content" 26 android:layout_height="wrap_content" 27 android:layout_alignParentRight="true" 28 android:layout_alignTop="@+id/tv_summary" 29 android:padding="10dp" 30 android:text="id" 31 android:textSize="12sp" /> 32 33 </RelativeLayout>
基本工作都完成了,下面完成異步任務類DownloadAsyncTask中的內容,因為在進入後台線程前沒有什麼准備工作,並且也不需要進度條,所以就只重寫了doInBackground()方法和onPostExecute()方法,代碼如下:
1 package com.yztc.lx.asynctasklistview.com.yztc.lx.async;
2
3 import android.content.Context;
4 import android.os.AsyncTask;
5 import android.util.Log;
6 import android.widget.ListView;
7 import android.widget.Spinner;
8 import android.widget.Toast;
9
10 import com.yztc.lx.asynctasklistview.com.yztc.lx.adapter.MyBaseAdapter;
11 import com.yztc.lx.asynctasklistview.com.yztc.lx.bean.News;
12 import com.yztc.lx.asynctasklistview.com.yztc.lx.utils.HttpUtils;
13 import com.yztc.lx.asynctasklistview.com.yztc.lx.utils.ParserJson;
14
15 import java.util.List;
16
17 /**
18 * Created by Lx on 2016/8/10.
19 */
20
21 public class DownloadAsyncTask extends AsyncTask<String, Void, List<News>> {
22 private Context mContext;
23 private ListView lv;
24 private Spinner sp;
25
26 public DownloadAsyncTask(Context mContext, ListView lv) {
27 this.mContext = mContext;
28 this.lv = lv;
29 }
30
31
32 @Override
33 protected List<News> doInBackground(String... params) {
34 List<News> list = null;
35 if(HttpUtils.isNetConn(mContext)){
36 byte[] b=HttpUtils.downloadFromNet(params[0]); //可變參數params當成一個數組使用,其中的params[0]就是我們傳遞過來的參數
37 String jsonString=new String(b);
38 Log.d("Tag",jsonString);
39 list=ParserJson.parserJsonToNews(jsonString);
40 Log.d("List",list.toString());
41 }
42 return list;
43 }
44
45 @Override
46 protected void onPostExecute(List<News> newses) {
47 if(newses!=null&&newses.size()!=0){
48 MyBaseAdapter adapter=new MyBaseAdapter(mContext,newses);
49 lv.setAdapter(adapter);
50 }else {
51 Toast.makeText(mContext,"數據加載失敗", Toast.LENGTH_SHORT).show();
52 }
53 }
54 }
因為要更新UI中的ListView,所以在DownloadAsyncTask的構造函數中傳入了ListView和Context兩個形參。在doInBackground()方法中完成了數據計算操作後,將返回一個List<News>類型的變量,會接著執行onPostExecute()方法,變量會傳到他的形參中。通過這個List集合,我們來完成ListView的數據的填充。填充ListView首先需要自定義一個適配器繼承自BaseAdapter,我們取名為MyBaseAdapter。為其傳入Context和list兩個參數,至於ListView的填充請看我的另一篇博客,下面直接上代碼:
1 package com.yztc.lx.asynctasklistview.com.yztc.lx.adapter;
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.TextView;
9
10 import com.yztc.lx.asynctasklistview.R;
11 import com.yztc.lx.asynctasklistview.com.yztc.lx.bean.News;
12
13 import java.util.List;
14
15 /**
16 * Created by Lx on 2016/8/10.
17 */
18
19 public class MyBaseAdapter extends BaseAdapter {
20 private Context mContext;
21 private List<News> list;
22 private LayoutInflater inflater;
23
24 public MyBaseAdapter(Context context, List<News> list) {
25 this.mContext = context;
26 this.list = list;
27 this.inflater=LayoutInflater.from(mContext);
28 }
29
30 /**
31 *
32 * @return 要填充的集合的長度
33 */
34 @Override
35 public int getCount() {
36 return list.size();
37 }
38
39
40 @Override
41 public Object getItem(int position) {
42 return list.get(position);
43 }
44
45 @Override
46 public long getItemId(int position) {
47 return position;
48 }
49
50 /**
51 *
52 * @param position 在適配器數據集合中的item的位置
53 * @param convertView 已經填充過的View,可以用來重用來提高加載速度
54 * @param parent 這個view將要展示的父容器
55 * @return
56 */
57 @Override
58 public View getView(int position, View convertView, ViewGroup parent) {
59 News news=list.get(position);
60 ViewHolder holder;
61 if(convertView==null){
62 holder=new ViewHolder();
63 convertView=inflater.inflate(R.layout.item,null);
64 holder.tv_id= (TextView) convertView.findViewById(R.id.tv_id);
65 holder.tv_summary= (TextView) convertView.findViewById(R.id.tv_summary);
66 holder.tv_title= (TextView) convertView.findViewById(R.id.tv_title);
67 convertView.setTag(holder);
68 }else{
69 holder= (ViewHolder) convertView.getTag();
70 }
71 holder.tv_id.setText(""+news.getId());
72 holder.tv_title.setText(news.getTitle());
73 holder.tv_summary.setText(news.getSummary());
74 return convertView;
75 }
76
77 class ViewHolder{
78 private TextView tv_title,tv_summary,tv_id;
79 }
80 }
這裡需要注意的是,不要忘了為ListView設置適配器setAdapter(adapter).。還有就是setText()方法有一個重載形式是:setText(int i)傳入了一個int類型的形參的話,系統會根據這個int類型的參數去查找資源ID,所以如果要為TextView設置一個整型的形參的話,需要將其轉換為字符串的格式,不然會報錯。
接下來要做的就是在主函數中調用我們的異步任務了:
1 package com.yztc.lx.asynctasklistview;
2
3 import android.os.Bundle;
4 import android.support.v7.app.AppCompatActivity;
5 import android.widget.ListView;
6 import android.widget.Spinner;
7
8 import com.yztc.lx.asynctasklistview.com.yztc.lx.async.DownloadAsyncTask;
9
10 public class MainActivity extends AppCompatActivity {
11
12 private ListView lv;
13 private String urlPath = "http://api.m.mtime.cn/News/NewsList.api?pageIndex=1";
14
15 @Override
16 protected void onCreate(Bundle savedInstanceState) {
17 super.onCreate(savedInstanceState);
18 setContentView(R.layout.activity_main);
19 lv = (ListView) findViewById(R.id.listview);
20 new DownloadAsyncTask(MainActivity.this,lv).execute(urlPath);
21 }
22 }
注意:不要忘了寫execute()方法。
至此,整個Demo就完成了,沒有什麼難得邏輯,就是一些小的問題需要注意一下,通過這個小Demo提高了我對Android中異步任務的理解,也加深了訪問網絡和自定義適配器的使用。
最後的截圖如下:
改變Activity啟動時的默認動畫,activity默認動畫
改變Activity啟動時的默認動畫,activity默認動畫why 在開發中我們可能需要改變這一行為,一般基於2種理由: how 筆者所知道的主要有3種方式,下面我們
Android應用程序的啟動流程
Android應用程序的啟動流程 Android系統中一個應用程序的主題是由ActivityThread構成的。不過涉及到很多細節如ActivityThread是由誰來創
Android之控件使用,android控件
Android之控件使用,android控件 Android系統為我們提供了大量的控件,例如:開關控件、單選按鈕、多選按鈕、單選菜單等等,那麼這些控件如何使用呢?本篇我
初次使用Android Studio時的配置
初次使用Android Studio時的配置 一、第一次安裝: Android Studio安裝完成後,第一次啟動AS前,為了避免重新下載新版本的SDK,操作如下: