Loader의 사용
Loader는 안드로이드 3.0부터 제공되는 api로 activity와 fragment의 복잡한 생명주기로 인하여startManagingCurosr()와 requery()를 대체한다.
즉, Activity와 Fragment의 변화에 따라 DB의 requery 작업을 수행하는 역할을 한다.
로더의 사용은 아래 세단계로 이루어 진다.
1. 각 Activity나 fragment에서 LoaderManager 생성
2. LoaderManager.LoaderCallabacks 구현
3. Loader의 생성 -> CursorLoader나 AsyncTaskLoader 사용
아래는 fragment에서 Loader를 이용하여 list를 만드는 예제이다.
public class FragmentLoader extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentManager fm = getFragmentManager();
if (fm.findFragmentById(android.R.id.content) == null) {
CursorLoaderListFragment list = new CursorLoaderListFragment();
fm.beginTransaction().add(android.R.id.content, list).commit();
}
}
public static class CursorLoaderListFragment extends ListFragment
implements LoaderManager.LoaderCallbacks<Cursor> {
SimpleCursorAdapter mAdapter;
String mCurFilter;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setEmptyText("No phone numbers"); //빈 화면에서 보여줄 문구
setHasOptionsMenu(true); // ActionBar를 사용하기 위해 설정
//cursor 부분에 null을 넘겨준다.
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);
getLoaderManager().initLoader(0, null, this); // Loader init
}
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,
};
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Uri baseUri = Contacts.CONTENT_URI;
String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
+ Contacts.HAS_PHONE_NUMBER + "=1) AND ("
+ Contacts.DISPLAY_NAME + " != '' ))";
//CursorLoader를 이용하여 Loader를 생성
return new CursorLoader(getActivity(), baseUri,
CONTACTS_SUMMARY_PROJECTION, select, null,
Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
//Load가 완료되면 cursor swap를한다.
mAdapter.swapCursor(data);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
//Loader 사용이 완료되면 cursor 해제
mAdapter.swapCursor(null);
}
}
}
initLoader
- id: loader를 구분하기 위한 id 설정 - 여러개의 contentProvider를 사용한다면 구분해야 한다.
- args: loader 생성시 추가적으로 bundle을 넘겨줄 수 있다.
- callback: LoaderCallback을 생성하여 전달한다.
LoaderManager.LoaderCallbacks
1. onCreateLoader()
initLoader()에 의해서 호출되는 method로 Loader 객체를 생성한다.
id와 args는 intiLoader()에서 넘어온다. 객체를 직접 생성하기 보다는 AsyncTaskLoader나 CursorLoader를 이용하여 생성한다.
2. onLoadFinished()
fragment 생명주기에 따라서 읽은 자료를 살제할때 사용한다.
Loader를 이용한 데이터 Search
사실 위 예제는 굳이 loader를 쓰지 않아도 된다. asyncQueryHandler를 사용해도 되고, 그냥 cursor adpater를 사용해도 된다.
단, 화면에서 검색기능을 이용하여, 실시간으로 list나, drop down view를 업데이트 해주는 시나리오에서는 loader를 사용하면 매우 편리하게 구현할 수 있다.
아래는 ActionBar에서 searchView를 이용하여 검색하고, 해당 결과를 화면에 실시간으로 보여주는 예제이다.
1. SearchView의 변경을 확인하기 위해 onQueryTextListener를 구현한다.
2. Loader의 구현을 위해 Loader.Callbacks를 구현한다.
public class FragmentListCursorLoader extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentManager fm = getFragmentManager();
if (fm.findFragmentById(android.R.id.content) == null) {
CursorLoaderListFragment list = new CursorLoaderListFragment();
fm.beginTransaction().add(android.R.id.content, list).commit();
}
}
public static class CursorLoaderListFragment extends ListFragment
implements OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> {
SimpleCursorAdapter mAdapter;
String mCurFilter;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setEmptyText("No phone numbers"); //빈화면에 출력 할 문구
setHasOptionsMenu(true);
// cursor 자리는 null로 비워둔다
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);
getLoaderManager().initLoader(0, null, this); //loader 생성 호출
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
//ActionBar에 search bar를 만든다.
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); //Search bar에 Query Text Listener 부착
item.setActionView(sv);
}
@Override
public boolean onQueryTextChange(String newText) {
mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
//Search 문구가 변경되면 restartLoader()를 호출한다.
getLoaderManager().restartLoader(0, null, this);
return true;
}
@Override
public boolean onQueryTextSubmit(String query) {
return true;
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
TextView tv = ((TwoLineListItem)v).getText1();
Toast.makeText(getActivity(), tv.getText() + " Clicked", Toast.LENGTH_SHORT).show();
Log.i("FragmentComplexList", "Item clicked: " + id);
}
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,
};
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Uri baseUri;
if (mCurFilter != null) {
baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
Uri.encode(mCurFilter));
} else {
baseUri = Contacts.CONTENT_URI;
}
String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
+ Contacts.HAS_PHONE_NUMBER + "=1) AND ("
+ Contacts.DISPLAY_NAME + " != '' ))";
//CursorLoader를 이용하여 Loader 객체 생성
return new CursorLoader(getActivity(), baseUri,
CONTACTS_SUMMARY_PROJECTION, select, null,
Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
mAdapter.swapCursor(data);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
mAdapter.swapCursor(null);
}
}
1. restartLoader()
'개발이야기 > Android' 카테고리의 다른 글
Android JobScheduler API #2 (0) | 2017.10.27 |
---|---|
Android Background 작업을 위한 JobScheduler #1 (2) | 2017.10.26 |
Cursor Adapter#3 - Resource Cursor Adatper (0) | 2017.10.20 |
Cursor Adapter#2 - Custom Cursor Adapter (0) | 2017.10.19 |
Cursor Adapter#1 - Simple cursor adapter (0) | 2017.10.19 |