网络交互就是基于HTTP协议请求和响应的过程。XMPP协议用于即时通讯。
示例:res\layout\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" > <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="请求图片" android:onClick="click"/> <ImageView android:id="@+id/iv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" /> </RelativeLayout>src/cn.itcast.imageviewer/MainActivity.java
package cn.itcast.imageviewer; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; import android.view.View; import android.widget.ImageView; import android.widget.Toast; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void click(View v){ //向服务器发送Http请求去请求图片 String path = "http://192.168.1.100:8080/sh.jpg"; try{ //1. 把网址封装成url对象 URL url = new URL(path); //2. 打开一个连接对象 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); //3. 给连接对象做设置 conn.setRequestMethod("GET"); conn.setConnectTimeout(8000); conn.setReadTimeout(8000); //4. 发送请求,建立连接 conn.connect(); //5. 获取响应码,如果为200开头,说明请求成功 if(conn.getResponseCode() == 200){ //获取服务器的流,服务器返回的数据是通过流写给客户端的,也就是说,流里就是请求的图片 InputStream is = conn.getInputStream(); //读取流里的数据,把数据构造成一个图片对象 Bitmap bm = BitmapFactory.decodeStream(is); //把图片显示至屏幕 ImageView iv = (ImageView) findViewById(R.id.iv); iv.setImageBitmap(bm); }else{ Toast.makeText(this, "请求失败啦啦啦", 0).show(); } }catch(Exception e){ e.printStackTrace(); } } }添加权限:
在Tomcat服务器webapps\ROOT存放图片:
运行结果:双击start.bat,启动tomcat。
启动2.3.3版本模拟器,图片显示成功:
Handler机制让子线程刷新UI,如果上面的示例运行在4.3版本的模拟器上,就会报警告:
在Android中,主线程是绝对不能阻塞的。因为,网络请求是耗时操作,主线程处于阻塞状态,用户任何操作都无效,处于类似于死机的状态。此时,点击HOME键有效,点击返回键、菜单键都没有反应。因为,返回键和菜单键是由当前应用程序自行处理,HOME键是由系统处理。应用程序可以阻塞自己的主线程,不可能阻塞Android系统。 为了保证用户体验良好,所有的耗时操作都不要写在主线程里,包括:请求网络、加载数据(数据库)和资源。 ANR:Application Not Responding,应用长时间不能响应用户操作。
查看出现ANR的原因,导出data/anr/traces.txt文件,打开。
ANR无法调试,解决方法就是在主线程中不要做耗时操作。 只有主线程可以刷新UI,主线程又称UI线程。这样做,是为了线程安全,只有一个线程可以刷新UI,如果有多个线程同时刷新UI,就可能出现线程安全导致UI刷新混乱的问题。
如果我们在MainActivity.java中启动一个线程进行网络请求,再次执行该应用程序就会报警告,如下:
通过异常可以看出来,刷新UI的代码必须在主线程中执行,但是请求图片的代码又由于不能引起阻塞,只能在子线程中执行,而且只有请求完图片之后才能刷新UI,这时候就形成了矛盾。Android里面提供了一种机制能够解决这个问题,也就是Handler机制。
主线程创建的时候,主线程中有一个消息队列MessageQueue,用来存放消息。还有一个Looper用来不断检测MessageQueue是否有消息。如果有消息就交给消息处理器Handler,Handler中有一个方法handleMessage,用来处理消息,这个方法是在主线程调用。那么,这个方法就可以刷新UI。如此,子线程想要刷新UI,只需调用Handler的sendMessag方法,将消息发送到MessageQueue即可。
示例:src/cn.itcast.imageviewer2/MainActivity.java
package cn.itcast.imageviewer2; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.View; import android.widget.ImageView; import android.widget.Toast; public class MainActivity extends Activity { Handler handler = new Handler(){ //程序员需要重写handlerMessage方法,刷新UI @Override public void handleMessage(Message msg) { switch(msg.what){ case 1: //判断消息是成功消息还是失败消息 ImageView iv = (ImageView) findViewById(R.id.iv); iv.setImageBitmap((Bitmap)msg.obj); break; case 2: Toast.makeText(MainActivity.this, "请求失败啦啦啦", 0).show(); break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void click(View v){ Thread t = new Thread(){ public void run(){ String path = "http://192.168.1.100:8080/sh.jpg"; try{ URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(8000); conn.setReadTimeout(8000); conn.connect(); if(conn.getResponseCode() == 200){ InputStream is = conn.getInputStream(); Bitmap bm = BitmapFactory.decodeStream(is); //创建消息对象 Message msg = new Message(); //消息对象可以携带数据 msg.obj = bm; msg.what = 1; //发送消息至主线程的消息队列 handler.sendMessage(msg); }else{ Message msg = new Message(); msg.what = 2; handler.sendMessage(msg); //由于Message没有携带任何数据,所以上面3句可以用下面1句替换。 //handler.sendEmptyMessage(2); } }catch(Exception e){ e.printStackTrace(); } } }; t.start(); } }运行结果:
添加缓存功能的图片查看器,为了提升效率,查看图片后,缓存起来,便于下次查看图片不必在通过网络请求。
代码:src/cn.itcast.imageviewer3/MainActivity.java
package cn.itcast.imageviewer3; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.View; import android.widget.ImageView; import android.widget.Toast; public class MainActivity extends Activity { Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { switch(msg.what){ case 1: ImageView iv = (ImageView) findViewById(R.id.iv); iv.setImageBitmap((Bitmap)msg.obj); break; case 2: Toast.makeText(MainActivity.this, "请求失败啦啦啦", 0).show(); break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void click(View v){ final String path = "http://192.168.1.100:8080/sh.jpg"; final File file = new File(getCacheDir(),getFileName(path)); if(file.exists()){ System.out.println("从缓存获取"); Bitmap bm = BitmapFactory.decodeFile(file.getAbsolutePath()); ImageView iv = (ImageView) findViewById(R.id.iv); iv.setImageBitmap(bm); }else{ Thread t = new Thread(){ public void run(){ try{ System.out.println("从网络获取"); URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(8000); conn.setReadTimeout(8000); conn.connect(); if(conn.getResponseCode() == 200){ InputStream is = conn.getInputStream(); //需要自己开启文件输出流,读取流里数据的同时,把数据写到本地 byte[] b = new byte[1024]; int len; FileOutputStream fos = new FileOutputStream(file); while((len = is.read(b)) != -1){ fos.write(b, 0, len); } fos.close(); //流里数据已经读取完毕,这行代码无法再构造图片了 //Bitmap bm = BitmapFactory.decodeStream(is); Bitmap bm = BitmapFactory.decodeFile(file.getAbsolutePath()); Message msg = new Message(); msg.obj = bm; msg.what = 1; handler.sendMessage(msg); }else{ Message msg = new Message(); msg.what = 2; handler.sendMessage(msg); } }catch(Exception e){ e.printStackTrace(); } } }; t.start(); } } public String getFileName(String path){ int index = path.lastIndexOf("/"); return path.substring(index+1); } }运行结果:
第一次,从网络获取。第二次再次点击按钮,从缓存获取。
在程序开发中,会遇到很多问题。例如,请求图片过程中遇到断网,网速慢,图片过大内存不够等等情况。由于这些情况大部分项目都会遇到,所以网上有很多写好的现成的模块。不要重新发明轮子,可以直接拿来用。 好的开源网站: http://code.google.com http://github.com 搜索,下载源码,解压,复制到我们自己的应用程序项目中。
res\layout\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" > <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="请求网络图片" android:onClick="click" /> <com.loopj.android.image.SmartImageView android:id="@+id/iv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_launcher" android:layout_centerInParent="true" /> </RelativeLayout>SmartImageView是自定义控件,以后会有专门的课程讲解。
src/cn.itcast.smartimageview/MainActivity.java
package cn.itcast.smartimageview; import android.app.Activity; import android.os.Bundle; import android.view.View; import com.loopj.android.image.SmartImageView; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void click(View v){ String path = "http://192.168.1.100:8080/sh.jpg"; SmartImageView iv = (SmartImageView) findViewById(R.id.iv); iv.setImageUrl(path); } }添加权限:
运行结果:
代码:res\layout\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" > <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="请求网络" android:onClick="click"/> <ScrollView android:layout_width="match_parent" android:layout_height="match_parent" > <TextView android:id="@+id/tv" android:layout_width="match_parent" android:layout_height="match_parent" /> </ScrollView> </RelativeLayout>src/cn.itcast.htmlviewer.tool/Tools.java
package cn.itcast.htmlviewer.tool; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; public class Tools { public static String getTextFromStream(InputStream is){ try{ byte[] b = new byte[1024]; int len; ByteArrayOutputStream bos = new ByteArrayOutputStream(); while((len = is.read(b)) != -1){ bos.write(b,0,len); } //把输出流里的内容转换成字节数组 String text = new String(bos.toByteArray()); return text; }catch(IOException e){ e.printStackTrace(); } return null; } }src/cn.itcast.htmlviewer/MainActivity.java
package cn.itcast.htmlviewer; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.View; import android.widget.TextView; import cn.itcast.htmlviewer.tool.Tools; public class MainActivity extends Activity { Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { TextView tv = (TextView) findViewById(R.id.tv); tv.setText((String)msg.obj); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void click(View v){ Thread t = new Thread(){ public void run(){ String path = "http://192.168.1.100:8080/baidu.html"; try{ URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(8000); conn.setReadTimeout(8000); //先发送请求,再获取响应码,getResponseCode方法自身会发送请求消息 if(conn.getResponseCode() == 200){ InputStream is = conn.getInputStream(); String text = Tools.getTextFromStream(is); //如果消息池中没有消息,new一个,如果有,复用这条空闲消息 Message msg = handler.obtainMessage(); msg.obj = text; handler.sendMessage(msg); } }catch(Exception e){ e.printStackTrace(); } } }; t.start(); } }添加权限:
运行结果:
如果html文件为gbk编码:
那么Tools.java中代码只要进行如下修改即可:
新闻客户端布局
res\layout\item_listview.xml
<?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" > <ImageView android:id="@+id/iv" android:layout_width="90dp" android:layout_height="70dp" android:src="@drawable/ic_launcher" android:layout_centerVertical="true" /> <!-- android:singleLine是为了让过长的标题不会导致换行,影响美观 --> <TextView android:id="@+id/tv_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="22sp" android:text="这是标题" android:singleLine="true" android:layout_toRightOf="@+id/iv" /> <!-- android:lines表示如果文本过长,最多显示几行 --> <TextView android:id="@+id/tv_detail" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="15sp" android:text="这是详细" android:lines="2" android:textColor="@android:color/darker_gray" 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="12345条评论" android:textColor="#ff0000" android:layout_alignParentRight="true" android:layout_below="@id/tv_detail" /> </RelativeLayout>效果:
获取新闻信息,将news.xml和images文件夹存放在Tomcat服务器中。
News.xml
<?xml version="1.0" encoding="UTF-8" ?> <newslist> <news> <title>西安一期就业快报</title> <detail>热烈祝贺西安一期平均薪水突破13k</detail> <comment>15687</comment> <image>http://192.168.1.100:8080/images/6.jpg</image> </news> <news> <title>程序员因写代码太乱被杀害</title> <detail>凶手是死者同事,维护死者代码时完全看不懂而痛下杀手</detail> <comment>16359</comment> <image>http://192.168.1.100:8080/images/7.jpg</image> </news> <news> <title>产品经理因频繁改需求被杀害</title> <detail>凶手是一名程序员,因死者对项目需求频繁改动而痛下杀手</detail> <comment>14112</comment> <image>http://192.168.1.100:8080/images/7.jpg</image> </news> <news> <title>3Q大战宣判: 腾讯获赔500万</title> <detail>最高法驳回360上诉, 维持一审宣判.</detail> <comment>6427</comment> <image>http://192.168.1.100:8080/images/1.jpg</image> </news> <news> <title>今日之声:北大雕塑被戴口罩</title> <detail>市民: 因雾霾起诉环保局; 公务员谈"紧日子": 坚决不出去.</detail> <comment>681</comment> <image>http://192.168.1.100:8080/images/2.jpg</image> </news> <news> <title>奥巴马见达赖是装蒜</title> <detail>外文局: 国际民众认可中国大国地位;法院: "流量清零"未侵权.</detail> <comment>1359</comment> <image>http://192.168.1.100:8080/images/3.jpg</image> </news> <news> <title>轻松一刻: 我要沉迷学习不自拔</title> <detail>放假时我醒了不代表我起床了, 如今我起床了不代表我醒了!</detail> <comment>11616</comment> <image>http://192.168.1.100:8080/images/4.jpg</image> </news> <news> <title>男女那些事儿</title> <detail>"妈, 我在东莞被抓, 要2万保释金, 快汇钱到xxx!"</detail> <comment>10339</comment> <image>http://192.168.1.100:8080/images/5.jpg</image> </news> <news> <title>赵帅哥语录一</title> <detail>少壮不努力,老大做IT</detail> <comment>14612</comment> <image>http://192.168.1.100:8080/images/8.jpg</image> </news> <news> <title>赵帅哥语录二</title> <detail>问君能有几多愁,恰似调完代码改需求</detail> <comment>13230</comment> <image>http://192.168.1.100:8080/images/8.jpg</image> </news> <news> <title>赵帅哥语录三</title> <detail>觉得我帅的人工资一般都比较高</detail> <comment>9928</comment> <image>http://192.168.1.100:8080/images/8.jpg</image> </news> <news> <title>今日之声:北大雕塑被戴口罩</title> <detail>市民: 因雾霾起诉环保局; 公务员谈"紧日子": 坚决不出去.</detail> <comment>681</comment> <image>http://192.168.1.100:8080/images/2.jpg</image> </news> <news> <title>奥巴马见达赖是装蒜</title> <detail>外文局: 国际民众认可中国大国地位;法院: "流量清零"未侵权.</detail> <comment>1359</comment> <image>http://192.168.1.100:8080/images/3.jpg</image> </news> </newslist> src/cn.itcast.news.domain/News.java package cn.itcast.news.domain; public class News { private String title; private String detail; private String comment; private String imageUrl; public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getDetail() { return detail; } public void setDetail(String detail) { this.detail = detail; } public String getComment() { return comment; } public void setComment(String comment) { this.comment = comment; } public String getImageUrl() { return imageUrl; } public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; } } src/cn.itcast.news/MainActivity.java package cn.itcast.news; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.List; import org.xmlpull.v1.XmlPullParser; import android.app.Activity; import android.os.Bundle; import android.util.Xml; import cn.itcast.news.domain.News; public class MainActivity extends Activity { List<News> newsList; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getNewsInfo(); } private void getNewsInfo(){ Thread t = new Thread(){ public void run(){ String path = "http://192.168.1.100:8080/news.xml"; try{ URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection)url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(8000); conn.setReadTimeout(8000); if(conn.getResponseCode() == 200){ //获取服务器返回的流,流里就是xml文件 InputStream is = conn.getInputStream(); parserNewsInfo(is); } }catch(Exception e){ e.printStackTrace(); } } }; t.start(); } private void parserNewsInfo(InputStream is){ XmlPullParser xp = Xml.newPullParser(); try{ xp.setInput(is,"utf-8"); int type = xp.getEventType(); News news = null; while(type != XmlPullParser.END_DOCUMENT){ switch(type){ case XmlPullParser.START_TAG: if("newslist".equals(xp.getName())){ newsList = new ArrayList<News>(); }else if("news".equals(xp.getName())){ news = new News(); }else if("title".equals(xp.getName())){ String title = xp.nextText(); news.setTitle(title); }else if("detail".equals(xp.getName())){ String detail = xp.nextText(); news.setDetail(detail); }else if("comment".equals(xp.getName())){ String comment = xp.nextText(); news.setComment(comment); }else if("image".equals(xp.getName())){ String image = xp.nextText(); news.setImageUrl(image); } break; case XmlPullParser.END_TAG: if("news".equals(xp.getName())){ newsList.add(news); } break; } type = xp.next(); } }catch(Exception e){ e.printStackTrace(); } } }把新闻信息显示至界面 修改res\layout\item_listview.xml中的ImageView标签为SmartImageView标签,便于显示图片。
src/cn.itcast.news/MainActivity.java package cn.itcast.news; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.List; import org.xmlpull.v1.XmlPullParser; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Xml; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.TextView; import cn.itcast.news.domain.News; import com.loopj.android.image.SmartImageView; public class MainActivity extends Activity { List<News> newsList; Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { ListView lv = (ListView)findViewById(R.id.lv); lv.setAdapter(new MyAdapter()); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getNewsInfo(); //由于getNewsInfo方法中是开启了一个子线程,与主线程并行。所以当listview显示内容时,newsList还没来得及创建,所以lv设置显示内容的代码不能运行在这里。要保证运行在xml解析完毕之后。 //ListView lv = (ListView)findViewById(R.id.lv); //lv.setAdapter(new MyAdapter()); } class MyAdapter extends BaseAdapter{ @Override public int getCount() { //之所以系统需要知道条目数量,是因为屏幕右侧的控制条显示基于此数据而定 return newsList.size(); } @Override public Object getItem(int position) { return null; } @Override public long getItemId(int position) { return 0; } //视图保存器 class ViewHolder{ TextView tv_title; TextView tv_detail; TextView tv_comment; SmartImageView siv; } @Override public View getView(int position, View convertView, ViewGroup parent) { View v = null; ViewHolder vh = null; News news = newsList.get(position); if(convertView == null){ v = View.inflate(MainActivity.this, R.layout.item_listview, null); vh = new ViewHolder(); //如果缓存为空,那么需要填充新的View对象,同时找到布局文件中的所有组件,并封装至ViewHolder对象中 vh.tv_title = (TextView)v.findViewById(R.id.tv_title); vh.tv_detail = (TextView)v.findViewById(R.id.tv_detail); vh.tv_comment = (TextView)v.findViewById(R.id.tv_comment); vh.siv = (SmartImageView) v.findViewById(R.id.iv); //把ViewHolder对象存入View对象中,缓存View对象,同时缓存了ViewHolder对象 v.setTag(vh); }else{ v = convertView; //从缓存中取出ViewHolder对象,这个对象中就封装了布局文件中所有的组件对象,那么就不需要再次findViewById了 vh = (ViewHolder)v.getTag(); } vh.tv_title.setText(news.getTitle()); vh.tv_detail.setText(news.getDetail()); vh.tv_comment.setText(news.getComment() + "条评论"); vh.siv.setImageUrl(news.getImageUrl()); return v; } } private void getNewsInfo(){ Thread t = new Thread(){ public void run(){ String path = "http://192.168.1.100:8080/news.xml"; try{ URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection)url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(8000); conn.setReadTimeout(8000); if(conn.getResponseCode() == 200){ InputStream is = conn.getInputStream(); parserNewsInfo(is); } }catch(Exception e){ e.printStackTrace(); } } }; t.start(); } private void parserNewsInfo(InputStream is){ XmlPullParser xp = Xml.newPullParser(); try{ xp.setInput(is,"utf-8"); int type = xp.getEventType(); News news = null; while(type != XmlPullParser.END_DOCUMENT){ switch(type){ case XmlPullParser.START_TAG: if("newslist".equals(xp.getName())){ newsList = new ArrayList<News>(); }else if("news".equals(xp.getName())){ news = new News(); }else if("title".equals(xp.getName())){ String title = xp.nextText(); news.setTitle(title); }else if("detail".equals(xp.getName())){ String detail = xp.nextText(); news.setDetail(detail); }else if("comment".equals(xp.getName())){ String comment = xp.nextText(); news.setComment(comment); }else if("image".equals(xp.getName())){ String image = xp.nextText(); news.setImageUrl(image); } break; case XmlPullParser.END_TAG: if("news".equals(xp.getName())){ newsList.add(news); } break; } type = xp.next(); } }catch(Exception e){ e.printStackTrace(); } //xml解析完毕,发送消息,通知主线程,设置lv的显示内容 handler.sendEmptyMessage(1); } }
添加权限:
运行结果:
使用get方式提交表单
使用MyEclipse,新建一个Servlet:Login.java,把该Web项目部署到Tomcat服务器上。
src/cn.itcast.login/Login.java
package cn.itcast.login; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class Login extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String name = request.getParameter("name"); String pass = request.getParameter("pass"); ServletOutputStream os = response.getOutputStream(); if("asd".equals(name)&&"123".equals(pass)){ os.write("登陆成功".getBytes("utf-8")); }else{ os.write("登陆失败".getBytes("utf-8")); } } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }WebRoot/index.jsp
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> </head> <body> <form action="/Web/servlet/Login" method=get> 账号:<input type="text" name="name"><br/> 密码:<input type="text" name="pass"><br/> <input type="submit" value="登陆"/> </form> </body> </html>打开浏览器,输入地址,然后输入用户名和密码,提交。
将浏览器编码改为UTF-8。
结果如下:
Android之Get方式提交数据: res\layout\activity_main.xml <LinearLayout 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" android:orientation="vertical"> <EditText android:id="@+id/et_name" android:layout_width="match_parent" android:layout_height="wrap_content"/> <EditText android:id="@+id/et_pass" android:layout_width="match_parent" android:layout_height="wrap_content"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="登陆" android:onClick="click" /> </LinearLayout> src/cn.itcast.getmethod.tool/Tools.java package cn.itcast.getmethod.tool; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; public class Tools { public static String getTextFromStream(InputStream is){ try{ byte[] b = new byte[1024]; int len; ByteArrayOutputStream bos = new ByteArrayOutputStream(); while((len = is.read(b)) != -1){ bos.write(b,0,len); } //把输出流里的内容转换成字节数组 String text = new String(bos.toByteArray()); return text; }catch(IOException e){ e.printStackTrace(); } return null; } } src/cn.itcast.getmethod/MainActivity.java package cn.itcast.getmethod; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.View; import android.widget.EditText; import android.widget.Toast; import cn.itcast.getmethod.tool.Tools; public class MainActivity extends Activity { Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { Toast.makeText(MainActivity.this, (String)msg.obj, 0).show(); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void click(View v){ EditText et_name = (EditText)findViewById(R.id.et_name); EditText et_pass = (EditText)findViewById(R.id.et_pass); String name = et_name.getText().toString(); String pass = et_pass.getText().toString(); //在url后面拼接要提交的数据 final String path = "http://localhost:8080/Web/servlet/Login?name=" + name + "&pass=" + pass; Thread t = new Thread(){ public void run(){ URL url; try { url = new URL(path); HttpURLConnection conn = (HttpURLConnection)url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(8000); conn.setReadTimeout(8000); if(conn.getResponseCode() == 200){ InputStream is = conn.getInputStream(); String text = Tools.getTextFromStream(is); Message msg = handler.obtainMessage(); msg.obj = text; handler.sendMessage(msg); } } catch (Exception e) { e.printStackTrace(); } } }; t.start(); } }
添加权限:
运行结果:
提交表单的乱码问题
将Login.java中“zhangsan”修改为“张三”,并且打印出接收到的用户名及密码。src/cn.itcast.login/Login.java
package cn.itcast.login; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class Login extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String name = request.getParameter("name"); String pass = request.getParameter("pass"); System.out.println(name); System.out.println(pass); ServletOutputStream os = response.getOutputStream(); if("张三".equals(name)&&"123".equals(pass)){ os.write("登陆成功".getBytes("utf-8")); }else{ os.write("登陆失败".getBytes("utf-8")); } } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }重新部署,运行。结果如下:
将浏览器编码调整为UTF-8。
可以看到,打印出来的为乱码。原因在于浏览器提交表单的时候,会把“张三”用UTF-8编码变成字节数组,然后传给服务器。服务器拿到这些字节以后,因为getParameter默认使用iso8859-1编码把读取到的字节数组构造成字符串,导致乱码。因此,解决方案为,首先使用iso8859-1把字符串重新转换成字节数组,然后再用utf-8构造成字符串即可。
重新部署,运行,结果如下:
尝试通过手机端发送中文的情况,如下:输入中文,首先选择谷歌拼音输入法。
由上面的图片可以看到,依然存在乱码问题。原因在于浏览器提交的数据都是经过URL编码的,所以,通过代码提交的数据就需要手动编码。
修改后,运行结果:
使用post方式提交表单,修改表单为post提交方式:
通过代码实现post提交请求,一方面通过流的方式将数据传输给服务器,一方面是给post请求头添加额外属性。
src/cn.itcast.postmethod/MainActivity.java
package cn.itcast.postmethod; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.View; import android.widget.EditText; import android.widget.Toast; import cn.itcast.getmethod.tool.Tools; public class MainActivity extends Activity { Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { Toast.makeText(MainActivity.this, (String)msg.obj, 0).show(); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void click(View v){ EditText et_name = (EditText)findViewById(R.id.et_name); EditText et_pass = (EditText)findViewById(R.id.et_pass); final String name = et_name.getText().toString(); final String pass = et_pass.getText().toString(); final String path = "http://192.168.1.100:8080/Web/servlet/Login"; Thread t = new Thread(){ public void run(){ URL url; try { url = new URL(path); HttpURLConnection conn = (HttpURLConnection)url.openConnection(); conn.setRequestMethod("POST"); conn.setConnectTimeout(8000); conn.setReadTimeout(8000); //post请求头需要添加额外属性 conn.setRequestProperty("Content-type", "application/x-www-form-urlencoded"); String content = "name=" + URLEncoder.encode(name) + "&pass=" + pass; conn.setRequestProperty("Content-Length", content.length() + ""); //开启请求头的流,把要提交的数据写入流中 //设置打开连接对象输出流 conn.setDoOutput(true); OutputStream os = conn.getOutputStream(); os.write(content.getBytes()); if(conn.getResponseCode() == 200){ InputStream is = conn.getInputStream(); String text = Tools.getTextFromStream(is); Message msg = handler.obtainMessage(); msg.obj = text; handler.sendMessage(msg); } } catch (Exception e) { e.printStackTrace(); } } }; t.start(); } }运行结果:
ANR
application not responding应用无响应异常主线程阻塞时间过长,就会抛出ANR主线程又称UI线程,因为只有在主线程中,才能刷新UI