首先应该明白两点:
1.Android不允许在主线程(对于android来说,主线程就是UI线程)中访问网络。
2.Android不允许在一个子线程中直接去更新主线程中的UI控件。
对于问题1,可能你会说,这还不好办吗?再开一个线程不就完了?是的,你很聪明,原理也很简单。
对于问题2,就需要用到线程间通信(IPC),Android很好的将其进行了封装,也就有了今天的Handler和AsyncTask。
适用情况:
AsyncTask是一个轻量级的后台异步任务类,简单、便捷。适合后台任务不太多的情况,因为每一个后台任务都要写成一个Class去继承AsyncTask,代码显得比较臃肿。否则就使用Handler吧,很强大,但对于新手来说较难掌握
使用方法:
AsyncTask定义了三种泛型类型 Params,Progress和Result。
- Params 启动任务执行的输入参数,比如HTTP请求的URL。
- Progress 后台任务执行的百分比。
- Result 后台执行任务最终返回的结果,比如String。
最少要重写以下方法:
- doInBackground(Params…) 后台执行,比较耗时的操作都可以放在这里。注意这里不能直接操作UI。此方法在后台线程执行,完成任务的主要工作,通常需要较长的时间。在执行过程中可以调用publicProgress(Progress…)来更新任务的进度。(必须重写)
- onPostExecute(Result) 相当于Handler 处理UI的方式,在这里面可以使用在doInBackground 得到的结果处理操作UI。 此方法在主线程执行,任务执行的结果作为此方法的参数返回(可以不重写,因为你可以用post(Runable r)来更新UI,但既然用了AsyncTask,直接重写这个方法多方便呢!)
有必要的话你还得重写以下这三个方法,但不是必须的:
- onProgressUpdate(Progress…) 可以使用进度条增加用户体验度。 此方法在主线程执行,用于显示任务执行的进度。
- onPreExecute() 这里是最终用户调用Excute时的接口,当任务执行之前开始调用此方法,可以在这里显示进度对话框。
- onCancelled() 用户调用取消时,要做的操作
使用AsyncTask类,以下是几条必须遵守的准则:
- Task的实例必须在UI thread中创建;
- execute方法必须在UI thread中调用;
- 不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)这几个方法;
- 该task只能被执行一次,否则多次调用时将会出现异常;
Handler的使用要搭配线程(Thread)。具体使用方法,请看下面小例子。
服务器端代码:生成一个0到10之间的的随机数,并封装成JSON对象
-
package com.test.servlet;
-
import java.io.IOException;
-
import java.io.PrintWriter;
-
import javax.servlet.ServletException;
-
import javax.servlet.http.HttpServlet;
-
import javax.servlet.http.HttpServletRequest;
-
import javax.servlet.http.HttpServletResponse;
-
public class TestServlet extends HttpServlet {
-
public void doGet(HttpServletRequest request, HttpServletResponse response)
-
throws ServletException, IOException {
-
doPost(request, response);
-
}
-
public void doPost(HttpServletRequest request, HttpServletResponse response)
-
throws ServletException, IOException {
-
response.setContentType("text/html");
-
PrintWriter out = response.getWriter();
-
out.println("{\"rand\":"+"\""+(int)(Math.random()*10)+"\""+"}");
-
out.flush();
-
out.close();
-
}
-
}
这时先通过浏览器访问测试是否成功:
注意:8888是本人修改了tomcat的端口号,默认是8080哦,你们别学我^_^.
Android端:
布局文件就一个Textview用来显示数据:
-
<?xml version="1.0" encoding="utf-8"?>
-
<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="xjtu.com.test.MainActivity">
-
<TextView
-
android:id="@+id/tv"
-
android:layout_width="match_parent"
-
android:layout_height="match_parent"
-
android:textSize="50sp"
-
android:gravity="center"/>
-
</LinearLayout>
注意在Manifest文件中加入网络访问权限:
<uses-permission android:name="android.permission.INTERNET"/>
代码:
第一种方式AsyncTask:
-
package xjtu.com.test;
-
import android.os.AsyncTask;
-
import android.support.v7.app.AppCompatActivity;
-
import android.os.Bundle;
-
import android.widget.TextView;
-
import org.json.JSONObject;
-
import xjtu.com.utils.HttpUtils;
-
public class MainActivity extends AppCompatActivity {
-
private TextView tv;
-
@Override
-
protected void onCreate(Bundle savedInstanceState) {
-
super.onCreate(savedInstanceState);
-
setContentView(R.layout.activity_main);
-
tv=(TextView)findViewById(R.id.tv);
-
new GetData().execute();
-
}
-
class GetData extends AsyncTask<Void,Void,String>{
-
@Override
-
protected String doInBackground(Void... params) {
-
String json_str= HttpUtils.getJsonContent(HttpUtils.BaseUrl);
-
String result="";
-
try{
-
JSONObject js_obj=new JSONObject(json_str);
-
result=js_obj.getString("rand");
-
}catch (Exception e){
-
e.printStackTrace();
-
}
-
return result;
-
}
-
@Override
-
protected void onPostExecute(String s) {
-
tv.setText(s);
-
}
-
}
-
}
onPostExecute(String s)的参数s实际上是doInBackground方法的返回值result
结果截图:
第二种方式:Handler
-
package xjtu.com.test;
-
import android.os.Handler;
-
import android.os.Message;
-
import android.support.v7.app.AppCompatActivity;
-
import android.os.Bundle;
-
import android.widget.TextView;
-
import org.json.JSONObject;
-
import xjtu.com.utils.HttpUtils;
-
public class MainActivity extends AppCompatActivity {
-
private TextView tv;
-
private Thread thread;
-
private Handler handler;
-
@Override
-
protected void onCreate(Bundle savedInstanceState) {
-
super.onCreate(savedInstanceState);
-
setContentView(R.layout.activity_main);
-
tv=(TextView)findViewById(R.id.tv);
-
//实例化一个handler在主线程中,等待接收来自子线程的Message
-
handler=new Handler(){
-
@Override
-
public void handleMessage(Message msg) {
-
if (msg.what == 0x123){//信息记号
-
Bundle bundle=msg.getData();
-
tv.setText(bundle.getString("text"));
-
}
-
}
-
};
-
}
-
@Override
-
protected void onResume() {
-
super.onResume();
-
thread=new Thread(new Runnable() {
-
@Override
-
public void run() {
-
while(!Thread.currentThread().isInterrupted()) {
-
String json_str = HttpUtils.getJsonContent(HttpUtils.BaseUrl);
-
String result = "";
-
try {
-
JSONObject js_obj = new JSONObject(json_str);
-
result = js_obj.getString("rand");
-
//与主线程通信
-
Message message = new Message();
-
message.what = 0x123;
-
Bundle bundle = new Bundle();
-
bundle.putString("text", result);
-
message.setData(bundle);
-
handler.sendMessage(message);
-
//线程休眠1秒
-
Thread.sleep(1000);
-
} catch (Exception e) {
-
e.printStackTrace();
-
}
-
}
-
}
-
});
-
//开启新线程
-
thread.start();
-
}
-
@Override
-
protected void onStop() {
-
super.onStop();
-
//当前Activity终止时,阻塞线程
-
thread.interrupt();
-
}
-
}
这样得到的随机数每秒更新一次。
最后,以上代码中还用到了一个访问网络的封装类,代码如下:
-
package xjtu.com.utils;
-
import java.io.ByteArrayOutputStream;
-
import java.io.InputStream;
-
import java.net.HttpURLConnection;
-
import java.net.URL;
-
public class HttpUtils {
-
public static String BaseUrl = "http://172.16.0.10:8888/Test/servlet/TestServlet";
-
public static String getJsonContent(String path) {
-
try {
-
URL url = new URL(path);
-
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
-
connection.setConnectTimeout(3000);
-
connection.setRequestMethod("GET");
-
connection.setDoInput(true);
-
int code = connection.getResponseCode();
-
if (code == 200) {
-
return changeInputString(connection.getInputStream());
-
}
-
} catch (Exception e) {
-
e.printStackTrace();
-
}
-
return "";
-
}
-
private static String changeInputString(InputStream inputStream) {
-
String jsonString = "";
-
ByteArrayOutputStream outPutStream = new ByteArrayOutputStream();
-
byte[] data = new byte[1024];
-
int len = 0;
-
try {
-
while ((len = inputStream.read(data)) != -1) {
-
outPutStream.write(data, 0, len);
-
}
-
jsonString = new String(outPutStream.toByteArray());
-
} catch (Exception e) {
-
e.printStackTrace();
-
}
-
return jsonString;
-
}
-
}