• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1page.title=로더
2parent.title=액티비티
3parent.link=activities.html
4@jd:body
5<div id="qv-wrapper">
6<div id="qv">
7    <h2>이 문서의 내용</h2>
8    <ol>
9    <li><a href="#summary">로더 API 요약</a></li>
10    <li><a href="#app">애플리케이션 안에서 로더 사용하기</a>
11      <ol>
12        <li><a href="#requirements"></a></li>
13        <li><a href="#starting">로더 시작</a></li>
14        <li><a href="#restarting">로더 다시 시작</a></li>
15        <li><a href="#callback">LoaderManager 콜백 사용하기</a></li>
16      </ol>
17    </li>
18    <li><a href="#example">예</a>
19       <ol>
20         <li><a href="#more_examples">추가 예</a></li>
21        </ol>
22    </li>
23  </ol>
24
25  <h2>Key 클래스</h2>
26    <ol>
27      <li>{@link android.app.LoaderManager}</li>
28      <li>{@link android.content.Loader}</li>
29
30    </ol>
31
32    <h2>관련 샘플</h2>
33   <ol>
34     <li> <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderCursor.html">
35LoaderCursor</a></li>
36     <li> <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html">
37LoaderThrottle</a></li>
38   </ol>
39  </div>
40</div>
41
42<p>로더는 Android 3.0부터 도입된 것으로, 액티비티 또는 프래그먼트에서 비동기식으로 데이터를 쉽게
43로딩할 수 있게 합니다. 로더의 특성은 다음과 같습니다.</p>
44  <ul>
45    <li>모든 {@link android.app.Activity}와 {@link
46android.app.Fragment}에 사용할 수 있습니다.</li>
47    <li>데이터의 비동기식 로딩을 제공합니다.</li>
48    <li>데이터의 출처를 모니터링하여 그 콘텐츠가 변경되면 새 결과를
49전달합니다.</li>
50    <li>구성 변경 후에 재생성된 경우, 마지막 로더의 커서로 자동으로
51다시 연결됩니다. 따라서 데이터를 다시 쿼리하지 않아도
52됩니다.</li>
53  </ul>
54
55<h2 id="summary">로더 API 요약</h2>
56
57<p>애플리케이션 안에서 로더를 사용하는 데 관련된 클래스와 인터페이스는
58여러 가지가 있습니다. 다음 표에서 요약되어 있습니다.</p>
59
60<table>
61  <tr>
62    <th>클래스/인터페이스</th>
63    <th>설명</th>
64  </tr>
65  <tr>
66    <td>{@link android.app.LoaderManager}</td>
67    <td>{@link android.app.Activity} 또는
68{@link android.app.Fragment}와 연관된 추상 클래스로, 하나 이상의 {@link
69android.content.Loader} 인스턴스를 관리하는 데 쓰입니다. 이것을 사용하면 애플리케이션이
70{@link android.app.Activity}
71 또는 {@link android.app.Fragment} 수명 주기와 함께 실행 시간이 긴 작업을 관리하는 데 도움이 됩니다. 이것의 가장 보편적인 용법은
72{@link android.content.CursorLoader}와 함께 사용하는 것이지만,
73다른 유형의 데이터를 로드하기 위해 애플리케이션이 자체 로더를 작성하는 것도 얼마든지 가능합니다.
74    <br />
75    <br />
76    액티비티 또는 프래그먼트당 {@link android.app.LoaderManager}는 하나씩밖에 없습니다. 하지만 {@link android.app.LoaderManager}에는 로더가 여러 개 있어도
77됩니다.</td>
78  </tr>
79  <tr>
80    <td>{@link android.app.LoaderManager.LoaderCallbacks}</td>
81    <td>클라이언트에 대해 {@link
82android.app.LoaderManager}와 상호 작용하도록 하는 콜백 인터페이스입니다. 예를 들어 {@link
83android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}
84콜백 메서드를 사용하여 새 로더를 생성할 수 있습니다.</td>
85  </tr>
86  <tr>
87    <td>{@link android.content.Loader}</td>
88    <td>데이터의 비동기식 로딩을 수행하는 추상 클래스입니다. 이것이 로더의 기본
89클래스입니다. 보통은 {@link
90android.content.CursorLoader}를 사용하기 마련이지만, 자신만의 하위 클래스를 구현해도 됩니다. 로더가 활성 상태인 동안에는
91소속 데이터의 출처를 모니터링하고 콘텐츠가 변경되면 새 결과를
92전달하는 것이 정상입니다. </td>
93  </tr>
94  <tr>
95    <td>{@link android.content.AsyncTaskLoader}</td>
96    <td>작업을 수행할 {@link android.os.AsyncTask}를 제공하는 추상 로더입니다.</td>
97  </tr>
98  <tr>
99    <td>{@link android.content.CursorLoader}</td>
100    <td>{@link android.content.AsyncTaskLoader}의 하위 클래스로, 이것이
101{@link android.content.ContentResolver}를 쿼리하고 {@link
102android.database.Cursor}를 반환합니다. 이 클래스는 커서 쿼리에 대한 표준 방식으로 {@link
103android.content.Loader} 프로토콜을 구현하며,
104{@link android.content.AsyncTaskLoader}에 구축되어
105배경 스레드에서 커서 쿼리를 수행하므로 애플리케이션의 UI를 차단하지 않습니다.
106이 로더를 사용하는 것이 프래그먼트나 액티비티의 API를 통해 관리된 쿼리를 수행하는 대신 {@link
107android.content.ContentProvider}에서
108비동기식으로 데이터를 로딩하는 최선의 방법입니다.</td>
109  </tr>
110</table>
111
112<p>위의 표에 나열된 클래스와 인터페이스가 애플리케이션 내에서 로더를 구현하는 데
113사용할 기본적인 구성 요소입니다. 생성하는 로더마다
114이 모든 것이 다 필요한 것은 아니지만, 로더를 초기화하려면 항상 {@link
115android.app.LoaderManager}를 참조해야 하고 {@link
116android.content.CursorLoader}와 같은 {@link android.content.Loader}
117클래스도 구현해야 합니다. 다음 몇 섹션에서는 애플리케이션 안에서 이와 같은
118클래스와 인터페이스를 사용하는 법을 보여줍니다.</p>
119
120<h2 id ="app">애플리케이션 안에서 로더 사용하기</h2>
121<p>이 섹션에서는 Android 애플리케이션 내에서 로더를 사용하는 방법을 설명합니다. 로더를
122사용하는 애플리케이션에는 보통 다음이 포함되어 있습니다.</p>
123<ul>
124  <li>{@link android.app.Activity} 또는 {@link android.app.Fragment}.</li>
125  <li>{@link android.app.LoaderManager}의 인스턴스.</li>
126  <li>{@link
127android.content.ContentProvider}가 지원하는 데이터를 로딩할 {@link android.content.CursorLoader}. 아니면, 개발자 나름의
128{@link android.content.Loader} 또는 {@link android.content.AsyncTaskLoader} 하위 클래스를 구현하여
129다른 출처에서 데이터를 로딩해도 됩니다.</li>
130  <li>{@link android.app.LoaderManager.LoaderCallbacks}의 구현.
131여기에서 새 로더를 생성하고 기존 로더에 대한 참조를
132관리합니다.</li>
133<li>로더의 데이터를 표시하는 방법(예: {@link
134android.widget.SimpleCursorAdapter})</li>
135  <li>{@link android.content.ContentProvider}와 같은 데이터 출처로,
136{@link android.content.CursorLoader}를 사용하는 경우에 해당.</li>
137</ul>
138<h3 id="starting">로더 시작</h3>
139
140<p>{@link android.app.LoaderManager}는 {@link android.app.Activity} 또는
141{@link android.app.Fragment} 내에서 하나 이상의 {@link
142android.content.Loader} 인스턴스를 관리합니다. 액티비티 또는 프래그먼트당 {@link
143android.app.LoaderManager}는 하나씩밖에 없습니다.</p>
144
145<p>보통은
146액티비티의 {@link
147android.app.Activity#onCreate onCreate()} 메서드 내에서, 또는 프래그먼트의
148{@link android.app.Fragment#onActivityCreated onActivityCreated()} 메서드 내에서 {@link android.content.Loader}를 초기화합니다. 이렇게 하려면
149다음과 같은 방법을 따릅니다.</p>
150
151<pre>// Prepare the loader.  Either re-connect with an existing one,
152// or start a new one.
153getLoaderManager().initLoader(0, null, this);</pre>
154
155<p>{@link android.app.LoaderManager#initLoader initLoader()} 메서드는
156다음과 같은 인수를 취합니다.</p>
157<ul>
158  <li>로더를 식별하는 고유한 ID. 이 예시에서 ID는 0입니다.</li>
159<li>생성 시 로더에 제공할 선택적 인수
160(이 예시에서는 <code>null</code>).</li>
161
162<li>{@link android.app.LoaderManager.LoaderCallbacks} 구현. 로더 이벤트를 보고하기 위해
163{@link android.app.LoaderManager}가 이것을 호출합니다. 이 예시에서는
164 로컬 클래스가 {@link
165android.app.LoaderManager.LoaderCallbacks} 인터페이스를 구현하여 자신에 대한 참조인
166{@code this}를 통과합니다.</li>
167</ul>
168<p>{@link android.app.LoaderManager#initLoader initLoader()} 호출로 로더가 초기화되었고 활성 상태이도록
169확실히 합니다. 이로써 발생할 수 있는 결과가 두 가지 있습니다.</p>
170<ul>
171  <li>ID가 지정한 로더가 이미 존재하는 경우, 마지막으로 생성된 로더를
172재사용합니다.</li>
173  <li>ID가 지정한 로더가 존재하지 <em>않는</em> 경우,
174{@link android.app.LoaderManager#initLoader initLoader()}가
175{@link android.app.LoaderManager.LoaderCallbacks} 메서드 {@link android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}를 발생시킵니다.
176여기에서 인스턴트화할 코드를 구현하고 새 로더를 반환합니다.
177자세한 논의는 <a href="#onCreateLoader">onCreateLoader</a> 섹션을 참조하십시오.</li>
178</ul>
179<p>어떤 경우에든 주어진 {@link android.app.LoaderManager.LoaderCallbacks}
180구현은 해당 로더와 연관되어 있으며, 로더 상태가 변경되면
181이것이 호출됩니다.  이 호출의 그러한 시점에서 발신자가
182시작된 상태에 있으며 요청한 로더가 이미 존재하고 자신의 데이터를
183생성해 놓은 경우, 시스템은 즉시 {@link
184android.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()}를
185호출합니다({@link android.app.LoaderManager#initLoader initLoader()} 도중).
186따라서 이런 일이 발생할 것에 대비해두어야만 합니다. 이 콜백에 대한 자세한 논의는 <a href="#onLoadFinished">
187onLoadFinished</a>를 참조하십시오.</p>
188
189<p>{@link android.app.LoaderManager#initLoader initLoader()}
190메서드는 생성된 {@link android.content.Loader}를 반환하지만, 이에 대한 참조를 캡처하지 않아도 된다는 점을
191유의하십시오. {@link android.app.LoaderManager}는 로더의 수명을
192자동으로 관리합니다. {@link android.app.LoaderManager}는
193필요에 따라 로딩을 시작하고 중단하며, 로더와 그에 연관된 콘텐츠의 상태를
194유지관리합니다. 이것이 시사하는 바와 같이, 로더와 직접적으로
195상호 작용하는 경우는 극히 드뭅니다(다만, 로더의 행동을 미세하게 조정하기 위해 로더 메서드를 사용하는 사례를 알아보려면
196<a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html"> LoaderThrottle</a> 샘플을 참조하면 좋습니다).
197가장 보편적으로 사용되는 메서드는 {@link
198android.app.LoaderManager.LoaderCallbacks}로, 이를 사용해 특정한 이벤트가 일어났을 때
199로딩 프로세스에 개입하게 됩니다. 이 주제에 대한 자세한 논의는 <a href="#callback">LoaderManager 콜백 사용하기</a>를 참조하십시오.</p>
200
201<h3 id="restarting">로더 다시 시작</h3>
202
203<p>위에서 나타난 것과 같이 {@link android.app.LoaderManager#initLoader initLoader()}를 사용하는 경우,
204이것은 지정된 ID에 해당되는 기존 로더가 있으면 그것을 사용합니다.
205없으면, 하나 생성합니다. 하지만 때로는 오래된 데이터를 폐기하고 새로 시작하고 싶을 때가
206있습니다.</p>
207
208<p>오래된 데이터를 폐기하려면 {@link
209android.app.LoaderManager#restartLoader restartLoader()}를 사용합니다. 예를 들어, 다음의
210{@link android.widget.SearchView.OnQueryTextListener} 구현은
211사용자의 쿼리가 변경되면 로더를 다시 시작합니다. 로더를 다시 시작해야 수정된 검색 필터를 사용하여
212새 쿼리를 수행할 수 있습니다.</p>
213
214<pre>
215public boolean onQueryTextChanged(String newText) {
216    // Called when the action bar search text has changed.  Update
217    // the search filter, and restart the loader to do a new query
218    // with this filter.
219    mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
220    getLoaderManager().restartLoader(0, null, this);
221    return true;
222}</pre>
223
224<h3 id="callback">LoaderManager 콜백 사용하기</h3>
225
226<p>{@link android.app.LoaderManager.LoaderCallbacks}는 클라이언트가
227{@link android.app.LoaderManager}와 상호 작용할 수 있게 해주는 콜백 인터페이스입니다. </p>
228<p>로더, 특히 {@link android.content.CursorLoader}는 중단된 후에도
229자신의 데이터를 유지할 것으로 기대됩니다. 이 때문에 애플리케이션이
230액티비티 또는 프래그먼트의 @link android.app.Activity#onStop
231onStop()} 및 {@link android.app.Activity#onStart onStart()}를 가로질러 데이터를 유지할 수 있고,
232따라서 사용자가 애플리케이션에 되돌아오면 데이터가 다시 로딩되기를 기다리지
233않아도 됩니다. 새 로더를 언제 생성해야 할지 알아보려면 {@link android.app.LoaderManager.LoaderCallbacks}
234메서드를 사용합니다. 또한 로더의 데이터 사용을 중단할 때가 되면
235이를 애플리케이션에 알리는 데에도 이것을 사용할 수 있습니다.</p>
236
237<p>{@link android.app.LoaderManager.LoaderCallbacks}에는 다음과 같은 메서드가
238포함됩니다.</p>
239<ul>
240  <li>{@link android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()} -
241주어진 ID에 대하여 인스턴트화하고 새 {@link android.content.Loader}를 반환합니다.
242</li></ul>
243<ul>
244  <li> {@link android.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()}
245- 이전에 생성된 로더가 로딩을 완료하면 호출됩니다.
246</li></ul>
247<ul>
248  <li>{@link android.app.LoaderManager.LoaderCallbacks#onLoaderReset onLoaderReset()}
249- 이전에 생성된 로더가 휴식 중이라서 해당되는 데이터를 사용할 수 없을 경우
250호출됩니다.
251</li>
252</ul>
253<p>이러한 메서드는 다음 섹션에 보다 자세하게 설명되어 있습니다.</p>
254
255<h4 id ="onCreateLoader">onCreateLoader</h4>
256
257<p>로더에 액세스하려 시도하는 경우(예를 들어 {@link
258android.app.LoaderManager#initLoader initLoader()}를 통해), 로더는 해당 ID가 지정하는 로더가 존재하는지
259여부를 확인합니다. 그런 로더가 없으면, {@link
260android.app.LoaderManager.LoaderCallbacks} 메서드 {@link
261android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}를 발생시킵니다. 여기에서
262새 로더를 생성합니다. 이것은 보통 {@link
263android.content.CursorLoader}이지만, 개발자 나름대로 {@link
264android.content.Loader} 하위 클래스를 구현해도 됩니다. </p>
265
266<p>이 예시에서는, {@link
267android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}
268 콜백 메서드가 {@link android.content.CursorLoader}를 생성합니다.
269{@link android.content.CursorLoader}를 자체 생성자 메서드를 사용하여 구축해야 합니다. 이것은
270{@link
271android.content.ContentProvider}로 쿼리를 수행하기 위해 필요한 모든 정보 집합을 필요로 합니다. 구체적으로 필요한 것은 다음과 같습니다.</p>
272<ul>
273  <li><em>uri</em> — 검색할 콘텐츠의 URI입니다. </li>
274  <li><em>예측</em> — 반환할 열 목록입니다.
275<code>null</code>을 전달하면 모든 열을 반환하며, 이는 비효율적입니다. </li>
276  <li><em>선택</em> — 반환할 행을 선언하는 필터로,
277SQL WHERE 절로 형식이 설정됩니다(WHERE 자체는 제외).
278<code>null</code>을 반환하면 주어진 URI에 대한 모든 행을 반환합니다. </li>
279  <li><em>selectionArgs</em> — 선택에 ?를 포함해도 됩니다. 이렇게 하면
280이것이 <em>selectionArgs</em>에서 가져온 값으로 교체되며, 이때 선택에 표시되는
281순서를 따릅니다. 값은 문자열로 바인딩됩니다. </li>
282  <li><em>sortOrder</em> — SQL ORDER BY 절 형식으로 설정된
283행의 순서 지정 방법입니다(ORDER BY 자체는 제외). <code>null</code>을
284전달하면 기본 정렬 순서를 사용하는데, 이는 순서가 없습니다.</li>
285</ul>
286<p>예:</p>
287<pre>
288 // If non-null, this is the current filter the user has provided.
289String mCurFilter;
290...
291public Loader&lt;Cursor&gt; onCreateLoader(int id, Bundle args) {
292    // This is called when a new Loader needs to be created.  This
293    // sample only has one Loader, so we don't care about the ID.
294    // First, pick the base URI to use depending on whether we are
295    // currently filtering.
296    Uri baseUri;
297    if (mCurFilter != null) {
298        baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
299                  Uri.encode(mCurFilter));
300    } else {
301        baseUri = Contacts.CONTENT_URI;
302    }
303
304    // Now create and return a CursorLoader that will take care of
305    // creating a Cursor for the data being displayed.
306    String select = &quot;((&quot; + Contacts.DISPLAY_NAME + &quot; NOTNULL) AND (&quot;
307            + Contacts.HAS_PHONE_NUMBER + &quot;=1) AND (&quot;
308            + Contacts.DISPLAY_NAME + &quot; != '' ))&quot;;
309    return new CursorLoader(getActivity(), baseUri,
310            CONTACTS_SUMMARY_PROJECTION, select, null,
311            Contacts.DISPLAY_NAME + &quot; COLLATE LOCALIZED ASC&quot;);
312}</pre>
313<h4 id="onLoadFinished">onLoadFinished</h4>
314
315<p>이 메서드는 이전에 생성된 로더가 로딩을 완료하면 호출됩니다.
316이 로더에 대해 제공된 마지막 데이터가 릴리스되기 전에 틀림없이
317이 메서드가 호출됩니다.  이 시점에서 오래된 데이터의
318사용 내용을 모두 제거해야 하지만(곧 릴리스될 것이므로), 데이터 릴리스를 직접 수행해서는 안 됩니다. 해당 데이터는
319로더의 소유이며, 로더가 알아서 처리할 것이기 때문입니다.</p>
320
321
322<p>로더는 애플리케이션이 데이터를 더 이상 사용하지 않는다는 사실을 알게 되면 곧바로 해당 데이터를
323릴리스할 것입니다.  예를 들어 데이터가 {@link
324android.content.CursorLoader}의 커서인 경우, 거기에서 직접 {@link
325android.database.Cursor#close close()}를 호출하면 안 됩니다. 커서가
326{@link android.widget.CursorAdapter}에 배치되는 경우, {@link
327android.widget.SimpleCursorAdapter#swapCursor swapCursor()} 메서드를 사용해야 합니다. 그래야
328오래된 {@link android.database.Cursor}가 종료되지 않습니다. 예:</p>
329
330<pre>
331// This is the Adapter being used to display the list's data.<br
332/>SimpleCursorAdapter mAdapter;
333...
334
335public void onLoadFinished(Loader&lt;Cursor&gt; loader, Cursor data) {
336    // Swap the new cursor in.  (The framework will take care of closing the
337    // old cursor once we return.)
338    mAdapter.swapCursor(data);
339}</pre>
340
341<h4 id="onLoaderReset">onLoaderReset</h4>
342
343<p>이 메서드는 이전에 생성된 로더가 휴식 중이라서 해당되는 데이터를 사용할 수 없는 경우
344호출됩니다. 이 콜백을 사용하면 데이터가 언제 릴리스될지 알아낼 수 있어
345이에 대한 참조를 직접 제거할 수 있습니다.  </p>
346<p>이 구현은
347{@link android.widget.SimpleCursorAdapter#swapCursor swapCursor()}를 호출하며,
348이때 값은 <code>null</code>을 씁니다.</p>
349
350<pre>
351// This is the Adapter being used to display the list's data.
352SimpleCursorAdapter mAdapter;
353...
354
355public void onLoaderReset(Loader&lt;Cursor&gt; loader) {
356    // This is called when the last Cursor provided to onLoadFinished()
357    // above is about to be closed.  We need to make sure we are no
358    // longer using it.
359    mAdapter.swapCursor(null);
360}</pre>
361
362
363<h2 id="example">예</h2>
364
365<p>일례를 제시하기 위해 아래에 {@link android.widget.ListView}를 표시하는 {@link
366android.app.Fragment}의 전체 구현을 나타내었습니다. 여기에는
367연락처 콘텐츠 제공자에 대한 쿼리 결과가 들어 있습니다. 이것은 {@link
368android.content.CursorLoader}를 사용하여 제공자에 대한 쿼리를 관리합니다.</p>
369
370<p>이 예시에서 나타낸 바와 같이 애플리케이션이 사용자의 연락처에 액세스하려면
371애플리케이션의 매니페스트에
372{@link android.Manifest.permission#READ_CONTACTS READ_CONTACTS} 권한이 포함되어 있어야 합니다.</p>
373
374<pre>
375public static class CursorLoaderListFragment extends ListFragment
376        implements OnQueryTextListener, LoaderManager.LoaderCallbacks&lt;Cursor&gt; {
377
378    // This is the Adapter being used to display the list's data.
379    SimpleCursorAdapter mAdapter;
380
381    // If non-null, this is the current filter the user has provided.
382    String mCurFilter;
383
384    @Override public void onActivityCreated(Bundle savedInstanceState) {
385        super.onActivityCreated(savedInstanceState);
386
387        // Give some text to display if there is no data.  In a real
388        // application this would come from a resource.
389        setEmptyText(&quot;No phone numbers&quot;);
390
391        // We have a menu item to show in action bar.
392        setHasOptionsMenu(true);
393
394        // Create an empty adapter we will use to display the loaded data.
395        mAdapter = new SimpleCursorAdapter(getActivity(),
396                android.R.layout.simple_list_item_2, null,
397                new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS },
398                new int[] { android.R.id.text1, android.R.id.text2 }, 0);
399        setListAdapter(mAdapter);
400
401        // Prepare the loader.  Either re-connect with an existing one,
402        // or start a new one.
403        getLoaderManager().initLoader(0, null, this);
404    }
405
406    @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
407        // Place an action bar item for searching.
408        MenuItem item = menu.add(&quot;Search&quot;);
409        item.setIcon(android.R.drawable.ic_menu_search);
410        item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
411        SearchView sv = new SearchView(getActivity());
412        sv.setOnQueryTextListener(this);
413        item.setActionView(sv);
414    }
415
416    public boolean onQueryTextChange(String newText) {
417        // Called when the action bar search text has changed.  Update
418        // the search filter, and restart the loader to do a new query
419        // with this filter.
420        mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
421        getLoaderManager().restartLoader(0, null, this);
422        return true;
423    }
424
425    @Override public boolean onQueryTextSubmit(String query) {
426        // Don't care about this.
427        return true;
428    }
429
430    @Override public void onListItemClick(ListView l, View v, int position, long id) {
431        // Insert desired behavior here.
432        Log.i(&quot;FragmentComplexList&quot;, &quot;Item clicked: &quot; + id);
433    }
434
435    // These are the Contacts rows that we will retrieve.
436    static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {
437        Contacts._ID,
438        Contacts.DISPLAY_NAME,
439        Contacts.CONTACT_STATUS,
440        Contacts.CONTACT_PRESENCE,
441        Contacts.PHOTO_ID,
442        Contacts.LOOKUP_KEY,
443    };
444    public Loader&lt;Cursor&gt; onCreateLoader(int id, Bundle args) {
445        // This is called when a new Loader needs to be created.  This
446        // sample only has one Loader, so we don't care about the ID.
447        // First, pick the base URI to use depending on whether we are
448        // currently filtering.
449        Uri baseUri;
450        if (mCurFilter != null) {
451            baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
452                    Uri.encode(mCurFilter));
453        } else {
454            baseUri = Contacts.CONTENT_URI;
455        }
456
457        // Now create and return a CursorLoader that will take care of
458        // creating a Cursor for the data being displayed.
459        String select = &quot;((&quot; + Contacts.DISPLAY_NAME + &quot; NOTNULL) AND (&quot;
460                + Contacts.HAS_PHONE_NUMBER + &quot;=1) AND (&quot;
461                + Contacts.DISPLAY_NAME + &quot; != '' ))&quot;;
462        return new CursorLoader(getActivity(), baseUri,
463                CONTACTS_SUMMARY_PROJECTION, select, null,
464                Contacts.DISPLAY_NAME + &quot; COLLATE LOCALIZED ASC&quot;);
465    }
466
467    public void onLoadFinished(Loader&lt;Cursor&gt; loader, Cursor data) {
468        // Swap the new cursor in.  (The framework will take care of closing the
469        // old cursor once we return.)
470        mAdapter.swapCursor(data);
471    }
472
473    public void onLoaderReset(Loader&lt;Cursor&gt; loader) {
474        // This is called when the last Cursor provided to onLoadFinished()
475        // above is about to be closed.  We need to make sure we are no
476        // longer using it.
477        mAdapter.swapCursor(null);
478    }
479}</pre>
480<h3 id="more_examples">추가 예</h3>
481
482<p>로더를 사용하는 법을 보여주는 몇 가지 다른 예가 <strong>ApiDemos</strong>에
483소개되어 있습니다.</p>
484<ul>
485  <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderCursor.html">
486LoaderCursor</a> — 위에 표시된 코드 조각의 완전한
487버전입니다.</li>
488  <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html"> LoaderThrottle</a> — 데이터가 변경될 때 콘텐츠 제공자가
489수행하는 쿼리의 수를 줄이기 위해 제한을 사용하는 방법을 예시로 나타낸 것입니다.</li>
490</ul>
491
492<p>SDK 샘플을 다운로드하고 설치하는 데 대한 정보는 <a href="http://developer.android.com/resources/samples/get.html">샘플
493가져오기</a>를 참조하십시오. </p>
494
495