您的位置:首页 > 理论基础 > 计算机网络

【Android 网络数据解析实现一个简单的新闻实例(一)】

2015-11-10 21:17 1051 查看
      一般安卓在学到异步任务AsyncTask之后都会有个安卓小项目的任务。得到(荔枝新闻,茶百科等)新闻网络接口来解析网络图片或文字到ListView组件上显示。其中要使用到的知识大概有:获取网络数据(HttpUtil),解析网络数据(NewsParse),防止因解析超时应用程序无响应(ANR:Application
Not Responding) 的异步任务(AsyncTask),还有一个自定义的适配器(NewsAdapter),还有就是实例化AsyncTask类传递路径进行解析加载的MainActivity了。剩下的就是两个xml了,一个是主方法的。一个是ListView的自定义布局xml,。本次博客就不讲解点击ListView后加载详情页面了。

  先上图看看:   


  两个布局文件:

  activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >

<TextView
android:id="@+id/textView1"
android:layout_width="fill_parent"
android:layout_height="60dp"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:text="琦天下新闻"
android:textColor="#ffffff"
android:paddingTop="15dp"
android:paddingLeft="110dp"
android:background="#009999"
android:textAppearance="?android:attr/textAppearanceLarge" />

<ListView
android:id="@+id/listView1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#cccccc"
android:layout_alignParentLeft="true"
android:layout_below="@+id/textView1" >
</ListView>

</RelativeLayout>


listview_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:orientation="horizontal"
android:layout_height="match_parent" >

<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="120dp"
android:background="#fff">
 
<ImageView
android:id="@+id/image"
android:layout_width="100dp"
android:layout_height="100dp"

android:src="@drawable/ic_launcher"

/>

<TextView
android:id="@+id/subject"
android:layout_width="fill_parent"
android:layout_height="40dp"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:textColor="#009966"
android:textSize="14sp"
android:layout_toRightOf="@+id/image"
android:text="TextView" />

<TextView
android:id="@+id/changed"
android:layout_width="fill_parent"
android:layout_height="20dp"
android:layout_alignParentBottom="true"
android:textColor="#999999"
android:layout_toRightOf="@+id/image"
android:text="TextView" />

<TextView
android:id="@+id/summary"
android:layout_width="fill_parent"
android:layout_height="60dp"
android:layout_above="@+id/changed"

android:layout_toRightOf="@+id/image"
android:text="TextView" />

</RelativeLayout>

</LinearLayout>


1,HttpUtil工具,专门用来网络获取数据的。直接上代码。

      这是HttpGet获取的方式是一种特别简单的方式。

public class HttpUtil {
public static byte[] getJsonString(String path) throws ClientProtocolException, IOException{
byte[] data = null;
HttpGet get = new HttpGet(path);
HttpClient client = new DefaultHttpClient();

HttpResponse response = null;
response = client.execute(get);
if(response.getStatusLine().getStatusCode()==200)
{
data = EntityUtils.toByteArray(response.getEntity());
}
return data;

}
     再上一种获取数据方式吧,两种是一样的都是byte的类型,这些都是为了同时加载图片和使文字使用的方式,当然还有流的方式,但是我下面这个demo是将流的方式再转成byte方式。

     

public class HttpUtil {
public static byte[] parseImage(String path){

ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try {
URL url = new URL(path);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setReadTimeout(5000);
connection.setDoInput(true);
connection.connect();
if(connection.getResponseCode() == 200){
InputStream inputStream = connection.getInputStream();
int temp = 0;
byte[] buffer = new byte[1024];
while ((temp = inputStream.read(buffer)) !=-1) {
outputStream.write(buffer, 0, temp);
outputStream.flush();
}
}
} catch (IOException e) {
e.printStackTrace();
}
return outputStream.toByteArray();
}

}
 2,NewsParse解析获取到的数据也称JSON解析。

  

public class ParseTool {
public static List<News> parseNews(String json) throws JSONException{

List<News> list = new ArrayList<News>();

JSONObject object = new JSONObject(json);

object = object.getJSONObject("paramz"); //这个是获取集合名的字段“{ }”
JSONArray array = object.getJSONArray("feeds");//这个是获取数组名的字段 “[ ]”
int len = array.length();
for(int i = 0 ; i <len; i ++){
object = array.getJSONObject(i);

object = object.getJSONObject("data");
String subject = object.getString("subject");
String summary = object.getString("summary");
String cover = object.getString("cover");

String changed = object.getString("changed");

News news = new News();
news.setSubject(subject);
news.setCover(cover);
news.setChanged(changed);
news.setSummary(summary);

list.add(news);

}
//这段用来查看有没有解析到
System.out.println("++++++++++++++++++++++++++++++++++++"+list);
return list;

}
}
3,自定义适配器NewsAdapter,是为了让ListView中的item是以自定义布局的方式显示的。而不是用ArrayAdapter的方式直接只显示一条数据。

public class NewsAdapter extends BaseAdapter {
private List<News> list = null;

private Context context =null;
//private ViewHolder holder =null;

public NewsAdapter(Context context, List<News> list) {
// TODO Auto-generated constructor stub
this.context = context;
this.list = list;
}
@Override
public int getCount() {
int count = 0 ;
if(list!=null){
count = list.size();
}
return count;
}

@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return list.get(position);
}

@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {

if(convertView==null){
convertView = LayoutInflater.from(context).inflate(R.layout.item_person, parent,false);
ViewHolder holder = new ViewHolder();
holder.cover = (ImageView) convertView.findViewById(R.id.image);
holder.subject = (TextView) convertView.findViewById(R.id.subject);
holder.summary = (TextView) convertView.findViewById(R.id.summary);
holder.changed = (TextView) convertView.findViewById(R.id.changed);

convertView.setTag(holder);

}
final ViewHolder holder = (ViewHolder) convertView.getTag();

String cover = list.get(position).getCover();
String subject = list.get(position).getSubject();
String summary = list.get(position).getSummary();
String changed = list.get(position).getChanged();
String imageUrl = "http://litchiapi.jstv.com"+cover;

holder.subject.setText(subject);
holder.summary.setText(summary);
holder.changed.setText(changed);
holder.cover.setImageResource(R.drawable.ic_launcher);
//根据图片的地址去下载图片---用接口回调解决
Bitmap bitmap = ImageUtil.readImage(imageUrl);//这里还用到了图片缓存到扩展卡,方便图片加载
if(bitmap!=null){
holder.cover.setImageBitmap(bitmap);
}else{
new DownImageTask(new DownImageTask.DownLoadBack() {  //这儿用到了接口回调,下面会讲到
@Override
public void response(Bitmap bitmap) {

holder.cover.setImageBitmap(bitmap);
}
}).execute(imageUrl);
}

return convertView;
}

class ViewHolder{
TextView subject,changed,summary;
ImageView cover;
}

}


ImageUtil.java

package com.example.newsapp.tool;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.os.StatFs;

/**
* 操作扩展卡中文件的工具类(以缓存下载的图片为例
* 扩展卡对应的目录:mut/sdcard
* @author Administrator
*
*/
public class ImageUtil {

//定义存储图片的目录
public static final String IMAGE_URL = Environment.getExternalStorageState()+"/cache/images";

public static final int FORMAT_PNG =1;
public static final int FORMAT_JPEG =1;
/**
* 判断扩展卡是否挂载
*
*/
public static boolean isMounted(){
String state = Environment.getExternalStorageState();//获取扩展卡的状态

return state.equals(Environment.MEDIA_MOUNTED);

}

/**
* 根据下载图片的路径获取图片的文件名
*
*/
public static String getFileName(String url){

return url.substring(url.lastIndexOf("/")+1);

}

/**
* 保存图片到扩展卡
* @throws IOException
*
*/
/*	public static void saveImage(String url,byte[] data) throws IOException{
//判断扩展是否挂载
if(!isMounted()){
return ;
}
File dir = new File(IMAGE_URL);
if(!dir.exists()){
dir.mkdir();
FileOutputStream fos = new FileOutputStream(new File(dir,getFileName(url)));
fos.write(data);
fos.close();
}
}
*/
/**
* 保存图片到扩展卡
* @throws IOException
*
*/

public static void saveImage(String url , Bitmap bitmap,int format) throws IOException{
if(!isMounted()){
return;
}
File dir = new File(IMAGE_URL);
if(!dir.exists()){
dir.mkdir();
}
FileOutputStream fos = new FileOutputStream(new File(dir,getFileName(url)));

bitmap.compress(format == 1? CompressFormat.PNG:CompressFormat.JPEG, 100, fos);

fos.close();
}
/**
* 从扩展卡获取图片
*/
public static Bitmap readImage(String url){
if(!isMounted()){
return null;
}
File file = new File(IMAGE_URL);
Bitmap bitmap = null;
if(file.exists()){
//根据图片文件路径得到Bitmap类型的对象
bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
}
return bitmap;

}
/**
* 清空图片缓存中的图片
*/
public static void clear(){
File dir = new File(IMAGE_URL);
if(dir.exists()){
File[] file = dir.listFiles();
for(File files : file){
files.delete();
}
}
}
/**
* 判断扩展卡的剩余空间
*/
public static long getSize(){
StatFs statFs = new StatFs(Environment.getExternalStorageDirectory().getAbsolutePath());
int count = statFs.getFreeBlocks();//得到剩余数据块的个数
int size = statFs.getBlockCount();//得到每个数据块的大小

long ssize = (count*size)/1024/1024;
return ssize;
}

}

   今后这些工具类直接收藏起来,到时候要用的时候直接放入后调用就行。

 4 AsyncTask异步任务类

  上面已经谈到过一点了,在主线程中执行网络解析任务,响应时超过5秒,基本上会报一种叫ANR的错误。为了解决错误前期就用了异步任务去解决,当然等后期学到框架的时候,这些东西只需要一两个jar包就可以搞定。

   NewsTask.java

package com.example.newsapp.asyncTask;

import java.io.IOException;
import java.util.List;

import org.apache.http.client.ClientProtocolException;
import org.json.JSONException;

import android.content.Context;
import android.os.AsyncTask;
import android.widget.ListView;

import com.example.newsapp.entity.News;
import com.example.newsapp.newAdapter.NewsAdapter;
import com.example.newsapp.tool.HttpUtil;
import com.example.newsapp.tool.ParseTool;

public class NewsTask extends AsyncTask<String, Void, List<News>> {

private Context context;
private ListView listView;

public NewsTask(Context context, ListView listView) {
super();
this.context = context;
this.listView = listView;
}
@Override
protected List<News> doInBackground(String... params) {
// TODO Auto-generated method stub
String url = params[0];
List<News> list = null;
if(url!=null)
{
try {
byte[] data = HttpUtil.getJsonString(url);
String jsonString = new String(data,"utf-8");
list = ParseTool.parseNews(jsonString);

} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return list;
}
@Override
protected void onPostExecute(List<News> result) {
// TODO Auto-generated method stub
super.onPostExecute(result);

NewsAdapter adapter = new NewsAdapter(context, result);
listView.setAdapter(adapter);
}
}
当然下载图片也得用异步任务,有的图片5秒可下载不来的。

DownloadImageTask.java

package com.example.newsapp.asyncTask;

import java.io.IOException;

import org.apache.http.client.ClientProtocolException;

import com.example.newsapp.tool.HttpUtil;
import com.example.newsapp.tool.ImageUtil;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;

public class DownImageTask extends AsyncTask<String, Void, Bitmap> {

private DownLoadBack downLoadBack;

public DownImageTask(DownLoadBack downLoadBack)
{
this.downLoadBack = downLoadBack;
}

@Override
protected Bitmap doInBackground(String... params) {
String url = params[0];
Bitmap bitmap = null;
if(url!=null)
{
try {
byte[] data = HttpUtil.getJsonString(url);

bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);

//图片下载完成时,把图片存到扩展卡
ImageUtil.saveImage(url, bitmap, ImageUtil.FORMAT_JPEG);
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return bitmap;
}

@Override
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);

downLoadBack.response(result);
}
//在这里还用到接口回调,很多人都不理解接口回调是什么?当然在这儿只不过是用来传值而已。调用者需要被调用者的数据,那么被调用者就定义了一个接口(含方法),调用者在实例被调用者的时候,通过接口回调并实现接口中的方法,就可以得到被调用者的数据了。
public interface DownLoadBack
{
public void response(Bitmap bitmap);
}
}
当然我这么说都不懂的,就看看:接口回调机制的详解

5 那就是主方法中要实现的了。MainActivity.java

package com.example.newsapp;

import com.example.newsapp.asyncTask.NewsTask;
import com.example.newsapp.entity.News;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;

public class MainActivity extends Activity {
private String path = "http://litchiapi.jstv.com/api/GetFeeds?column=0&PageSize=10&pageIndex=1&val=100511D3BE5301280E0992C73A9DEC41";   //这里就是地址了

private ListView listView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (ListView) findViewById(R.id.listView1);

new NewsTask(this, listView/**这里传入的参数,与异步任务类构造函数的形参一致,位置别写反了*/).execute(path); //这里执行了异步任务的方法,并传入路径。

listView.setOnItemClickListener(new OnItemClickListener() {  //这里我使用了一个点击ListView的监听,这儿一般是点击后就会跳转并显示该条目的详情,在这片文章中我还完善。

@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {

String item = (String) listView.getItemAtPosition(position).toString();
Toast.makeText(getApplicationContext(), item, Toast.LENGTH_LONG).show();
}
});
}

}
6 当然最后还得在清单文件(AndroidManifest.xml)中加上几个权限了。

    
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>


哈哈,在这儿,基本就完成了ListView显示数据。
源码在这里……

    

  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: