网络交互就是基于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 = bos.toString();
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 = bos.toString();
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编码变成字节数组,然后传给服务器。服务器拿到这些字节以后,因为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();
}
}
运行结果:
- 确定图片的网址
- 发送http请求
URL url = new URL(address);
//获取连接对象,并没有建立连接
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//设置连接和读取超时
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
//设置请求方法,注意必须大写
conn.setRequestMethod("GET");
//建立连接,发送get请求
//conn.connect();
//建立连接,然后获取响应吗,200说明请求成功
conn.getResponseCode();
- 服务器的图片是以流的形式返回给浏览器的
//拿到服务器返回的输入流
InputStream is = conn.getInputStream();
//把流里的数据读取出来,并构造成图片
Bitmap bm = BitmapFactory.decodeStream(is);
- 把图片设置为ImageView的显示内容
ImageView iv = (ImageView) findViewById(R.id.iv);
iv.setImageBitmap(bm);
- 添加网络权限
##1.1 主线程不能被阻塞
- 在Android中,主线程被阻塞会导致应用不能刷新ui界面,不能响应用户操作,用户体验将非常差
- 主线程阻塞时间过长,系统会抛出ANR异常
- ANR:Application Not Response;应用无响应
- 任何耗时操作都不可以写在主线程
- 因为网络交互属于耗时操作,如果网速很慢,代码会阻塞,所以网络交互的代码不能运行在主线程
##1.2 只有主线程能刷新ui
- 刷新ui的代码只能运行在主线程,运行在子线程是没有任何效果的
- 如果需要在子线程中刷新ui,使用消息队列机制
##1.3 消息队列
- Looper一旦发现Message Queue中有消息,就会把消息取出,然后把消息扔给Handler对象,Handler会调用自己的handleMessage方法来处理这条消息
- handleMessage方法运行在主线程
- 主线程创建时,消息队列和轮询器对象就会被创建,但是消息处理器对象,需要使用时,自行创建
//消息队列
Handler handler = new Handler(){
//主线程中有一个消息轮询器looper,不断检测消息队列中是否有新消息,
//如果发现有新消息,自动调用此方法,注意此方法是在主线程中运行的
public void handleMessage(android.os.Message msg) {
}
};
- 在子线程中往消息队列里发消息
//创建消息对象
Message msg = new Message();
//消息的obj属性可以赋值任何对象,通过这个属性可以携带数据
msg.obj = bm;
//what属性相当于一个标签,用于区分出不同的消息,从而运行不能的代码
msg.what = 1;
//发送消息
handler.sendMessage(msg);
- 通过switch语句区分不同的消息
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
//如果是1,说明属于请求成功的消息
case 1:
ImageView iv = (ImageView) findViewById(R.id.iv);
Bitmap bm = (Bitmap) msg.obj;
iv.setImageBitmap(bm);
break;
case 2:
Toast.makeText(MainActivity.this, "请求失败", 0).show();
break;
}
}
public class MainActivity extends Activity {
static ImageView iv;
static MainActivity ma;
static Handler handler = new Handler(){
//此方法在主线程中调用,可以用来刷新ui
public void handleMessage(android.os.Message msg) {
//处理消息时,需要知道到底是成功的消息,还是失败的消息
switch (msg.what) {
case 1:
//把位图对象显示至imageview
iv.setImageBitmap((Bitmap)msg.obj);
break;
case 0:
Toast.makeText(ma, "请求失败", 0).show();
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iv = (ImageView) findViewById(R.id.iv);
ma = this;
}
public void click(View v){
Thread t = new Thread(){
@Override
public void run() {
//下载图片
//1.确定网址
String path = "http://192.168.13.13:8080/dd.jpg";
try {
//2.把网址封装成一个url对象
URL url = new URL(path);
//3.获取客户端和服务器的连接对象,此时还没有建立连接
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//4.对连接对象进行初始化
//设置请求方法,注意大写
conn.setRequestMethod("GET");
//设置连接超时
conn.setConnectTimeout(5000);
//设置读取超时
conn.setReadTimeout(5000);
//5.发送请求,与服务器建立连接
conn.connect();
//如果响应码为200,说明请求成功
if(conn.getResponseCode() == 200){
//获取服务器响应头中的流,流里的数据就是客户端请求的数据
InputStream is = conn.getInputStream();
//读取出流里的数据,并构造成位图对象
Bitmap bm = BitmapFactory.decodeStream(is);
// ImageView iv = (ImageView) findViewById(R.id.iv);
// //把位图对象显示至imageview
// iv.setImageBitmap(bm);
Message msg = new Message();
//消息对象可以携带数据
msg.obj = bm;
msg.what = 1;
//把消息发送至主线程的消息队列
handler.sendMessage(msg);
}
else{
// Toast.makeText(MainActivity.this, "请求失败", 0).show();
Message msg = handler.obtainMessage();
msg.what = 0;
handler.sendMessage(msg);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
t.start();
}
}
public class MainActivity extends Activity {
static ImageView iv;
static MainActivity ma;
static Handler handler = new Handler(){
//此方法在主线程中调用,可以用来刷新ui
public void handleMessage(android.os.Message msg) {
//处理消息时,需要知道到底是成功的消息,还是失败的消息
switch (msg.what) {
case 1:
//把位图对象显示至imageview
iv.setImageBitmap((Bitmap)msg.obj);
break;
case 0:
Toast.makeText(ma, "请求失败", 0).show();
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iv = (ImageView) findViewById(R.id.iv);
ma = this;
}
public void click(View v){
Thread t = new Thread(){
@Override
public void run() {
//下载图片
//1.确定网址
String path = "http://192.168.13.13:8080/dd.jpg";
try {
//2.把网址封装成一个url对象
URL url = new URL(path);
//3.获取客户端和服务器的连接对象,此时还没有建立连接
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//4.对连接对象进行初始化
//设置请求方法,注意大写
conn.setRequestMethod("GET");
//设置连接超时
conn.setConnectTimeout(5000);
//设置读取超时
conn.setReadTimeout(5000);
//5.发送请求,与服务器建立连接
conn.connect();
//如果响应码为200,说明请求成功
if(conn.getResponseCode() == 200){
//获取服务器响应头中的流,流里的数据就是客户端请求的数据
InputStream is = conn.getInputStream();
//读取出流里的数据,并构造成位图对象
Bitmap bm = BitmapFactory.decodeStream(is);
// ImageView iv = (ImageView) findViewById(R.id.iv);
// //把位图对象显示至imageview
// iv.setImageBitmap(bm);
Message msg = new Message();
//消息对象可以携带数据
msg.obj = bm;
msg.what = 1;
//把消息发送至主线程的消息队列
handler.sendMessage(msg);
}
else{
// Toast.makeText(MainActivity.this, "请求失败", 0).show();
Message msg = handler.obtainMessage();
msg.what = 0;
handler.sendMessage(msg);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
t.start();
}
}
- 把服务器返回的流里的数据读取出来,然后通过文件输入流写至本地文件
//1.拿到服务器返回的输入流
InputStream is = conn.getInputStream();
//2.把流里的数据读取出来,并构造成图片
FileOutputStream fos = new FileOutputStream(file);
byte[] b = new byte[1024];
int len = 0;
while((len = is.read(b)) != -1){
fos.write(b, 0, len);
}
- 创建bitmap对象的代码改成
Bitmap bm = BitmapFactory.decodeFile(file.getAbsolutePath());
- 每次发送请求前检测一下在缓存中是否存在同名图片,如果存在,则读取缓存
public class MainActivity extends Activity {
static ImageView iv;
static MainActivity ma;
static Handler handler = new Handler(){
//此方法在主线程中调用,可以用来刷新ui
public void handleMessage(android.os.Message msg) {
//处理消息时,需要知道到底是成功的消息,还是失败的消息
switch (msg.what) {
case 1:
//把位图对象显示至imageview
iv.setImageBitmap((Bitmap)msg.obj);
break;
case 0:
Toast.makeText(ma, "请求失败", 0).show();
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iv = (ImageView) findViewById(R.id.iv);
ma = this;
}
public void click(View v){
//下载图片
//1.确定网址
final String path = "http://192.168.13.13:8080/dd.jpg";
final File file = new File(getCacheDir(), getFileName(path));
//判断,缓存中是否存在该文件
if(file.exists()){
//如果缓存存在,从缓存读取图片
System.out.println("从缓存读取的");
Bitmap bm = BitmapFactory.decodeFile(file.getAbsolutePath());
iv.setImageBitmap(bm);
}
else{
//如果缓存不存在,从网络下载
System.out.println("从网上下载的");
Thread t = new Thread(){
@Override
public void run() {
try {
//2.把网址封装成一个url对象
URL url = new URL(path);
//3.获取客户端和服务器的连接对象,此时还没有建立连接
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//4.对连接对象进行初始化
//设置请求方法,注意大写
conn.setRequestMethod("GET");
//设置连接超时
conn.setConnectTimeout(5000);
//设置读取超时
conn.setReadTimeout(5000);
//5.发送请求,与服务器建立连接
conn.connect();
//如果响应码为200,说明请求成功
if(conn.getResponseCode() == 200){
//获取服务器响应头中的流,流里的数据就是客户端请求的数据
InputStream is = conn.getInputStream();
//读取服务器返回的流里的数据,把数据写到本地文件,缓存起来
FileOutputStream fos = new FileOutputStream(file);
byte[] b = new byte[1024];
int len = 0;
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{
// Toast.makeText(MainActivity.this, "请求失败", 0).show();
Message msg = handler.obtainMessage();
msg.what = 0;
handler.sendMessage(msg);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
t.start();
}
}
public String getFileName(String path){
int index = path.lastIndexOf("/");
return path.substring(index + 1);
}
}
public class MainActivity extends Activity {
static ImageView iv;
static MainActivity ma;
static Handler handler = new Handler(){
//此方法在主线程中调用,可以用来刷新ui
public void handleMessage(android.os.Message msg) {
//处理消息时,需要知道到底是成功的消息,还是失败的消息
switch (msg.what) {
case 1:
//把位图对象显示至imageview
iv.setImageBitmap((Bitmap)msg.obj);
break;
case 0:
Toast.makeText(ma, "请求失败", 0).show();
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iv = (ImageView) findViewById(R.id.iv);
ma = this;
}
public void click(View v){
//下载图片
//1.确定网址
final String path = "http://192.168.13.13:8080/dd.jpg";
final File file = new File(getCacheDir(), getFileName(path));
//判断,缓存中是否存在该文件
if(file.exists()){
//如果缓存存在,从缓存读取图片
System.out.println("从缓存读取的");
Bitmap bm = BitmapFactory.decodeFile(file.getAbsolutePath());
iv.setImageBitmap(bm);
}
else{
//如果缓存不存在,从网络下载
System.out.println("从网上下载的");
Thread t = new Thread(){
@Override
public void run() {
try {
//2.把网址封装成一个url对象
URL url = new URL(path);
//3.获取客户端和服务器的连接对象,此时还没有建立连接
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//4.对连接对象进行初始化
//设置请求方法,注意大写
conn.setRequestMethod("GET");
//设置连接超时
conn.setConnectTimeout(5000);
//设置读取超时
conn.setReadTimeout(5000);
//5.发送请求,与服务器建立连接
conn.connect();
//如果响应码为200,说明请求成功
if(conn.getResponseCode() == 200){
//获取服务器响应头中的流,流里的数据就是客户端请求的数据
InputStream is = conn.getInputStream();
//读取服务器返回的流里的数据,把数据写到本地文件,缓存起来
FileOutputStream fos = new FileOutputStream(file);
byte[] b = new byte[1024];
int len = 0;
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{
// Toast.makeText(MainActivity.this, "请求失败", 0).show();
Message msg = handler.obtainMessage();
msg.what = 0;
handler.sendMessage(msg);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
t.start();
}
}
public String getFileName(String path){
int index = path.lastIndexOf("/");
return path.substring(index + 1);
}
}
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void click(View v){
//下载图片
//1.确定网址
String path = "http://192.168.13.13:8080/dd.jpg";
//2.找到智能图片查看器对象
SmartImageView siv = (SmartImageView) findViewById(R.id.iv);
//3.下载并显示图片
siv.setImageUrl(path);
}
}
- code.google.com
- github.com
- 在github搜索smart-image-view
- 下载开源项目smart-image-view
- 使用自定义组件时,标签名字要写包名
<com.loopj.android.image.SmartImageView/>
- SmartImageView的使用
SmartImageView siv = (SmartImageView) findViewById(R.id.siv);
siv.setImageUrl("http://192.168.1.102:8080/dd.jpg");
public class MainActivity extends Activity {
List<News> newsList;
Handler handler = new Handler(){
public void handleMessage(android.os.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();
// ListView lv = (ListView) findViewById(R.id.lv);
// //要保证在设置适配器时,新闻xml文件已经解析完毕了
// lv.setAdapter(new MyAdapter());
}
class MyAdapter extends BaseAdapter{
//得到模型层中元素的数量,用来确定listview需要有多少个条目
@Override
public int getCount() {
// TODO Auto-generated method stub
return newsList.size();
}
@Override
//返回一个View对象,作为listview的条目显示至界面
public View getView(int position, View convertView, ViewGroup parent) {
News news = newsList.get(position);
View v = null;
ViewHolder mHolder;
if(convertView == null){
v = View.inflate(MainActivity.this, R.layout.item_listview, null);
mHolder = new ViewHolder();
//把布局文件中所有组件的对象封装至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 = (SmartImageView) v.findViewById(R.id.iv);
//把ViewHolder对象封装至View对象中
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() + "条评论");
//给新闻图片imageview设置内容
mHolder.siv.setImageUrl(news.getImageUrl());
return v;
}
class ViewHolder{
//条目的布局文件中有什么组件,这里就定义什么属性
TextView tv_title;
TextView tv_detail;
TextView tv_comment;
SmartImageView siv;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
}
private void getNewsInfo() {
Thread t = new Thread(){
@Override
public void run() {
String path = "http://192.168.13.13:8080/news.xml";
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
//发送http GET请求,获取相应码
if(conn.getResponseCode() == 200){
InputStream is = conn.getInputStream();
//使用pull解析器,解析这个流
parseNewsXml(is);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
t.start();
}
private void parseNewsXml(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();
}
//发消息,让主线程设置listview的适配器,如果消息不需要携带数据,可以发送空消息
handler.sendEmptyMessage(1);
// for (News n : newsList) {
// System.out.println(n.toString());
// }
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class MainActivity extends Activity {
List<News> newsList;
Handler handler = new Handler(){
public void handleMessage(android.os.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();
// ListView lv = (ListView) findViewById(R.id.lv);
// //要保证在设置适配器时,新闻xml文件已经解析完毕了
// lv.setAdapter(new MyAdapter());
}
class MyAdapter extends BaseAdapter{
//得到模型层中元素的数量,用来确定listview需要有多少个条目
@Override
public int getCount() {
// TODO Auto-generated method stub
return newsList.size();
}
@Override
//返回一个View对象,作为listview的条目显示至界面
public View getView(int position, View convertView, ViewGroup parent) {
News news = newsList.get(position);
View v = null;
ViewHolder mHolder;
if(convertView == null){
v = View.inflate(MainActivity.this, R.layout.item_listview, null);
mHolder = new ViewHolder();
//把布局文件中所有组件的对象封装至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 = (SmartImageView) v.findViewById(R.id.iv);
//把ViewHolder对象封装至View对象中
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() + "条评论");
//给新闻图片imageview设置内容
mHolder.siv.setImageUrl(news.getImageUrl());
return v;
}
class ViewHolder{
//条目的布局文件中有什么组件,这里就定义什么属性
TextView tv_title;
TextView tv_detail;
TextView tv_comment;
SmartImageView siv;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
}
private void getNewsInfo() {
Thread t = new Thread(){
@Override
public void run() {
String path = "http://192.168.13.13:8080/news.xml";
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
//发送http GET请求,获取相应码
if(conn.getResponseCode() == 200){
InputStream is = conn.getInputStream();
//使用pull解析器,解析这个流
parseNewsXml(is);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
t.start();
}
private void parseNewsXml(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();
}
//发消息,让主线程设置listview的适配器,如果消息不需要携带数据,可以发送空消息
handler.sendEmptyMessage(1);
// for (News n : newsList) {
// System.out.println(n.toString());
// }
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
- 发送GET请求
URL url = new URL(path);
//获取连接对象
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//设置连接属性
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
//建立连接,获取响应吗
if(conn.getResponseCode() == 200){
}
- 获取服务器返回的流,从流中把html源码读取出来
byte[] b = new byte[1024];
int len = 0;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
while((len = is.read(b)) != -1){
//把读到的字节先写入字节数组输出流中存起来
bos.write(b, 0, len);
}
// 把字节数组输出流中的内容转换成字符串
// 默认使用utf-8
// String text = bos.toString();
text = new String(bos.toByteArray());
- 乱码的出现是因为服务器和客户端码表不一致导致
//手动指定码表
String text = new String(bos.toByteArray(), "gb2312");
public class MainActivity extends Activity {
Handler handler = new Handler(){
public void handleMessage(android.os.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(){
@Override
public void run() {
String path = "http://192.168.13.13:8080/baidu.html";
try {
URL url = new URL(path);
//获取连接对象,此时还未建立连接
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
//先建立连接,然后获取响应码
if(conn.getResponseCode() == 200){
//拿到服务器返回的输入流,流里的数据就是html的源文件
InputStream is = conn.getInputStream();
//从流里把文本数据取出来
String text = Utils.getTextFromStream(is);
//发送消息,让主线程刷新ui,显示源文件
Message msg = handler.obtainMessage();
msg.obj = text;
handler.sendMessage(msg);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
t.start();
}
}
#5. 提交数据 ##5.1 GET方式提交数据
- get方式提交的数据是直接拼接在url的末尾
final String path = "http://192.168.1.104/Web/servlet/CheckLogin?name=" + name + "&pass=" + pass;
- 发送get请求,代码和之前一样
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setReadTimeout(5000);
conn.setConnectTimeout(5000);
if(conn.getResponseCode() == 200){
}
- 浏览器在发送请求携带数据时会对数据进行URL编码,我们写代码时也需要为中文进行URL编码
String path = "http://192.168.1.104/Web/servlet/CheckLogin?name=" + URLEncoder.encode(name) + "&pass=" + pass;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) {
Toast.makeText(MainActivity.this, (String)msg.obj, 0).show();
}
};
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();
Thread t = new Thread(){
@Override
public void run() {
//提交的数据需要经过url编码,英文和数字编码后不变
@SuppressWarnings("deprecation")
String path = "http://192.168.13.13/Web2/servlet/LoginServlet?name=" + URLEncoder.encode(name) + "&pass=" + pass;
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();
String text = Utils.getTextFromStream(is);
Message msg = handler.obtainMessage();
msg.obj = text;
handler.sendMessage(msg);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
t.start();
}
}
public class Utils {
public static String getTextFromStream(InputStream is){
byte[] b = new byte[1024];
int len = 0;
//创建字节数组输出流,读取输入流的文本数据时,同步把数据写入数组输出流
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
while((len = is.read(b)) != -1){
bos.write(b, 0, len);
}
//把字节数组输出流里的数据转换成字节数组
String text = new String(bos.toByteArray());
return text;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
- post提交数据是用流写给服务器的
- 协议头中多了两个属性
- Content-Type: application/x-www-form-urlencoded,描述提交的数据的mimetype
- Content-Length: 32,描述提交的数据的长度
//给请求头添加post多出来的两个属性
String data = "name=" + URLEncoder.encode(name) + "&pass=" + pass;
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setRequestProperty("Content-Length", data.length() + "");
- 设置允许打开post请求的流
conn.setDoOutput(true);
- 获取连接对象的输出流,往流里写要提交给服务器的数据
OutputStream os = conn.getOutputStream();
os.write(data.getBytes());
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) {
Toast.makeText(MainActivity.this, (String)msg.obj, 0).show();
}
};
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();
Thread t = new Thread(){
@Override
public void run() {
//提交的数据需要经过url编码,英文和数字编码后不变
@SuppressWarnings("deprecation")
String path = "http://192.168.13.13/Web2/servlet/LoginServlet";
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
//拼接出要提交的数据的字符串
String data = "name=" + URLEncoder.encode(name) + "&pass=" + pass;
//添加post请求的两行属性
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setRequestProperty("Content-Length", data.length() + "");
//设置打开输出流
conn.setDoOutput(true);
//拿到输出流
OutputStream os = conn.getOutputStream();
//使用输出流往服务器提交数据
os.write(data.getBytes());
if(conn.getResponseCode() == 200){
InputStream is = conn.getInputStream();
String text = Utils.getTextFromStream(is);
Message msg = handler.obtainMessage();
msg.obj = text;
handler.sendMessage(msg);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
t.start();
}
}
-
UI停止刷新,应用无法响应用户操作
-
耗时操作不应该在主线程进行
-
ANR
- application not responding
- 应用无响应异常
- 主线程阻塞时间过长,就会抛出ANR
-
主线程又称UI线程,因为只有在主线程中,才能刷新UI
- 主线程创建时,系统会同时创建消息队列对象(MessageQueue)和消息轮询器对象(Looper)
- 轮询器的作用,就是不停的检测消息队列中是否有消息(Message)
- 消息队列一旦有消息,轮询器会把消息对象传给消息处理器(Handler),处理器会调用handleMessage方法来处理这条消息,handleMessage方法运行在主线程中,所以可以刷新ui
- 总结:只要消息队列有消息,handleMessage方法就会调用
- 子线程如果需要刷新ui,只需要往消息队列中发一条消息,触发handleMessage方法即可
- 子线程使用处理器对象的sendMessage方法发送消息