[컴][안드로이드] LoaderManager 동작 이해

LoaderManager Sequence Diagram / LoaderManager flowchart /


android 3.0 이후부터 LoaderManager 를 제공한다. comparability package 를 사용하면 1.6 에서부터 사용가능하다고 한다.


Loaders 의 특징

  1. every Activities or Fragments : 모든 Activity 와 Fragment 에서 가능하다.(한 activity 또는 fragment 당 한개의 LoaderManager 를 갖는다. 한 개의 LoaderManager 는 여러개의 Loaders 를 가질 수 있다.)
  2. async data loading : data의 비동기 loading 을  을 제공한다.
  3. monitor and deliver the data source : data 의 source 를 모니터하면서 content 가 변경되면 전달해 준다.
  4. 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 하게 된다. 여기서는 이 함수가 호출되는 시점을 알아보자.
  1. onCreateLoader() : getLoaderManager().initLoader()(support library 에서는 getSupportLoaderManager().initLoader) 를 호출하면 바로 onCreateLoader() 가 호출된다.
  2. onLoadFinished() : onStart() 이후, AsyncTask 가 동작을 시작한다. 이 AsyncTask 동작이 끝나면, onLoadFinished() 가 호출된다. 이 곳에는 data 가 load 되고 난 후의 UI update 내용을 적으면 된다.
  3. onLoadReset() : android.app.LoaderManager,restartLoader()에서 새로운 loader 를 가져오고 쓰이지 않는 loader 를 destroy() 하는데 이 때 destroy() 에서 onLoadReset() 이 호출된다.


ref. 6에서 LoaderManager 를 잘 설명해 주고 있다. 그리고 ref. 6의 글쓴이가 만든 source 이다.
https://github.com/alexjlockwood/AppListLoader
이 source 에서는 Fragment 가 LoaderManager.LoaderCallbacks 를 implement 하고 있으니 참고하자.
class AppListFragment extends ListFragment implements
      LoaderManager.LoaderCallbacks<List<AppEntry>>
아래는 위의 소스에 대한 대략적인 sequence diagram 이다.
소스는 대략적으로 아래처럼 동작한다.
  1. AppListFragment 에서 LoaderManager.initLoader() 를 호출(onStart() 이전에 호출해야할 듯 하다.)하면서, AppListFragment 를 LoaderCallBack 으로 등록하고,
  2. LoaderManager 가 onCreateLoader() 를 호출
  3. AppLoader() 가 만들어진다.
  4. Activity 가 만들어지면서 onStart() 를 호출할 때
  5. LoadManager.doStart() 를 호출하게 되고,
  6. AppLoader 의 startLoading() 을 호출한다.
  7. 이 때 forceLoading() 을 호출하는데,
  8. 이 forceLoading() (AsyncTaskLoader.onForceLoading()) 이 LoadTask() 를 만들고 Thread 를 이용해서 실행한다.
  9. 그러면 thread 가 doInBackground() 를 실행하게 되고,
  10. 이 작업이 끝난 후에 onPostExecute() 를 UI thread Handler 에게 넘기게 된다.
  11. 이 onPostExecute() 를 실행하면서 AppLoader 의 deliveryResult() 가 호출된다.
  12. 이 때 super.deliveryResult() 를 실행하면, onLoadFinished() 를 호출해 준다.

Diagrams

LoaderManager_seqDiagram


 +--------------------------+------------------------+
 |                          |                        |
 |   +-------------------+  |                        |
 |   | 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

  1. How to Use Loaders in Android | Grokking Android

References

  1. SimpleCursorLoader - content provider 가 필요없는 loader
  2. SimpleCursorLoader Example
  3. Android tutorial: Simple (but persistent) data storage with SQLite
  4. Get Started Developing For Android With Eclipse, Reloaded
  5. https://github.com/alexjlockwood/AppListLoader
  6. Life Before Loaders (part 1)
  7. http://code.google.com/codesearch#vhKRzqrOaj0/trunk/src/org/ray/veader/DataHelper.java&ct=rc&cd=14&q=openOrCreateDatabase&sq=
  8. Loaders | Android Developers
  9. Android Development - Example, tutorial, Source Code : Custom Loader : Loading data using Loader Manager in android asynchronously
  10. How to Use Loaders in Android | Grokking Android

댓글 5개:

  1. 잘봤습니다. 근데 일정시간마다 작업을 해야하는경우에는 어떻게 해야 하나요?

    답글삭제
  2. 잘봤습니다. 근데 일정시간마다 작업을 해야하는경우에는 어떻게 해야 하나요?

    답글삭제
    답글
    1. AlarmManager 를 한 번 검색 해 보는 것이 도움이 될 듯 하네요.

      삭제
  3. 오... 로더에 대해서 전혀 개념없이 막막하게 찾고있었는데 자료를 너무 정리 잘 해놓으셨네요.
    덕분에 많이 배우고 갑니다.
    감사합니다.

    답글삭제