Loader使用及异步支持

2018-08-01  本文已影响0人  hdychi

一、简介

很多时候,我们可能想在Activity或Fragment中加载数据,例如使用ContentProvider获取数据库数据。显然,类似ContenProvider从数据库中读取数据多数是耗时行为,不能在主线程中完成加载因为这会阻塞主线程。Android 3.0后,提供了Loader加载器,可以轻松的在Activity或Fragment中异步加载数据。Loader具有以下特性:

此外,Loader和Activity或Fragment的生命周期绑定,Activity或Fragment管理LoaderManager,而LoaderManager又管理着Loader。在它们生命周期结束时(onDestroy),它们会把Loader也Destroy。并且Loader不能作为内部类的对象初始化,因为内部类会持有外部Activity/Fragment的引用,造成内存泄漏。

二、使用

常用的Loader API有:

Loader API 摘要

在应用中使用加载器时,可能会涉及到多个类和接口。 下表汇总了这些类和接口:

LoaderManager

|

一种与 ActivityFragment 相关联的的抽象类,用于管理一个或多个 Loader 实例。 这有助于应用管理与ActivityFragment 生命周期相关联的、运行时间较长的操作。它最常见的用法是与 CursorLoader 一起使用,但应用可自由写入其自己的加载器,用于加载其他类型的数据。

每个 Activity 或片段中只有一个 LoaderManager。但一个 LoaderManager 可以有多个加载器。

|
|

LoaderManager.LoaderCallbacks

|

一种回调接口,用于客户端与 LoaderManager 进行交互。例如,您可使用 onCreateLoader() 回调方法创建新的加载器。

|
|

Loader

|

一种执行异步数据加载的抽象类。这是加载器的基类。 您通常会使用 CursorLoader,但您也可以实现自己的子类。加载器处于活动状态时,应监控其数据源并在内容变化时传递新结果。

|
|

AsyncTaskLoader

|

提供 AsyncTask 来执行工作的抽象加载器。

|
|

CursorLoader

|

AsyncTaskLoader 的子类,它将查询 ContentResolver 并返回一个 Cursor。此类采用标准方式为查询游标实现 Loader 协议。它是以 AsyncTaskLoader 为基础而构建,在后台线程中执行游标查询,以免阻塞应用的 UI。使用此加载器是从 ContentProvider 异步加载数据的最佳方式,而不用通过片段或 Activity 的 API 来执行托管查询。

|

LoaderManager.LoaderCallbacks中通常由用户实现,onCreateLoader方法返回想要使用的Loader实例,onLoadFinished为Loader加载数据完成后的回调函数,onLoaderReset为Loader重置时的初始化函数。

以下是一个使用Loader异步读取手机中截图图片路径的例子:

回调类PictureLoaderCallback.java:

package com.meituan.huangdanyang.practise;

import android.content.Context;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.util.Log;

import java.util.ArrayList;
import java.util.List;

public class PictureLoaderCallback implements LoaderManager.LoaderCallbacks<Cursor> {
    private Context context;
    private OnLoadFinishListener onLoadFinishListener;
    private List<String> res;
    private Long last;
    public PictureLoaderCallback(Context context) {
        this.context = context;
    }

    public PictureLoaderCallback(Context context, OnLoadFinishListener onLoadFinishListener) {
        this.context = context;
        this.onLoadFinishListener = onLoadFinishListener;
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        Log.i("开始时间",System.currentTimeMillis() + "");
        last = System.currentTimeMillis();
        return new CursorLoader(context, MediaStore.Images.Media.EXTERNAL_CONTENT_URI,null,
                MediaStore.Images.Media.MIME_TYPE + "=? or "
                        + MediaStore.Images.Media.MIME_TYPE + "=?",
                new String[] {"image/jpeg", "image/png"},
                MediaStore.Images.Media.DATE_MODIFIED
        );
    }

    @Override
    public void onLoadFinished(@NonNull Loader<Cursor> loader, Cursor data) {
        if (data == null) {
            return;
        }
        Log.i("返回时间",(System.currentTimeMillis() - last) + "");
        last = System.currentTimeMillis();
        res = new ArrayList<>();
        data.moveToPrevious();
        while (data.moveToNext()) {
          /* for (int i = 0;i < data.getColumnCount();i++) {
               System.out.print(data.getString(i) + " ");
           }*/
          if (data.getString(1).matches(".*Screenshots.*")) {
              res.add(data.getString(1));
          }
          /* System.out.println();*/
        }
        Log.i("结束时间",System.currentTimeMillis() - last + "");
        if (onLoadFinishListener != null) {
            onLoadFinishListener.onComplete(res);
        }
    }

    @Override
    public void onLoaderReset(@NonNull Loader<Cursor> loader) {

    }

}

在Fragment中初始化Loader及使用Loader加载完成后的数据:

public class HomeFragment extends BaseFragment implements OnLoadFinishListener<String>{

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        getLoaderManager().initLoader(0, null, new PictureLoaderCallback(getContext(),
                this));
    }

    @Override
    public void onComplete(List<String> data) {
        for (String item:data) {
           Log.i("ITEM",item);
        }
    }
}

这样在Loader加载完成时,调用onLoadFinished方法再调用Fragment传入的OnLoadFinishListener的onComplete方法,Fragment中就拿到了加载到的数据。

三、异步机制

Loader中是怎么支持异步加载的呢?

AsyncTaskLoader是支持异步加载的Loader的抽象类。上面代码中的CursorLoader就是AsyncTaskLoader的子类。

AsyncTaskLoader是怎么支持异步的?如它的名字一般,AsyncTaskLoader异步实现是依靠Android的AsyncTask。

AsyncTaskLoader有一个继承自AsyncTask的内部类:

final class LoadTask extends ModernAsyncTask<Void, Void, D> implements Runnable {
        private final CountDownLatch mDone = new CountDownLatch(1);

        // Set to true to indicate that the task has been posted to a handler for
        // execution at a later time.  Used to throttle updates.
        boolean waiting;

        /* Runs on a worker thread */
        @Override
        protected D doInBackground(Void... params) {
            if (DEBUG) Log.v(TAG, this + " >>> doInBackground");
            try {
                D data = AsyncTaskLoader.this.onLoadInBackground();
                if (DEBUG) Log.v(TAG, this + "  <<< doInBackground");
                return data;
            } catch (OperationCanceledException ex) {
                if (!isCancelled()) {
                    // onLoadInBackground threw a canceled exception spuriously.
                    // This is problematic because it means that the LoaderManager did not
                    // cancel the Loader itself and still expects to receive a result.
                    // Additionally, the Loader's own state will not have been updated to
                    // reflect the fact that the task was being canceled.
                    // So we treat this case as an unhandled exception.
                    throw ex;
                }
                if (DEBUG) Log.v(TAG, this + "  <<< doInBackground (was canceled)", ex);
                return null;
            }
        }

        /* Runs on the UI thread */
        @Override
        protected void onPostExecute(D data) {
            if (DEBUG) Log.v(TAG, this + " onPostExecute");
            try {
                AsyncTaskLoader.this.dispatchOnLoadComplete(this, data);
            } finally {
                mDone.countDown();
            }
        }

        /* Runs on the UI thread */
        @Override
        protected void onCancelled(D data) {
            if (DEBUG) Log.v(TAG, this + " onCancelled");
            try {
                AsyncTaskLoader.this.dispatchOnCancelled(this, data);
            } finally {
                mDone.countDown();
            }
        }

        /* Runs on the UI thread, when the waiting task is posted to a handler.
         * This method is only executed when task execution was deferred (waiting was true). */
        @Override
        public void run() {
            waiting = false;
            AsyncTaskLoader.this.executePendingTask();
        }

        /* Used for testing purposes to wait for the task to complete. */
        public void waitForLoader() {
            try {
                mDone.await();
            } catch (InterruptedException e) {
                // Ignore
            }
        }
    }

doInBackgound中调用了AsyncTaskLoader.this.onLoadInBackground(),这里就是真正执行耗时操作的地方,由子类实现抽象类AsyncTaskLoader的接口。联系CursorLoader的源码:

 @Override
    public Cursor loadInBackground() {
        synchronized (this) {
            if (isLoadInBackgroundCanceled()) {
                throw new OperationCanceledException();
            }
            mCancellationSignal = new CancellationSignal();
        }
        try {
            Cursor cursor = ContentResolverCompat.query(getContext().getContentResolver(),
                    mUri, mProjection, mSelection, mSelectionArgs, mSortOrder,
                    mCancellationSignal);
            if (cursor != null) {
                try {
                    // Ensure the cursor window is filled.
                    cursor.getCount();
                    cursor.registerContentObserver(mObserver);
                } catch (RuntimeException ex) {
                    cursor.close();
                    throw ex;
                }
            }
            return cursor;
        } finally {
            synchronized (this) {
                mCancellationSignal = null;
            }
        }
    }

CursorLoader实现了AsynTaskLoader的loadInBackground方法,很清楚,在这个方法里调用ContentResolverCompat的query方法进行查询操作。所以,这个耗时的查询操作将在子线程中执行。

执行完成后,根据AsyncTask的机制,调用onPostExecute方法:从而 AsyncTaskLoader.this.dispatchOnLoadComplete(this, data):

 void dispatchOnLoadComplete(LoadTask task, D data) {
        if (mTask != task) {
            if (DEBUG) Log.v(TAG, "Load complete of old task, trying to cancel");
            dispatchOnCancelled(task, data);
        } else {
            if (isAbandoned()) {
                // This cursor has been abandoned; just cancel the new data.
                onCanceled(data);
            } else {
                commitContentChanged();
                mLastLoadCompleteTime = SystemClock.uptimeMillis();
                mTask = null;
                if (DEBUG) Log.v(TAG, "Delivering result");
                deliverResult(data);
            }
        }
    }

没有异常的情况下,调用deliverResult(data);把结果分发下去。这是个Loader中就有的方法,看看CursorLoader重写的deliverResult(data)方法:

 @Override
    public void deliverResult(Cursor cursor) {
        if (isReset()) {
            // An async query came in while the loader is stopped
            if (cursor != null) {
                cursor.close();
            }
            return;
        }
        Cursor oldCursor = mCursor;
        mCursor = cursor;

        if (isStarted()) {
            super.deliverResult(cursor);
        }

        if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) {
            oldCursor.close();
        }
 }

其实还是调用了祖先Loader的方法。

  public void deliverResult(D data) {
        if (mListener != null) {
            mListener.onLoadComplete(this, data);
        }
    }

这是个观察者模式,也就是说,观察者观察被观察者数据的改变作出反应。

至于mListener从哪来的,这就由LoadManagerImpl管理了。

LoadManagerImpl的静态内部类给Loader的mListener赋值:

    public static class LoaderInfo<D> extends MutableLiveData<D>
            implements Loader.OnLoadCompleteListener<D> {

        private final int mId;
        private final @Nullable Bundle mArgs;
        private final @NonNull Loader<D> mLoader;
        private LifecycleOwner mLifecycleOwner;
        private LoaderObserver<D> mObserver;
        private Loader<D> mPriorLoader;

        LoaderInfo(int id, @Nullable Bundle args, @NonNull Loader<D> loader,
                @Nullable Loader<D> priorLoader) {
            mId = id;
            mArgs = args;
            mLoader = loader;
            mPriorLoader = priorLoader;
            mLoader.registerListener(id, this);
        }
        ...
           @Override
        public void onLoadComplete(@NonNull Loader<D> loader, @Nullable D data) {
            if (DEBUG) Log.v(TAG, "onLoadComplete: " + this);
            if (Looper.myLooper() == Looper.getMainLooper()) {
                setValue(data);
            } else {
                // The Loader#deliverResult method that calls this should
                // only be called on the main thread, so this should never
                // happen, but we don't want to lose the data
                if (DEBUG) {
                    Log.w(TAG, "onLoadComplete was incorrectly called on a "
                            + "background thread");
                }
                postValue(data);
            }
        }

        @Override
        public void setValue(D value) {
            super.setValue(value);
            // Now that the new data has arrived, we can reset any prior Loader
            if (mPriorLoader != null) {
                mPriorLoader.reset();
                mPriorLoader = null;
            }
        }
}

这个LoadInfo,继承自LiveData,而LiveData又是Android提供的一种观察者模式的数据存储,它也能与UI控件的生命周期绑定,从而不会产生内存泄漏。在数据改变时通知观察者。这里就不再说LiveData了

LiveData的官方文档:https://developer.android.com/topic/libraries/architecture/livedata

既然是观察者模式,那么观察者在哪呢,其实就在这:

 Loader<D> setCallback(@NonNull LifecycleOwner owner,
                @NonNull LoaderCallbacks<D> callback) {
            LoaderObserver<D> observer = new LoaderObserver<>(mLoader, callback);
            // Add the new observer
            observe(owner, observer);
            // Loaders only support one observer at a time, so remove the current observer, if any
            if (mObserver != null) {
                removeObserver(mObserver);
            }
            mLifecycleOwner = owner;
            mObserver = observer;
            return mLoader;
        }

也就是说,观察者对于数据改变产生的“反应”,其实主要就是LoaderCallbacks中的回调函数。

上一篇下一篇

猜你喜欢

热点阅读