ContentProvider없이 CursorLoader 사용


107

Android SDK 문서에 따르면 startManagingCursor()메서드가 더 이상 사용되지 않습니다.

이 메소드는 더 이상 사용되지 않습니다. 대신 LoaderManager와 함께 새 CursorLoader 클래스를 사용하십시오. Android 호환성 패키지를 통해 이전 플랫폼에서도 사용할 수 있습니다. 이 방법을 사용하면 활동이 활동의 ​​수명주기에 따라 지정된 커서의 수명주기를 관리 할 수 ​​있습니다. 즉, 활동이 중지되면 주어진 Cursor에서 자동으로 deactivate ()를 호출하고 나중에 다시 시작할 때 requery ()를 호출합니다. 활동이 파괴되면 관리되는 모든 커서가 자동으로 닫힙니다. HONEYCOMB 이상을 대상으로하는 경우 대신 getLoaderManager ()를 통해 사용할 수있는 LoaderManager를 대신 사용하는 것이 좋습니다.

그래서 저는 CursorLoader. 하지만 생성자에서 URI가 필요할 때 사용자 지정 CursorAdapter및 사용하지 않고 어떻게 사용할 수 있습니까?ContentProviderCursorLoader


우리가 컨텐트 프로없이 CursorAdapter를 사용하는 @ 알렉스 록우드 왜 나에게 제안 해주십시오 stackoverflow.com/questions/20419278/...

우리가 컨텐트 프로없이 CursorAdapter를 사용하는 이유는 내가 제안 해주십시오 stackoverflow.com/questions/20419278/...

답변:


155

콘텐츠 공급자가 필요없는 간단한 CursorLoader 를 작성했습니다 .

import android.content.Context;
import android.database.Cursor;
import android.support.v4.content.AsyncTaskLoader;

/**
 * Used to write apps that run on platforms prior to Android 3.0. When running
 * on Android 3.0 or above, this implementation is still used; it does not try
 * to switch to the framework's implementation. See the framework SDK
 * documentation for a class overview.
 *
 * This was based on the CursorLoader class
 */
public abstract class SimpleCursorLoader extends AsyncTaskLoader<Cursor> {
    private Cursor mCursor;

    public SimpleCursorLoader(Context context) {
        super(context);
    }

    /* Runs on a worker thread */
    @Override
    public abstract Cursor loadInBackground();

    /* Runs on the UI thread */
    @Override
    public void deliverResult(Cursor cursor) {
        if (isReset()) {
            // An async query came in while the loader is stopped
            if (cursor != null) {
                cursor.close();
            }
            return;
        }
        Cursor oldCursor = mCursor;
        mCursor = cursor;

        if (isStarted()) {
            super.deliverResult(cursor);
        }

        if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) {
            oldCursor.close();
        }
    }

    /**
     * Starts an asynchronous load of the contacts list data. When the result is ready the callbacks
     * will be called on the UI thread. If a previous load has been completed and is still valid
     * the result may be passed to the callbacks immediately.
     * <p/>
     * Must be called from the UI thread
     */
    @Override
    protected void onStartLoading() {
        if (mCursor != null) {
            deliverResult(mCursor);
        }
        if (takeContentChanged() || mCursor == null) {
            forceLoad();
        }
    }

    /**
     * Must be called from the UI thread
     */
    @Override
    protected void onStopLoading() {
        // Attempt to cancel the current load task if possible.
        cancelLoad();
    }

    @Override
    public void onCanceled(Cursor cursor) {
        if (cursor != null && !cursor.isClosed()) {
            cursor.close();
        }
    }

    @Override
    protected void onReset() {
        super.onReset();

        // Ensure the loader is stopped
        onStopLoading();

        if (mCursor != null && !mCursor.isClosed()) {
            mCursor.close();
        }
        mCursor = null;
    }
}

AsyncTaskLoader수업 만 필요합니다 . Android 3.0 이상 또는 호환성 패키지와 함께 제공되는 것입니다.

나는 또한와ListLoader 호환되며 LoadManager일반 java.util.List컬렉션 을 검색하는 데 사용되는를 작성 했습니다.


13
이것을 사용하는 멋진 코드 예제를 찾았습니다 -bitbucket.org/ssutee/418496_mobileapp/src/fc5ee705a2fd/demo/…- 매우 유용합니다!
Shushu

@Cristian 예를 들어 주셔서 감사합니다. 수업과 관련된 라이선스는 무엇입니까? 어떻게 재사용 할 수 있습니까?
codinguser

2
라이센스는 Apache 2.0입니다. 언제 어디서나 원할 때 재사용 할 수 있습니다. 개선 사항이 있으면 알려주십시오.
Cristian

14
좋은 물건! 사용자는 한 가지 제한 사항을 알고 있어야합니다. 이는 데이터 변경 사항을 새로 고치는 메커니즘이 없다는 것입니다 (로더가 수행해야하는 것처럼)
emmby

1
: 당신이 사람이 여기 @Jadeye ListLoaderSupportListLoader
크리스티안

23

콘텐츠 공급자 대신 데이터베이스 클래스를 사용하는 고유 한 로더를 작성하십시오. 가장 쉬운 방법은 CursorLoader호환성 라이브러리에서 클래스 의 소스를 가져와 공급자 쿼리를 자신의 db 도우미 클래스에 대한 쿼리로 바꾸는 것입니다.


1
이것이 제 생각에는 가장 쉬운 방법입니다. 내 앱 CursorLoader에서 SQLite 커서를 관리하기 위해 하위 항목을 만들었습니다. 생성자에서 appart loadInBackground는 공급자 쿼리를 커서 쿼리 로 대체하는 메서드 만 재정의하면 되었습니다
Jose_GD

14

SimpleCursorLoader는 간단한 솔루션이지만 데이터가 변경 될 때 로더 업데이트를 지원하지 않습니다. CommonsWare에는 SQLiteCursorLoader를 추가하고 데이터 변경시 다시 쿼리를 지원하는 loaderex 라이브러리가 있습니다.

https://github.com/commonsguy/cwac-loaderex


2
그러나 자동 재 쿼리를 사용하려면 UI와 업데이트에 동일한 로더를 사용해야하므로 백그라운드 서비스에 대한 유용성이 제한됩니다.
ge0rg

12

세 번째 옵션은 간단히 재정의하는 것입니다 loadInBackground.

public class CustomCursorLoader extends CursorLoader {
    private final ForceLoadContentObserver mObserver = new ForceLoadContentObserver();

    @Override
    public Cursor loadInBackground() {
        Cursor cursor = ... // get your cursor from wherever you like

        if (cursor != null) {
            // Ensure the cursor window is filled
            cursor.getCount();
            cursor.registerContentObserver(mObserver);
        }

        return cursor;
    }
};

데이터베이스가 변경 될 때 커서를 다시 쿼리하는 것도 처리합니다.

유일한주의 사항 : Google은 무한한 지혜가 그들의 패키지를 비공개로하기로 결정했기 때문에 다른 관찰자를 정의해야합니다. 클래스를 원래의 것과 동일한 패키지 (또는 compat 패키지)에 넣으면 실제로 원래 관찰자를 사용할 수 있습니다. 관찰자는 매우 가볍고 다른 곳에서는 사용되지 않으므로 큰 차이가 없습니다.


빠른 테스트에서 내 관찰은 커서가 콘텐츠 공급자를 대상으로하는 경우에만 registerContentObserver가 커서에 대해 호출된다는 것입니다. 이것을 확인 / 거부 할 수 있습니까?
Nick Campion 2012

1
반드시 ContentProvider 일 필요는 없습니다. 그러나 커서는 알림 uri (setNotificationUri)에 등록되어야하며, 그런 다음 ContentResolver.notifyChange를 호출하여 누군가 (일반적으로 ContentProvider이지만 무엇이든 가능)에 의해 알림을 받아야합니다.
Timo Ohr 2012

4
네. CustomLoader의 loadInBackground() 에서 커서를 반환하기 전에 cursor.setNotificationUri(getContext().getContentResolver(), uri);uri가 Uri.parse("content://query_slot1"). URI가 실제로 존재하는지 여부는 상관없는 것 같습니다. 그리고 일단 DB에서 작업을 마쳤습니다. getContentResolver().notifyChange(uri, null);트릭을 할 것이라고 말 하십시오. 그런 다음 쿼리 수가 적은 앱에 대해 상수 파일에 "query uri slot"을 거의 만들 수 없습니다. 나는 런타임에 DB 레코드를 삽입하는 것을 테스트하고 작동하는 것처럼 보이지만 여전히 좋은 연습인지 의심합니다. 어떠한 제안?
Yeung의

이 방법을 @Yeung의 제안과 함께 사용하고 있으며 데이터베이스 업데이트시 커서 자동 재로드를 포함하여 모든 것이 작동합니다.
DavidH 2015

unregisterContentObserver가 필요하지 않습니까?
GPack 2016-04-07

2

Timo Ohr가 제안한 세 번째 옵션은 Yeung의 의견과 함께 가장 간단한 대답을 제공합니다 (Occam의 면도기). 아래는 저에게 적합한 전체 수업의 예입니다. 이 클래스를 사용하는 데는 두 가지 규칙이 있습니다.

  1. 이 추상 클래스를 확장하고 getCursor () 및 getContentUri () 메소드를 구현하십시오.
  2. 기본 데이터베이스가 변경 될 때 (예 : 삽입 또는 삭제 후) 항상 다음을 호출하십시오.

    getContentResolver().notifyChange(myUri, null);

    여기서 myUri는 getContentUri () 메소드 구현에서 반환 된 것과 동일합니다.

다음은 내가 사용한 클래스의 코드입니다.

package com.example.project;

import android.content.Context;
import android.database.Cursor;
import android.content.CursorLoader;
import android.content.Loader;

public abstract class AbstractCustomCursorLoader extends CursorLoader
  {
    private final Loader.ForceLoadContentObserver mObserver = new Loader.ForceLoadContentObserver();

    public AbstractCustomCursorLoader(Context context)
      {
        super(context);
      }

    @Override
    public Cursor loadInBackground()
      {
        Cursor cursor = getCursor();

        if (cursor != null)
          {
            // Ensure the cursor window is filled
            cursor.getCount();
            cursor.registerContentObserver(mObserver);
          }

        cursor.setNotificationUri(getContext().getContentResolver(), getContentUri());
        return cursor;
      }

    protected abstract Cursor getCursor();
    protected abstract Uri getContentUri();
  }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.