Android中使用网络访问来加载网上的内容,并将其解析出来加载到控件中,是一种很常见的操作。但是Android的UI线程(也就是主线程)中是不允许进行耗时操作的,因为耗时操作会阻塞主线程,影响用户体验。而访问网络同样是一个耗时操作,并且Android3.0以后是不允许在主线程中访问网络的,所以我们这里用Android封装好的AsyncTask类来完成这些耗时操作。

  项目的目录结构如下:

  

  AsyncTask是一个抽象类,实际上他是封装好的一个类,底层也是用handler和thread来实现的,我们首先应该定义一个类来继承它。AsyncTask的继承是包含三个泛型参数的,这点官方文档上有详细说明,第一个参数是要操作的数据的类型,我们一般传入一个String字符串来表示网址;第二个参数是想要展示进度条的时候,进度条的参数类型,一般指定为Integer;第三个参数是doInBackground()方法操作返回的数据类型,这里根据你想操作什么样的数据类型,就返回什么样的数据类型。注意:这三个参数不是一定要设置,用到了哪个设置哪个,不需要的参数可以设置为Void。

  然后我们来看一下AsyncTask中的四个重要方法,基本上使用的时候我们都要重写这几个方法:

  • onPreExecute():这个方法是在UI线程中调用,当我们调用AsyncTask的execute()方法的时候,此方法会首先执行,主要是完成一些初始化的操作,比如多用于初始化并显示进度条
  • doInBackground(Params...):该方法在onPreExecute()方法调用结束之后调用,他的形参是一个可变参数,此方法在WorkThread也就是工作线程中完成,所有的耗时操作都要放在这个方法中执行,他可以将计算的结果返回到onPostExecute()方法中,同时在这里也可以调用publishProgress()方法来跳到onProgressUpdate()方法完成进度条刻度的更新,需要主要的是publishProgress()方法在WorkThread中完成,而onProgressUpdate()方法在UI线程中完成
  • onProgressUpdate(Progress...):当调用了publishProgress()方法的时候,在UI线程被调用此方法,实现进度条的更新
  • onPostExecute(Result):此方法在后台任务执行完后调用,在UI线程中执行,后台执行计算出的Result可以返回到此方法中,在此方法中可以更新UI界面组件

  具体的AsyncTask的使用及源码分析请看这篇链接:http://blog.csdn.net/seu_calvin/article/details/52172248

  大概看完了这四个方法,下面我们开始看看这次的Demo:

  因为是想要从网络上获取json数据,所以要先准备一个接口,我的接口是时光网的:

 http://api.m.mtime.cn/News/NewsList.api?pageIndex=1
  为了得到这个接口中的Json格式的数据,我们先定义了一个HttpUtils类,在其中先定义了一个测试网络是否联通的类isNetConn(Context context)如下所示:
 
 /**
* 获取网络状态
*
* @param context 上下文
* @return 联通状态
*/
public static boolean isNetConn(Context context) {
//获取网络连接管理对象
ConnectivityManager manager = (ConnectivityManager) context.getSystemService(context.CONNECTIVITY_SERVICE);
//获取活跃状态的网络信息对象
NetworkInfo info = manager.getActiveNetworkInfo();
if (info != null) {
return info.isConnected(); //返回是否链接
} else {
return false;
} }

又定义了一个返回byte数组downloadFromNet()方法,来获取数据的byte[]数组:
  /**
* 获取网络上下载下来的数据的byte数组
*
* @param urlPath 网络URL路径
* @return 网络上获取的json字符串的byte数组形式
*/
public static byte[] downloadFromNet(String urlPath) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
URL url = null;
try {
url = new URL(urlPath);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
conn.setDoInput(true);
conn.connect();
if (conn.getResponseCode() == 200) {
InputStream is = conn.getInputStream();
int len;
byte b[] = new byte[1024];
//注意这里:is.read(b) 中的b数组一定要写,不然读取的数据不对
while ((len = is.read(b)) != -1) {
baos.write(b, 0, len);
baos.flush();
}
return baos.toByteArray();
}
return baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return baos.toByteArray();
}

之所以返回byte[]数组类型,是方便我们下载其他东西的时候也可以使用。

  获取到了json字符串,下一步就是将其解析出来,定义一个ParserJson方法,json字符串的解析相信大家应该都是了解的,因为这是Android中非常重要的一部分知识,这里就不再赘述,直接上代码:

json串对应的实体类:

 package com.yztc.lx.asynctasklistview.com.yztc.lx.bean;

 import java.util.List;

 /**
* 外层JsonObject对象
* Created by Lx on 2016/8/10.
*/ public class ShiGuang { private int totalCount;
private int pageCount;
private List<News> newsList;
}
 package com.yztc.lx.asynctasklistview.com.yztc.lx.bean;

 /**
* Created by Lx on 2016/8/10.
*/ public class News {
private int id;
private int type;
private String image;
private String title;
private String title2;
private String summary;
private String summaryInfo;
private String tag;
private int commentCount; @Override
public String toString() {
return "News{" +
"id=" + id +
", type=" + type +
", image='" + image + '\'' +
", title='" + title + '\'' +
", title2='" + title2 + '\'' +
", summary='" + summary + '\'' +
", summaryInfo='" + summaryInfo + '\'' +
", tag='" + tag + '\'' +
", commmentCount=" + commentCount +
'}';
} public int getId() {
return id;
} public void setId(int id) {
this.id = id;
} public int getType() {
return type;
} public void setType(int type) {
this.type = type;
} public String getTitle() {
return title;
} public void setTitle(String title) {
this.title = title;
} public String getImage() {
return image;
} public void setImage(String image) {
this.image = image;
} public String getTitle2() {
return title2;
} public void setTitle2(String title2) {
this.title2 = title2;
} public String getSummary() {
return summary;
} public void setSummary(String summary) {
this.summary = summary;
} public String getSummaryInfo() {
return summaryInfo;
} public void setSummaryInfo(String summaryInfo) {
this.summaryInfo = summaryInfo;
} public String getTag() {
return tag;
} public void setTag(String tag) {
this.tag = tag;
} public int getCommmentCount() {
return commentCount;
} public void setCommmentCount(int commmentCount) {
this.commentCount = commmentCount;
}
}

下面是ParserJson类:

 package com.yztc.lx.asynctasklistview.com.yztc.lx.utils;

 import com.yztc.lx.asynctasklistview.com.yztc.lx.bean.News;

 import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject; import java.util.ArrayList;
import java.util.List; /**
* Created by Lx on 2016/8/10.
*/ public class ParserJson {
public static List<News> parserJsonToNews(String jsonString){
List<News> list=null;
try {
list=new ArrayList<>();
JSONObject obj=new JSONObject(jsonString);
JSONArray arr=obj.getJSONArray("newsList");
for(int i=0;i<arr.length();i++){
JSONObject obj1=arr.getJSONObject(i);
News news=new News();
news.setId(obj1.getInt("id"));
news.setTitle(obj1.getString("title"));
news.setSummary(obj1.getString("summary"));
list.add(news);
}
} catch (JSONException e) {
e.printStackTrace();
}
return list;
}
}

json串格式化后的一部分如下所示:

本Demo中只解析了newsList中的部分内容,包括id,title,summary这三部分,但是实体类中基本上都定义了。

  定义完了这些工具类之后,我们在主页面的布局中加入一个ListView控件,再定义一个item.xml用来作为ListView的自布局,主界面就不上代码了,下面看一下item的布局:

 <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"> <TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="Title"
android:textSize="20sp" /> <TextView
android:id="@+id/tv_summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/tv_title"
android:padding="10dp"
android:text="Summary"
android:textSize="12sp" /> <TextView
android:id="@+id/tv_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignTop="@+id/tv_summary"
android:padding="10dp"
android:text="id"
android:textSize="12sp" /> </RelativeLayout>

   基本工作都完成了,下面完成异步任务类DownloadAsyncTask中的内容,因为在进入后台线程前没有什么准备工作,并且也不需要进度条,所以就只重写了doInBackground()方法和onPostExecute()方法,代码如下:

 package com.yztc.lx.asynctasklistview.com.yztc.lx.async;

 import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.Toast; import com.yztc.lx.asynctasklistview.com.yztc.lx.adapter.MyBaseAdapter;
import com.yztc.lx.asynctasklistview.com.yztc.lx.bean.News;
import com.yztc.lx.asynctasklistview.com.yztc.lx.utils.HttpUtils;
import com.yztc.lx.asynctasklistview.com.yztc.lx.utils.ParserJson; import java.util.List; /**
* Created by Lx on 2016/8/10.
*/ public class DownloadAsyncTask extends AsyncTask<String, Void, List<News>> {
private Context mContext;
private ListView lv;
private Spinner sp; public DownloadAsyncTask(Context mContext, ListView lv) {
this.mContext = mContext;
this.lv = lv;
} @Override
protected List<News> doInBackground(String... params) {
List<News> list = null;
if(HttpUtils.isNetConn(mContext)){
byte[] b=HttpUtils.downloadFromNet(params[0]); //可变参数params当成一个数组使用,其中的params[0]就是我们传递过来的参数
String jsonString=new String(b);
Log.d("Tag",jsonString);
list=ParserJson.parserJsonToNews(jsonString);
Log.d("List",list.toString());
}
return list;
} @Override
protected void onPostExecute(List<News> newses) {
if(newses!=null&&newses.size()!=0){
MyBaseAdapter adapter=new MyBaseAdapter(mContext,newses);
lv.setAdapter(adapter);
}else {
Toast.makeText(mContext,"数据加载失败", Toast.LENGTH_SHORT).show();
}
}
}

  因为要更新UI中的ListView,所以在DownloadAsyncTask的构造函数中传入了ListView和Context两个形参。在doInBackground()方法中完成了数据计算操作后,将返回一个List<News>类型的变量,会接着执行onPostExecute()方法,变量会传到他的形参中。通过这个List集合,我们来完成ListView的数据的填充。填充ListView首先需要自定义一个适配器继承自BaseAdapter,我们取名为MyBaseAdapter。为其传入Context和list两个参数,至于ListView的填充请看我的另一篇博客,下面直接上代码:

 package com.yztc.lx.asynctasklistview.com.yztc.lx.adapter;

 import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView; import com.yztc.lx.asynctasklistview.R;
import com.yztc.lx.asynctasklistview.com.yztc.lx.bean.News; import java.util.List; /**
* Created by Lx on 2016/8/10.
*/ public class MyBaseAdapter extends BaseAdapter {
private Context mContext;
private List<News> list;
private LayoutInflater inflater; public MyBaseAdapter(Context context, List<News> list) {
this.mContext = context;
this.list = list;
this.inflater=LayoutInflater.from(mContext);
} /**
*
* @return 要填充的集合的长度
*/
@Override
public int getCount() {
return list.size();
} @Override
public Object getItem(int position) {
return list.get(position);
} @Override
public long getItemId(int position) {
return position;
} /**
*
* @param position 在适配器数据集合中的item的位置
* @param convertView 已经填充过的View,可以用来重用来提高加载速度
* @param parent 这个view将要展示的父容器
* @return
*/
@Override
public View getView(int position, View convertView, ViewGroup parent) {
News news=list.get(position);
ViewHolder holder;
if(convertView==null){
holder=new ViewHolder();
convertView=inflater.inflate(R.layout.item,null);
holder.tv_id= (TextView) convertView.findViewById(R.id.tv_id);
holder.tv_summary= (TextView) convertView.findViewById(R.id.tv_summary);
holder.tv_title= (TextView) convertView.findViewById(R.id.tv_title);
convertView.setTag(holder);
}else{
holder= (ViewHolder) convertView.getTag();
}
holder.tv_id.setText(""+news.getId());
holder.tv_title.setText(news.getTitle());
holder.tv_summary.setText(news.getSummary());
return convertView;
} class ViewHolder{
private TextView tv_title,tv_summary,tv_id;
}
}

  这里需要注意的是,不要忘了为ListView设置适配器setAdapter(adapter).。还有就是setText()方法有一个重载形式是:setText(int i)传入了一个int类型的形参的话,系统会根据这个int类型的参数去查找资源ID,所以如果要为TextView设置一个整型的形参的话,需要将其转换为字符串的格式,不然会报错。

  接下来要做的就是在主函数中调用我们的异步任务了:

 package com.yztc.lx.asynctasklistview;

 import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.ListView;
import android.widget.Spinner; import com.yztc.lx.asynctasklistview.com.yztc.lx.async.DownloadAsyncTask; public class MainActivity extends AppCompatActivity { private ListView lv;
private String urlPath = "http://api.m.mtime.cn/News/NewsList.api?pageIndex=1"; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lv = (ListView) findViewById(R.id.listview);
new DownloadAsyncTask(MainActivity.this,lv).execute(urlPath);
}
}

  注意:不要忘了写execute()方法。

  至此,整个Demo就完成了,没有什么难得逻辑,就是一些小的问题需要注意一下,通过这个小Demo提高了我对Android中异步任务的理解,也加深了访问网络和自定义适配器的使用。
  最后的截图如下:
 

使用异步任务加载网络上json数据并加载到ListView中的更多相关文章

  1. Android 自定义 ListView 显示网络上 JSON 格式歌曲列表

    本文内容 环境 项目结构 演示自定义 ListView 显示网络上 JSON 歌曲列表 参考资料 本文最开始看的是一个国人翻译的文章,没有源代码可下载,根据文中提供的代码片段,自己新建的项目(比较可恶 ...

  2. KnockoutJS 3.X API 第七章 其他技术(1) 加载和保存JSON数据

    Knockout允许您实现复杂的客户端交互性,但几乎所有Web应用程序还需要与服务器交换数据,或至少将本地存储的数据序列化. 最方便的交换或存储数据的方式是JSON格式 - 大多数Ajax应用程序今天 ...

  3. Knockout应用开发指南 第六章:加载或保存JSON数据

    原文:Knockout应用开发指南 第六章:加载或保存JSON数据 加载或保存JSON数据 Knockout可以实现很复杂的客户端交互,但是几乎所有的web应用程序都要和服务器端交换数据(至少为了本地 ...

  4. 第六章:加载或保存JSON数据

    加载或保存JSON数据 Knockout可以实现很复杂的客户端交互,但是几乎所有的web应用程序都要和服务器端交换数据(至少为了本地存储需要序列化数据),交换数据最方便的就是使用JSON格式 – 大多 ...

  5. iOS开发网络篇—JSON数据的解析

    iOS开发网络篇—JSON数据的解析 iOS开发网络篇—JSON介绍 一.什么是JSON JSON是一种轻量级的数据格式,一般用于数据交互 服务器返回给客户端的数据,一般都是JSON格式或者XML格式 ...

  6. Android Volley 库通过网络获取 JSON 数据

    本文内容 什么是 Volley 库 Volley 能做什么 Volley 架构 环境 演示 Volley 库通过网络获取 JSON 数据 参考资料 Android 关于网络操作一般都会介绍 HttpC ...

  7. Snail—iOS网络学习之得到网络上的数据

    在开发项目project中,尤其是手机APP,一般都是先把界面给搭建出来.然后再从网上down数据 来填充 那么网上的数据是怎么得来的呢,网络上的数据无非就经常使用的两种JSON和XML 如今 大部分 ...

  8. D3.js加载csv和json数据

    1.加载数据的基本命令 D3提供了方法可以对不同的数据类型进行加载,比如d3.text(), d3.xml(), d3.json(), d3.csv(), 和d3.html(). <!DOCTY ...

  9. 6.Knockout.Js(加载或保存JSON数据)

    前言 Knockout可以实现很复杂的客户端交互,但是几乎所有的web应用程序都要和服务器端交换数据(至少为了本地存储需要序列化数据),交换数据最方便的就是使用JSON格式 – 大多数的Ajax应用程 ...

随机推荐

  1. 批处理命令 - if

    0.功能 Performs conditional processing in batch programs. 执行批处理程序中的条件处理. 1.简介 IF [NOT] ERRORLEVEL numb ...

  2. Hibernate笔记——缓存机制详细分析

    原文:http://www.cnblogs.com/xiaoluo501395377/p/3377604.html ========================================== ...

  3. Android中通过导入静态数据库来提高应用第一次的启动速度

    一个Android应用给用户的第一印象非常重要,除了要有好的创意和美观的界面,性能也是很关键的部分,本文讨论的就是第一次启动的速度问题. Android应用的启动过程不能让用户等待太长时间,个人觉得最 ...

  4. UBoot常用命令手册

    UBoot常用命令手册 U-Boot还提供了更加详细的命令帮助,可以通过”?”显示支持的命令列表,通过help [CommandName]命令还可以查看每个命令的参数说明. 1.bootm bootm ...

  5. 笔记二、本地git命令

    参考书籍:     <Pro Git>中文版.pdf   git init           // 建立一个git仓库, 本地目录为工作目录, .git目录是中央数据目录 git ini ...

  6. Hibernate 的<generator class="native"></generator>的不同属性含义

    1) assigned主键由外部程序负责生成,无需Hibernate参与. 2) hilo通过hi/lo 算法实现的主键生成机制,需要额外的数据库表保存主键生成历史状态. 3) seqhilo与hil ...

  7. JS获取系统的指定定年月日

    /** * 获取系统当前时间 */ function getNowYearMouth(){ var date=new Date; var nowYearMouth=date.getMonth()+1; ...

  8. SQL计算实际工作日(天)及两个时间(工作日)间隔(小时)!

    Code highlighting produced by Actipro CodeHighlighter (freeware)-->去掉法定节假日(周六,周天)和指定节假日 USE [DBNa ...

  9. CSS处理溢出

    如果内容超过的区块的大小,内容就会溢出,可以使用下面的方法控制溢出 overflow visible 不剪切内容也不添加滚动条 auto 在必需时对象内容才会被裁切或显示滚动条 hidden 不显示超 ...

  10. CSS之可收缩的底部边框

    简述 <div>用来定义文档中的分区或节,<span>用来组合文档中的行内元素.我们可以通过<div>和 <span>将HTML元素组合起来. 下面我们 ...