Android网络编程

1、关于网络图片的查看
注意在Android中,主线程被阻塞会导致应用不能刷新ui界面,不能响应用户操作,用户体验将非常差。
//imageView 显示图片的设置:
iv.setImageResource(resId); 通过资源id,进行设置显示图片
iv.setImageBitmap(bim); //通过位图显示图片, Bitmap 由位图工厂工厂创建 decodeStream(inputstream)获取。
2)任何耗时操作都要写在主线程中。 android.os.NetWorkOnMainThreadException 网络工作在主线程上异常。

就是将服务器端的图片下载到客户端显示出来

<RelativeLayout 
    android:layout_width="match_parent"
    android:layout_height="match_parent"
>
><Button 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="下载图片" 
        android:onClick="click"
        android:layout_centerHorizontal="true"
        />
     <ImageView      //用来显示图片
         android:id="@+id/iv"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"

         />
</RelativeLayout>

要得到图片首先要知道图片的网址,path然后封装到url对象中。
获得URlConnection 由url.openConnetction()得到,转成HttpURLConnection
此时并未开始连接,首先对连接对象初始化,设置连接对象的请求方法(注意这里GET区分大小写必须大写否则运行时异常)协议异常 以下代码在4.0以下版本可以正常使用 4.0以上会报运行在主线程上异常。android.os.NetworkOnMainThreadException

import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
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) {
        String path = "http://img05.3dmgame.com/uploads/allimg/150407/265_150407215242_1_lit.png";
        try {
            // 将网址封装成url对象。
            URL url = new URL(path);
            // 获取服务器和客户端的连接对象 此时还没有建立连接
            HttpURLConnection coon = (HttpURLConnection) url.openConnection();
            // 对连接对象进行初始化 设置请求方式
            coon.setRequestMethod("GET");
            // 设置连接超时时间
            coon.setConnectTimeout(5000);
            // 设置读取超时时间
            coon.setReadTimeout(5000);
            // 发送请求,与服务器建立连接。
            coon.connect();
            // 得到的响应码如果等于200说明请求成功。
            if (coon.getResponseCode() == 200) {
                // 获取服务器响应头中的流 流里面的数据就是客户端请求的数据
                InputStream is = coon.getInputStream();
                // 读取流中的数据,并创建位图对象 这时图片就在位图中     这里用到位图工厂 解出流中的图片数据。
                Bitmap bm = BitmapFactory.decodeStream(is);

                ImageView iv = (ImageView) findViewById(R.id.iv);
                // 把位图对象显示在ImageView中。
                iv.setImageBitmap(bm);

            } else {
                Toast.makeText(MainActivity.this, "请求失败", 0).show();
            }

        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
}

上面的程序跑在了主线程上。会报异常。
2、主线程阻塞和消息队列
在Android中,主线程被阻塞会导致应用不能刷新ui界面,不能响应用户操作,用户体验将非常差
* 主线程阻塞时间过长,系统会抛出ANR异常
* ANR:Application Not Response;应用无响应
创建一个子线程 从写run方法:
Thread t = new Thread(){
@Override
public void run(){
super.run();
从写父类的run方法。
}
};

t.start;


另外需要注意的是主线程又称为UI线程。因为只有主线程才能刷新UI。
刷新ui的代码只能运行在主线程,运行在子线程是没有任何效果的
* 如果需要在子线程中刷新ui,就要使用消息队列机制。
* 什么是消息队列机制呢
消息队列:looper消息轮巡器 Message Queue 消息队列
Looper一旦发现Message Queue中有消息,就会把消息取出,然后把消息扔给Handler对象,Handler会调用自己的handleMessage方法来处理这条消息
handleMessage方法运行在主线程。
这里写图片描述
在子线程中发送消息给主线程:
1、现在主线程中创建Handler 对象 复写handleMessage
Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) { //复写handlemessage 方法 这里的msg就是子线程发送过来的。
ImageView iv= (ImageView) findViewById(R.id.iv); //只有主线程中才能刷新ui 将视图显示定义在handlemessage方法中。
iv.setImageBitmap((Bitmap) msg.obj); //obj 强转成Bitmap 位图对象。

}

};

 Message  msg= new Message();   //实际开发当中我们会用Message msg = handler.obtainMessage();获得Message对象节约内存。
//消息对象可以携带数据。
 msg.obj = bim;
//把消息发送到主线程的消息队列。
  handler.sendMessage(msg);   

消息队列机制
* 主线程创建时,系统会同时创建消息队列对象(MessageQueue)和消息轮询器对象(Looper)
* 轮询器的作用,就是不停的检测消息队列中是否有消息(Message)
* 消息队列一旦有消息,轮询器会把消息对象传给消息处理器(Handler),处理器会调用handleMessage方法来处理这条消息,handleMessage方法运行在主线程中,所以可以刷新ui
* 总结:只要消息队列有消息,handleMessage方法就会调用
* 子线程如果需要刷新ui,只需要往消息队列中发一条消息,触发handleMessage方法即可
* 子线程使用处理器对象的sendMessage方法发送消息
注意处理消息时,有成功的消息,也有失败的也需要handlemessage处理怎么区别呢?
message对象中有一个message.what (int类型的值) 就用这个赋值就行区分。

    Message msg = handler.obtainMessage();
                        // 消息对象可以携带数据。
                        msg.obj = bim;
                        msg.what = 1;
                        // 把消息发送到主线程的消息队列。
                        handler.sendMessage(msg);


----------


Handler handler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            switch (msg.what) {
            case 1:      //根据msg.what的值进行判断  哪个消息时成功处理的  哪个是处理失败的。
                ImageView iv = (ImageView) findViewById(R.id.iv);
                iv.setImageBitmap((Bitmap) msg.obj);   //负责将图片信息显示在屏幕上,网络图片需要开启数据连接或WiFi
                break;  

            default:
                Toast.makeText(MainActivity.this, "请求失败了哦亲", 0).show();
                break;
            }

        }

    };

获取一个字符串网址文件名 http://p2.so.qhimg.com/sdr/575_768_/t01cfdbb0c8a5a482d7.jpg 图片名称的方法:
以最后一个反斜杠作为切割符 获取其索引值。

  public  String getFileName(String path){
          int  index= path.lastIndexOf("/");    
    return  path.subString(index+1);(从哪个位置开始截取获得)
}

将文件写入缓存文件的代码单元:

InputStream is = coon.getInputStream();
File file = new File(getCacheDir(), getFileName(path));// 放到缓存文件下。 第二个参数是缓存文件的名字
//文件输出流将数据写到文件中。
  FileOutputStream  fos = new FileOutputStream(file); 
  byte[] b = new byte[1024]; // 每次读取1k
 int len=0;
while((len=is.read(b))!=-1){    //注意必要忘记传入字符数组。b
     fos.write(b, 0, len);
   }
fos.close();   //这样就将输入流中的数据写入到了缓存文件中。

网络请求图片带缓存版的最终版:

package com.zh.acheshowdata;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

import com.zh.acheshowdata.R;

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.widget.ImageView;
import android.widget.Toast;

public class MainActivity extends Activity {
    static ImageView iv;
    static MainActivity ma;

    static Handler handler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            switch (msg.what) {
            case 1:

                iv.setImageBitmap((Bitmap) msg.obj);
                break;

            default:
                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;
        final String path = "http://p2.so.qhimg.com/sdr/575_768_/t01cfdbb0c8a5a482d7.jpg";
        final File file = new File(getCacheDir(), getFileName(path));// 放到缓存文件下。

        if (file.exists()) {
            // 如果缓存存在的话
            Bitmap bim = BitmapFactory.decodeFile(file.getAbsolutePath());
            iv.setImageBitmap(bim);

        } else {
            Thread t = new Thread() {
                @Override
                public void run() {
                    // TODO Auto-generated method stub
                    super.run();

                    try {
                        URL url = new URL(path);
                        // 由url对象获得打开 连接对象 httpurlconnection
                        HttpURLConnection coon = (HttpURLConnection) url.openConnection();
                        // 设置请求的方法 连接超时时间,和读取超时间
                        coon.setRequestMethod("GET");
                        coon.setConnectTimeout(5000);
                        coon.setReadTimeout(5000);
                        // 判断得到的状态码 此时已经建立连接
                        if (coon.getResponseCode() == 200) {
                            InputStream is = coon.getInputStream();

                            // 文件输出流将数据写到文件中。
                            FileOutputStream fos = new FileOutputStream(file);
                            byte[] b = new byte[1024]; // 每次读取1k
                            int len = 0;
                            while ((len = is.read(b)) != -1) {
                                fos.write(b, 0, len);
                            }
                            fos.close();

                            Bitmap bim = BitmapFactory.decodeFile(file.getAbsolutePath());

                            Message msg = handler.obtainMessage();
                            // 消息对象可以携带数据。
                            msg.obj = bim;
                            msg.what = 1;
                            // 把消息发送到主线程的消息队列。
                            handler.sendMessage(msg);

                        } else {
                            // Toast.makeText(MainActivity.this, "对不起,请求失败",
                            // 0).show();
                            Message msg = handler.obtainMessage();
                            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);

    }
}

将文件读取流封装成一个工具类: 从输入流中直接读取到数据

package com.zh.htmlv.utls;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

//从输入流中得到文本。
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(),"gbk");  这个地方可以改变客户端的编码表  需要注意的是Android开发客户端用utf-8改变服务器的。
            return text;
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值