Android Loaders(二)Loader的使用

Android Loaders(二)Loader的使用

转载请注明:http://blog.csdn.net/liaoqianchuan00/article/details/24094733

参考翻译自:https://docs.google.com/presentation/d/1_5puFz6kUK1cSYvTmJbvQFYpgj8LXZECBrn65w62UKk/edit#slide=id.p

 

使用LoaderManager

1.       简单的API让你的Activity/Fragment可以和Loaders进行交互。

2.       每个Activity和每个Fragment只有一个LoaderManager的实例,他们不共享这些Loaders

3.       主要的方法:

 

initLoader(intid, Bundle args, LoaderCallbacks<D> callbacks)

 

restartLoader(intid, Bundle args, LoaderCallbacks<D> callbacks)

 

destroyLoader(intid)

 

getLoader(intid)

 

privatefinal LoaderCallbacks<Result> loaderCallbacks
        = newLoaderCallbacks<Result>() {

      @Override
      public Loader<Result>onCreateLoader(int id, Bundle args) {
             return newMyLoader(getActivity(), args.getLong("id"));
      }

      @Override
      public voidonLoadFinished(Loader<Result> loader, Result result) {
             handleResult(result);
      }

      @Override
      public voidonLoaderReset(Loader<Result> loader) {
      }};

 

注意:绝对不要直接调用一个Loader的框架方法,而你需要使用LoaderManager来自动调用。

 

如何初始化Loaders

在Activity中

 

       @Override

       protectedvoid onCreate(Bundle savedInstanceState) {

              super.onCreate(savedInstanceState);

              ...

              getSupportLoaderManager().initLoader(LOADER_ID,null, callbacks);

       }

 

在Fragment中

 

@Override

       publicvoid onActivityCreated(Bundle savedInstanceState) {

              super.onActivityCreated(savedInstanceState);

              ...

              getLoaderManager().initLoader(LOADER_ID,null, callbacks);

       }

Loader状态

每个Loader有三个状态:startedstoppedreset。而我们的LoaderManager会自动的根据Activity或者Fragment的状态来改变这些Loader的状态。

 

1.       Activity/FragmentstartsàLoader starts: onStartLoading()

2.       Activity变成InVisible或者FragmentdetachedàLoader stops: onStopLoading()

3.       Activity/Fragment被重新创建à没有回调。LoaderManager将会继续接收results,然后将结果保存到缓存。

4.       Activity/Fragment被销毁或者restartLoader/destroyLoader被调用àLoader resets: onReset()

 

使用Bundle

private void onNewQuery(String query) {

              Bundleargs = new Bundle();

              args.putString("query",query);

              getLoaderManager().restartLoader(LOADER_ID,args, loaderCallbacks);

       }

 

///////////////////////////////////

@Override

       publicLoader<Result> onCreateLoader(int id, Bundle args) {

              returnnew QueryLoader(getActivity(), args.getString("query"));

       }

 

很多时候你都不需要使用参数,直接传入null。

LoaderCallBacks是你的Fragment/Activity的一部分。你也可以直接使用Fragment/Activity的实例变量。

 

@Override

       publicvoid onCreate(Bundle savedInstanceState) {

              super.onCreate(savedInstanceState);

              this.newsId= getArguments().getLong("newsId");

       }

...

       @Override

       publicLoader<News> onCreateLoader(int id, Bundle args) {

              returnnew NewsLoader(getActivity(), NewsFragment.this.newsId);

       }

 

LoaderManager的一个Bug

当一个Fragment因为configuration变化被重新创建的时候(比如旋转屏幕,改变语言等),你在onActivityCreated()里面调用了initLoader()之后,他的LoaderManager调用了两次onLoadFinished。

 

解决方案:

1.       如果代码逻辑允许,可以不用处理。

2.       将之前的result保存起来,检查结果是否有变化。

3.       在onCreate()里面调用setRetainInstance(true)。

 

One-shot Loader

很多时候,我们只想运行一次这个Loader一次。比如:我们点击一个按钮然后提交一些信息到服务器。在用户点击了一个按钮后,我们调用initLoader,这个时候用户旋转屏幕,在旋转完屏幕之后,我们想要使用之前loader得到的结果。注意在旋转屏幕的时候,我们的Loader还没有提交完数据。像我们之前用AsyncTask的话,是没有办法在Activity/Fragment重新创建之后拿到之前任务返回的result的。但是使用Loader就简单多了。

 

在LoaderCallbacks中加入下面的代码

 

@Override

       publicvoid onLoadFinished(Loader<Integer> loader, Result result) {

              getLoaderManager().destroyLoader(LOADER_ID);

              ...            // Process the result

       }

 

在Activity/Fragment的创建过程中加入:

 

@Override

       publicvoid onActivityCreated(Bundle savedInstanceState) {

              super.onCreate(savedInstanceState);

              ...

              //Reconnect to the loader only if present

              if(getLoaderManager().getLoader(LOADER_ID) != null) {

                     getLoaderManager().initLoader(LOADER_ID,null, this);

              }

       }

 

常见的错误使用

1.       滥用loader id。在创建和restart一个loader的时候,不需要每次都去增加这个ID值。对于某种Loader,只需要一个唯一的id值。

 

2.       不需要在整个应用程序的范围类为每种Loader创建一个唯一的id值。因为每个LoaderManager都是独立的。你只需要保证你的Activity活着Fragment范围内这种Loader的ID值唯一就可以了。所以直接在Activity或者Fragment中创建一个私有的常量ID就可以了。

 

3.       避免出现FragmentManager exceptions

 

你不能在LoaderCallback这些回调里面直接创建FragmentTransaction(包括DialogFragment这种dialog)。

解决方法就是使用Handler来处理FragmentTransaction。

publicclass LinesFragment extends ContextMenuSherlockListFragment implementsLoaderCallbacks<List<LineInfo>>, Callback {

      ...

      @Override

      public void onCreate(BundlesavedInstanceState) {

             super.onCreate(savedInstanceState);

             handler = new Handler(this);

             adapter = newLinesAdapter(getActivity());

      }

      @Override

      public void onActivityCreated(BundlesavedInstanceState) {

             super.onActivityCreated(savedInstanceState);

             setListAdapter(adapter);

             setListShown(false);

             getLoaderManager().initLoader(LINES_LOADER_ID,null, this);

      }

   @Override

      public Loader<List<LineInfo>>onCreateLoader(int id, Bundle args) {

             return newLinesLoader(getActivity());

      }

      @Override

      public voidonLoadFinished(Loader<List<LineInfo>> loader, List<LineInfo>data) {

             if (data != null) {

                    adapter.setLinesList(data);

             } else if (isResumed()) {

                    handler.sendEmptyMessage(LINES_LOADING_ERROR_WHAT);

             }

             // The list should now be shown.

             if (isResumed()) {

                    setListShown(true);

             } else {

                    setListShownNoAnimation(true);

             }

      }

@Override

      public voidonLoaderReset(Loader<List<LineInfo>> loader) {

             adapter.setLinesList(null);

      }

      @Override

      public boolean handleMessage(Messagemessage) {

             switch (message.what) {

             case LINES_LOADING_ERROR_WHAT:

                    MessageDialogFragment
                 .newInstance(R.string.error_title, R.string.lines_loading_error)

                  .show(getFragmentManager());

                    return true;

             }

             return false;

      }

 

 

你可能感兴趣的:(loader)