在实际应用中经常会遇到比较耗时任务的处理,比如网络连接,数据库操作等情况时,如果这些操作都是放在主线程(UI线程)中,则会造成UI的假死现象,Android中可以使用AsyncTask和Handler两种异步方式来解决这种问题。
AsyncTask(异步任务处理)
在使用AsyncTask时处理类需要继承AsyncTask,提供三个泛型参数,并且重载AsyncTask的四个方法(至少重载一个)。
An
asynchronous task is defined by a computation that runs on a background thread and whose result is published on the UI thread. An asynchronous task is defined by 3 generic types, called Params
, Progress
and Result
,
and 4 steps, called onPreExecute
, doInBackground
, onProgressUpdate
and onPostExecute
.
三个泛型参数
1.Param 任务执行器需要的数据类型
2.Progress 后台计算中使用的进度单位数据类型
3.Result 后台计算返回结果的数据类型
在设置参数时通常是这样的:String... params,这表示方法可以有0个或多个此类型参数;有时参数可以设置为不使用,用Void...即可。
四个方法
1.onPreExecute() 执行预处理,它运行于UI线程,可以为后台任务做一些准备工作,比如绘制一个进度条控件。
2.doInBackground(Params...) 后台进程执行的具体计算在这里实现,doInBackground(Params...)是AsyncTask的关键,此方法必须重载。在这个方法内可以使用publishProgress(Progress...)改变当前的进度值。
3.onProgressUpdate(Progress...) 运行于UI线程。如果在doInBackground(Params...) 中使用了publishProgress(Progress...),就会触发这个方法。在这里可以对进度条控件根据进度值做出具体的响应。
4.onPostExecute(Result) 运行于UI线程,可以对后台任务的结果做出处理,结果就是doInBackground(Params...)的返回值。此方法也要经常重载,如果Result为null表明后台任务没有完成(被取消或者出现异常)。
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> { protected Long doInBackground(URL... urls) { int count = urls.length; long totalSize = 0; for (int i = 0; i < count; i++) { totalSize += Downloader.downloadFile(urls[i]); publishProgress((int) ((i / (float) count) * 100)); // Escape early if cancel() is called if (isCancelled()) break; } return totalSize; } protected void onProgressUpdate(Integer... progress) { setProgressPercent(progress[0]); } protected void onPostExecute(Long result) { showDialog("Downloaded " + result + " bytes"); } }
执行代码:
new DownloadFilesTask().execute(url1, url2, url3);
将android的POST方法放入doInBackground(URL... url)
// Post方式请求 public static void requestByPost() throws Throwable { String path = "https://reg.163.com/logins.jsp"; // 请求的参数转换为byte数组 String params = "id=" + URLEncoder.encode("helloworld", "UTF-8") + "&pwd=" + URLEncoder.encode("android", "UTF-8"); byte[] postData = params.getBytes(); // 新建一个URL对象 URL url = new URL(path); // 打开一个HttpURLConnection连接 HttpURLConnection urlConn = (HttpURLConnection) url.openConnection(); // 设置连接超时时间 urlConn.setConnectTimeout(5 * 1000); // Post请求必须设置允许输出 urlConn.setDoOutput(true); // Post请求不能使用缓存 urlConn.setUseCaches(false); // 设置为Post请求 urlConn.setRequestMethod("POST"); urlConn.setInstanceFollowRedirects(true); // 配置请求Content-Type urlConn.setRequestProperty("Content-Type", "application/x-www-form-urlencode"); // 开始连接 urlConn.connect(); // 发送请求参数 DataOutputStream dos = new DataOutputStream(urlConn.getOutputStream()); dos.write(postData); dos.flush(); dos.close(); // 判断请求是否成功 if (urlConn.getResponseCode() == HTTP_200) { // 获取返回的数据 byte[] data = readStream(urlConn.getInputStream()); Log.i(TAG_POST, "Post请求方式成功,返回数据如下:"); Log.i(TAG_POST, new String(data, "UTF-8")); } else { Log.i(TAG_POST, "Post方式请求失败"); } }
//***************************************************分割*************************************************************
在使用if(HTTP_200 == urlConn.getResponseCode())我在考虑getResponseCode()是不是一个阻塞方法(这里一定是,不然网络会一直失败),如果是阻塞那么会维持多久,多久后会给你网络代码编号?
这就很奇怪了,在URLConnection.java并没有表现出来是阻塞方法,但是其中httpURLConnect.java和URLConnection.java都是抽象类(abstract类)当我们调用抽象类的方法,经过查阅资料stackoverflow,发现具体的实现机制在底层sun.net.www.protocol.http.HttpURLConnection.。
这里我猜想java.net.HttpURLConnection和sun.net.www.protocol.http.HttpURLConnection存在继承关系,java.net.HttpURLConnection继承了java.net.URLConnection.java抽象方法,同时java.net.HttpURLConnection自己也是抽象类,一些实体实现对应到sun.net.www.protocol.http.HttpURLConnection中(这里sun.net.www.protocol.http.HttpURLConnection继承java.net.HttpURLConnection),在我们使用对应方法的时候就会调用实现实体(也可以说是重写后的方法)。
getResponseCode()的阻塞实现也就找到了,在getInputStream()中。