3.2使用HTTP访问网络
在Android上发送HTTP请求的方式一般有两个,HttpURLConnection和HttpClient,在这里我们使用HttpURLConnection。
首先需要获取到HttpURLConnection的实例,一般只需new出一个URL对象,并传入目标的网络地址,然后调用一下openConnection()方法即可,如下所示:
URL url = new url(“http://www.baidu.com”);
HttpURLConnection connection =(HttpURLConnection) url.openConnection();
得到了HttpURLConnection的实例之后,我们可以设置一下HTTP请求所使用的方法。常用的方法主要有两个,GET和POST。GET表示希望从服务器那里获取数据,而POST则表示希望提交数据给服务器。接下来就可以进行设置连接超时、读取超时的毫秒数;之后再调用getInputStream()方法就可以获取到服务器返回的输入流了,剩下的任务就是对输入流进行读取;最后将连接关闭掉。
下面我们就在com.wyd.search.common包中新建HttpUtils.java类,代码如下所示:
public final class HttpUtils {
private static final String LOG_TAG = "HttpUtils Connect Tag";
public static String openUrl(String url, String method, Bundle params, String enc){
String response = null;
if(method.equals("GET")){
url = url + "?" + encodeUrl(params);
}
try {
Log.d(LOG_TAG, "Url:"+url);
HttpURLConnection conn = (HttpURLConnection) new
URL(url).openConnection();
conn.setRequestProperty("User-Agent", System.getProperties()
.getProperty("http.agent"));
conn.setReadTimeout(10000); //设置超时时间
if(method.equals("POST")){
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.getOutputStream().write(encodeUrl(params).getBytes("UTF-8"));
}
response = read(conn.getInputStream(),enc);
} catch (Exception e) {
Log.e(LOG_TAG, e.getMessage());
throw new RuntimeException(e.getMessage(),e);
}
return response;
}
private static String read(InputStream in, String enc) throws IOException {
StringBuilder sb = new StringBuilder();
InputStreamReader isr = null;
BufferedReader r = null;
if(enc != null){
//按指定的编码读入流
r = new BufferedReader(new InputStreamReader(in,enc), 1000);
}else{
//按默认的编码读入
r = new BufferedReader(new InputStreamReader(in), 1000);
}
for (String line = r.readLine(); line != null; line = r.readLine()) {
sb.append(line);
}
in.close();
return sb.toString();
}
public static String encodeUrl(Bundle parameters) {
if (parameters == null)
return "";
StringBuilder sb = new StringBuilder();
boolean first = true;
for (String key : parameters.keySet()) {
if (first)
first = false;
else
sb.append("&");
sb.append(key + "=" + parameters.getString(key));
}
return sb.toString();
}
}
3.3查询活动的搜索条件
我们在查询的时候,会有一些限制条件,比如说查询的IP是否合法、手机号码位数是否够......因此要进行一些限制。
我们在com.wyd.search.common包中新建ActivityUtils类,代码如下:
public final class ActivityUtils {
public static void showDialog(Context context, String button,String title, String message){
new AlertDialog.Builder(context)
.setTitle(title)
.setMessage(message)
.setNeutralButton(button, null)
.create()
.show();
}
//校验str是否为空或为""
public static boolean validateNull(String str){
if(str == null || str.equals("")){
return false;
}else{
return true;
}
}
//校验str中是否全部是数字,用到了正则表达式
public static boolean validateNumber(String str){
Pattern pattern = Pattern.compile("[0-9]");
Matcher matcher = pattern.matcher(str);
return matcher.matches();
}
//校验Ip是否合法
public static boolean validateIp(String str){
Pattern pattern = Pattern.compile("[0-9],.");
Matcher matcher = pattern.matcher(str);
return matcher.matches();
}
}
4 查询工具创建
在第二阶段,我们进行具体实体类的编程,首先我们需要申请相应的接口数据,这个我们可以轻松在聚合数据网站上得到,然后把具体的功能一个个实现,我们在第一步新建的包中新建相应的查询实体类。具体实现如下:
4.1快递单号查询
在com.wyd.search.ems包中新建Ems.java类,代码如下:
public class Ems implements Serializable {
private static final long serialVersionUID = 1L;
// 变量声明
private String message; //消息体
private String time; //时间
private String context; //状态
private String status; //返回值状态:0,查询失败;1:查询成功
private String company; //快递公司名称
private String order; //单号
接下来新建快递查询的监听类EmsListener,代码如下:
public class EmsSearch extends Activity {
private Spinner emsCompanies;
private EditText emsOrder;
private Resources res;
private String[] emsCodes;
private int pos = 0;
private String selectedCom;
private Search search;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.ems);
res = this.getResources();
emsCodes = res.getStringArray(R.array.ems_code);
emsOrder = (EditText)this.findViewById(R.id.ems_order);
emsCompanies = (Spinner)this.findViewById(R.id.ems_com);
ArrayAdapter<CharSequence> adapter =
ArrayAdapter.createFromResource(this, R.array.ems_company,
android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.
simple_spinner_dropdown_item);
emsCompanies.setAdapter(adapter);
emsCompanies.setOnItemSelectedListener(selectedListener);
search = new Search();
}
private AdapterView.OnItemSelectedListener selectedListener = new
AdapterView.OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent, View v, int position,
long id) {
// 用户选择了,记录下用户选择的公司所对应的代码
pos = position;
selectedCom = (String)parent.getItemAtPosition(position);
}
public void onNothingSelected(AdapterView<?> parent) {
// TODO Auto-generated method stub
}
};
public String getCode(){
return emsCodes[pos-1]; //因为城市spinner中多了一个请选择
}
//检验是否选择了一个快递公司
public boolean validateSelect(){
if(pos < 1 || pos > emsCodes.length){
return false;
}else{
return true;
}
}
public void onClick(View v){
if(v.getId() == R.id.ems_search){
String order = this.emsOrder.getText().toString().trim();
if(this.validateSelect()){
if(ActivityUtils.validateNull(order)){
//校验成功
String companyCode = this.getCode();
Bundle bundle = new Bundle();
bundle.putString("company", selectedCom);
bundle.putString("order", order);
this.getIntent().putExtras(bundle); //记录下公司和单号
EmsListener listener = new EmsListener(this);
search.asyncRequest(companyCode, order, listener);
}else{
ActivityUtils.showDialog(this, res.getString(R.string.ok), res.getString(R.string.tip), res.getString(R.string.ems_order_error));
}
}else{
ActivityUtils.showDialog(this, res.getString(R.string.ok), res.getString(R.string.tip), res.getString(R.string.ems_noselect));
}
}
}
}
最后就是查询后返回相应的数据,新建SearchResult.java,代码如下:
public class SearchResult extends Activity {
private TextView emsCompany,emsOrder,emsTime,emsContext;
private Resources res;
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.ems_info);
this.emsCompany = (TextView)this.findViewById(R.id.ems_company);
this.emsOrder = (TextView)this.findViewById(R.id.ems_order);
this.emsTime = (TextView)this.findViewById(R.id.ems_time);
this.emsContext = (TextView)this.findViewById(R.id.ems_context);
res = this.getResources();
this.showResult();
}
private void showResult() {
Bundle bundle = this.getIntent().getExtras();
Ems ems = (Ems) bundle.getSerializable("ems");
if(ems != null){
if(ems.getStatus().equals("1")){
this.emsCompany.setText(ems.getCompany());
this.emsOrder.setText(ems.getOrder());
this.emsTime.setText(ems.getTime());
this.emsContext.setText(ems.getContext());
}else{
ActivityUtils.showDialog(this,res.getString(R.string.ok), res.getString(R.string.tip),
ems.getMessage());
}
}else{
ActivityUtils.showDialog(this, res.getString(R.string.ok), res.getString(R.string.tip),
res.getString(R.string.ems_fail));
}
}
}
运行程序,效果如下:
4.1手机号归属地查询
我们首先在com.wyd.search.telephone包中新建手机号归属地查询的实体类Telephone.java,代码如下:
public class Telephone implements Serializable{
private static final long serialVersionUID = 1L;
private String mobile; //
private String queryResult; //返回结果
private String province; //省份
private String city; //城市
private String areaCode; //区号
private String postCode; //
private String corp; //
private String card; //卡类型
我们可在聚合数据网站上申请数据,然后应用网站提供的接口,通过对申请的数据得知,手机号码的查询涵盖中国移动、中国联通和中国电信,查询的结果包括省份、城市、区号和卡类型。
接下来新建快递查询的监听类TelephoneListener,代码如下:
public class TelephoneListener implements RequestListener {
private TelephoneSearch context;
private ProgressDialog progress;
private Resources res;
public TelephoneListener(TelephoneSearch context){
this.context = context;
res = context.getResources();
progress=ProgressDialog.show(context,res.getString(R.string.
tel_searching),
res.getString(R.string.getting));
progress.show();
}
@Override
public void onComplete(final String result) {
this.context.runOnUiThread(new Runnable() {
@Override
public void run() {
Intent intent = new Intent();
Bundle bundle = new Bundle();
bundle.putSerializable("telephone", parseJSON(result));
intent.putExtras(bundle);
intent.setClass(context, SearchResult.class);
progress.dismiss();
context.startActivity(intent);
}
});
}
public void onException(Exception e) {
this.context.runOnUiThread(new Runnable() {
public void run() {
if(progress != null){
progress.dismiss();
}
ActivityUtils.showDialog(context, res.getString(R.string.ok), res.getString(R.string.tip), res.getString(R.string.get_nothing)); }
});
}
public Telephone parseJSON(String jsonStr){
Telephone tel = null;
jsonStr = jsonStr.substring(14, jsonStr.length()-2);
try {
tel = new Telephone();
JSONObject jsonObj = new JSONObject(jsonStr);
tel.setMobile(jsonObj.getString("Mobile"));
tel.setQueryResult(jsonObj.getString("QueryResult"));
tel.setProvince(jsonObj.getString("Province"));
tel.setCity(jsonObj.getString("City"));
tel.setAreaCode(jsonObj.getString("AreaCode"));
tel.setPostCode(jsonObj.getString("PostCode"));
tel.setCorp(jsonObj.getString("Corp"));
tel.setCard(jsonObj.getString("Card"));
} catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage());
}
return tel;
}
private static final String LOG_TAG = "TelephoneListener";
}
我们从聚合数据网站上获得的数据可以使用XML和JSON两种方法解析,这里我们解析成JSON格式数据,因为相对于XML,JSON的主要优势在于它的体积更小,在网络上传输的时候可以更省流量。
然而,解析JSON数据的方法也有很多,我们使用JSONObject,首先将HTTP请求的地址加上,然后在得到了服务器返回的数据后调用parseJSONWithJSONObject()方法来解析数据。每个JSONObject对象中包含Mobile、Province、AreaCode和Card等这些数据,接下来只需要调用getString()方法将这些数据取出,并打印出来即可。
最后就是更新UI的问题,要将解析出来的数据显示出来,代码如下:
public class SearchResult extends Activity {
private TextView mobile,province,city,areacode,postcode,card;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.telephone_info);
mobile = (TextView)this.findViewById(R.id.telephone_mobile);
province = (TextView)this.findViewById(R.id.telephone_province);
city = (TextView)this.findViewById(R.id.telephone_city);
areacode = (TextView)this.findViewById(R.id.telephone_areacode);
postcode = (TextView)this.findViewById(R.id.telephone_postcode);
card = (TextView)this.findViewById(R.id.telephone_card);
this.showResult();
}
public void showResult(){
Intent intent = this.getIntent();
Bundle bundle = intent.getExtras();
Telephone tel = (Telephone)bundle.getSerializable("telephone");
if(tel != null){
mobile.setText(tel.getMobile());
province.setText(tel.getProvince());
city.setText(tel.getCity());
areacode.setText(tel.getAreaCode());
postcode.setText(tel.getPostCode());
card.setText(tel.getCard());
}
}
}
4.3天气的查询
我们首先在com.wyd.search.weather包中新建手机号归属地查询的实体类Weather.java,代码如下:
public class Weather implements Serializable {
private static final long serialVersionUID = 13835783478884L;
private String day; //星期
private String date; //日期
private String low; //最低温度
private String high; //最高温度
private String code; //天气状况码
private String text; //天气状况码,对应的天气类型
接着我偶们继续新建Search类,代码如下:
public class Search {
//这里使用的是雅虎天气查询url
private static final String HTTP_URL="http://weather.yahooapis.com/forecastrss";
private static final String METHOD = "GET";
private static final String LOG_TAG = "com.search.weather.Search";
public String request(String woeid){
if(woeid != null){
Bundle params = new Bundle();
params.putString("w", woeid); //城市对应的id
params.putString("u", "c"); //华氏温度是f,摄氏温度是c。这里采用摄氏度
return HttpUtils.openUrl(HTTP_URL, METHOD, params,null);
}else{
return null;
}
}
//异步封装
public void asyncRequest(final String city, final RequestListener listener){
//子线程中更新UI
new Thread(new Runnable() {
@Override
public void run() {
try {
String response = request(city);
listener.onComplete(response);
} catch (Exception e) {
Log.e(LOG_TAG, e.getMessage());
listener.onException(e);
}
}
}).start();
}
}
下面创建WeatherListener.java,代码如下:
public class WeatherListener implements RequestListener {
private WeatherSearch context;
private ProgressDialog progress;
private Resources res;
public WeatherListener(WeatherSearch context){
this.context = context;
res = this.context.getResources();
this.progress = ProgressDialog.show(context, res.getString(R.string.wea_searching), res.getString(R.string.getting));
this.progress.show();
}
public void onComplete(final String result) {
context.runOnUiThread(new Runnable() {
public void run() {
try {
InputStream is = null;
is = new ByteArrayInputStream(result.getBytes("UTF-8"));
List<Weather> results = WeatherPullParser.getData(is);
if(results.size()>=2){
Weather today = results.get(0);
Weather tomorrow = results.get(1);
Intent intent = new Intent();
Bundle bundle = new Bundle();
bundle.putSerializable("today", today);
bundle.putSerializable("tomorrow", tomorrow);
intent.putExtras(bundle);
intent.setClass(context, SearchResult.class);
if(progress != null){
progress.dismiss();
}
context.startActivity(intent);
}else{
if(progress != null){
progress.dismiss();
}
ActivityUtils.showDialog(context, res.getString(R.string.ok), res.getString(R.string.tip), res.getString(R.string.xml_error));
}
} catch (UnsupportedEncodingException e) {
Log.e(LOG_TAG, e.getMessage());
ActivityUtils.showDialog(context, res.getString(R.string.ok), res.getString(R.string.tip), res.getString(R.string.xml_error));
}
}
});
}
public void onException(final Exception e) {
context.runOnUiThread(new Runnable() {
@Override
public void run() {
if(progress != null){
progress.dismiss();
}
ActivityUtils.showDialog(context, res.getString(R.string.ok), res.getString(R.string.tip), res.getString(R.string.get_nothing));
}
});
}
private static final String LOG_TAG = "WeatherListener";
}
然后我们就要对服务器返回的数据进行解析了,由于这里我们用的是雅虎天气提供的数据,所以这里我们运用Pull的解析方式。
新建WeatherPullParser.java,代码如下:
public class WeatherPullParser {
public static List<Weather> getData(InputStream reader){
List<Weather> result = null;
XmlPullParser parser = Xml.newPullParser();
Weather wea = null;
String tagName = null;
try {
parser.setInput(reader, "UTF-8");
int eventCode = parser.getEventType(); //返回事件码类型
while(eventCode != XmlPullParser.END_DOCUMENT){
switch(eventCode){
case XmlPullParser.START_DOCUMENT:
//初始化
result = new ArrayList<Weather>();
break;
case XmlPullParser.START_TAG:
//一个元素的开始
tagName = parser.getName(); //获取当前标签的名称
if(tagName.equalsIgnoreCase("forecast")){
wea = new Weather();
wea.setDay(parser.getAttributeValue(null, "day"));
wea.setDate(parser.getAttributeValue(null, "date"));
wea.setLow(parser.getAttributeValue(null, "low"));
wea.setHigh(parser.getAttributeValue(null, "high"));
wea.setText(parser.getAttributeValue(null, "text"));
wea.setCode(parser.getAttributeValue(null, "code"));
result.add(wea);
}
break;
}
eventCode = parser.next(); //解析下一个元素
}
} catch (Exception e) {
Log.e("WeatherPullParser", e.getMessage());
}
return result;
}
}
最后就是就是就将解析出阿里的数据显示出来,在这个包下新建SearchResult.java,代码如下:
public class SearchResult extends Activity {
private TextView today,tomorrow,low1,low2,high1,high2;
private Resources res;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.weather_info);
today = (TextView)this.findViewById(R.id.wea_today);
tomorrow = (TextView)this.findViewById(R.id.wea_tomorrow);
low1 = (TextView)this.findViewById(R.id.wea_low);
low2 = (TextView)this.findViewById(R.id.wea_low2);
high1 = (TextView)this.findViewById(R.id.wea_high);
high2 = (TextView)this.findViewById(R.id.wea_high2);
res = this.getResources();
this.showResult();
}
private void showResult() {
Intent intent = this.getIntent();
Bundle bundle = intent.getExtras();
Weather w_today = (Weather)bundle.getSerializable("today");
Weather w_tomorrow = (Weather)bundle.getSerializable("tomorrow");
String unit = res.getString(R.string.wea_unit);
if(w_today != null){
String today_codition = this.getCondition(w_today.getCode()); //获取code对应的天气
today.setText(today_codition);
low1.setText(w_today.getLow()+unit);
high1.setText(w_today.getHigh()+unit);
}
if(w_tomorrow != null){
String tommorrow_condition = this.getCondition(w_tomorrow.getCode());
tomorrow.setText(tommorrow_condition);
low2.setText(w_tomorrow.getLow()+unit);
high2.setText(w_tomorrow.getHigh()+unit);
}
}
//根据代码获取对应的天气信息。在weather的string-array中匹配
private String getCondition(String code) {
String[] weathers = res.getStringArray(R.array.weather);
int temp = Integer.parseInt(code);
if(temp < 48 && temp >= 0){
return weathers[temp];
}
return res.getString(R.string.wea_undefine);
}
}