Android总结四(Handler、网络编程)

一、耗时操作

  1.什么是ANR

    在应用程序的主线程中执行一段耗时的代码, 就有可能出现ANR异常.

    耗时的代码未执行结束时, 界面会卡住, 用户对界面进行了操作, 10秒之后耗时代码如果还未结束, 就会出现ANR异常

  2.怎么避免ANR

    主线程中不要执行耗时的代码

    如果一定要做耗时的事情, 开启新线程, 在新线程中执行

  3.UI Thread

    安卓手机中主线程负责刷新界面, 以及处理用户的操作

    应用程序的界面都是由主线程创建的

    界面的修改也只能在主线程中执行

  4.Handler

    有的时候我们需要执行一些耗时的代码, 会开启新线程, 这时又需要更新界面, 必须在主线程中操作, 那么就需要使用Handler来进行线程之间的通信

    1)sendMessage():

      新线程向主线程发送一个包含数据的消息, 主线程获取消息中的数据

      在主线程中创建Handler子类对象, 重写handleMessage()方法

      新线程中可以使用Handler的引用调用sendMessage()方法, 发送一个Message对象

      只要执行了sendMessage()方法, 那么主线程会自动执行handleMessage()方法, 收到Message对象

    2)post():

      新线程向主线程发送一段代码, 主线程直接执行

      在主线程中创建Handler对象

      新线程中可以使用Handler调用post()方法发送一个Runnable对象

      主线程会自动执行Runable的run()

  5.示例代码

sendMessage:

package com.gnnuit.anr;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.TextView;

public class SendMessageActivity extends Activity {

    private TextView tv;
    private Handler handler = new Handler() {
        public void handleMessage(android.os.Message msg) {// 该方法在sendMessage()方法之后执行, 形参就是发送过来的Message对象
            tv.setText(msg.obj + "");// 主线程更新界面
        };
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv = (TextView) findViewById(R.id.tv);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    public void go(View v) {
        new Thread() {
            public void run() {
                for (int i = 1; i < 10; i++) {
                    System.out.println(i + "");
                    SystemClock.sleep(1000);
                    // Message msg = new Message(); // 创建消息对象,此种方法效率不高
                    Message msg = handler.obtainMessage(); // 从消息池中获取一个Message
                    msg.obj = i;// 把数据放在消息对象中
                    handler.sendMessage(msg);// 在新线程中发送消息对象, 主线程会自动执行handleMessage()方法
                }
            };
        }.start();
    }

}

post:

package com.gnnuit.anr;

import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.TextView;

public class PostActivity extends Activity {

    private TextView tv;
    private Handler handler = new Handler();
    private int i;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv = (TextView) findViewById(R.id.tv);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    public void go(View v) {
        System.out.println("go:" + Thread.currentThread().getName() + "");
        new Thread() {
            public void run() {
                System.out.println("for:" + Thread.currentThread().getName() + "");
                for (i = 1;; i++) {
                    System.out.println(i + "");
                    handler.post(new Runnable() {// 在新线程中使用Handler向主线程发送一段代码, 主线程自动执行run()方法

                        @Override
                        public void run() {
                            System.out.println("run:" + Thread.currentThread().getName() + "");
                            tv.setText(i + "");
                        }
                    });
                    SystemClock.sleep(1000);
                }
            };
        }.start();
    }

}

 二、网络编程

  1.获取网络文本

    使用URL封装一个地址

    openConnection()得到HttpUrlConnection对象

    getResponseCode()得到响应码

    getInputStream()得到输入流读取数据

    注意: 安卓4.0以后联网需要开启新线程, 在新线程中操作界面还需要使用Handler

    示例代码:

package com.gnnuit.nettext;

import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class ClassicActivity extends Activity {

    private EditText et;
    private TextView tv;
    private Handler handler = new Handler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        et = (EditText) findViewById(R.id.et);
        tv = (TextView) findViewById(R.id.tv);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    public void go(View v) {

        new Thread() {
            public void run() {
                try {
                    TextService service = new TextService();
                    String path = et.getText().toString().trim();// 从EditText获取地址
                    final String text = service.getText(path);// 访问网络, 得到文本
                    handler.post(new Runnable() {

                        @Override
                        public void run() {
                            tv.setText(text);// 设置到TextView中
                        }
                    });
                } catch (Exception e) {
                    e.printStackTrace();
                    handler.post(new Runnable() {

                        @Override
                        public void run() {
                            Toast.makeText(getApplicationContext(), "网络连接失败", Toast.LENGTH_SHORT).show();
                        }
                    });
                }
            };
        }.start();
    }

}

TextService.java

package com.gnnuit.nettext;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

import android.accounts.NetworkErrorException;

public class TextService {

    public String getText(String path) throws Exception {
        URL url = new URL(path);// 把路径封装成URL对象
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();// 打开连接对象(还未联网)
        conn.setReadTimeout(5000);// 设置超时时间, 如果连接超过5000毫秒未响应, 就抛出异常

        int code = conn.getResponseCode();// 获取响应码(真正联网)
        if (code == 200) {// 如果成功
            InputStream is = conn.getInputStream();// 获取输入流
            ByteArrayOutputStream os = new ByteArrayOutputStream();// 可以写出数据到内存的输出流
            byte[] buffer = new byte[8192];
            int length;
            while ((length = is.read(buffer)) != -1) {// 从网络读取数据
                os.write(buffer, 0, length);// 向内存写出数据
            }
            is.close();
            os.close();
            conn.disconnect();
            byte[] bytes = os.toByteArray();// 把写到内存的数据读取出来
            String text = new String(bytes);// 解码为字符串(默认UTF-8)
            return text;
        }
        conn.disconnect();
        throw new NetworkErrorException("服务器正忙:" + code);
    }

}

  2.AsyncHttpClient

    AsyncHttpClient是一个开源项目, 可以自动创建新线程联网, 不论成功或失败都会在主线程执行回调函数

  示例代码:

package com.gnnuit.nettext;

import org.apache.http.Header;

import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.TextHttpResponseHandler;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class AsyncActivity extends Activity {

    private EditText et;
    private TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        et = (EditText) findViewById(R.id.et);
        tv = (TextView) findViewById(R.id.tv);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    public void go(View v) {
        String path = et.getText().toString().trim();
        AsyncHttpClient client = new AsyncHttpClient();
        client.get(path, new TextHttpResponseHandler() {

            @Override
            public void onSuccess(int statusCode, Header[] headers, String responseString) {
                tv.setText(responseString);
            }

            @Override
            public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
                Toast.makeText(getApplicationContext(), "服务器忙" + statusCode, Toast.LENGTH_SHORT).show();
                throwable.printStackTrace();
            }
        });
    }

}

  3.获取网络图片

    使用AsyncHttpClient获取图片数据

    由于图片数据通常较大, 应该使用缓存, 当数据接收到之后保存在本地

    下次再访问相同路径时添加请求头, If-Modified-Since, 传递文件的最后修改时间

    服务端如果响应304, 就读取本地数据, 如果响应200, 代表服务端数据已更新, 重新从服务端读取

    注意: 也可以使用SmartImageView, 但是如果服务端同名文件更新, 有Bug

package com.gnnuit.netimage;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

import org.apache.http.Header;

import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.AsyncHttpResponseHandler;
import com.loopj.android.image.SmartImageView;

import android.os.Bundle;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Toast;

public class MainActivity extends Activity {

    //private com.loopj.android.image.SmartImageView iv;
    private EditText et;
    private ImageView iv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        et = (EditText) findViewById(R.id.et);
        //iv = (SmartImageView) findViewById(R.id.iv);
        iv=(ImageView) findViewById(R.id.iv);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    public void go(View v) throws Exception {
        // iv.setImageUrl(et.getText().toString().trim());//

        String path = et.getText().toString().trim();                                    // 获取网络地址
        final File file = new File(getCacheDir(), URLEncoder.encode(path, "UTF-8"));    // 定义缓存文件的路径
        
        AsyncHttpClient client = new AsyncHttpClient();    
        if (file.exists())                                                                // 如果缓存文件存在
            client.addHeader("If-Modified-Since", format(file.lastModified()));            // 添加请求头
        
        client.get(path, new AsyncHttpResponseHandler() {
            public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
                if (statusCode == 200) {
                    Bitmap bm = BitmapFactory.decodeByteArray(responseBody, 0, responseBody.length);    // 获取网络数据
                    iv.setImageBitmap(bm);
                    try {
                        FileOutputStream out = new FileOutputStream(file);
                        out.write(responseBody);    // 保存到本地
                        out.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
                if (statusCode == 304) {
                    Bitmap bm = BitmapFactory.decodeFile(file.getAbsolutePath());    // 从缓存文件读取数据
                    iv.setImageBitmap(bm);
                } else
                    Toast.makeText(getApplicationContext(), "网络错误: " + statusCode, Toast.LENGTH_SHORT).show();
            }
        });
    }
    
    public String format(long ms) {
        Date date = new Date(ms);
        SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH);
        String s = format.format(date);
        return s;
    }

}

  3.获取网络XML

    使用AsyncHttpClient和TextHttpResponseHandler获取文本数据

    使用XmlPullParser和StringReader解析XML生成JavaBean

  4.获取网络JSON

    使用AsyncHttpClient和JsonHttpResponseHandler获取JSONArray

    使用JSONArray和JSONObject解析JSON生成JavaBean

示例代码:

package com.gnnuit.netxml;

import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;

import org.apache.http.Header;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.xmlpull.v1.XmlPullParser;

import android.app.Activity;
import android.os.Bundle;
import android.util.Xml;
import android.view.View;
import android.widget.Toast;

import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.JsonHttpResponseHandler;
import com.loopj.android.http.TextHttpResponseHandler;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    
    public void getXml(View v) {
        new AsyncHttpClient().get("http://192.168.1.251:8080/WebServer/persons.xml", new TextHttpResponseHandler() {
            public void onSuccess(int statusCode, Header[] headers, String responseString) {
                List<Person> persons = parseXml(responseString);
                for (Person p : persons) 
                    System.out.println(p);
                Toast.makeText(getApplicationContext(), "获取XML成功", Toast.LENGTH_SHORT).show();
            }
            public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
            }
        });
    }
    
    public void getJson(View v) {
        new AsyncHttpClient().get("http://192.168.1.251:8080/WebServer/persons.js", new JsonHttpResponseHandler() {
            public void onSuccess(int statusCode, Header[] headers, JSONArray arr) {
                try {
                    for (int i = 0; i < arr.length(); i++ ) {    // 遍历JSONArray
                        JSONObject obj = arr.getJSONObject(i);    // 得到每一个JSONObject
                        int id = obj.getInt("id");                // 获取每个JSONObject中的数据, 封装成Person
                        String name = obj.getString("name");
                        int age = obj.getInt("age");
                        Person p = new Person(id, name, age);
                        System.out.println(p);
                    }
                    Toast.makeText(getApplicationContext(), "获取JSON成功!!!", Toast.LENGTH_SHORT).show();
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        });
        
        /*
        new AsyncHttpClient().get("http://192.168.1.251:8080/WebServer/persons.js", new TextHttpResponseHandler() {
            public void onSuccess(int statusCode, Header[] headers, String responseString) {
                List<Person> persons = parseJson(responseString);
                for (Person p : persons) 
                    System.out.println(p);
                Toast.makeText(getApplicationContext(), "获取JSON成功", Toast.LENGTH_SHORT).show();
            }
            public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
            }
        });
        */
    }
    
    @SuppressWarnings("unused")
    private List<Person> parseJson(String json) {
        List<Person> persons = new ArrayList<Person>();
        try {
            JSONArray arr = new JSONArray(json);        // 把字符串封装成一个JSONArray
            for (int i = 0; i < arr.length(); i++ ) {    // 遍历JSONArray
                JSONObject obj = arr.getJSONObject(i);    // 得到每一个JSONObject
                int id = obj.getInt("id");                // 获取每个JSONObject中的数据, 封装成Person
                String name = obj.getString("name");
                int age = obj.getInt("age");
                Person p = new Person(id, name, age);
                persons.add(p);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return persons;
    }
    
    private List<Person> parseXml(String xml) {
        List<Person> persons = new ArrayList<Person>();
        try {
            XmlPullParser parser = Xml.newPullParser();
            parser.setInput(new StringReader(xml));
            Person p = null;
            for (int type = parser.getEventType(); type != XmlPullParser.END_DOCUMENT; type = parser.next()) {
                if (type == XmlPullParser.START_TAG) {
                    if ("person".equals(parser.getName())) {        // 如果开始的是person, 创建对象, 获取id数据
                        String id = parser.getAttributeValue(0);
                        p = new Person();
                        p.setId(Integer.parseInt(id));
                        persons.add(p);
                    } else if ("name".equals(parser.getName())) {    // 如果开始的是name, 获取下一个文本
                        String name = parser.nextText();
                        p.setName(name);
                    } else if ("age".equals(parser.getName())) {    // 如果开始的是age, 获取下一个文本
                        String age = parser.nextText();
                        p.setAge(Integer.parseInt(age));
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return persons;
    }

}
原文地址:https://www.cnblogs.com/FlySheep/p/3841190.html