美文网首页
六、Android 异步处理技术

六、Android 异步处理技术

作者: 锦文豪武 | 来源:发表于2018-09-29 17:51 被阅读0次

    移动应用开发要求我们正确的处理好主线程和子线程之间的关系,耗时的操作应该放到子线程中,避免阻塞主线程,导致ANR。异步处理技术是提高应用性能,解决主线程和子线程通信问题的关键。
    在Android中,异步处理技术有很多种,常见的有Thread、AsyncTask、Handler、Looper、Executors等。

    一个完整的异步处理技术继承树: 微信图片_20180929164441.png

    A、Thread(线程)

    线程是Java语言的一个概念,他实际执行任务的基本单元,上图可以看出Thread是Android 中异步处理技术的基础。

    (1)创建线程的两种方式:

    a、继承Thread类并重写run方法

    public class MyThread extends Thread {
    @Override
    public void run() {
        super.run();
        /**实现业务逻辑,文件读取,网络请求等*/
      }
    }
       /**
        * 启动Thread
        */
    public void startThread(){
    MyThread myThread = new MyThread();
    /**使用start启动线程*/
    myThread.start();
    }
    

    b、实现Runable 接口并实现run方法

    public class MyRunable implements Runnable {
    @Override
    public void run() {
        /**实现具体业务逻辑,文件读写,网络请求*/
      }
    }
     /***
      * 启动Runnable
     */
    public void startRunble(){
         new Thread(new MyRunable()).start();
    } 
    

    c、线程三种类型

    Android应用中各类的线程本质上都基于Linux系统的pthreads,在应用层可以分为三种类型的线程

    (1)主线程(mainThread)

    主线程又叫ui线程,随应用的启动而启动,主线程是用来运行Android 组件,同时刷新屏幕上的UI元素。Android 系统如果检测到非线程更新UI组件,那么就会抛出CalledFromWrongThreadException异常,只有主线程才能操作UI,是因为Android的UI工具包不是线程安全.主线程创建的handler会顺序执行接收到的消息,包括从其他线程发送的消息。因此,如果消息队列中前面的消息没有执行完,那么它可能会阻塞队列中的其他消息的及时处理。

    (2)Binder线程

    Binder线程用于不同进程之间线程的通信,每个线程都维护了一个线程池,用来处理其他进程中线程发送的消息,这些进程包括系统服务、Intents、ContentProvider和Service等。在大部分情况下,应用不需要关心Binder线程,因为系统会优先将请求转换为使用主线程。一个典型的需要使用Binder 线程的场景是应用提供一个给其他进程通过AIDL接口绑定的Service.

    (3)后台线程

    在应用中显示创建的线程都是后台线程,也就是当刚创建出来时,这些线程的执行体是空的,需要手动添加任务。在Linux系统层面,主线程和后台线程是一样的,在Android 框架中,通过WindowManager赋予了主线程只能处理UI更新以及后台线程不能直接操作UI的限制。

    B、HandlerThread(继承了Thread)

    HandlerThread 是个集成了Looper和MessageQueue的线程,当启动HandlerThread时,会同时生成Looper和MessageQueue,然后等待消息进行处理,run方法如下:

    @Override
        public void run() {
       mTid = Process.myTid();
       Looper.prepare();
    synchronized (this) {
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
    }
    

    好处就是我们不需要自己去创建和维护Looper,用法和普通线程一样,语句如下:

       /**
        * HandlerThread
        */
    public void startHandlerThread(){
        HandlerThread handlerThread = new HandlerThread("HandlerThread");
        handlerThread.start();
    new Handler(handlerThread.getLooper()){
        @Override
          public void handleMessage(Message msg) {
            super.handleMessage(msg);
            /**处理收到的消息*/
            }
     };
    }
       /**
        * HandlerThread
       */
    public void startHandlerThread(){
          HandlerThread handlerThread = new HandlerThread("HandlerThread");
         handlerThread.start();
         new Handler(handlerThread.getLooper()){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            /**处理收到的消息*/
             }
        };
    }
    

    HandlerThread 中只有一个队列消息,队列中的教习是顺序执行的,因此是线程安全的,当然吞吐量自然受到一定的影响,队列中的任务可能会被前面没有执行完的任务阻塞。HandlerThread内部机制确保了在创建Looper和发送消息之间不存竞态条件,这个通过将HandlerThread.getLooper()实现为一个阻塞操作实现的,只有当HandlerThread准备好接受消息之后曹辉返回,源码如下:

     * This method returns the Looper associated with this thread. If this thread not  been   started
      * or for any reason isAlive() returns false, this method will return null. If this thread
      * has been started, this method will block until the looper has been initialized.  
     * @return The looper.
      */
      public Looper getLooper() {
    if (!isAlive()) {
        return null;
    }
    
    // If the thread has been started, wait until the looper has been created.
    synchronized (this) {
        while (isAlive() && mLooper == null) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }
    }
    return mLooper;
    }
    

    如果具体业务要求在HandlerThread 开始接收消息之前要进行某些初始化的操作的话,可以重写HandlerThread的onLooperPrepared函数,例如可以在这个函数中创建与HandlerThread关联的Handler实例,这同时也可以对外隐藏我们的Handler实例,语句:

     import android.os.Handler;
     import android.os.HandlerThread;
     import android.os.Message;
     import android.os.Process;
    
     /**
      * Created by chaohao.zhao on 2018/9/25.
       */
    
    public class MyHandlerThread extends HandlerThread {
    private Handler handler;
    public MyHandlerThread() {
        /**Process.THREAD_PRIORITY_BACKGROUND:后台线程建议设置这个优先级,值为10。*/
       super("MyHandlerThread", Process.THREAD_PRIORITY_BACKGROUND);
    }
    
       @Override
       public void run() {
           super.run();
    }
    
      @Override
      protected void onLooperPrepared() {
        super.onLooperPrepared();
        handler = new Handler(getLooper())
        {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what){
                    case 1:
                        break;
                    case 2:
                        break;
                }
            }
        };
    }
    public void method1(){
        handler.sendEmptyMessage(1);
    }
       public void method(){
        handler.sendEmptyMessage(2);
       }
    }
    

    C、AsyncQueryHandler(继承Handler)

    AsyncQueryHandler是用于在ContentProvider上面执行异步的CRUD(Create,Read,Update,Delete)操作工具类,CRUD操作会被放到一个单独的子线程中执行,当操作结束获取到结果后,将通过消息的方式传递给调用AsyncQueryHandler的线程,通常就是主线程,AtyncQueryHandler是一个抽象类,继承Handler,通过封装ContentResolver、HandlerThread、Handler等实现对ContentProvider的异步操作,

    原理图:


    微信图片_20180929165334.png

    (1)AsyncQueryHandler四个操作方法

    AsyncQueryHandler封装了四个方法来操作ContentProvider,分别对应上面说到的CRUD操作。

    a、Delete:

     /**
      * This method begins an asynchronous delete. When the delete operation is
      * done {@link #onDeleteComplete} is called.
      *
      * @param token A token passed into {@link #onDeleteComplete} to identify
      *  the delete operation.
      * @param cookie An object that gets passed into {@link #onDeleteComplete}
      * @param uri the Uri passed to the delete operation.
      * @param selection the where clause.
      */
     public final void startDelete(int token, Object cookie, Uri uri,
        String selection, String[] selectionArgs) {
    // Use the token as what so cancelOperations works properly
    Message msg = mWorkerThreadHandler.obtainMessage(token);
    msg.arg1 = EVENT_ARG_DELETE;
    
    WorkerArgs args = new WorkerArgs();
    args.handler = this;
    args.uri = uri;
    args.cookie = cookie;
    args.selection = selection;
    args.selectionArgs = selectionArgs;
    msg.obj = args;
    
    mWorkerThreadHandler.sendMessage(msg);
    }
    

    b、Update:

     /**
      * This method begins an asynchronous update. When the update operation is
      * done {@link #onUpdateComplete} is called.
      *
      * @param token A token passed into {@link #onUpdateComplete} to identify
      *  the update operation.
      * @param cookie An object that gets passed into {@link #onUpdateComplete}
      * @param uri the Uri passed to the update operation.
      * @param values the ContentValues parameter passed to the update operation.
      */
     public final void startUpdate(int token, Object cookie, Uri uri,
        ContentValues values, String selection, String[] selectionArgs) {
    // Use the token as what so cancelOperations works properly
    Message msg = mWorkerThreadHandler.obtainMessage(token);
    msg.arg1 = EVENT_ARG_UPDATE;
    
    WorkerArgs args = new WorkerArgs();
    args.handler = this;
    args.uri = uri;
    args.cookie = cookie;
    args.values = values;
    args.selection = selection;
    args.selectionArgs = selectionArgs;
    msg.obj = args;
    
    mWorkerThreadHandler.sendMessage(msg);
    }
    

    c、Read(Query)

     /**
      * This method begins an asynchronous query. When the query is done
      * {@link #onQueryComplete} is called.
      *
      * @param token A token passed into {@link #onQueryComplete} to identify
      *  the query.
      * @param cookie An object that gets passed into {@link #onQueryComplete}
      * @param uri The URI, using the content:// scheme, for the content to
      *         retrieve.
      * @param projection A list of which columns to return. Passing null will
      *         return all columns, which is discouraged to prevent reading data
      *         from storage that isn't going to be used.
      * @param selection A filter declaring which rows to return, formatted as an
      *         SQL WHERE clause (excluding the WHERE itself). Passing null will
      *         return all rows for the given URI.
      * @param selectionArgs You may include ?s in selection, which will be
      *         replaced by the values from selectionArgs, in the order that they
      *         appear in the selection. The values will be bound as Strings.
      * @param orderBy How to order the rows, formatted as an SQL ORDER BY
      *         clause (excluding the ORDER BY itself). Passing null will use the
      *         default sort order, which may be unordered.
      */
      public void startQuery(int token, Object cookie, Uri uri,
        String[] projection, String selection, String[] selectionArgs,
        String orderBy) {
    // Use the token as what so cancelOperations works properly
    Message msg = mWorkerThreadHandler.obtainMessage(token);
    msg.arg1 = EVENT_ARG_QUERY;
    
    WorkerArgs args = new WorkerArgs();
    args.handler = this;
    args.uri = uri;
    args.projection = projection;
    args.selection = selection;
    args.selectionArgs = selectionArgs;
    args.orderBy = orderBy;
    args.cookie = cookie;
    msg.obj = args;
    
    mWorkerThreadHandler.sendMessage(msg);
    }
    

    d、Create(Insert)

     /**
      * This method begins an asynchronous insert. When the insert operation is
      * done {@link #onInsertComplete} is called.
      *
      * @param token A token passed into {@link #onInsertComplete} to identify
      *  the insert operation.
      * @param cookie An object that gets passed into {@link #onInsertComplete}
      * @param uri the Uri passed to the insert operation.
      * @param initialValues the ContentValues parameter passed to the insert operation.
      */
     public final void startInsert(int token, Object cookie, Uri uri,
        ContentValues initialValues) {
    // Use the token as what so cancelOperations works properly
    Message msg = mWorkerThreadHandler.obtainMessage(token);
    msg.arg1 = EVENT_ARG_INSERT;
    
    WorkerArgs args = new WorkerArgs();
    args.handler = this;
    args.uri = uri;
    args.cookie = cookie;
    args.values = initialValues;
    msg.obj = args;
    
    mWorkerThreadHandler.sendMessage(msg);
    }
    

    AsyncQueryHandler的子类可以根据实际需求实现下面的回调函数,从而得到上面的操作的返回结果。

     /**
      * Created by chaohao.zhao on 2018/9/25.
      *
      */
    
     public class MyAsyncQueryHandler extends AsyncQueryHandler{
    public MyAsyncQueryHandler(ContentResolver cr) {
        super(cr);
    }
    
    @Override
    protected void onDeleteComplete(int token, Object cookie, int result) {
        super.onDeleteComplete(token, cookie, result);
    }
    
    @Override
    protected void onInsertComplete(int token, Object cookie, Uri uri) {
        super.onInsertComplete(token, cookie, uri);
    }
    
    @Override
    protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
        super.onQueryComplete(token, cookie, cursor);
    }
    
    @Override
    protected void onUpdateComplete(int token, Object cookie, int result) {
        super.onUpdateComplete(token, cookie, result);
    }
    }
    

    D、Intentservice

    我们知道Service的各个生命周期函数试运行在主线程的,因此它本身并不是一个异步处理技术。为了能够在Service 中做耗时操作,android引入了一个Service的子类:IntentService。
    IntentService具有Service一样的生命周期,同时也提供在后台线程处理异步任务的机制。与HandlerThread类似,IntentService也是一个后台线程中顺序执行所有的任务,我们通过Context.startService传递一个Intent类型的参数可以启动IntentService的异步执行,如果此时IntenService正在运行中,那么这个新的Intent将会进入队列进行排队,直到后台线程处理完队列前面的任务;如果此时IntentService没有在运行,那么将会启动一个新的IntentService,当后台线程队列中所有任务处理完成后,Intentservice 将会结束它的生命周期,因此IntenceService不需要我们手动结束。
    IntentService 本身是一个抽象类,因此使用前需要继承并实现onHandlerIntent方法,这个方法中实现具体的后台业务处理逻辑,同时在子类的结构构造方法中调用super(String name)传入子类的名字。

    (1)例子语句

     public class MyIntentService extends IntentService {
         public MyIntentService(){
             super(MyIntentService.class.getName());
        /**如果设置为true,那么IntentService的onStartCommand 方法将返回START_REDELIVER_INTENT
         * 这时,如果onHandlerIntent方法返回之前的进程死掉了,那么进程将会重新启动,intent将会重新投递
         * */
        setIntentRedelivery(true);
    }
    
    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     *
     * @param name Used to name the worker thread, important only for debugging.
     */
    public MyIntentService(String name) {
        super(name);
    }
    
    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        /**这个方法是后台线程中调用的*/
    
    }
     }
    

    (2)在清单文件中注册:

     <!--注册service-->
     <service android:name=".handle.MyIntentService"/>
    

    (3)IntentService源码(继承Service)

    IntentService是通过handlerThread来实现后台任务处理的

     public abstract class IntentService extends Service {
     private volatile Looper mServiceLooper;
     private volatile ServiceHandler mServiceHandler;
     private String mName;
     private boolean mRedelivery;
    
     private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }
    
        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }
    
    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     *
     * @param name Used to name the worker thread, important only for debugging.
     */
    public IntentService(String name) {
        super();
        mName = name;
    }
    
    /**
     * Sets intent redelivery preferences.  Usually called from the constructor
     * with your preferred semantics.
     *
     * <p>If enabled is true,
     * {@link #onStartCommand(Intent, int, int)} will return
     * {@link Service#START_REDELIVER_INTENT}, so if this process dies before
     * {@link #onHandleIntent(Intent)} returns, the process will be restarted
     * and the intent redelivered.  If multiple Intents have been sent, only
     * the most recent one is guaranteed to be redelivered.
     *
     * <p>If enabled is false (the default),
     * {@link #onStartCommand(Intent, int, int)} will return
     * {@link Service#START_NOT_STICKY}, and if the process dies, the Intent
     * dies along with it.
     */
    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }
    
    @Override
    public void onCreate() {
        // TODO: It would be nice to have an option to hold a partial wakelock
        // during processing, and to have a static startService(Context, Intent)
        // method that would launch the service & hand off a wakelock.
    
        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();
    
        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }
    
    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }
    
    /**
     * You should not override this method for your IntentService. Instead,
     * override {@link #onHandleIntent}, which the system calls when the IntentService
     * receives a start request.
     * @see android.app.Service#onStartCommand
     */
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }
    
    @Override
    public void onDestroy() {
        mServiceLooper.quit();
    }
    
    /**
     * Unless you provide binding for your service, you don't need to implement this
     * method, because the default implementation returns null.
     * @see android.app.Service#onBind
     */
    @Override
    @Nullable
    public IBinder onBind(Intent intent) {
        return null;
    }
    
      /**
      * This method is invoked on the worker thread with a request to process.
     * Only one Intent is 
     at a time, but the processing happens on a
     * worker thread that runs independently from other application logic.
     * So, if this code takes a long time, it will hold up other requests to
     * the same IntentService, but it will not hold up anything else.
     * When all requests have been handled, the IntentService stops itself,
     * so you should not call {@link #stopSelf}.
     *
     * @param intent The value passed to {@link
     *               android.content.Context#startService(Intent)}.
     *               This may be null if the service is being restarted after
     *               its process has gone away; see
     *               {@link android.app.Service#onStartCommand}
     *               for details.
     */
    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
    }
    

    E、Executor Framework

    我们知道,创建和销毁对象(例如线程),是存在开销的,如果应用中频繁出现线程的创建和销毁,那么会影响到应用的性能。使用Java Executor框架可以通过线程池等机制解决这个问题。改善应用的体验。

    (1)Executora框架为我们提供的能力

    创建工作线程池,同时通过队列来控制能够在这些线程的任务个数
    检测导致线程意外终止的错误
    等待线程执行完成并获取执行结果
    批量执行线程,并通过固定的顺序获取执行结果。
    在合适时机启动后台线程,从而保证线程执行结果可以很快反馈给用户

    Executor 框架的基础是一个名为Executor的接口定义,Executor的主要目的是分离任务的创建和执行,最终是实现上述的功能点。

    a、源码:

     public interface Executor {
    
    /**
     * Executes the given command at some time in the future.  The command
     * may execute in a new thread, in a pooled thread, or in the calling
     * thread, at the discretion of the {@code Executor} implementation.
     *
     * @param command the runnable task
     * @throws RejectedExecutionException if this task cannot be
     * accepted for execution
     * @throws NullPointerException if command is null
     */
    void execute(Runnable command);
    }
    

    b、简单的例子:

    通过实现Executor接口并重写executor方法从而实现自己的Executor类,

     public class MyExecutorFramework implements Executor {
    
    @Override
    public void execute(@NonNull Runnable command) {
        new Thread(command).start();
    
    }
    
    }
    

    当然那么简单的例子很少有的,通常要增加类似的队列,任务的优先级等功能,最终实现一个线程池,线程池是任务队列和工作现成的集合,这两者组合起来实现生产者消费者模式。
    Executor框架为开发者提供了预定义的线程池实现,内容如下:


    微信图片_20180929170129.png

    代码例子:

     /**
      * 固定大小的线程池
      * @return
      */
     public Executor executorsSize(){
    return Executors.newFixedThreadPool(2);
    }
    
     /**
      * 可变大小的线程池
      * @return
      */
     public Executor executorsChangeSize(){
         return Executors.newCachedThreadPool();
     }
    
     /**
      * 单个线程的线程池
      * @return
      */
     public Executor executorSigle(){
         return Executors.newSingleThreadExecutor();
     }
    

    预定义的线程池都是基于ThreadPoolExecutor类之上构建的,而通过ThreadPoolExecutor开发者可以自定义线程池的一些行为,源码中构造函数的定义:

     public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), defaultHandler);
     }
    
    微信图片_20180929170301.png

    F、AsyncTask

    从开始的继承树流程图中可以看出AsyncTask是在Executor框架基础上进行封装的,将耗时任务移动到工作线程中执行,同时提供方便的接口实现工作线程和主线程的通信,使用AsyncTask一般会用到如下方法

    (1)抽象类AsyncTask 的泛型的参数

    AsyncTAsk(Params,Progress,Result)


    微信图片_20180929170418.png

    (2)回调方法

    注:只有doInbackground在工作线程中执行,其他的都在主线程中执行,
    具体方法描述:


    微信图片_20180929170512.png

    (3)例子

    /**
     * Created by chaohao.zhao on 2018/9/26.
     * 
     */
    
    public class MyAsyncTask extends AsyncTask<String,Void,String> {
    @Override
    protected String doInBackground(String... params) {
        /**在工作线程中执行,进行一步任务处理,譬如进行数据请求*/
        return null;
    }
    
    /**
     * 异步执行前的操作
     * 譬如显示加载框
     * */
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
    }
    
    
    /**
     * 关闭
     * @param result
     */
    @Override
    protected void onCancelled(String result) {
        super.onCancelled(result);
    }
    
    
    /**
     * 用于更新UI,关闭加载框
     * @param result 是doInBackground方法返回值
     */
    @Override
    protected void onPostExecute(String result) {
        super.onPostExecute(result);
      }
    }
    
    /**
     *获取doInBackground执行进度
     */
    @Override
    protected void onProgressUpdate(Void... values) {
    super.onProgressUpdate(values);
    }
    

    调用:

    String params= "";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_parent_layout);
    new MyAsyncTask().execute(params);
    }
    

    G、Loader(异步数据加载框架)

    Loader是Android3.0开始引入的一个异步数据加载框架,它使得Activity,Fragment中异步加载数据变得很简单,同时它在数据根源发生变化时,能够发出消息通知。Loader框架涉及的API

    (1)Loader框架涉及的API

    微信图片_20180929170928.png

    LoaderManager.LoaderCallbacks:LoaderManager的回调接口,

    三个方法:


    微信图片_20180929171026.png

    (2)Loader例子:

    public class ParentLayoutActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks{
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_parent_layout);
    
    }
    
    /**
     * 创建Loader的地方,此处使用CursorLoader
     * @param id
     * @param args
     * @return
     */
    @Override
    public Loader onCreateLoader(int id, Bundle args) {
        return null;
    }
    
    /**
     * 关闭
     * @param loader
     * @param data
     */
    @Override
    public void onLoadFinished(Loader loader, Object data) {
    
    }
    
    /**
     * 加载无效时。数据会回调这个
     * @param loader
     */
    @Override
    public void onLoaderReset(Loader loader) {
    
      }
    }
    

    相关文章

      网友评论

          本文标题:六、Android 异步处理技术

          本文链接:https://www.haomeiwen.com/subject/xwrboftx.html