2015-02-03 2 views
2

SQLiteCursors는 AIDL 파일을 내가 그들을 만들 때 나는 알림을 설정에도 불구하고,

내 SQLite는 커서가 변경 통보되지 않습니다 (2 회 수정 됨) 존재 때 DB를 통지하지 않습니다 통지되지 않습니다 액세스됩니다. 나는 방법을 테스트하기 위해이 같은 시도 : 나는 ContentResolver.notifyChange의 소스로 파고했을 때

weatherCursor.getCount(); // returns 1 
deleteAllRecords(); 
Uri uri = weatherCursor.getNotificationUri(); 
getContext().getContentResolver().notifyChange(uri, null); 
weatherCursor.getCount(); // still returns 1 
weatherCursor.close(); 
weatherCursor = getContext().getContentResolver().query(uri, null, null, null, null); 
weatherCursor.getCount(); //finally returns 0 

, 나는 그것이 IContentObserver라는 클래스를 사용하려고 발견,하지만 가져 오기가 해결 될 수 없다. 이것은 try 블록에서 수행하고 catch 블록은 비어 있으므로 자동으로 실패합니다.

마찬가지로 AbstractCursor.setNotificationUriIContentService이라는 클래스를 사용하려고하는 ContentResolver.registerContentObserver을 호출하고 catch 블록이있는 try 블록 내에서 호출합니다. 위와 달리 IContentService에 대한 import 문도 보지 못했습니다.

코멘트 작성자 (@Lawrence Choy)는 "IContentObserver은 실제로는 IContentObserver.aidl에 정의 된 자동 생성 파일입니다."라고 설명했습니다. 필자는 "IContentObserver.aidl"에 대한 전체 시스템을 조사했으며, Android Studio가 최근 검색을 저장하는 데 사용하는 파일 만이 내 컴퓨터에이 파일을 저장하지 않았습니다. 파일 위치가 확실치 않지만 SDK 관리자를 통해 빌드 도구와 API를 삭제하고 다시 설치해 보았습니다. 아직 어디에도 없습니다.

[다른 수정] 위의 코드 단편 (평가자에서 실행)은 다음 코드를 모두 관련 부분으로 압축하기위한 것이지만 여기에는 완전한 코드 집합이 있습니다. 첫 번째는 deleteAllRecords()의 코드입니다. 내 테스트 단위 중 하나에서 오류가 발생했습니다. 그것은 성공적으로 모든 레코드를 삭제하지만 내가 당연시 여겼던 커서를 업데이트하지 못했습니다. WeatherProvider.delete.query 방법 여기에

public static Cursor queryWholeTable(Context context, Uri uri){ 
    return context.getContentResolver().query(uri, null, null, null, null); 
} 

: 그리고 여기

public void deleteAllRecords() { 

    Cursor weatherCursor = queryWholeTable(mContext, WeatherEntry.CONTENT_URI); 
    Cursor locationCursor = queryWholeTable(mContext, LocationEntry.CONTENT_URI); 

    int initialWeatherCount = weatherCursor.getCount(); 
    int initialLocationCount = locationCursor.getCount(); 

    int deletedWeatherCount = mContext.getContentResolver().delete(
      WeatherEntry.CONTENT_URI, 
      null, 
      null 
    ); 
    int finalWeatherCount = weatherCursor.getCount(); 
    int deletedLocationCount = mContext.getContentResolver().delete(
      LocationEntry.CONTENT_URI, 
      null, 
      null 
    ); 
    int finalLocationCount = locationCursor.getCount(); 

    assertEquals(initialWeatherCount, deletedWeatherCount); 
//ASSERT BELOW FAILS! 
    assertEquals(0, finalWeatherCount); 
    weatherCursor.close(); 

    assertEquals(initialLocationCount, deletedLocationCount); 
//ASSERT BELOW FAILS! 
    assertEquals(0, finalLocationCount); 
    locationCursor.close(); 

    //Added when above tests failed to see if records were actually deleted. 

    weatherCursor = queryWholeTable(mContext, WeatherEntry.CONTENT_URI); 
    locationCursor = queryWholeTable(mContext, LocationEntry.CONTENT_URI); 

    int weatherVeryFinal = weatherCursor.getCount(); 
    int locationVeryFinal = locationCursor.getCount(); 

    assertEquals(0, weatherVeryFinal); 
    assertEquals(0, locationVeryFinal); 

    locationCursor.close(); 
    weatherCursor.close(); 

} 

.queryWholeTable입니다

@Override 
public int delete(Uri targetUri, String selection, String[] selectionArgs) { 
    final int match = sUriMatcher.match(targetUri); 
    final String table; 
    final Uri notificationUri; 
    switch (match) { 
     case WEATHER: 
      table = WeatherEntry.TABLE_NAME; 
      notificationUri = WeatherEntry.CONTENT_URI; 
      break; 
     case LOCATION: 
      table = LocationEntry.TABLE_NAME; 
      notificationUri = LocationEntry.CONTENT_URI; 
      break; 
     default: 
      throw new UnsupportedOperationException("Unknown delete uri: " + targetUri.toString()); 
    } 
    int affected = mOpenHelper.getWritableDatabase().delete(table, selection, selectionArgs); 
    if (selection == null || affected > 0) { 
     getContext().getContentResolver().notifyChange(notificationUri, null); 
    } 
    return affected; 
} 

public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 
        String sortOrder) { 
    // Here's the switch statement that, given a URI, will determine what kind of request it is, 
    // and query the database accordingly. 
    Cursor retCursor; 
    switch (sUriMatcher.match(uri)) { 
     // "weather/*/*" 
     case WEATHER_WITH_LOCATION_AND_DATE: { 
      retCursor = getWeatherByLocationSetting(uri, projection, sortOrder, true); 
      break; 
     } 
     // "weather/*" 
     case WEATHER_WITH_LOCATION: { 
      retCursor = getWeatherByLocationSetting(uri, projection, sortOrder, false); 
      break; 
     } 
     // "weather" 
     case WEATHER: { 
      retCursor = mOpenHelper.getReadableDatabase().query(
        WeatherEntry.TABLE_NAME, 
        projection, 
        selection, 
        selectionArgs, 
        null, 
        null, 
        sortOrder 
      ); 
      break; 
     } 
     // "location/*" 
     case LOCATION_ID: { 
      retCursor = mOpenHelper.getReadableDatabase().query(
        LocationEntry.TABLE_NAME, 
        projection, 
        LocationEntry._ID + " = " + ContentUris.parseId(uri), 
        selectionArgs, 
        null, 
        null, 
        sortOrder 
      ); 
      break; 
     } 
     // "location" 
     case LOCATION: { 
      retCursor = mOpenHelper.getReadableDatabase().query(
        LocationEntry.TABLE_NAME, 
        projection, 
        selection, 
        selectionArgs, 
        null, 
        null, 
        sortOrder 
      ); 
      break; 
     } 

     default: 
      throw new UnsupportedOperationException("Unknown uri: " + uri); 
    } 
    retCursor.setNotificationUri(getContext().getContentResolver(), uri); 
    return retCursor; 
} 

당신이 볼 수 있듯이, .setNotificationUri 항상 커서가 생성 될 때 호출은, 어떤 것이 삭제되면 항상 .notifyChange이 호출됩니다.

+0

당신은'ContentResolver'를 오버라이드하려고하거나 단순히 일반적인 사용으로 프로젝트를 빌드 할 수 없습니까? –

+0

ContentResolver를 오버라이드하려고하지 않고 실제로 프로젝트가 빌드 및 실행되지만 커서에 변경 사항이 통지되지 않고'ContentResolver.notifyChange()'에 대한 소스를 살펴볼 때 나는 그것은 IContentObserver.notifyChange'라고 불렸고 당황했습니다. 이로 인해 내 코드가 문제를 일으키고 있는지 또는 누락 된 파일인지 여부를 알 수 없습니다. –

+2

이것이 문제의 원인이 아닌 것 같습니다. 'IContentObserver'는 실제로'IContentObserver.aidl'에 정의 된 자동 생성 된 파일입니다. 이 파일은 SDK 관리자에서 다운로드 한 소스에없는 Android 소스 파일에서 빌드 할 때 생성됩니다. 응용 프로그램을 실행하면 장치에서이 파일을 사용할 수있게됩니다. –

답변

1

AIDL이 그 이유가 아닙니다. 그 이유는 데이터를로드하는 방법입니다. SQLiteDatabase에서 직접 액세스합니까? 아니면 Loader를 사용하여 쿼리합니까?

커서는 "지연로드 (lazy-loading)"패턴 중 하나 일 뿐이며 기본 데이터에 어떤 일이 일어나는지 알지 못합니다. 콘텐츠 변경 사항을 감시하려면 방문자 (예 : 로더)가 필요합니다.

1) loader/contentprovider를 사용하지 않으면 커서에 알림이 표시되지 않으므로 커서 무효화를 직접 처리해야합니다.

2) Loader를 사용하는 경우 - 일부 Uri에서 데이터를로드하는 CursorLoader라고 생각합니다. 이 경우에는 SQLite CRUD 연산을 처리하고 내용 변경에 대한 모든 가입자 (예 : 기존 로더)에 알리는 ContentProvider를 갖는 것이 일반적입니다.

1) 또는 2)의 yout 구현이 잘못되었다고 생각됩니다.

ContentProvider, Loader 및 몇 가지 예제를 더 자세히 읽어 보시기 바랍니다. my repo의 앱 중 하나를 결제 할 수 있습니다 (예 : this one (CursorLoader 사용법, LoaderManager 구현, ContentProvider 구현)을 확인하고 IDE에서 작동하는지 살펴보십시오.

+0

ContentProvider를 사용하려고하는데 구현이 잘못되었다는 것을 알고 있습니다. –

+0

나는 로더를 보았고, 자동적으로 업데이트되었지만,'.notifyChange'와'.setNotificationUri'가 일반 커서를 위해해야하는 것은 아니다. 로더의 근본적인 장점은 비동기식으로 작업한다는 것인데, 알아두면 좋지만 내 문제는 해결되지 않습니다. –

+0

'.setNotificationUri'는 Cursor에게 ContentProvider Uri의 변경 사항을 모니터링하도록 지시합니다. ContentProvider 구현이 잘못된 경우 커서에 알림이 전달되지 않을 수 있습니다. SQLite db에 직접 데이터를 삽입하고이 목적으로 ContentResolver를 사용하지 않으면 Cursor가 업데이트되지 않습니다. 로더의 장점 : 비동기식, 메모리 누출 방지, 커서를 관리, 데이터 변경을 모니터합니다. – Drew

관련 문제