6. Loader

来源:互联网 发布:机械臂雅可比矩阵表示 编辑:程序博客网 时间:2024/06/05 22:59

1. Loader Framework

Loader framework在与content provider或者其他数据源进行操作时,提供了一种健壮的异步操作。
Loader Framework有那些特性:

  • Asynchronous data management
    Loader是在后台线程与数据源进行操作的,当数据源有新的数据时会在App中触发一个Callback。

  • Lifecycle management
    当与Loader关联的Activity或Fragment stop,那么loader也会stop。因此,当发生configuration changes(如横竖屏)时,在后台正在运行的
    Loader会继续运行,而不是stop。

  • Cached data
    如果Loader异步处理的数据结构不能deliver,那么Loader就好Cached这个数据结果,当一个接收数据的接收者准备好了,Loader就会把数据结果deliver到这个接收者。例如:当发生configuration change,Activity被重建的场景。

  • Leak protection
    如果一个Activity正在经历一个configuration change,Loader framework会确保Context对象不会丢失而导致泄露。Loader framework操作仅仅是Application context,因此主要的线程相关的泄露不会发生。

注意:
这里写图片描述

对于任何Loader类型,我们必须定义三个Callback:
one that creates a new loader;one that runs whenever the loader delivers new data;one that runs when the loader is reset - i.e., the loader stops delivering data。
All callbacks - most importantly, the delivery of data - are reported on the UI thread。

Loader Framework就是在android.app包中的API,包含的类有LoaderManager,Loader,AsyncTaskLoader 和 CursorLoader。
关系图:
这里写图片描述

Loader

Loader Lifecycle
这里写图片描述
这里写图片描述

LoaderManager

LoaderManager类一个抽象类,它会管理Activity或Fragment使用的所有Loader。LoaderManager在Activity/Fragment与Loader之间扮演了一个中间人的角色。在Activity/Fragment中调用getLoaderManager()方法来获取LoaderManager实例。

LoaderManager中四个主要的组成方法:
这里写图片描述
Activity/Fragment通过LoaderManager.LoaderCallbacks接口与LoaderManager交互,LoaderMananger.LoaderCallbacks接口必须在Activity/Fragment中实现。
Eg:
这里写图片描述

initLoader vs restartLoader

LoaderManager可以通过initLoader()或restartLoader()来创建一个Loader。

  • initLoader()
    如果Loader的标识符(id)匹配的话,initLoader()会重用可访问到的Loader。如果没有Loader匹配initLoader()中的ID,onCreateLoader()会请求一个新的Loader,之后data load被初始化,在onLoadFinished()中获取数据结果;如果initLoader中的ID已经存在,那么就在onLoadFinished()中直接接收最新的数据结果。
    Activity/Fragment的configuration change时使用initLoader()方法,这样仍然能接收到最新的数据结果。

  • restartLoader()
    此方法不会重用Loader。如果有一个ID匹配的Loader,restartLoader()会destroy这个Loader以及Loader的数据,然后再调用onCreateLoader()创建一个新的Loader。

这里写图片描述

LoaderCallbacks

相关的回调方法:
这里写图片描述
回调方法做的工作:
onCreateLoader:Loader initialization
此回调方法初始化一个Loader,LoaderManager.initLoader()会触发调用此回调方法,initLoader()会初始化一个带唯一标识符的Loader。如果initLoader()方法指定标识符的Loader不存在,那么onCreateLoader()回调方法会创建一个新的Loader,然后把创建后的Loader返回给LoaderManager,然后LoaderManager会管理Loader的lifecycle和data loading。如果有同一标识符的Loader,那么就不会再创建新的Loader,而是使用已经存在的Loader通过onLoadFinished()回调方法传输最新的数据。

onLoadFinished():Data loading
当数据源获取完或者更新时,会调用 onLoadFinisher() 回调方法加载数据;我们也可以在Activity/Fragment中通过调用 Loader.forceLoad() 方法强制获取新的数据。
我们可以通过调用 Loader.cancelLoad() 方法取消数据加载。如果此方法在数据加载开始之前调用的话,那么数据加载的请求会被取消;如果数据加载已经开始,那么这个数据解决会被丢弃,而且不会传输到Activity/Fragment。

onLoaderReset:Loader reset
当Activity/Fragment被销毁或者调用 LoaderManager.destroyLoader(id) 时,Loader会被销毁。Activity/Fragment会通过onLoaderReset()回调方法接收到Loader被销毁,在onLoaderReset()回调方法中可以做些资源释放的工作。

LoaderCallbacks线性工作时的流程图:
这里写图片描述

AsyncTaskLoader

AsyncTaskLoader会尽量保持同时进行的Task数(即运行的Thread)最少。例如,Activity/Fragment调用 forceLoad() 方法强制的连续的加载数据,但是不是每一次调用就会传回结果。这个原因是AsyncTaskLoader初始化一个新的加载的话,会把之前的加载取消掉。这个意思是在加载完成之前反复多次的调用 forceLoad() 将会延迟传输数据结果直到最后调用加载完成。(In practice, this means that calling forceLoad() repeatedly before previous loads are finished will postpone the delivery of the result until the last invoked load is done.)
如果内容连续多次的改变,Loader的onLoadFinished()方法会在UI Thread频繁调用并且UI Thread多次刷新重绘UI控件,那么我们可以调用 setUpdateThrottle(long delayMs) 方法设置AsyncTaskLoader连续加载数据的间隔时长。

2. CursorLoader

CursorLoader只能使用Cursor对象从Content Provider传输数据,而不是从SQLite数据库。
读取通信录示例:

public class ContactActivity extends ListActivity implements LoaderManager.LoaderCallbacks<Cursor> {    private static final int CONTACT_LOADER_ID = 0;    private static String[] CONTACT_SUMMARY_PROJECTION = new String[] {ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME};    private SimpleCursorAdapter cursorAdapter = null;    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        initAdapter();        getLoaderManager().initLoader(CONTACT_LOADER_ID, null, this);    }    private void initAdapter() {        cursorAdapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_1, null, new String[] {ContactsContract.Contacts.DISPLAY_NAME}, new int[] {android.R.id.text1}, 0);        setListAdapter(cursorAdapter);    }    @Override    public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {        return new CursorLoader(this, ContactsContract.Contacts.CONTENT_URI, CONTACT_SUMMARY_PROJECTION, null, null, ContactsContract.Contacts.DISPLAY_NAME + " ASC");    }    @Override    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {        cursorAdapter.swapCursor(cursor);    }    @Override    public void onLoaderReset(Loader<Cursor> loader) {        cursorAdapter.swapCursor(null);    }}

3. Custom Loaders

要定义一个完整的Loader需要包含下面的feature:

  • Loader lifecycle

  • Background loading

  • Content management

  • Deliver cached result

Loader lifecycle

Loader包含一系列的状态转变的方法,自定义Loader时可能需要实现:
这里写图片描述
Loader State(状态):

  • Reset
    The initial and final state of a loader, where it has released any cached data.

  • Started
    Starts an asynchronous data load and delivers the result through a cabllback invocation of LoaderCallback.onLoadFinished.

  • Stop
    The loader stops delivering data to the client. It may still load data in the background on content change, but the data is cached in the loader so that the latest data can be retrieved easily without initiating a new data load.

  • Abandoned
    Intermediate(中间的) state before reset, where data is stored until a new loader is connected to the data source. This is rarely used; the LoaderManager abandons loaders on restart so that the data is available while the restart is underway(处理之中).
    这里写图片描述

一个Loader的lifecycle是被LoaderManager控制的,正常情况下Activity/Fragment不应该直接调用Loader的方法来修改状态。
我们可以调用 forceLoad() 方法来显示地强制加载数据。forceLoad() 和 startLoading() 之间的不同之处是 forceLoad()只是强制加载新的数据,但是不会修改Loader的状态。
Forceload仅仅应该在started状态时调用,否则,数据结果就不会被传到Activity/Fragment。

Background Loading

Loader 应该在Background Thread异步地加载数据。
Eg:
Loader:

public class CustomLoader extends AsyncTaskLoader<Integer> {    public CustomLoader(Context context) {        super(context);    }    @Override    protected void onStartLoading() {        super.onStartLoading();        // TODO 待添加缓存数据        forceLoad();    }    // 在此方法中异步加载数据    @Override    public Integer loadInBackground() {        return loadData();    }    // 模拟加载数据    private int loadData() {        SystemClock.sleep(500);        Random random = new Random();        return random.nextInt(100);    }}

Activity:

public class CustomLoaderActivity extends Activity implements LoaderManager.LoaderCallbacks<Integer> {    private final int CUSTOM_LOADER_ID = 0;    private TextView content = null;    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_custom_loader);        content = (TextView) findViewById(R.id.custom_loader_content);        getLoaderManager().initLoader(CUSTOM_LOADER_ID, null, this);    }    @Override    public Loader onCreateLoader(int i, Bundle bundle) {        return new CustomLoader(this);    }    @Override    public void onLoadFinished(Loader loader, Integer data) {        content.setText(Integer.toString(data));    }    @Override    public void onLoaderReset(Loader loader) {        // TODO 回收    }}

Content Management

当基本数据改变时,Loader应该自动地创建一个新的后台数据加载。因此,这个基本数据必须设置一个 observable,例如 CursorLoader利用ContentObserver来获得content provider中的数据更新。
Observer机制典型:

  • Observable and Observer
    An in-memory data model of Java objects can be monitored by implementing the model as an Observable that reports changes to an Observer class. Both classes reside in the java.uitl package.

  • Broadcasted intent to a BroadcastReceiver
    A content-independent content notifier, this can be userd locally within an application or across process boundaries.

  • FileObserver
    Observers file system changes with an android.os.FileObserver that monitors a path in the file system and sends events when change occur. The events that are reported can be configured with an event filter, which can limit the observations, such as addition, deletion, and move.

当Observer收到一个更新的通知,它会上报到Loader来异步地加载新的数据,这个应该通过 forceLoad()或者onContentChanged() 方法来做。
当Loader started后,自定义的Loader调用 takeContentChanged() 方法来检查是否有内容要加载。
注意:
这里写图片描述

Eg:
这里写图片描述
Content Observation的生命周期:
从Loader started 到 reset,Content Observation 应该一直是活跃的。这样Loader即使是stopped状态也能继续在后台加载数据并且从Cache中提供新的数据。

### Delivering Cached Results
自定义Loader时,我们应该提供Cache,这样可以更快的传输数据到Activity/Fragment。
我们可以直接调用 Loader.deliverResult() 方法传输数据到Activity/Fragment,也可以或者重写 Loader.deliverResult() 方法把数据缓存起来。
这里写图片描述
Eg: Custom File Loader
FileLoader

public class FileLoader extends AsyncTaskLoader<List<String>> {    // Cache the list of file names    private List<String> fileNames = null;    private SdCardObserver sdCardObserver = null;    private class SdCardObserver extends FileObserver {        public SdCardObserver(String path) {            super(path, FileObserver.CREATE|FileObserver.DELETE);        }        @Override        public void onEvent(int i, String s) {            // This call will force a new asynchronous data load            // if the loader is started otherwise it will keep            // a reference that the data has changed for future loads.            onContentChanged();        }    }    public FileLoader(Context context) {        super(context);        String path = getContext().getFilesDir().getPath();        Log.i(MyApplication.TAG, "FileLoader # path: " + path);        sdCardObserver = new SdCardObserver(path);    }    @Override    protected void onStartLoading() {        super.onStartLoading();        // Start observing the content        sdCardObserver.startWatching();        // 如果缓存数据不为空,直接传输缓存数据        if (fileNames != null) {            deliverResult(fileNames);        }        // 如果缓存数据为空或者有内容改变的话,就强制加载数据        if (fileNames == null || takeContentChanged()) {            forceLoad();        }    }    @Override    public List<String> loadInBackground() {        File directory = getContext().getFilesDir();        Log.i(MyApplication.TAG, "FileLoader # loadInBackground # directory: " + directory);        return Arrays.asList(directory.list());    }    // 重写deliverResult()方法把获取到的数据缓存起来    @Override    public void deliverResult(List<String> data) {        // 如果Loader是reset状态,那么直接返回        if (isReset()) {            return;        }        Log.i(MyApplication.TAG, "FileLoader # deliverResult # data: " + data);        // Cache the data        fileNames = data;        // Only deliver result if the loader is started        if (isStarted()) {            super.deliverResult(data);        }    }    @Override    protected void onStopLoading() {        super.onStopLoading();        // Try to cancle an ongoing load, because the result will not be delivered anyway.        cancelLoad();    }    @Override    protected void onReset() {        super.onReset();        sdCardObserver.stopWatching();        clearResource();    }    private void clearResource() {        fileNames = null;    }}

Activity:

public class FileListActivity extends ListActivity implements LoaderManager.LoaderCallbacks<List<String>> {    private final int FILE_LOADER_ID = 1;    private ArrayAdapter<String> fileAdapter = null;    private List<String> fileNames = new ArrayList<String>();    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        getLoaderManager().initLoader(FILE_LOADER_ID, null, this);        fileAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, android.R.id.text1, fileNames);        fileAdapter.setNotifyOnChange(true);        setListAdapter(fileAdapter);    }    @Override    public Loader<List<String>> onCreateLoader(int i, Bundle bundle) {        return new FileLoader(this);    }    @Override    public void onLoadFinished(Loader<List<String>> loader, List<String> data) {        Log.i(MyApplication.TAG, "FileListActivity # onLoadFinished # data: " + data);        fileAdapter.clear();        fileAdapter.addAll(data);        fileAdapter.notifyDataSetChanged();    }    @Override    public void onLoaderReset(Loader<List<String>> loader) {        fileNames = null;        fileAdapter.clear();    }}

Handling Multiple Loaders

每个Activity/Fragment中只能有一个LoaderManager,而一个LoaderManager可以有多个Loader。我们可通过Loader的Id来区分并管理。
Eg:

public class MulLoaderSkeletonActivity extends Activity implements LoaderManager.LoaderCallbacks {    private final int FIREST_LOADER_ID = 1;    private final int SECOND_LOADER_ID = 2;    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        getLoaderManager().initLoader(FIREST_LOADER_ID, null, this);        getLoaderManager().initLoader(SECOND_LOADER_ID, null, this);    }    @Override    public Loader onCreateLoader(int id, Bundle bundle) {        switch (id) {            case FIREST_LOADER_ID:                return new FirstLoader(this);            case SECOND_LOADER_ID:                return new SecondLoader(this);        }        return null;    }    @Override    public void onLoadFinished(Loader loader, Object o) {        switch (loader.getId()) {            case FIREST_LOADER_ID:                // TODO                break;            case SECOND_LOADER_ID:                // TODO                break;        }    }    @Override    public void onLoaderReset(Loader loader) {        switch (loader.getId()) {            case FIREST_LOADER_ID:                // TODO                break;            case SECOND_LOADER_ID:                // TODO                break;        }    }}
原创粉丝点击