Loader

Android 2013. 10. 29. 13:26

Android 3.0에서 도입 된 로더에 의해, 활동이나 조각에서 데이터를 비동기 적으로로드가 쉽게되었습니다. 로더에는 다음과 같은 특징이 있습니다.

  • 모든  Activity  와  Fragment  에서 사용할 수 있습니다.
  • 비동기 데이터로드를 제공합니다.
  • 그 데이터 소스를 모니터링하고 내용이 변경 될 때 새로운 결과를 전달합니다.
  • 설정 변경이 있은 후에 다시 생성되면 자동으로 로더 커서에 다시 연결합니다. 따라서 다시 데이터를 요청해야합니다.

로더 API 요약

응용 프로그램 로더의 사용을 채택 할 것 같은 클래스와 인터페이스는 다양합니다. 다음 표는 그 요약입니다.

클래스 / 인터페이스설명
LoaderManager하나 이상의 Loader 인스턴스를 관리하기위한  Activity  와  Fragment  관련 추상 클래스입니다. 그러면 This 응용 프로그램이 Activity  와  Fragment  의 라이프 사이클과 연동하여 장시간 작업을 관리 할 수 있습니다. 이와 더불어  CursorLoader  을 사용하는 것이 가장 일반적이지만 응용 프로그램이 그 다음 데이터 유형을로드하기 위해 자신의 로더를 작성하는 것은 자유입니다. 활동 및 조각마다 하나의  LoaderManager  밖에 존재하지 않습니다. 하지만 LoaderManager  여러 로더를 가질 수 있습니다.

LoaderManager.LoaderCallbacks클라이언트가  LoaderManager  와 양방향으로 상호 작용하는 콜백 인터페이스입니다. 예를 들어, onCreateLoader ()  콜백 메서드를 사용하여 새 로더를 만듭니다.
Loader비동기 데이터로드를 실행하는 추상 클래스입니다. 이것이 로더의 기본 클래스입니다. CursorLoader  의 사용이 일반적이지만, 독자적인 서브 클래스를 구현할 수 있습니다. 로더가 활성화 동안 해당 데이터 소스는 모니터링 된 콘텐츠에 변화가 있으면 새로운 결과를 전달합니다.
AsyncTaskLoader처리하기위한  AsyncTask  를 제공하는 추상 로더입니다.
CursorLoaderAsyncTaskLoader  의 서브 클래스에서, ContentResolver  에 문의하여 Cursor  를 돌려줍니다. 이것은 커서의 문의에 대해 표준적인 방법으로  Loader  프로토콜을 구현 한 클래스에서 응용 프로그램의 UI를 차단하지 않도록 백그라운드 스레드에서 커서를 작동시키기 위해 AsyncTaskLoader  를 기반으로 구축되어 있습니다. 조각과 활동의 API에서 관리 된 쿼리를 실행하는 것이 아니라,ContentProvider  에서 데이터를 비동기 적으로로드하도록하기 위해이 로더를 사용하는 것이 가장 좋은 방법입니다.

위 표에있는 클래스와 인터페이스는 응용 프로그램 로더를 구현하는 데 사용하게 될 필수적인 구성 요소입니다. 만드는 로더 각각이 모두 필요하지 않지만 로더를 초기화하고 CursorLoader 등  Loader  클래스를 구현하기 위해서는, LoaderManager 의 참조가 항상 필요합니다. 응용 프로그램에서 이러한 클래스와 인터페이스를 사용하는 방법을 다음 절에서 설명합니다.


응용 프로그램 로더 사용

이 섹션에서는 Android 애플리케이션에서 로더의 사용 방법을 설명합니다. 로더를 사용하는 응용 프로그램은 일반적으로 다음이 포함되어 있습니다.

로더의 시작

LoaderManager  는 하나 이상의  Loader  인스턴스를  Activity  와  Fragment  에서 관리합니다. 활동 및 단편마다 하나의  LoaderManager  밖에 존재하지 않습니다.

일반적으로 활동의  onCreate ()  메서드 또는 조각  onActivityCreated ()  메서드에서 Loader  를 초기화합니다. 다음과 같이하여 작업을 수행합니다.

/ / Prepare the loader. Either re-connect with an existing one, 
/ / or start a new one.
getLoaderManager (). initLoader (0, null, this);

initLoader ()  메서드는 다음 매개 변수를받습니다.

  • 로더를 식별하는 고유 ID이 예제에서는 ID는 0.
  • 로더의 구축시에 공급하는 선택적 인수 (이 예제에서는  null  ).
  • LoaderManager.LoaderCallbacks  구현에서 LoaderManager  이것을 호출하여 로더 이벤트를 전한다. 이 예에서는 로컬 클래스가 LoaderManager.LoaderCallbacks  인터페이스를 구현하므로 전달 참조는 자신의  this  된다.

initLoader ()  를 호출하여 로더의 초기화 및 활성화가 이루어집니다. 다음 두 가지 결과가 발생할 수 있습니다.

  • 지정된 ID의 로더가 이미 존재하는 경우에는 마지막으로 만들어진 로더가 재사용된다.
  • 지정된 ID의 로더가 존재 하지 않는 경우 initLoader ()  이  LoaderManager.LoaderCallbacks  방법의  onCreateLoader ()  트리거가된다. 여기가 새로운 로더를 인스턴스화하고 반환 코드를 구현하는 장소가된다. onCreateLoader  섹션을 참조하십시오.

어느 경우에서도 구현 된  LoaderManager.LoaderCallbacks  는 로더와 연관되어 로더의 상태가 변경 될 때 호출되는 것입니다. 이 호출하려고 할 때 호출자가 시작된 상태에 있고, 요청 된 로더가 이미 존재 해, 그 데이터를 생성 한 경우 시스템이 즉시  onLoadFinished ()  를 호출하기 때문에 ( initLoader ()  의 사이)이 발생하게 준비해 둘 필요가 있습니다. 이 콜백에 대해 자세하게 해설하고있다  onLoadFinished  를 참조하십시오.

initLoader ()  메소드는 생성 된  Loader  를 반환하지만 그것에 대한 참조를 가지고 있지 않은 점에 주목하십시오. LoaderManager  는 로더의 생존 상태를 자동으로 관리합니다. LoaderManager  는 필요할 때 선적 시작하고 중지 로더의 상태와 관련 콘텐츠를 보유하고 있습니다. 이것이 의미하는 것처럼 로더와 직접 통신하는 것은 거의 없습니다 (하지만 로더의 동작을 조정하려면 로더의 메서드를 사용할 수 있으므로, 그 예는 LoaderThrottle  샘플을 참조 하십시오). 이벤트가 발생할 때 선적 처리를 끼우는 목적으로 공통적으로 사용되는 것이  LoaderManager.LoaderCallbacks  방법입니다. 이 항목에 대한 자세한 설명은 LoaderManager 콜백 사용  을 참조하십시오.

로더 재개

initLoader ()  를 사용하면 위에서 설명한대로 지정된 ID를 가진 기존의 로더가있는 경우 그것을 사용합니다. 없으면 만듭니다. 오래된 데이터를 버리거나 다시하고 싶은 것도 있고합니다.

오래된 데이터를 버릴에는 restartLoader ()  를 사용합니다. 예를 들어이  SearchView.OnQueryTextListener  의 구현을 통해 사용자가 쿼리를 변경하면 로더가 재개됩니다. 로더는 개정 된 검색 필터를 사용하여 새 질문을 할 수 있도록 다시 시작해야합니다.

public boolean onQueryTextChanged (String newText) { 
/ / Called when the action bar search text has changed. Update
/ / the search filter, and restart the loader to do a new query
/ / with this filter.
mCurFilter =! TextUtils.isEmpty (newText )? newText : null;
getLoaderManager (). restartLoader (0, null, this);
return true;
}

LoaderManager 콜백 사용

LoaderManager.LoaderCallbacks  클라이언트가  LoaderManager  와 상호 작용 수 있도록하는 콜백 인터페이스입니다.

로다, 특히  CursorLoader  은 정지해도 그 데이터를 보유하고있는 것으로 예상되고 있습니다. 그러면 응용 프로그램, 활동 및 조각  onStop ()  와  onStart ()  메소드 사이에서 그 데이터를 지켜주고 있기 때문에 사용자가 응용 프로그램에 돌려 보낼 때 데이터를 다시로드를 기다릴 필요가 없습니다. 새로운 로더를 만들 타이밍을 알고 싶을 때나, 로더의 데이터 사용을 중지하고 타이밍을 응용 프로그램에 알리기 위해  LoaderManager.LoaderCallbacks  방법을 사용합니다.

LoaderManager.LoaderCallbacks  에는 다음 메서드가 포함되어 있습니다.

  • onLoadFinished () - 이전에 만든 로더가로드를 완료했을 때 호출된다.
  • onLoaderReset () - 이전에 만든 로더가 재설정되고 그로 인하여 그 데이터를 사용할 수 없게되었을 때 호출된다.

이러한 방법의 자세한 내용은 다음 섹션에서 설명합니다.

onCreateLoader

로더에 대한 액세스를 시도 할 때 (예를 들어  initLoader ()  를 사용하여) 지정된 ID로 기존 로더의 존재 여부를 확인합니다. 도하지 않으면 그 액세스가LoaderManager.LoaderCallbacks  방법의  onCreateLoader ()  트리거입니다. 여기가 새로운 로더를 작성하는 곳입니다. 일반적으로 이것이  CursorLoader  이지만, 독자적으로  Loader  의 서브 클래스를 구현할 수 있습니다.

이 예제에서는 onCreateLoader ()  콜백 메서드가  CursorLoader  을 만들고 있습니다. 생성자를 사용하여  CursorLoader  를 작성해야 거기에는  ContentProvider  에서 쿼리를 실행하는 데 필요한 모든 정보 집합이 필요합니다. 구체적으로는 다음이 필요합니다.

  • uri - 검색 할 컨텐츠 URI.
  • projection - 반환 열 목록. null  을 전달하면 모두 반환되지만 비효율적이다.
  • selection - SQL의 WHERE 절 (WHERE 자신은 제외)으로 포맷 된 반환 행 필터의 선언. null  를 전달 제공 한 URI에 대해 모든 행이 반환된다.
  • selectionArgs - selection에?이 있으면 그들이 나타나는 순서대로  selectionArgs  가 제공하는 값으로 그들을 대체합니다. 값은 String 배열로 바인딩된다.
  • sortOrder - SQL의 ORDER BY 절 (ORDER BY는 제외)로 포맷 된 행의 순서 성. null  을 전달하면 기본 정렬 순서로 정렬되지 않습니다.

다음은 그 예입니다.

// If non-null, this is the current filter the user has provided. 
String mCurFilter;
...
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// This is called when a new Loader needs to be created. This
// sample only has one Loader, so we don't care about the ID.
// First, pick the base URI to use depending on whether we are
// currently filtering.
Uri baseUri;
if (mCurFilter != null) {
baseUri
= Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
Uri.encode(mCurFilter));
} else {
baseUri
= Contacts.CONTENT_URI;
}

// Now create and return a CursorLoader that will take care of
// creating a Cursor for the data being displayed.
String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
+ Contacts.HAS_PHONE_NUMBER + "=1) AND ("
+ Contacts.DISPLAY_NAME + " != '' ))";
return new CursorLoader(getActivity(), baseUri,
CONTACTS_SUMMARY_PROJECTION
, select, null,
Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
}


onLoadFinished

이 메서드는 이전에 만들어진 로더가로드를 완료 할 때 호출됩니다. 이 메소드는,이 로더에 의해 제공된 마지막 데이터를 해제하기 전에 호출되도록 보장되어 있습니다. 이때 사용하는 기존 데이터는 모두 삭제해야하지만 (즉시 풀어 놓는 것이니까) 그 로더가 그 데이터를 보관하고 그 후에도 그것을 관리하기 때문에 자신의 데이터를 공개해야 는 없습니다.

로더는 응용 프로그램이 데이터를 더 이상 사용하지 않으면 안다고 데이터를 삭제합니다. 예를 들어, 데이터가  CursorLoader  가 제공하는 커서의 경우 스스로 그  close ()  를 호출해서는 없습니다. 커서가  CursorAdapter  에 놓여있는 경우 swapCursor ()  메서드를 사용하여 이전  Cursor  가 폐쇄되지 않도록해야합니다. 다음은 그 예입니다.

// This is the Adapter being used to display the list's data. 
SimpleCursorAdapter mAdapter;
...

public void onLoaderReset(Loader<Cursor> loader) {
// This is called when the last Cursor provided to onLoadFinished()
// above is about to be closed. We need to make sure we are no
// longer using it.
mAdapter
.swapCursor(null);
}

onLoaderReset

이 메서드는 이전에 만들어진 로더가 재설정 될 때마다 호출되며, 그로 인하여 그 데이터를 사용할 수 없게됩니다. 이 콜백 메서드는 데이터가 해방 되려고하는시기를 알 수 있으며, 그로 인하여 데이터 참조를 제거 할 수 있습니다.

이 구현은 다음과 같이  swapCursor ()  에  null  값을 전달하여 호출합니다.

// This is the Adapter being used to display the list's data. 
SimpleCursorAdapter mAdapter;
...

public void onLoaderReset(Loader<Cursor> loader) {
// This is called when the last Cursor provided to onLoadFinished()
// above is about to be closed. We need to make sure we are no
// longer using it.
mAdapter
.swapCursor(null);
}

실례

예를 들어, 연락처에 콘텐츠 공급자의 결과가 들어간  ListView  보기 Fragment 의 완전한 구현이 여기에 있습니다. 이것은 프로 바이더에서 쿼리를 관리하는 CursorLoader  을 사용하고 있습니다.

응용 프로그램이이 예제와 사용자의 연락처에 액세스하려면 매니페스트에  READ_CONTACTS  의 권한을 추가해야합니다.

public static class CursorLoaderListFragment extends ListFragment 
implements OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> {

// This is the Adapter being used to display the list's data.
SimpleCursorAdapter mAdapter;

// If non-null, this is the current filter the user has provided.
String mCurFilter;

@Override public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);

// Give some text to display if there is no data. In a real
// application this would come from a resource.
setEmptyText
("No phone numbers");

// We have a menu item to show in action bar.
setHasOptionsMenu
(true);

// Create an empty adapter we will use to display the loaded data.
mAdapter
= new SimpleCursorAdapter(getActivity(),
android
.R.layout.simple_list_item_2, null,
new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS },
new int[] { android.R.id.text1, android.R.id.text2 }, 0);
setListAdapter
(mAdapter);

// Prepare the loader. Either re-connect with an existing one,
// or start a new one.
getLoaderManager
().initLoader(0, null, this);
}

@Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// Place an action bar item for searching.
MenuItem item = menu.add("Search");
item
.setIcon(android.R.drawable.ic_menu_search);
item
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
SearchView sv = new SearchView(getActivity());
sv
.setOnQueryTextListener(this);
item
.setActionView(sv);
}

public boolean onQueryTextChange(String newText) {
// Called when the action bar search text has changed. Update
// the search filter, and restart the loader to do a new query
// with this filter.
mCurFilter
= !TextUtils.isEmpty(newText) ? newText : null;
getLoaderManager
().restartLoader(0, null, this);
return true;
}

@Override public boolean onQueryTextSubmit(String query) {
// Don't care about this.
return true;
}

@Override public void onListItemClick(ListView l, View v, int position, long id) {
// Insert desired behavior here.
Log.i("FragmentComplexList", "Item clicked: " + id);
}

// These are the Contacts rows that we will retrieve.
static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {
Contacts._ID,
Contacts.DISPLAY_NAME,
Contacts.CONTACT_STATUS,
Contacts.CONTACT_PRESENCE,
Contacts.PHOTO_ID,
Contacts.LOOKUP_KEY,
};
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// This is called when a new Loader needs to be created. This
// sample only has one Loader, so we don't care about the ID.
// First, pick the base URI to use depending on whether we are
// currently filtering.
Uri baseUri;
if (mCurFilter != null) {
baseUri
= Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
Uri.encode(mCurFilter));
} else {
baseUri
= Contacts.CONTENT_URI;
}

// Now create and return a CursorLoader that will take care of
// creating a Cursor for the data being displayed.
String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
+ Contacts.HAS_PHONE_NUMBER + "=1) AND ("
+ Contacts.DISPLAY_NAME + " != '' ))";
return new CursorLoader(getActivity(), baseUri,
CONTACTS_SUMMARY_PROJECTION
, select, null,
Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
}

public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
mAdapter
.swapCursor(data);
}

public void onLoaderReset(Loader<Cursor> loader) {
// This is called when the last Cursor provided to onLoadFinished()
// above is about to be closed. We need to make sure we are no
// longer using it.
mAdapter
.swapCursor(null);
}
}


: