异步请求的基本实现

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);
    }
}

posted @ 2016-06-30 15:19  六月的海  阅读(246)  评论(0编辑  收藏  举报