5

OSM 데이터를 사용하여 주어진 위치 집합의 사용자에게 경로를 제공하는 Android 앱을 개발 중입니다. 사용자는 SearchView로 들어가기를 원하는 위치를 입력 할 수 있으며, 사용자 유형에 따라 검색 결과를 필터링하여 결과를 좁히면 드롭 다운 ListView에서 대상을 선택할 수 있습니다. 이 필터링은 onQueryTextChange()를 사용하여 수행됩니다. ContentProvider를 사용하여 데이터베이스에서이 데이터를 쿼리하고 LoaderManager.LoaderCallbacks 인터페이스를 구현하여 ContentProvider를 다시 쿼리하고 사용할 어댑터에 대한 새 데이터를 제공합니다.CursorLoader를 사용하여 ContentProvider에서 데이터를로드하는 중 오류가 발생했습니다.

이 모든 것들은 광대하고 대부분의 시간 동안 작동하며 예상대로 정확하게 수행됩니다. 그러나, 드물게, 응용 프로그램이 다음 스택 추적으로 중단됩니다. 나는 사용자가 입력됩니다로 발생 다른 검색과 다른 시간을 입력합니다 SearchView을 취소 할 때

java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteQuery: SELECT _id, suggest_text_1, suggest_intent_data FROM Locations WHERE (suggest_text_1 LIKE ?) 
    at android.database.sqlite.SQLiteClosable.acquireReference(SQLiteClosable.java:55) 
    at android.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:58) 
    at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:152) 
    at android.database.sqlite.SQLiteCursor.onMove(SQLiteCursor.java:124) 
    at android.database.AbstractCursor.moveToPosition(AbstractCursor.java:214) 
    at android.database.CursorWrapper.moveToPosition(CursorWrapper.java:162) 
    at android.support.v4.widget.CursorAdapter.getItemId(CursorAdapter.java:225) 
    at android.widget.AdapterView.rememberSyncState(AdapterView.java:1195) 
    at android.widget.AdapterView$AdapterDataSetObserver.onChanged(AdapterView.java:811) 
    at android.widget.AbsListView$AdapterDataSetObserver.onChanged(AbsListView.java:6280) 
    at android.database.DataSetObservable.notifyChanged(DataSetObservable.java:37) 
    at android.widget.BaseAdapter.notifyDataSetChanged(BaseAdapter.java:50) 
    at android.support.v4.widget.CursorAdapter.swapCursor(CursorAdapter.java:347) 
    at android.support.v4.widget.SimpleCursorAdapter.swapCursor(SimpleCursorAdapter.java:326) 
    at android.support.v4.widget.CursorAdapter.changeCursor(CursorAdapter.java:315) 
    at android.support.v4.widget.CursorFilter.publishResults(CursorFilter.java:68) 
    at android.widget.Filter$ResultsHandler.handleMessage(Filter.java:282) 
    at android.os.Handler.dispatchMessage(Handler.java:102) 
    at android.os.Looper.loop(Looper.java:136) 
    at android.app.ActivityThread.main(ActivityThread.java:5017) 
    at java.lang.reflect.Method.invokeNative(Native Method) 
    at java.lang.reflect.Method.invoke(Method.java:515) 
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595) 
    at dalvik.system.NativeStart.main(Native Method) 

는 일반적으로이 오류가 발생합니다.

편집 : 부분적인 ContentProvider가 포함되어 삽입, 삭제 및 업데이트가 생략됩니다.

public class SearchContentProvider extends ContentProvider { 
    private DbHelper helper; 

    private static final String AUTH = "com.dgh1.Navigation.SearchContentProvider"; 
    private static final String LOCATIONS_PATH = "Location"; 
    private static final String GEOFENCES_PATH = "Fences"; 
    private static final String PEOPLE_PATH = "People"; 
    public static final Uri LOCATION_URI = Uri.parse("content://" + AUTH + "/" +  LOCATIONS_PATH); 
    public static final Uri GEOFENCE_URI = Uri.parse("content://" + AUTH + "/" + GEOFENCES_PATH); 
    public static final Uri PEOPLE_URI = Uri.parse("content://" + AUTH + "/" + PEOPLE_PATH); 

    private static final int LOCATIONS = 10; 
    private static final int NAME_LOCATION = 11; 
    private static final int NODE_LOCATION = 20; 
    private static final int GEOFENCES = 30; 
    private static final int MARKERS = 40; 
    private static final int GEOFENCE = 50; 
    private static final int PEOPLE = 60; 
    private static final int PERSON = 70; 

    private static final String URI_ERROR = "Unknown URI: "; 

    private static final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); 

    static { 
     matcher.addURI(AUTH, LOCATIONS_PATH, LOCATIONS); 
     matcher.addURI(AUTH, GEOFENCES_PATH, GEOFENCES); 
     matcher.addURI(AUTH, PEOPLE_PATH, PEOPLE); 
     matcher.addURI(AUTH, LOCATIONS_PATH + "/#", NODE_LOCATION); 
     matcher.addURI(AUTH, GEOFENCES_PATH + "/#", GEOFENCE); 
     matcher.addURI(AUTH, PEOPLE_PATH + "/#", PERSON); 
     matcher.addURI(AUTH, LOCATIONS_PATH + "/*", NAME_LOCATION); 
    } 

    @Override 
    public boolean onCreate() { 
     helper = new DbHelper(getContext()); 
     return true; 
    } 

    @Override 
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { 
     Cursor cursor = null; 
     int uriType = matcher.match(uri); 

     switch (uriType) { 
      case LOCATIONS: 
       if (selectionArgs == null) { 
        cursor = helper.getAllLocations(); 
       } else { 
        cursor = helper.getSuggestionsData(projection, selection,  selectionArgs); 
       } 
       break; 
      case NODE_LOCATION: 
       cursor = helper.getSingleLocationById(uri.getLastPathSegment()); 
       break; 
      case NAME_LOCATION: 
       cursor = helper.getSingleLocationByName(selectionArgs[0]); 
       break; 
      case MARKERS: 
       cursor = helper.getAllMarkers(); 
       break; 
      case GEOFENCES: 
       cursor = helper.getAllFences(); 
       break; 
      case PEOPLE: 
       if (selectionArgs == null) { 
        cursor = helper.getAllPeople(); 
       } else { 
        cursor = helper.findPersonById(selectionArgs[0]); 
       } 
       break; 
      default: 
       Log.d(URI_ERROR, uri.toString()); 
     } 
     cursor.setNotificationUri(getContext().getContentResolver(), uri); 
     return cursor; 
    } 
    . 
    . 
    . 
} 

로더 Initialsing 어댑터 설정 :

public boolean onCreateOptionsMenu(Menu menu) { 
    // Inflate the menu; this adds items to the action bar if it is present. 
    getMenuInflater().inflate(R.menu.options_menu, menu); 

    SearchManager manager = (SearchManager) getSystemService(Context.SEARCH_SERVICE); 
    MenuItem item = menu.findItem(R.id.action_search); 
    SearchView view = (SearchView) MenuItemCompat.getActionView(item); 
    view.setIconified(false); 
    view.setSearchableInfo(
      manager.getSearchableInfo(getComponentName())); 
    view.setSuggestionsAdapter(adapter); 
    view.setOnQueryTextListener(this); 

    getSupportLoaderManager().initLoader(1, null, this); 

    return true; 
} 

LoaderManager.LoaderCallbacks 구현 :

@Override 
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) { 
    return new CursorLoader(this, SearchContentProvider.CONTENT_URI, new String[] { DbHelper.ID, DbHelper.LOCATION_NAME, DbHelper.LOCATION_NODE_ID }, 
                  DbHelper.LOCATION_NAME + " LIKE ?", new String[] { "%" + cursorFilter + "%"}, null); 
} 

@Override 
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) { 
    if (!(cursor.isClosed())) 
     adapter.swapCursor(cursor); 
    } 
} 

@Override 
public void onLoaderReset(Loader<Cursor> cursorLoader) { 
    adapter.swapCursor(null); 
} 

OnQueryTextchange()에서 DbHel

@Override 
public boolean onQueryTextChange(String s) { 
    cursorFilter = !TextUtils.isEmpty(s) ? s : null; 
    getSupportLoaderManager().restartLoader(0, null, this); 
    return true; 
} 

선언 당 :에서 onCreate (에서

public static final String LOCATION_TABLE = "Locations"; 
public static final String LOCATION_NAME = SearchManager.SUGGEST_COLUMN_TEXT_1; 
public static final String LOCATION_NODE_ID = SearchManager.SUGGEST_COLUMN_INTENT_DATA; 

선언) :

adapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_2, null, new String[] { DbHelper.LOCATION_NAME }, 
              new int[] { android.R.id.text2 }, 0); 

내 질문은 누군가가 전에 이것을 경험, 그렇다면, 당신은 해결책을 발견있다? 아니면 단순히 구현에 잘못 되었습니까?

+0

'query()'의'cursor.setNotificationUri()'다음에'db.close'를 사용하십시오. 함수가 끝나면 데이터베이스 연결을 항상 닫습니다. –

+0

@RohanKandwal 도움을 주셔서 감사합니다, 나는 그 변화를 넣었지만 불행히도 문제를 해결하지는 못합니다. – user3168815

+0

@ user3168815 ContentProvider (Sqlite), LoaderManager 및 CursorAdapter에서도 비슷한 문제가 발견되었습니다. 어떤 해결책을 찾았습니까? – ensecoz

답변

4

다음은 로더에서 작동하도록 AutocompleteTextView에서 수행 한 작업입니다. 기본적으로 AutocompleteTextView의 자체 필터링 메커니즘을 비활성화하고 대신에 로더 API를 사용합니다. 아래의 예는 액티비티를 사용하고 있습니다. 조각을 사용하는 경우 필요에 따라 수정하십시오.

String mCurrentFilter = ""; 

@Override 
protected void onCreate(Bundle savedInstanceState) { 

    ... 
    mACTextView = new NonFilterableAutoCompleteTextView(this); 
    // Start with a null cursor since data is not ready yet 
    mAdapter = new CursorAdapter(this, null, 0){...}; 
    mACTextView.setAdapter(mAdapter); 
    mACTextView.addTextChangedListener(new TextWatcher() { 

     @Override 
     public void beforeTextChanged(CharSequence s, int start, int count, int after) { 

     } 

     @Override 
     public void onTextChanged(CharSequence s, int start, int before, int count) { 

     } 

     @Override 
     public void afterTextChanged(Editable s) { 

      mCurrentFilter = s.toString(); 
      getLoaderManager().restartLoader(LOADER_SUGGESTIONS, null, ExampleActivity.this); 
     } 
    }); 
    getLoaderManager().initLoader(LOADER_SUGGESTIONS, null, this); 
} 

@Override 
public Loader<Cursor> onCreateLoader(int id, Bundle args) { 

    switch (id) { 
     case LOADER_SUGGESTIONS: 
      // Use the current filter to perform the query. For simplicity sake, assume an empty filter ("") will return all records. 
      return new CursorLoader(getActivity(), Uri.parse("Whatever your content provider URI is " + mCurrentFilter)), null, null, null, null); 

     ... 
    } 

    return null; 
} 

@Override 
public void onLoadFinished(Loader<Cursor> loader, Cursor data) { 

    switch (loader.getId()) { 
     case LOADER_SUGGESTIONS: 
      mAdapter.swapCursor(data); 
      break; 
     ... 
    } 
} 

/** 
* An AutoCompleteTextView which does not perform any background filtering. This class will 
* not perform any filtering and is intended to be used with CursorLoaders and CursorAdapters and 
* have the cursor in the adapter swapped when the loader has new data. 
* <p/> 
* This is required since using the standard AutoCompleteTextView with CursorLoaders and swapCursor 
* causes races conditions with the widget's own filtering happening in the background. The default filtering mechanism 
* will run on a background thread with an instance of the old cursor. 
* 
* @author AngraX 
*/ 
public static class NonFilterableAutoCompleteTextView extends AutoCompleteTextView { 

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

    public NonFilterableAutoCompleteTextView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
    } 

    public NonFilterableAutoCompleteTextView(Context context, AttributeSet attrs, int defStyle) { 
     super(context, attrs, defStyle); 
    } 

    @Override 
    protected void performFiltering(CharSequence text, int keyCode) { 
     // I say NO! 
    } 
} 
관련 문제