一、耗时操作
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; } }