Skip to content

Latest commit

 

History

History
2646 lines (2214 loc) · 93.2 KB

Android应用开发-网络编程1.md

File metadata and controls

2646 lines (2214 loc) · 93.2 KB

网络编程

1. 请求网络图片

网络交互就是基于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();
                }
        }
}

添加权限:

img

在Tomcat服务器webapps\ROOT存放图片:

img

运行结果:双击start.bat,启动tomcat。

img

img

启动2.3.3版本模拟器,图片显示成功:

img

Handler机制让子线程刷新UI,如果上面的示例运行在4.3版本的模拟器上,就会报警告:

img

在Android中,主线程是绝对不能阻塞的。因为,网络请求是耗时操作,主线程处于阻塞状态,用户任何操作都无效,处于类似于死机的状态。此时,点击HOME键有效,点击返回键、菜单键都没有反应。因为,返回键和菜单键是由当前应用程序自行处理,HOME键是由系统处理。应用程序可以阻塞自己的主线程,不可能阻塞Android系统。 为了保证用户体验良好,所有的耗时操作都不要写在主线程里,包括:请求网络、加载数据(数据库)和资源。 ANR:Application Not Responding,应用长时间不能响应用户操作。

img

img

查看出现ANR的原因,导出data/anr/traces.txt文件,打开。

img

img

ANR无法调试,解决方法就是在主线程中不要做耗时操作。 只有主线程可以刷新UI,主线程又称UI线程。这样做,是为了线程安全,只有一个线程可以刷新UI,如果有多个线程同时刷新UI,就可能出现线程安全导致UI刷新混乱的问题。

如果我们在MainActivity.java中启动一个线程进行网络请求,再次执行该应用程序就会报警告,如下:

img

img

通过异常可以看出来,刷新UI的代码必须在主线程中执行,但是请求图片的代码又由于不能引起阻塞,只能在子线程中执行,而且只有请求完图片之后才能刷新UI,这时候就形成了矛盾。Android里面提供了一种机制能够解决这个问题,也就是Handler机制。

2. Handler机制

img

主线程创建的时候,主线程中有一个消息队列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();
        }
}

运行结果:

img

添加缓存功能的图片查看器,为了提升效率,查看图片后,缓存起来,便于下次查看图片不必在通过网络请求。

代码: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);
        }
}

运行结果:

img

第一次,从网络获取。第二次再次点击按钮,从缓存获取。

img

3. 获取开源代码

在程序开发中,会遇到很多问题。例如,请求图片过程中遇到断网,网速慢,图片过大内存不够等等情况。由于这些情况大部分项目都会遇到,所以网上有很多写好的现成的模块。不要重新发明轮子,可以直接拿来用。 好的开源网站: http://code.google.com http://github.com 搜索,下载源码,解压,复制到我们自己的应用程序项目中。

img

img

img

img

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是自定义控件,以后会有专门的课程讲解。

img

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);
    }
} 

添加权限:

img

运行结果:

img

4. Html源文件查看器

代码: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();
        }
}

添加权限:

img

运行结果:

img

如果html文件为gbk编码:

img

那么Tools.java中代码只要进行如下修改即可:

img

新闻客户端布局

img

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>

效果:

img

获取新闻信息,将news.xml和images文件夹存放在Tomcat服务器中。

img

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标签,便于显示图片。

img

img

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);
        }
}

添加权限:

img

运行结果:

img

使用get方式提交表单

使用MyEclipse,新建一个Servlet:Login.java,把该Web项目部署到Tomcat服务器上。

img

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>

打开浏览器,输入地址,然后输入用户名和密码,提交。

img

将浏览器编码改为UTF-8。

img

结果如下:

img

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();
        }
}

添加权限:

img

运行结果:

img

提交表单的乱码问题

将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);
        }
} 

重新部署,运行。结果如下:

img

将浏览器编码调整为UTF-8。 img

img

img

可以看到,打印出来的为乱码。原因在于浏览器提交表单的时候,会把“张三”用UTF-8编码变成字节数组,然后传给服务器。服务器拿到这些字节以后,因为getParameter默认使用iso8859-1编码把读取到的字节数组构造成字符串,导致乱码。因此,解决方案为,首先使用iso8859-1把字符串重新转换成字节数组,然后再用utf-8构造成字符串即可。

img

重新部署,运行,结果如下:

img

img

img

尝试通过手机端发送中文的情况,如下:输入中文,首先选择谷歌拼音输入法。

img

img

img

img

由上面的图片可以看到,依然存在乱码问题。原因在于浏览器提交的数据都是经过URL编码的,所以,通过代码提交的数据就需要手动编码。

img

img

修改后,运行结果:

img

img

使用post方式提交表单,修改表单为post提交方式:

img

img

img

通过代码实现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();
        }
} 

运行结果:

img


1. 网络图片查看器

  • 确定图片的网址
  • 发送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();
	}
}

1.4 案例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){
		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.5 加入缓存图片的功能

  • 把服务器返回的流里的数据读取出来,然后通过文件输入流写至本地文件
//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);
	}

}

1.6 案例2:带缓存的网络图片查看器

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);
	}

}

1.7 案例3:开源图片查看器

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);
	}

}

2. 获取开源代码的网站

  • 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");

3. 新闻客户端

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();
		}

	}

}

3.1 案例5:新闻客户端

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();
		}

	}

}

4. Html源文件查看器

  • 发送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());

4.1 乱码的处理

  • 乱码的出现是因为服务器和客户端码表不一致导致
//手动指定码表
String text = new String(bos.toByteArray(), "gb2312");

4.2 案例4:HTML源文件查看器

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;

5.2 案例6:使用get方式提交数据

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;
	}
}

5.3 POST方式提交数据

  • 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());

5.4 案例7:使用post方式提交数据

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();
	}
}

6. 网络请求

6.1 主线程阻塞

  • UI停止刷新,应用无法响应用户操作

  • 耗时操作不应该在主线程进行

  • ANR

    • application not responding
    • 应用无响应异常
    • 主线程阻塞时间过长,就会抛出ANR
  • 主线程又称UI线程,因为只有在主线程中,才能刷新UI

6.2 消息队列机制

  • 主线程创建时,系统会同时创建消息队列对象(MessageQueue)和消息轮询器对象(Looper)
  • 轮询器的作用,就是不停的检测消息队列中是否有消息(Message)
  • 消息队列一旦有消息,轮询器会把消息对象传给消息处理器(Handler),处理器会调用handleMessage方法来处理这条消息,handleMessage方法运行在主线程中,所以可以刷新ui
  • 总结:只要消息队列有消息,handleMessage方法就会调用
  • 子线程如果需要刷新ui,只需要往消息队列中发一条消息,触发handleMessage方法即可
  • 子线程使用处理器对象的sendMessage方法发送消息