Android AsyncTask 深度解析

Android 中耗时操作是在子线程中完成的,子线程不能更新UI,于是便出现了大家熟悉的异步消息处理机制Handler,Handler把在子线程中创建的Message对象发送到消息队列中,经由Looper轮询取出再交由Handler中handleMessage()方法处理。Handler机制完美解决了子线程中有数据却不能更新UI的问题,但是创建Handler对象、重写handleMessage()方法处理消息、创建子线程并创建Message对象显然有些复杂,为了代码更加简单、使用更加方便Android在API 3 推出了AsyncTask这一更简单、好用的异步操作工具。

其实AsyncTask使用的仍然是Handler机制,其内部封装了一个Handler的子类对象InternalHandler对象,关于源码部分后面介绍,下面介绍AsyncTask的使用:

AsyncTask是一个抽象类,其直接父类是Object,我们可以自定义一个类继承AsyncTask,重写其内部的抽象方法doInBackground(Params ... params);

AsyncTask 这个类有三个泛型限定,AsyncTask<Params ,Progress ,Result> 接下来会介绍到它们的作用。

AsyncTask之方法解析一:doInBackground(Params ... params)

1.在onPreExecute()方法执行结束之后该方法会被自动调用;

2.该方法在子线程中执行,通常做一些耗时操作;

3.该方法中的可变参数params是由AsyncTask子类对象调用execute(Params ... params)方法时传递而来,该参数的类型由AsyncTask类上的第一个泛型限定;

4.该方法的返回值将会传递给onPostExecute(Result ... result)方法作为参数使用,返回值类型由AsyncTask类上的第三个泛型限定;

5.在该方法中可以调用publishProgress(Progress ... progress)发布任务完成的进度,该进度值progress将会被发布到主线程onUpdateProgress(Progress ... progress)方法中,作为该方法的参数使用。

AsyncTask之方法解析二:onPreExecute()

1.该方法在主线程中执行;

2.该方法在任务开始前执行,通常用来初始化任务,比如向用户展示一个进度条。

AsyncTask之方法解析三:onPostExecute(Result ... result)

1.该方法在主线程中执行;

2.该方法在耗时操作完成后执行,即在doInBackground(Params ... params)方法结束后执行;

3.该方法的参数result是耗时操作返回的结果,即doInBackground(Params ... params)返回的结果,参数类型由AsyncTask类上的第三个泛型限定;

AsyncTask之方法解析四:onProgressUpdate(Progress ... progress)

1.该方法在主线程中执行;

2.该方法在publishProgress(Progress... progress)每次调用后执行,通常用来向用户展示耗时操作的进度;

3.该方法的参数progress是耗时操作的进度,由publishProgress(Progress ... progress)传递而来,参数类型由AsyncTask类上的第二个泛型限定;

了解完以上方法之后,我们就可以开始写个小demo测试一下啦!

public class MainActivity extends AppCompatActivity {
    public static final String TAG = "MainActivity";
    private MainActivity context;
    private ProgressDialog mDialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        context = MainActivity.this;
        MyAsyncTask task = new MyAsyncTask();
        task.execute(10);//对应类中第一个泛型Integer
    }

    /**
     * 求和异步任务
     */
    class MyAsyncTask extends AsyncTask<Integer, Integer, Long> {

        /**
         * 初始化任务,展示进度条,在主线程
         */
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            Log.d(TAG, "--- onPreExecute --- " + Thread.currentThread().getName());
            mDialog = new ProgressDialog(context);//创建一个进度对话框
            mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
            mDialog.show();
        }

        /**
         * 在子线程执行耗时操作,通过publishProgress(Progress ... progress)方法将任务执行的进度传出
         * @param params 该参数由task.execute(10);传递而来
         * @return
         */
        @Override
        protected Long doInBackground(Integer... params) {
            long sum = 0;
            for (int i = 1; i < params[0]; i++) {
                sum += i;
                publishProgress(params[0], i);//发布进度,参数将传递到onProgressUpdate方法中
                try {
                    Thread.sleep(1000);
                    Log.d(TAG, "--- doInBackground --- " + Thread.currentThread().getName() + "  i == " + i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return sum;
        }

        /**
         * 执行在主线程,向用户展示任务的进度
         *
         * @param values 该参数由publishProgress(Progress ... progress)传递而来
         */
        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            mDialog.setMax(values[0]);
            mDialog.setProgress(values[1]);
        }

        /**
         * 执行在主线程,任务执行完成后的操作,隐藏进度条等
         *
         * @param aLong 该参数由doInBackground()返回
         */
        @Override
        protected void onPostExecute(Long aLong) {
            super.onPostExecute(aLong);
            //返回操作结果 , 进度对话框消失
            mDialog.dismiss();
            Log.d(TAG, "--- onPostExecute --- " + Thread.currentThread().getName() + "  aLong == " + aLong);
        }
    }
}

这个小demo通过求1-10的和,在doInBackground()方法中通过延时的方式模拟了耗时操作,简单的演示了AsyncTask的使用。
Log日志如下:

02-17 09:40:32.214 8207-8207/com.qj.asynctasktest D/MainActivity: --- onPreExecute --- main
02-17 09:40:33.270 8207-8246/com.qj.asynctasktest D/MainActivity: --- doInBackground --- AsyncTask #1  i == 1
02-17 09:40:34.273 8207-8246/com.qj.asynctasktest D/MainActivity: --- doInBackground --- AsyncTask #1  i == 2
02-17 09:40:35.277 8207-8246/com.qj.asynctasktest D/MainActivity: --- doInBackground --- AsyncTask #1  i == 3
02-17 09:40:36.279 8207-8246/com.qj.asynctasktest D/MainActivity: --- doInBackground --- AsyncTask #1  i == 4
02-17 09:40:37.280 8207-8246/com.qj.asynctasktest D/MainActivity: --- doInBackground --- AsyncTask #1  i == 5
02-17 09:40:38.281 8207-8246/com.qj.asynctasktest D/MainActivity: --- doInBackground --- AsyncTask #1  i == 6
02-17 09:40:39.282 8207-8246/com.qj.asynctasktest D/MainActivity: --- doInBackground --- AsyncTask #1  i == 7
02-17 09:40:40.284 8207-8246/com.qj.asynctasktest D/MainActivity: --- doInBackground --- AsyncTask #1  i == 8
02-17 09:40:41.286 8207-8246/com.qj.asynctasktest D/MainActivity: --- doInBackground --- AsyncTask #1  i == 9
02-17 09:40:42.288 8207-8246/com.qj.asynctasktest D/MainActivity: --- doInBackground --- AsyncTask #1  i == 10
02-17 09:40:42.310 8207-8207/com.qj.asynctasktest D/MainActivity: --- onPostExecute --- main  aLong == 55

由Log可看出:

onPreExecute()和onPostExecute()方法都执行在主线程,onProgressUpdate()能够更改UI(更改进度条进度)必然也是主线程。

doInBackground()方法执行在线程池某一线程中,(看过源码后得到的结果,通过Log可以知道该方法没有执行在主线程中)

AsyncTaskTest源码下载

AsyncTask使用中的注意事项:

1. AsyncTask实例对象必须在UI线程中创建;

2. execute()方法必须在UI线程中调用;

3. 不要手动调用onPreExecute()、doInbackground()、onProgressUpdate()、onPostExecute()这四个方法;

4. 同一个task任务只能被执行一次,尝试执行第二次会抛出异常;

5. execute()执行任务默认是串行的,(API 11之后)多个AsyncTask任务是顺序执行的,如果想并行可以用executeOnExecutor()方法。AsyncTask中为我们提供了一个并行的线程池THREAD_POOL_EXECUTOR。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值