Android网络编程(一)

网络编程

网络图片查看

网络图片查看器

try {
        // 2.把网址字符串封装成一个URL
        URL url = new URL(path);
        // 3.获取连接对象
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();

        // 4.做参数设置,注意大写
        conn.setRequestMethod("GET");
        // 5.设置连接超时时间
        conn.setConnectTimeout(8000);
        // 设置读取超时时间
        conn.setReadTimeout(8000);
        // 发送请求,建立连接
        conn.connect();
        // 6. 获取返回码,判断请求返回状态
        if (conn.getResponseCode() == 200) {
            // 请求成功
            // 7.拿到服务器返回的流,里面的数据就是客户端请求的内容
            InputStream is = conn.getInputStream();
            // 在确定流中数据是图片的情况下可以使用Google提供的API直接生成图片,就不需要自己读取流了
//====================================================================================================================
            //从流里读取数据就是在下载数据,如果网速过慢就会造成主线程阻塞(主线程阻塞在2.3可以,但是4.0+版本的Android会抛出异常)
            Bitmap bm = BitmapFactory.decodeStream(is);
//====================================================================================================================
            ImageView iv = (ImageView) findViewById(R.id.imageView1);
            iv.setImageBitmap(bm);
        } else {
            Toast.makeText(this, "请求失败", Toast.LENGTH_SHORT).show();
        }

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

以上代码,注意细节~

  • (应用)主线程阻塞

    • 界面会停止刷新
    • 应用停止响应用户任何操作
    • 影响用户体验
  • 规范

    • 耗时的操作不要放在主线程((4.0以后的版本强制要求不可以放在主线程,要不就报错)放在子线程里面就可以了)

ANR异常

  • application not responding(应用无响应)
  • 主线程长时间阻塞,会爆出ANR异常

只有主线程才可以刷新UI(View)

消息队列

问题:怎么用子线程中处理过的数据去刷新UI

MessageQueue 消息队列

Looper 轮询器(会一直不停的检测消息队列里面是否有消息) * 消息队列没有数据,就什么都不干 * 消息队列消息,就扔给Handler去处理

Handler * handleMessage()方法用来处理消息 - 重写该方法来处理我们的消息

在主线程创建时会同时创建MessageQueue和Looper对象,但是Handler对象在程序员需要使用是,(程序员)自行创建。 只要消息队列有消息,handleMessage()方法就会在主线程调用

子线程需要刷新UI,只需要往主线程的消息队列中发送一条数据即可


栗子:

public class MainActivity extends Activity {
    // 创建消息处理器
    Handler handler = new Handler() {
        // 重写handleMessage方法
        public void handleMessage(android.os.Message msg) {
            switch (msg.what) {
            case 0:
                ImageView iv = (ImageView) findViewById(R.id.imageView1);
                iv.setImageBitmap((Bitmap) msg.obj);
                break;  
            case 1:
                Toast.makeText(MainActivity.this, "请求失败", Toast.LENGTH_SHORT)
                        .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() {
            @Override
            public void run() {
                // 网址
                String path = "http://192.168.15.27:8080/dd.jpg";

                try {
                    // 2.把网址字符串封装成一个URL
                    URL url = new URL(path);
                    // 3.获取连接对象
                    HttpURLConnection conn = (HttpURLConnection) url
                            .openConnection();

                    // 4.做参数设置,注意大写
                    conn.setRequestMethod("GET");
                    // 5.设置连接超时时间
                    conn.setConnectTimeout(8000);
                    // 设置读取超时时间
                    conn.setReadTimeout(8000);
                    // 发送请求,建立连接
                    conn.connect();
                    // 6. 获取返回码,判断请求返回状态
                    if (conn.getResponseCode() == 200) {
                        // 请求成功
                        // 7.拿到服务器返回的流,里面的数据就是客户端请求的内容
                        InputStream is = conn.getInputStream();
                        // 在确定流中数据是图片的情况下可以使用Google提供的API直接生成图片,就不需要自己读取流了
                        Bitmap bm = BitmapFactory.decodeStream(is);

                        // ImageView iv = (ImageView)
                        // findViewById(R.id.imageView1);
                        // iv.setImageBitmap(bm);

                        // 发送消息至主线程消息队列
                        Message msg = new Message();
                        // 利用消息对象携带数据
                        msg.obj = bm;
                        // 设置状态标识(这里设置的是成功获取返回0)
                        msg.what = 0;

                        handler.sendMessage(msg);

                    } else {

                        // Message msg = new Message();
                        // msg.what = 0;
                        // handler.sendMessage(msg);
                        // 不需要携带数据时也可以发送空消息,参数what
                        handler.sendEmptyMessage(1);
                    }

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

    }
}

数据缓存到本地

看代码

public class MainActivity extends Activity {

// 创建消息处理器
Handler handler = new Handler() {
    // 重写handleMessage方法
    public void handleMessage(android.os.Message msg) {
        switch (msg.what) {
        case 0:
            ImageView iv = (ImageView) findViewById(R.id.imageView1);
            iv.setImageBitmap((Bitmap) msg.obj);
            break;
        case 1:
            Toast.makeText(MainActivity.this, "请求失败", Toast.LENGTH_SHORT)
                    .show();
            break;
        }
    };
};

    …………

public void click(View V) {
    // 网址
    final String path = "http://192.168.15.27:8080/dd.jpg";
    final File file = new File(getCacheDir(), getFileName(path));
    // 判断文件是否已将缓存到本地
    if (!file.exists()) {
        // 是 直接读取显示
        Bitmap bm = BitmapFactory.decodeFile(file.getAbsolutePath());
        ImageView iv = (ImageView) findViewById(R.id.imageView1);
        iv.setImageBitmap(bm);

    } else {
        Thread t = new Thread() {
            @Override
            public void run() {
                try {
                    // 2.把网址字符串封装成一个URL
                    // 3.获取连接对象
                    // 4.做参数设置,注意大写
                    // 5.设置连接超时时间
                    // 设置读取超时时间
                    // 发送请求,建立连接
                    // 6. 获取返回码,判断请求返回状态
                    if (conn.getResponseCode() == 200) {
                        // 请求成功
                        // 7.拿到服务器返回的流,里面的数据就是客户端请求的内容
                        InputStream is = conn.getInputStream();

                        FileOutputStream fos = new FileOutputStream(file);

                        byte[] b = new byte[1024];
                        int len;
                        while ((len = is.read(b)) != -1) {
                            fos.write(b, 0, len);
                        }
                        fos.close();
                        //生成图片
                        Bitmap bm = BitmapFactory.decodeFile(file
                                .getAbsolutePath());

                        // 在确定流中数据是图片的情况下可以使用Google提供的API直接生成图片,就不需要自己读取流了
                        // Bitmap bm = BitmapFactory.decodeStream(is);
                        // ImageView iv = (ImageView)
                        // 发送消息至主线程消息队列
                        // 利用消息对象携带数据
                        // 设置状态标识(这里设置的是成功获取返回0)

                        ………………

                    } else {
                        handler.sendEmptyMessage(1);
                    }

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

        };
        t.start();

    }
}

/**
 * 获取文件名
 * 
 * @param path
 * @return
 */
public String getFileName(String path) {
    int index = path.lastIndexOf("/");
    return path.substring(index + 1);
}


获取开源代码的网站

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

使用第三方ImageView(自己试)

测试用第三方资源:android-smart-image-view-master.zip


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
    text = new String(bos.toByteArray());
    

新闻客户端实现

注意:线程异步问题

因为主线程不能执行网络下载等其他耗时的操作,而必须使用子线程去实现网络下载等操作,但是要注意这里面的线程异步问题。 如:主线程在刷新UI是调用了子线程没有处理完毕内容导致的空指针等异常

ViewHolder * 避免了重复生成大量的view对象 * 先把布局文件中的所有的组件封装到ViewHolder对象中 * ViewHolder的对象会与View一起被缓存起来 * 需要的时候直接xxx


乱码问题的解决

解决乱码问题非常简单: 统一两遍的编码集。 android下默认编码 utf-8

客户端: 中文和特殊字符 URLEncoder.encode(); 服务器端: 默认的编码 iso-8859-1 和 本地编码(gbk)

发现 数据

�� gbk->utf-8 ??? utf-8 -> iso-8859-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;
    

GET方式提交数据的缺点: * 组拼http的URL的方式不安全。 * GET方式提交数据,对数据的长度是有要求的。http规范最大长度4K。

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

POST方式提交数据的优缺点: * 代码写起来麻烦 注意4个细节。

优点: * 安全。 * 提交数据的长度没有限制。

原文地址:https://www.cnblogs.com/istarry/p/4426050.html