Android进阶——性能优化之尽量多使用AsyncTask进行短时间网络通信

来源:互联网 发布:学编程需要什么基础 编辑:程序博客网 时间:2024/06/06 08:35

引言

对于我们Android 开发来说,网络操作应该是最普遍不过的操作了吧,因为没有网络操作的APP应该就没有存在的价值吧,往往网络操作这部分又通常是耗时的,所以为了良好的用户体验,我们必须把耗时操作放到非UI线程,而实现方式有很多种,比较常见的应该就是Handler+Thread 和AsyncTask这两种了吧,这两种方式各有利弊,尤其是涉及到与UI交互的时候使用起来更得注意额外处理一些逻辑,但是从一定程度上AsyncTask使用起来更简捷方便些。

一、异步任务AsyncTask概述

总所周知,在Android程序开始运行的时候会单独启动一个进程,默认情况下所有这个程序操作都在这个进程中进行。一个Android程序默认情况下只有一个进程,但一个进程中可以有多个线程,其中最重要的一个线程叫做UI线程(也叫Main Thread),除了UI线程外的线程都叫子线程(Worker Thread)。UI线程主要负责控制UI界面的显示、更新、交互、组件的创建等。因此,UI线程中的操作延迟越短越好(流畅)。把一些耗时的操作(网络请求、数据库操作、逻辑计算等)放到单独的线程,可以避免主线程阻塞。在API 3之前,我们都是使用Thread+Handler的方式进行非UI和UI交互,API 3的时候 Android给我们提供了一种轻量级的异步任务类AsyncTask,它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线中更新UI。AsyncTask封装了Thread和Handler,通过AsyncTask可以更加方便地执行后台任务以及在主线程中访问UI,但是AsyncTask并不适合进行特别耗时的后台任务,对于特别耗时的后台任务,建议使用线程池。因为AsyncTask的设计是围绕线程和处理器的一个辅助类,并不构成一个通用的线程框架。Asynctasks应该用于短作业(最多几秒钟)如果你需要保持线程运行很长一段时间,我们强烈建议您使用不同的java.util.concurrent包如执行API提供的线程池和Futuretask。

二、AsyncTask原型

Asynctask是一个抽象的泛型类,它提供了Params,Progress和Result这三个泛型参数,其中Params表示参数的类型,Progress表示后台任务执行进度的类型,而Result则表示后台任务返回数据的类型,如果AsyncTask确实不需要传递具体的参数,那么这三个泛型参数可以用Void来代替。该类中实现异步操作,并提供接口反馈当前异步执行结果及进度,这些接口中有直接运行在主线程中的(如 onPostExecute,onPreExecute等)。

public abstract class AsyncTask<Params, Progress, Result>{    public AsyncTask() {        mWorker = new WorkerRunnable<Params, Result>() {            public Result call() throws Exception {                mTaskInvoked.set(true);                Result result = null;                try {                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);                    //noinspection unchecked                    result = doInBackground(mParams);                    Binder.flushPendingCommands();                } catch (Throwable tr) {                    mCancelled.set(true);                    throw tr;                } finally {                    postResult(result);                }                return result;            }        };        mFuture = new FutureTask<Result>(mWorker) {            @Override            protected void done() {                try {                    postResultIfNotInvoked(get());                } catch (InterruptedException e) {                    android.util.Log.w(LOG_TAG, e);                } catch (ExecutionException e) {                    throw new RuntimeException("An error occurred while executing doInBackground()",                            e.getCause());                } catch (CancellationException e) {                    postResultIfNotInvoked(null);                }            }        };    }    ...}
  • Params:传递给AsyncTask的类型,作为doInBackground的参数类型
  • Progress:作为onProgressUpdate和publishProgress的参数类型
  • Result:由doInBackground返回,作为onPostExecute的参数类型

三、AsyncTask的执行流程

  • onPreExecute():在主线程中执行,一般在异步任务执行之前被调用,可以用于做些UI初始化工作。比如弹出进度加载框

  • doInBackground(Params… params):在线程池中执行用于执行异步任务,params参数表示异步任务输入的参数(源自执行异步任务调用execute(Params… params)方法时候传入)。另外通常在此方法中通过调用publishProgress方法来更新任务的进度(publishProgress方法会调用onProgressUpdata方法),而且还会将计算结果返回到onPostExecute方法中。

  • onProgressUpdata(Progress…values):在主线程中执行,当后台任务的执行进度发生改变时此方法会被调用。

  • onPostExecute(Result result):在主线程中执行,在一部任务执行之后,此方法会被调用,其中result参数是后台任务的返回值,即doInBackground的返回值。比如可以利用返回的结果更新UI,或者关闭进度条对话框等。

这里写图片描述
一般来说基本流程是 onPreExecute先执行,接着是doInBackground,最后才是onPostExecute。除了上述四个方法以外,AsyncTask还提供了onCancelled()方法,他同样是在主线程中执行,当异步任务被取消时,onCancelld()方法会被调用,这时候onPostExecute则不会被调用。

四、AsyncTask的使用步骤

1、继承AsyncTask实现onPreExecute、doInBackground、onProgressUpdata、onPostExecute方法,并指定要传入的相应参数类型

    /**    *从自定义异步任务的原型可以得知,这个异步任务是需要在doInBackground里执行一个需要传入N个String类型作为形参的方法    */    protected class SendHttpTask extends AsyncTask<String, Void, Void> {        @Override        protected void onPreExecute() {            LogUtil.showLog("",false);            super.onPreExecute();        }        @Override        protected void onPostExecute(Void aVoid) {            LogUtil.showLog("",false);            super.onPostExecute(aVoid);        }        @Override        protected void onProgressUpdate(Void... values) {            LogUtil.showLog("",false);            super.onProgressUpdate(values);        }        @Override        protected Void doInBackground(String... params) {            LogUtil.showLog("",false);            String type = params[0];//传入的第一个参数            String content = params[1];//传入的第二个参数            sendHttp.sendHttp(type, content);            return null;        }    }

2、直接new 创建对象并通过调用execute(Params… params)方法执行异步任务,其中参数为泛型不定参数,类型不定,参数个数不定,取决于我们创建自定义AsyncTask时候传入的参数类型,而且execute方法返回的是AsyncTask对象

private SendHttpTask sendCommonTask;/**错误示范 if (sendCommonTask == null) {     sendCommonTask = new SendHttpTask(); }    sendCommonTask.execute("0", idEdtQuestion.getText().toString().trim());*/sendCommonTask= (SendHttpTask) new SendHttpTask().execute("0",idEdtQuestion.getText().toString().trim());

五、AsyncTask在使用的过程中的注意事项

  • AsyncTask的类必须是在主线程中加载,这就意味着第一次访问AsyncTask必须发生在主线程,当然这个过程在Android 4.1及以上版本已经被系统自动完成了。在Android 5.0的源码中,可以查看ActivityThread的mian方法,它会调用AsyncTask的init方法,这就满足了AsyncTask的类必须在主线程中进行加载这个条件了。

  • AsyncTask的对象必须在主程中创建。

  • execute方法必须在UI线程调用。

  • 不要在程序中手动去调用onpreExecute(),doInBackground, onProgressUpdata和onPostExecute方法,这些方法是由系统自动调用的。

  • 一个AsyncTask对象只能执行一次,因为本质也是Thread嘛,即同一实例只能调用一次execute方法,否则会报运行时异常,比如楼上的错误示范。

  • 在Android 1.6之前,AsyncTask是串行执行任务的,Android 1.6的时候AsyncTask开始采用线程池来处理并行任务,但是从Android 3.0开始,为了避免AsyncTask所带来的并发错误,AsyncTask有采用一个线程来串行执行任务。尽管如此,在Android 3.0之后的版本,我们仍可以通过AsyncTask的executeOnExecutor方法来并行执行认为。

  • 运行中可以随时调用cancel(boolean)方法取消任务,如果成功调用isCancel()会返回true,并不会执行onPostExecute(),取而代之的是调用onCancelled()。从源码看,如果这个任务已经执行了这个时候调用cancel是不会真正的把task结束,而是继续执行,只不过改变的是执行之后的回调方法的onPostExecute还是onCancelled.

  • 当异步任务中涉及到UI操作的时候,需要注意谨防内存溢出的问题,一般可遵循以下规范,如果作用于长时间的任务,且是内部类,那么保存了Context或者Activity的引用,会导致相关资源不会被GC及时回收有可能导致溢出,所以第一,在Activity生命周期结束前,可以通过调用cancel方法取消AsyncTask,第二,如果一定要写成内部类的形式,对Context采用弱引用WeakRefrence,在使用之前判断是否为空。

原创粉丝点击