android 3.0 이후부터 LoaderManager 를 제공한다. comparability package 를 사용하면 1.6 에서부터 사용가능하다고 한다.
Loaders 의 특징
- every Activities or Fragments : 모든 Activity 와 Fragment 에서 가능하다.
(한 activity 또는 fragment 당 한개의 LoaderManager 를 갖는다. 한 개의 LoaderManager 는 여러개의 Loaders 를 가질 수 있다.)
- async data loading : data의 비동기 loading 을 을 제공한다.
- monitor and deliver the data source : data 의 source 를 모니터하면서 content 가 변경되면 전달해 준다.
- automatically reconnect : configuratoin 이 바뀐후(orientation 이 변경되는 것 등의)에 다시 만들어 질 때,
자동적으로 마지막으로 사용한 loader의 cursor 에 접속한다. 그래서 data 를 다시 query 할 필요가 없다.
만약 download 를 하고 있고, 그 progress 를 activity 에서 보여주고 있다면, download 를 하는 도중에
화면의 orientation 을 전환하면 값을 잃어버리게 된다. 이런 경우에 LoaderManager 를 이용하면 좋다고 한다.[ref. 9]
Loader Callbacks
LoaderManager 가 시작할 때 , 끝날 때 우리가 원하는 일을 시킬 수 있다. 이 일을 하는 것이 LoaderCallbacks 이다.
이 LoaderCallbacks 는 LoaderManager.initLoader() 에서 register 하게 된다.LoaderManager.LoaderCallbacks<?> 을 implement 하면
- onCreateLoader()
- onLoadFinished()
- onLoaderReset()
를 override 하게 된다. 여기서는 이 함수가 호출되는 시점을 알아보자.- onCreateLoader() : getLoaderManager().initLoader()(support library 에서는 getSupportLoaderManager().initLoader) 를
호출하면 바로 onCreateLoader() 가 호출된다.
- onLoadFinished() : onStart() 이후, AsyncTask 가 동작을 시작한다. 이 AsyncTask 동작이 끝나면, onLoadFinished() 가 호출된다.
이 곳에는 data 가 load 되고 난 후의 UI update 내용을 적으면 된다.
- onLoadReset() : android.app.LoaderManager,restartLoader()에서 새로운 loader 를 가져오고 쓰이지 않는 loader 를 destroy()
하는데 이 때 destroy() 에서 onLoadReset() 이 호출된다.
ref. 6에서 LoaderManager 를 잘 설명해 주고 있다. 그리고 ref. 6의 글쓴이가 만든 source 이다.
https://github.com/alexjlockwood/AppListLoader
AppListLoader-master.zip
이 source 에서는 Fragment 가 LoaderManager.LoaderCallbacks 를 implement 하고 있으니 참고하자.
class AppListFragment extends ListFragment implements
LoaderManager.LoaderCallbacks<List<AppEntry>>
아래는 위의 소스에 대한 대략적인 sequence diagram 이다.
소스는 대략적으로 아래처럼 동작한다.- AppListFragment 에서 LoaderManager.initLoader() 를 호출(onStart() 이전에 호출해야할 듯 하다.)하면서,
AppListFragment 를 LoaderCallBack 으로 등록하고,
- LoaderManager 가 onCreateLoader() 를 호출
- AppLoader() 가 만들어진다.
- Activity 가 만들어지면서 onStart() 를 호출할 때
- LoadManager.doStart() 를 호출하게 되고,
- AppLoader 의 startLoading() 을 호출한다.
- 이 때 forceLoading() 을 호출하는데,
- 이 forceLoading() (AsyncTaskLoader.onForceLoading()) 이 LoadTask() 를 만들고 Thread 를 이용해서 실행한다.
- 그러면 thread 가 doInBackground() 를 실행하게 되고,
- 이 작업이 끝난 후에 onPostExecute() 를 UI thread Handler 에게 넘기게 된다.
- 이 onPostExecute() 를 실행하면서 AppLoader 의 deliveryResult() 가 호출된다.
- 이 때 super.deliveryResult() 를 실행하면, onLoadFinished() 를 호출해 준다.
Diagrams
+--------------------------+------------------------+
| | |
| +-------------------+ | |
| | onCreateLoader() | | |
| +--------+----------+ | |
| | | |
| +--------+----------+ | |
| | onStartLoading() +--|-----------+ |
| +-------------------+ | | |
| | | |
| | +--------+----------+ |
| +-------------|--+ loadInBackground()| |
| | | +-------------------+ |
| | | |
| +--------+----------+ | |
| | deliverResult() | | |
| +--------+----------+ | |
| | | |
| +--------+----------+ | |
| | onLoadFinished() | | |
| +-------------------+ | |
+--------------------------+------------------------+
Stacks
AppListLoader : public class AppListLoader extends AsyncTaskLoader<List<AppEntry>>UI Thread
Stack trace
MainActivity$AppListFragment.onCreateLoader(int, Bundle) line: 87
LoaderManagerImpl.createLoader(int, Bundle, LoaderManager$LoaderCallbacks) line: 487
LoaderManagerImpl.createAndInstallLoader(int, Bundle, LoaderManager$LoaderCallbacks) line: 496
LoaderManagerImpl.initLoader(int, Bundle, LoaderManager$LoaderCallbacks) line: 550
MainActivity$AppListFragment.onActivityCreated(Bundle) line: 77
FragmentManagerImpl.moveToState(Fragment, int, int, int, boolean) line: 892
FragmentManagerImpl.moveToState(int, int, int, boolean) line: 1083
BackStackRecord.run() line: 635
FragmentManagerImpl.execPendingActions() line: 1431
MainActivity(FragmentActivity).onStart() line: 523
Instrumentation.callActivityOnStart(Activity) line: 1133
MainActivity(Activity).performStart() line: 4475
...
UI Thread
Stack trace
AppListLoader.onStartLoading() line: 129
AppListLoader(Loader).startLoading() line: 197
LoaderManagerImpl$LoaderInfo.start() line: 263
LoaderManagerImpl.doStart() line: 711
MainActivity$AppListFragment(Fragment).onStart() line: 985
MainActivity$AppListFragment(Fragment).performStart() line: 1336
FragmentManagerImpl.moveToState(Fragment, int, int, int, boolean) line: 907
FragmentManagerImpl.moveToState(int, int, int, boolean) line: 1083
FragmentManagerImpl.moveToState(int, boolean) line: 1065
FragmentManagerImpl.dispatchStart() line: 1849
MainActivity(FragmentActivity).onStart() line: 536
Instrumentation.callActivityOnStart(Activity) line: 1133
MainActivity(Activity).performStart() line: 44753
...
Stack trace
AppListLoader.loadInBackground() line: 52
AppListLoader.loadInBackground() line: 1
AppListLoader(AsyncTaskLoader).onLoadInBackground() line: 240
AsyncTaskLoader$LoadTask.doInBackground(Void...) line: 51
AsyncTaskLoader$LoadTask.doInBackground(Object[]) line: 40
ModernAsyncTask$2.call() line: 123
...
UI Thread
Stack trace
AppListLoader.deliverResult(List) line: 86
AppListLoader.deliverResult(Object) line: 1
AppListLoader(AsyncTaskLoader).dispatchOnLoadComplete(AsyncTaskLoader$LoadTask, Object) line: 221
AsyncTaskLoader$LoadTask.onPostExecute(Object) line: 61
AsyncTaskLoader$LoadTask(ModernAsyncTask).finish(Object) line: 461
...
UI Thread
Stack trace
MainActivity$AppListFragment.onLoadFinished(Loader, List) line: 92
MainActivity$AppListFragment.onLoadFinished(Loader, Object) line: 1
LoaderManagerImpl$LoaderInfo.callOnLoadFinished(Loader, Object) line: 425
LoaderManagerImpl$LoaderInfo.onLoadComplete(Loader, Object) line: 393
AppListLoader(Loader).deliverResult(Object) line: 103
AppListLoader.deliverResult(List) line: 111
AppListLoader.deliverResult(Object) line: 1
AppListLoader(AsyncTaskLoader).dispatchOnLoadComplete(AsyncTaskLoader$LoadTask, Object) line: 221
AsyncTaskLoader$LoadTask.onPostExecute(Object) line: 61
AsyncTaskLoader$LoadTask(ModernAsyncTask).finish(Object) line: 461
...
See Also
- How to Use Loaders in Android | Grokking Android
References
- SimpleCursorLoader - content provider 가 필요없는 loader
- SimpleCursorLoader Example
- Android tutorial: Simple (but persistent) data storage with SQLite
- Get Started Developing For Android With Eclipse, Reloaded
- https://github.com/alexjlockwood/AppListLoader
- Life Before Loaders (part 1)
- http://code.google.com/codesearch#vhKRzqrOaj0/trunk/src/org/ray/veader/DataHelper.java&ct=rc&cd=14&q=openOrCreateDatabase&sq=
- Loaders | Android Developers
- Android Development - Example, tutorial, Source Code : Custom Loader : Loading data using Loader Manager in android asynchronously
- How to Use Loaders in Android | Grokking Android