UI 进程的限制
对于一个合格的安卓应用来说,流畅的界面操作是必不可少的。但是实际的应用会有不少的IO操作,例如更新数据库,访问本地文件或者网络,这些都需要消耗不少时间。如果界面线程和这些IO混合在一起,就会拖慢界面。这个时候,就需要将这些耗时的工作剥离,转由其他的异步线程来处理。
异步和同步
java 程序是一步一步的执行的,比如有三个任务,task1, task2,task3,默认情况下,这三个任务按顺序执行。
task1.run();
task2.run();
task3.run();
task2必须等待task1执行完毕后才能开始,task3同理。假设task1是一个IO类的任务,比如访问远端API,那么在请求发出和收到回复之间的时间, cpu是空闲的。这就造成了资源的浪费。
有一种办法,可以让这段空闲的时间做其他的事情,这就是异步线程。其实也就是再启动一个线程, java 中可以用 thread类 或者 实现 runnable接口来实现一个线程。android 中不必这么做,sdk 提供了更方便的实现。那就是 AsyncTask。
安卓中的简单实现
AsyncTask,即异步任务。只需要继承并重写几个生命周期方法,就可以实现一个异步线程。
要点
AsyncTask 的要点有两部分
-
生命周期方法
有三个重要的方法, 按照执行的时机先后,依次是 onPostExecute, doInBackground, onPostExecute,分别表异步任务执行前的工作,待执行任务,和执行结束后的工作
onProgressUpdate 和 publishProgress 支持自定义进度。在 doInBackground 中对具体业务进度进行分析后,得出一个进度值(类型自定),通过 publishProgress 发布进度,onProgressUpdate
得到执行(参数就是刚才发布的进度值)。 -
更新UI
大部分时候,task 进行中和结束后会更新UI,比如进度条,可能是更新一个结果列表(listView)。那么,如何更新呢? 在 activity 中自然是可以findViewById,然后更新。但是 task 中没有这样的context。目前知道的办法是,创建 task 的时候,将这个 listView 作为构造方法的参数传递进去,然后就可以将它存储为 task 的成员变量,进而在任务结束后操作。
基本结构
下面的类用于遍历获取目录中所有文件的数目
//定义类的时候指定了线程相关的3个参数的类型,分别是启动参数,进度参数,返回值
//File 是执行execute方法时的参数类型, 第一个 Integer 是进度的参数类型,第二个 Integer 是后台线程返回数据的类型,即 doInBackground 的返回值类型
public class FileCrawler extends AsyncTask<File, Integer, Integer> {
@Override
// params 就是下面执行execute的时候传入的参数 directoryPath
protected Long doInBackground(File ...params) {
//这里执行一些耗时的代码
}
@Override
//categories 是 doInBackground 返回的结果
protected void onPostExecute(Integer sum){
//异步线程结束后,执行这里。通常可以在这里更新UI
}
}
可以直接在activity的onCreate中调用
new FileCrawler(getBaseContext()).execute(directoryPath);
例子
文件遍历是一个比较复杂的问题,尤其对于大量的文件,直接用递归的话可能会出现内存溢出问题。apache 的 common io 是一个不错的工具包,有很多io相关的工具类。这里使用其中的 FileUtils来遍历文件。
FileCrawler.java
/**
* Created by wangpi on 6/30/2016.
*/
public class FileCrawler extends AsyncTask<File, Integer, Integer>{
private TextView view;
public FileCrawler(TextView view ){
this.view = view;
}
@Override
protected Integer doInBackground(File... folders) {
Collection<File> files = null;
for(File folder : folders){
if(folder.exists() && folder.isDirectory()){
if(files == null) {
files = FileUtils.listFiles(folder, null, true);
}else{
files.addAll(FileUtils.listFiles(folder, null, true));
}
}
}
if(files != null){
return files.size();
}else{
return 0;
}
}
@Override
protected void onPostExecute(Integer sum){
view.setText("File amout : " + sum);
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
File[] folders = {Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)};
new FileCrawler((TextView)findViewById(R.id.info)).execute(folders);
}
}