Android 网络学习之获取server文本文件
上次我们学习怎样从网络上获取一张图片,今天我们学习怎样从网络上获取文本文件。以XML文件为样例。
由于XML文件在实际开发中最为常见。
我们以以下图片为样例学习怎样从网络上获取XML文件
我们的xml文件为:
<?xml version="1.0" encoding="UTF-8" ?> <newslist> <news> <title>9月起办理手机卡需实名认证</title> <detail>方案要求,从2015年2月1日起,基础电信企业和虚拟运营商的各类营销渠道在为用户办理电话入网手续时,停止人工录入方式,改用专用移动应用程序</detail> <comment>15687</comment> <image>http://192.168.1.100:8080/images/6.jpg</image> </news> <news> <title>富士康50亿美元印度建厂 中国制造成本直逼美国</title> <detail>中国制造”和“印度制造”正展开新一轮的竞争与合作关系。全球最大的代工企业、中国台湾的富士康8月8日在印度签约。它到印度投资设厂的计划变为现实</detail> <comment>16359</comment> <image>http://192.168.1.100:8080/images/7.jpg</image> </news> <news> <title>刘强东也学乔布斯 增加“1元年薪”俱乐部</title> <detail>8月8日,京东集团董事长兼CEO刘强东与奶茶妹妹的结婚证照片在朋友圈里刷屏了。可就在前一天,刘强东还将自己未来十年的年薪降至1元</detail> <comment>14112</comment> <image>http://192.168.1.100:8080/images/7.jpg</image> </news>
后面的太长省略了。
既然我们要将xml文件里的内容显示到界面上,那必须先要拿到xml文件里的内容。
1: 从网络上获取XML文件的内容
开启一个子线程从网络上获取server的数据
public void getNewsInfo() { //在子线程中获取server的数据 Thread thread = new Thread(){ @Override public void run() { //1:确定地址 String path = "http://192.168.1.123:8080/news.xml"; try { URL url = new URL(path); //建立连接 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); //设置请求方式 conn.setRequestMethod("GET"); //设置请求超时时间 conn.setConnectTimeout(5000); //设置读取超时时间 conn.setReadTimeout(5000); //推断是否获取成功 if(conn.getResponseCode() == 200) { //获得输入流 InputStream is = conn.getInputStream(); //解析输入流中的数据 parseXmlInfo(is); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; //启动线程 thread.start(); }
获取到server给的数据,然后解析XML文件。解析XML文件我们使用的是PULL解析器
public void parseXmlInfo(InputStream is) { /*我们用pull解析器解析xml文件*/ //1.先拿到pull解析器 XmlPullParser xParser = Xml.newPullParser(); try { xParser.setInput(is, "utf-8"); //获取事件的类型 int eventType = xParser.getEventType(); News news = null; while(eventType != XmlPullParser.END_DOCUMENT) { switch (eventType) { case XmlPullParser.START_TAG: //当事件的開始类型newslist。代表的是xml文件的数据開始 if("newslist".equals(xParser.getName())) { //这时候我们就new出来一个list,用于保存数据 newList = new ArrayList<News>(); }//当事件类型是news。说明是一条新闻 else if ("news".equals(xParser.getName())) { //new出一个news的对象 news = new News(); } else if ("detail".equals(xParser.getName())) { String detail = xParser.nextText(); news.setDetail(detail); } else if ("title".equals(xParser.getName())) { String title = xParser.nextText(); news.setTitle(title); } else if ("comment".equals(xParser.getName())) { String comment = xParser.nextText(); news.setComment(comment); } else if ("image".equals(xParser.getName())) { String image = xParser.nextText(); news.setImage(image); } break; case XmlPullParser.END_TAG: //当结束时间是news时。说明一条news已经解析完毕,而且增加到集合中 if("news".equals(xParser.getName())) { newList.add(news); } break; } eventType = xParser.next(); } //打印 for (News n : newList) { System.out.println(n.toString()); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } }打印为:
能够看到我们是从server取到了数据,而且数据是正确的。
2: 既然能够正确的取到数据,那我们就将数据显示到界面上,既然要显示。 那就的用listview。既然要用listview。那就必须为listview的每个添加一个布局文件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="wrap_content" android:layout_height="wrap_content" tools:context="${relativePackage}.${activityClass}" > <ImageView android:id="@+id/iv" android:layout_width="90dp" android:layout_height="70dp" android:src="@drawable/ic_launcher" android:layout_centerVertical="true" /> <TextView android:id="@+id/tv_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="大标题" android:textSize="20sp" android:layout_toRightOf="@id/iv" android:singleLine="true" /> <TextView android:id="@+id/tv_detail" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="这是新闻的具体信息" android:textSize="15sp" android:textColor="@android:color/darker_gray" android:lines="2" android:layout_toRightOf="@id/iv" android:layout_below="@id/tv_title" /> <TextView android:id="@+id/tv_comment" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="19999" android:textColor="#FF0000" android:layout_alignParentRight="true" android:layout_below="@id/tv_detail" /> </RelativeLayout>
我们找到listview。然后设置Adapter就可以
ListView lv = (ListView) findViewById(R.id.lv); lv.setAdapter(new MyAdapter());
class MyAdapter extends BaseAdapter { @Override public int getCount() { // TODO Auto-generated method stub return newList.size(); } @Override public Object getItem(int arg0) { // TODO Auto-generated method stub return null; } @Override public long getItemId(int arg0) { // TODO Auto-generated method stub return 0; } @Override public View getView(int position, View convertView, ViewGroup parent) { //取到一条新闻 News news = newList.get(position); //找到一个view对象 View v = View.inflate(MainActivity.this, R.layout.item_listview, null); //依据ID找到控件 TextView tv_title = (TextView) v.findViewById(R.id.tv_title); TextView tv_detail = (TextView) v.findViewById(R.id.tv_detail); TextView tv_comment = (TextView) v.findViewById(R.id.tv_comment); ImageView siv = (ImageView)v.findViewById(R.id.iv); //设置数据 tv_title.setText(news.getTitle()); tv_detail.setText(news.getDetail()); tv_comment.setText(news.getComment() + "条评论"); siv.setImageResource(R.drawable.ic_launcher); return v; } }
执行看效果。之前。我须要贴出OnCreate方法
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getNewsInfo(); ListView lv = (ListView) findViewById(R.id.lv); lv.setAdapter(new MyAdapter()); }
执行看效果:
执行效果为NULL指针异常,这是为什么呢?
既然NULL指针异常。代码是哪行有问题? 细致一看是
lv.setAdapter(new MyAdapter());这是由于,我们的获取server的数据是在一个子线程中执行的,而我们setAdapter的任务是在主线程中获取的。这时候当我们的server数据还没获取完成,我们就设置数据了。就会导致异常发生。所以我们要当server的数据获取完后,才去设置adapter
改动代码:添加消息机制
//当数据接受完毕后,发生消息 handler.sendEmptyMessage(1); //打印 for (News n : newList) { System.out.println(n.toString()); }
//在消息中设置数据 Handler handler = new Handler() { public void handleMessage(android.os.Message msg) { ListView lv = (ListView) findViewById(R.id.lv); lv.setAdapter(new MyAdapter()); }; };
这种话就出现了刚開始的截图。可是我们还须要优化listview
优化后的listview
public View getView(int position, View convertView, ViewGroup parent) { News news = newList.get(position); ViewHolder mHolder; View v = null; //当缓冲为空的时候。创建view对象。并将组件封装到view的tag中 if(convertView == null) { v = View.inflate(MainActivity.this, R.layout.item_listview, null); mHolder = new ViewHolder(); mHolder.tv_title = (TextView) v.findViewById(R.id.tv_title); mHolder.tv_detail = (TextView) v.findViewById(R.id.tv_detail); mHolder.tv_comment = (TextView) v.findViewById(R.id.tv_comment); mHolder.siv = (ImageView)v.findViewById(R.id.iv); v.setTag(mHolder); } else {//当缓冲不为空时。就将缓冲的数据取出来用,不用又一次创建,这样可节省系统资源 v = convertView; mHolder =(ViewHolder) v.getTag(); } //设置数据到组件 mHolder.tv_title.setText(news.getTitle()); mHolder.tv_detail.setText(news.getDetail()); mHolder.tv_comment.setText(news.getComment() + "条评论"); mHolder.siv.setImageResource(R.drawable.ic_launcher); return v; }
这样我们的从服务端的获取数据就算完毕了,可是我们还没获取server的图片资源,图片资源是通过一个地址封装到xml文件里的,我们须要再次拿到xml文件里的图片地址再次请求server获取图片资源,关于怎样获取server图片资源我上节都讲过了,这里就不做了。