首先应该明白两点:
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; -
} -
}