前言
啦啦啦各位小伙伴们好~
一起进入我们今天的主题。今天我们将和大家学习网络访问和Web服务开发的相关知识,一起学习熟练使用 HttpURLConnection 访问 WebService,熟悉使用多线程以及 Handler 更新 UI,熟悉使用 XmlPullParser 解析 xml 文档数据,了解 RecyclerView 控件的使用。
基础知识
一、网络访问&Web服务开发
1 、实验WebService 地址
(1)实验中所使用的 WebService 地址为:http://ws.webxml.com.cn/WebServices/WeatherWS.asmx?op=getWeather
在浏览器打开实验需要的 WebService 网站可以看到截图如下:
可以看到,查询需要用到两个参数 theCityCode(默认参数为上海)和 theUserID,如果使用免费的 WebService,其中 theUserID 置空即可,输入后点击调用,查看返回值:
可以看到其中返回的数据为 xml 文件格式,我们需要用 XmlPullParser 提取我们需要的信息。
(2)有兴趣可以在网站上看一下如何免费使用 WebService:http://www.webxml.com.cn/zh_cn/web_services.aspx
PS:免费用户 24 小时内查询不超过 50 次并且获取二次数据大于间隔 600ms
(如果测试时超过上限更换一下网络 IP 重新测试即可)
2 、网络访问
在本次实验中使用 HttpURLConnection 实现网络访问:
(1) 在 manifest 中添加网络访问权限:
(2) 判断是否有可用网络:使用 ConnectivityManager 获取手机所有连接管理对象,使用 manager 获取 NetworkInfo 对象,最后判断当前网络状态是否为连接状态即可。
(3) 定义我们需要用到的 WebService 地址:
(4) 使用 HttpURLConnection 新建一个 http 连接,新建一个 URL 对象,打开连接即可,并且设置访问方法以及时间设置:
(5)将我们需要请求的字段以流的形式写入 connection 之中,这一步相当于将需要的参数提交到网络连接,并且请求网络数据(类似于 html 中的表单操作,将 post 数据提交到服务器)
(6)网页获取 xml 转化为字符串:
在 logcat 中查看一下 response,实际上就是将网站上的 xml 转化为 string 而已,便于下一步的 xml 数据解析
(7)关闭 connection:
(8)注:Android4.0 之后,http 请求需要开启子线程,然后由子线程执行请求,所以我们之前所写代码都是在子线程中完成的,并且使用 XmlPullParser 进行解析从而得到我们想要的数据:
3 、Handler 更新 UI
在之前的实验中我们已经知道,子线程中不能直接修改 UI 界面,需要中间人 handler 进行UI 界面的修改:
Message 消息机制:负责在不同的线程之间进行交互处理,我们先定义消息类型:
然后将我们需要的内容通过消息传递回来:
4 、XmlPullParser 解析 xml 文档
首先获取 XmlPullParser 对象实例,然后设置需要解析的字符串,最后按照 tag 逐个获取所需要的 string:
注:
由于我们获取的 xml 字符串是 string 类型的数组(ArrayofString),所以我们也可以将按照 string(Tag)获取的字符串储存到ArrayList<String>中,然后在 Handler 中再进行处理,不断的将所需要的字符串进行分割处理,以用来更新 UI 界面。
异常处理部分:
1)城市名输入错误时:注意判断取回的字符串是否是“查询结果为空。…”
2)二次查询间隔<600ms 时:注意判断取回的字符串是否是“发现错误:免费用户不能使用高速访问。…”
3)达到上限 50 次:注意判断取回的字符串是否是“发现错误:免费用户 24 小时内访问超过规定数量。…”
二 、RecyclerView 控件使用
RecyclerView 是 support-v7 包中的新组件,是一个强大的滑动组件,与经典的 ListView相比,同样拥有 item 回收复用的功能。RecyclerView 是 ListView 的升级版,最为突出的就是其拓展性极强,并且灵活性高。
(1)导入 RecyclerView 的 jar包,导入方法:
http://jingyan.baidu.com/article/e6c8503c7190b7e54f1a1893.html
(2) RecylerView 的设置
(3)Adapter 的构造:
package com.example.administrator.ex9;
import java.util.ArrayList;
import java.util.List;
import android.R.integer;
import android.content.Context;
import android.graphics.Bitmap;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
public class WeatherAdapter extends RecyclerView.Adapter<WeatherAdapter.ViewHolder>{
private ArrayList<Weather> weather_list;
private LayoutInflater mInflater;
public interface OnItemClickLitener
{
void onItemClick(View view, int position,Weather item);
}
private OnItemClickLitener mOnItemClickLitener;
public void setOnItemClickLitener(OnItemClickLitener mOnItemClickLitener)
{
this. mOnItemClickLitener = mOnItemClickLitener;
}
public WeatherAdapter(Context context,ArrayList<Weather> items) {
super();
weather_list = items;
mInflater = LayoutInflater.from(context);
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View view = mInflater.inflate(R.layout. weather_item, viewGroup, false);
ViewHolder holder = new ViewHolder(view);
holder. Date = (TextView)view.findViewById(R.id. date);
holder. Weather_description =(TextView)view.findViewById(R.id. weather_description);
holder. Temperature = (TextView)view.findViewById(R.id. temperature);
return holder;
}
@Override
public void onBindViewHolder( final ViewHolder viewHolder, final int i) {
viewHolder. Date.setText( weather_list.get(i).getDate());
viewHolder. Weather_description.setText( weather_list.get(i).getWeather_description());
viewHolder. Temperature.setText( weather_list.get(i).getTemperature());
if ( mOnItemClickLitener != null)
{
viewHolder. itemView.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto- - generated method stub
mOnItemClickLitener.onItemClick(viewHolder. itemView, i, weather_list.get(i));
}
});
}
}
@Override
public int getItemCount() {
return weather_list.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder{
public ViewHolder(View itemView) {
super(itemView);
}
TextView Date;
TextView Weather_description;
TextView Temperature;
}
}
(3) Handler 中得到 weather_list 之后,绑定 adpter:
实验内容
实现一个简单的天气查询应用,具体要求如下:
1)该界面为应用启动之后的主界面 2)点击 Search 按钮后(若网络不可用)
3)输入城市名 Search(网络可用且城市名正确) 4)输入城市名 Search(城市名不正确)
5)快速点击按钮(二次查询<600ms) 6)查询达到上限 50 次
实验过程
本次实验主要是实现一个简单的天气查询应用,实现天气查询的功能。本次实验主要涉及使用 HttpURLConnection 访问 WebService,使用多线程以及Handler 更新 UI,熟悉使用 XmlPullParser 解析 xml 文档数据,了解RecyclerView 控件的使用。首先,写好界面的 XML 布局文件。这里我们需要设置一个 EditText 和一个搜索 Button,并在一个以@drawable/shape 为背景的 LinearLayout 中嵌套几个LinearLayout 以显示不同的数据,再往下设置一个 ListView 显示天气的详细内容,最后设置一个RecyclerView来显示接下来五天的天气概况。listview的item界面布局中设置两个 TextView,RecyclerView 的 item 界面布局中设置三个TextView。背景 shape 如下:
接下来完成 MainActivity.java 类,在类中我们将使用 HttpURLConnection实现网络访问。在初始化控件后,在 manifest 中添加网络访问权限:
定义我们需要用到的 WebService 地址:
然后判断是否有可用网络。这里使用 ConnectivityManager 获取手机所有连接管理对象,使用 manager 获取 NetworkInfo 对象,最后判断当前网络状态是否为连接状态(写到 search 按钮事件中):
在按钮事件中,我们还需要利用设置好的时间参数,用判断语句判断两次查询间隔是否小于 600ms 以及查询栏输入是否为空:
http 请求需要开启子线程 ,然 后由 子 线程执 行请 求, 并且使用XmlPullParser 进行解析从而得到我们想要的数据:
在子线程中,使用 HttpURLConnection 新建一个 http 连接,新建一个 URL对象,打开连接即可,并且设置访问方法以及时间设置:
将我们需要请求的字段以流的形式写入 connection 之中,这一步相当于将需要的参数提交到网络连接,并且请求网络数据:
网页获取 xml 转化为字符串:
在子线程中使用 Message 消息机制,以实现在不同的线程之间进行交互处理。在定义消息类型后,将我们需要的内容通过消息传递回来:
然后关闭 connection:
在子线程执行完请求之后,XmlPullParser 解析 xml 文档,首先获取XmlPullParser 对象实例,然后设置需要解析的字符串,最后按照 tag 逐个获取所需要的 string:
然后使用Handler 进行更新,子线程中不能直接修改 UI 界面,需要 handler进行 UI 界面的修改:
更新 Toast 的产生:
更新城市名和更新时间信息(由于我们获取的 xml 字符串是 string 类型的数组(ArrayofString),所以我们也可以将按照 string(Tag)获取的字符串储存到 ArrayList<String>中,然后在 Handler 中再进行处理):
在取有背景色的文本框所包含的数据时 , 我们需要 Java 中的StringTokenizer 类不断的将所需要的字符串进行分割处理,以用来更新 UI 界面。
StringTokenizer 是字符串分隔解析类型,属于:Java.util 包。在 StringTokenizer 中,所有方法均为 public,书写格式为:[修饰符] <返回类型><方法名([参数列表])>——
int countTokens():返回 nextToken 方法被调用的次数。
boolean hasMoreTokens():返回是否还有分隔符。
boolean hasMoreElements():返回是否还有分隔符。
String nextToken():返回从当前位置到下一个分隔符的字符串。
Object nextElement():返回从当前位置到下一个分隔符的字符串。
String nextToken(String delim):与 4 类似,以指定的分隔符返回结果。
则可以分割其中的数据来更新 UI 界面:
在更新 ListView 中的信息时,我们还是需要使用键值对来保存数据:
接下来完成拓展内容 RecyclerView 控件使用。
RecyclerView 是 support-v7 包中的新组件,是一个强大的滑动组件,与经典的 ListView 相比,同样拥有 item 回收复用的功能。RecyclerView 是ListView 的升级版,最为突出的就是其拓展性极强,并且灵活性高。在导入 RecyclerView 的 jar 包后,对 RecylerView 进行设置:
按照文档中的要求设置好适配器 WeatherAdapter,并在 Weather 类中存储相应的数据:
更新 recyclerView 中的信息时,按照 string(Tag)获取字符串:
并且设置三个字符串组将五天的天气信息添加到其中:
按照顺序将五天的信息添加进 weather_list 中去:
Handler 中得到 weather_list 之后,绑定 adpter:
完成实验~
实验截图
其他总结
WebService:
WebService 是发布在网络上的 API,可以通过发送 XML 调用,WebService返回结果也是 XML 数据。
Android 开发过程中为什么要多线程:
我们创建的 Service、Activity 以及 Broadcast 均是一个主线程处理,这里我们可以理解为 UI 线程。但是在操作一些耗时操作时,比如 I/O 读写的大文件读写,数据库操作以及网络下载需要很长时间,为了不阻塞用户界面,出现 ANR的响应提示窗口,这个时候我们可以考虑使用 Thread 线程来解决。
实验中我们还使用多线程以及 Handler 更新 UI。
使用 Handler 更新 UI 的原因:
(1)如果当前线程和主线程不相等的话,更新 UI 就会抛出异常;
(2)Activity 检查当前线程和主线程是否相等是在 onResume()方法才
开始的;
(3)所以在新开启的线程,如果不休眠直接更新 UI 的话不会抛出异常;
(4)如果休眠再做更新 UI 操作的话就会抛出异常。
源码下载
源码下载点击这里~
注
1、本实验实验环境:
操作系统 Windows 10
实验软件 Android Studio 2.2.1
虚拟设备:Nexus_5X
API:23
2、贴代码的时候由于插入代码框的大小问题,代码格式不太严整,望见谅~