全面升级Android面试之Asynctask面试题集
来源:互联网 发布:淘宝欢乐逛 编辑:程序博客网 时间:2024/06/04 20:05
1.Asynctask究竟是什么?我们为什么要使用它?
我们知道,Android应用的主线程(UI 线程)肩负着绘制用户界面和及时响应用户操作的重任,为了避免“用户点击按钮后没反应”这样的糟糕用户体验,我们就要确保主线程时刻保持着较高的响应性。为了做到这一点,我们就要把耗时的任务移出主线程,那么耗时的任务交给谁来完成呢?答案就是工作者线程。Android开发中我们通常让主线程负责前台用户界面的绘制以及响应用户的操作,让工作者线程在后台执行一些比较耗时的任务。Android中的工作者线程主要有AsyncTask、IntentService、HandlerThread,它们本质上都是对线程或线程池的封装。 Android之所以提供AsyncTask这个类,就是为了方便我们在后台线程中执行操作,然后将结果发送给主线程,从而在主线程中进行UI更新等操作。
2.Asynctask有什么优缺点?
使用的优点: 简单快捷,过程可控
使用的缺点: 在使用多个异步操作和并需要进行Ui变更时,就变得复杂起来.
3.Asynctask的基本用法
在使用AsyncTask时,我们无需关注Thread和Handler,AsyncTask内部会对其进行管理,这样我们就只需要关注于我们的业务逻辑即可。
AsyncTask有四个重要的回调方法,分别是:onPreExecute、doInBackground, onProgressUpdate 和 onPostExecute。这四个方法会在AsyncTask的不同时期进行自动调用,我们只需要实现这几个方法的内部逻辑即可。这四个方法的一些参数和返回值都是基于泛型的,而且泛型的类型还不一样,所以在AsyncTask的使用中会遇到三种泛型参数:Params, Progress 和 Result,如下图所示:
1.Params表示用于AsyncTask执行任务的参数的类型
2.Progress表示在后台线程处理的过程中,可以阶段性地发布结果的数据类型
3.Result表示任务全部完成后所返回的数据类型
我们通过调用AsyncTask的execute()方法传入参数并执行任务,然后AsyncTask会依次调用以下四个方法:
onPreExecute
该方法的签名如下所示:
该方法有MainThread注解,表示该方法是运行在主线程中的。在AsyncTask执行了execute()方法后就会在UI线程上执行onPreExecute()方法,该方法在task真正执行前运行,我们通常可以在该方法中显示一个进度条,从而告知用户后台任务即将开始。
doInBackground
该方法的签名如下所示:
该方法有WorkerThread注解,表示该方法是运行在单独的工作线程中的,而不是运行在主线程中。doInBackground会在onPreExecute()方法执行完成后立即执行,该方法用于在工作线程中执行耗时任务,我们可以在该方法中编写我们需要在后台线程中运行的逻辑代码,由于是运行在工作线程中,所以该方法不会阻塞UI线程。该方法接收Params泛型参数,参数params是Params类型的不定长数组,该方法的返回值是Result泛型,由于doInBackgroud是抽象方法,我们在使用AsyncTask时必须重写该方法。在doInBackground中执行的任务可能要分解为好多步骤,每完成一步我们就可以通过调用AsyncTask的publishProgress(Progress…)将阶段性的处理结果发布出去,阶段性处理结果是Progress泛型类型。当调用了publishProgress方法后,处理结果会被传递到UI线程中,并在UI线程中回调onProgressUpdate方法,下面会详细介绍。根据我们的具体需要,我们可以在doInBackground中不调用publishProgress方法,当然也可以在该方法中多次调用publishProgress方法。doInBackgroud方法的返回值表示后台线程完成任务之后的结果。
onProgressUpdate
上面我们知道,当我们在doInBackground中调用publishProgress(Progress…)方法后,就会在UI线程上回调onProgressUpdate方法,该方法的方法签名如下所示:
该方法也具有MainThread注解,表示该方法是在主线程上被调用的,且传入的参数是Progress泛型定义的不定长数组。如果在doInBackground中多次调用了publishProgress方法,那么主线程就会多次回调onProgressUpdate方法。
onPostExecute
该方法的签名如下所示:
该方法也具有MainThread注解,表示该方法是在主线程中被调用的。当doInBackgroud方法执行完毕后,就表示任务完成了,doInBackgroud方法的返回值就会作为参数在主线程中传入到onPostExecute方法中,这样就可以在主线程中根据任务的执行结果更新UI。
下面我们就以下载多个文件的示例演示AsyncTask的使用过程。
布局文件如下所示:
<RelativeLayout 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" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <Button android:id="@+id/btnDownload" android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="onClick" android:text="开始下载" /> <TextView android:id="@+id/textView" android:layout_below="@id/btnDownload" android:layout_width="match_parent" android:layout_height="wrap_content" /></RelativeLayout>
界面上有一个“开始下载”的按钮,点击该按钮即可通过AsyncTask下载多个文件,对应的Java代码如下所示:
import android.app.Activity;import android.os.AsyncTask;import android.os.Build;import android.os.Bundle;import android.util.Log;import android.view.View;import android.widget.Button;import android.widget.TextView;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.InputStream;import java.net.HttpURLConnection;import java.net.MalformedURLException;import java.net.URL;public class MainActivity extends Activity implements Button.OnClickListener { TextView textView = null; Button btnDownload = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView)findViewById(R.id.textView); btnDownload = (Button)findViewById(R.id.btnDownload); Log.i("ljz", "MainActivity -> onCreate, Thread name: " + Thread.currentThread().getName()); } @Override public void onClick(View v) { //要下载的文件地址 String[] urls = { "http://blog.csdn.net/lijizhi19950123/article/details/77922527", "http://blog.csdn.net/lijizhi19950123/article/details/77920054", "http://blog.csdn.net/lijizhi19950123/article/details/77914601", "http://blog.csdn.net/lijizhi19950123/article/details/77914187", "http://blog.csdn.net/lijizhi19950123/article/details/77913957" }; DownloadTask downloadTask = new DownloadTask(); downloadTask.execute(urls); } //public abstract class AsyncTask<Params, Progress, Result> //在此例中,Params泛型是String类型,Progress泛型是Object类型,Result泛型是Long类型 private class DownloadTask extends AsyncTask<String, Object, Long> { @Override protected void onPreExecute() { Log.i("iSpring", "DownloadTask -> onPreExecute, Thread name: " + Thread.currentThread().getName()); super.onPreExecute(); btnDownload.setEnabled(false); textView.setText("开始下载..."); } @Override protected Long doInBackground(String... params) { Log.i("iSpring", "DownloadTask -> doInBackground, Thread name: " + Thread.currentThread().getName()); //totalByte表示所有下载的文件的总字节数 long totalByte = 0; //params是一个String数组 for(String url: params){ //遍历Url数组,依次下载对应的文件 Object[] result = downloadSingleFile(url); int byteCount = (int)result[0]; totalByte += byteCount; //在下载完一个文件之后,我们就把阶段性的处理结果发布出去 publishProgress(result); //如果AsyncTask被调用了cancel()方法,那么任务取消,跳出for循环 if(isCancelled()){ break; } } //将总共下载的字节数作为结果返回 return totalByte; } //下载文件后返回一个Object数组:下载文件的字节数以及下载的博客的名字 private Object[] downloadSingleFile(String str){ Object[] result = new Object[2]; int byteCount = 0; String blogName = ""; HttpURLConnection conn = null; try{ URL url = new URL(str); conn = (HttpURLConnection)url.openConnection(); InputStream is = conn.getInputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buf = new byte[1024]; int length = -1; while ((length = is.read(buf)) != -1) { baos.write(buf, 0, length); byteCount += length; } String respone = new String(baos.toByteArray(), "utf-8"); int startIndex = respone.indexOf("<title>"); if(startIndex > 0){ startIndex += 7; int endIndex = respone.indexOf("</title>"); if(endIndex > startIndex){ //解析出博客中的标题 blogName = respone.substring(startIndex, endIndex); } } }catch(MalformedURLException e){ e.printStackTrace(); }catch(IOException e){ e.printStackTrace(); }finally { if(conn != null){ conn.disconnect(); } } result[0] = byteCount; result[1] = blogName; return result; } @Override protected void onProgressUpdate(Object... values) { Log.i("iSpring", "DownloadTask -> onProgressUpdate, Thread name: " + Thread.currentThread().getName()); super.onProgressUpdate(values); int byteCount = (int)values[0]; String blogName = (String)values[1]; String text = textView.getText().toString(); text += "\n博客《" + blogName + "》下载完成,共" + byteCount + "字节"; textView.setText(text); } @Override protected void onPostExecute(Long aLong) { Log.i("iSpring", "DownloadTask -> onPostExecute, Thread name: " + Thread.currentThread().getName()); super.onPostExecute(aLong); String text = textView.getText().toString(); text += "\n全部下载完成,总共下载了" + aLong + "个字节"; textView.setText(text); btnDownload.setEnabled(true); } @Override protected void onCancelled() { Log.i("iSpring", "DownloadTask -> onCancelled, Thread name: " + Thread.currentThread().getName()); super.onCancelled(); textView.setText("取消下载"); btnDownload.setEnabled(true); } }}
下面对以上代码进行一下说明。
1.我们在MainActivity中定义了内部类DownloadTask,DownloadTask继承自AsyncTask,在该例中,Params泛型是String类型,Progress泛型是Object类型,Result泛型是Long类型。
2.我们定义了一个Url字符串数组,将该数组传递给AsyncTask的execute方法,用于异步执行task。
3.在执行了downloadTask.execute(urls)之后,AsyncTask会自动回调onPreExecute方法,在该方法中我们将textView设置为“开始下载…”几个字,告知用户即将执行下载操作。通过控制台输出我们也可以看出该方法是在主线程中执行的。
4.在执行了onPreExecute方法之后,AsyncTask会回调doInBackground方法,该方法中的输入参数是String类型的不定长数组,此处的String就对应着Params泛型类型,我们在该方法中遍历Url数组,依次下载对应的文件,当我们下载完一个文件,就相当于我们阶段性地完成了一部分任务,我们就通过调用publishProgress方法将阶段性处理结果发布出去。在此例中我们将阶段性的处理结果定义为Object类型,即Progress泛型类型。通过控制台输出我们可以看出doInBackground方法是运行在新的工作线程”AsyncTask #1”中的,AsyncTask的工作线程都是以”AsyncTask #”然后加上数字作为名字。当所有文件下载完成后,我们就可以通过totalSize返回所有下载的字节数,返回值类型为Long,对应着AsyncTask中的Result泛型类型。
5.在doInBackground方法中,每当下载完一个文件,我们就会调用publishProgress方法发布阶段性结果,之后AsyncTask会回调onProgressUpdate方法,在此例中,onProgressUpdate的参数为Object类型,对应着AsyncTask中的Progress泛型类型。通过控制台输出我们可以发现,该方法是在主线程中调用的,在该方法中我们会通过textView更新UI,告知用户哪个文件下载完成了,这样用户体验相对友好。
6.在整个doInBackground方法执行完毕后,AsyncTask就会回调onPostExecute方法,在该方法中我们再次通过textView更新UI告知用户全部下载任务完成了。
7.在通过execute方法执行了异步任务之后,可以通过AsyncTask的cancel方法取消任务,取消任务后AsyncTask会回调onCancelled方法,这样不会再调用onPostExecute方法。
8.在使用Android的过程中,有以下几点需要注意:
AsyncTask的实例必须在主线程中创建。
AsyncTask的execute方法必须在主线程中调用。
onPreExecute() onPostExecute(Result), doInBackground(Params…) 和 onProgressUpdate(Progress…)这四个方法都是回调方法,Android会自动调用,我们不应自己调用。
对于一个AsyncTack的实例,只能执行一次execute方法,在该实例上第二次执行execute方法时就会抛出异常。
4.Asynctask源码解析
可以参考这篇文章http://blog.csdn.net/iispring/article/details/50670388
- 全面升级Android面试之Asynctask面试题集
- 全面升级Android面试之Activity面试题集
- 全面升级Android面试之fragment面试题集
- 全面升级Android面试之Service面试题集
- 全面升级Android面试之Broadcast receiver面试题集
- 全面升级Android面试之Content Provider面试题集
- 全面升级Android面试之webview面试题集
- 全面升级Android面试之Binder面试题集
- 全面升级Android面试之handler面试题集
- 全面升级Android面试之HandlerThread面试题集
- 全面升级Android面试之IntentService面试题集
- 全面升级Android面试之ListView
- Android之AsyncTask面试
- 全面升级Android面试之事件分发机制
- 全面升级Android面试之View的绘制流程
- 全面升级Android面试之热更新问题
- 全面升级Android面试之组件化与插件化
- 全面升级Android面试之进程保活
- apache url重写 出现The requested URL was not found on this server的问题index.php路径加上index.php就正常的问题
- 常见的浏览器端的存储技术
- An internal error occurred during: "Launching House on Tomcat 7.x". java.lang.NullPointerException
- 资料位置保存
- Linux Shell脚本攻略2:命令之乐(1)
- 全面升级Android面试之Asynctask面试题集
- 送外卖 拓扑排序+状压DP+最短路
- LeetCode-19-Remove-Nth-Node-From-End-of-List 链表水题
- PAT乙级1070. 结绳(25)
- 《C#图解教程》第1章 C#和.NET框架 读书笔记
- InstallShield 软件打包完整教程
- 【Computer vision】摄像机矩阵的分解
- 51nod 1020 逆序排列【DP】
- STL中的队栈以及优先队列