2017-01-09 80 views
0

我已經實施SearchableSpinner到我的項目。它在Fragment之內。可搜索微調結合領域

我使用的是Realm作爲數據庫。 在我onCreateView方法我有這個...

@Override 
public View onCreateView(LayoutInflater inflater, final ViewGroup container, Bundle savedInstanceState) { 
      View view = inflater.inflate(R.layout.ncrdocument, container, false); 
    realm = Realm.getDefaultInstance(); 
    documents = new ArrayList<>(); 
    documents = realm.where(MaterialDoc.class).findAll(); 
    ArrayAdapter<MaterialDoc> adapter = new ArrayAdapter<>(this.getContext(), android.R.layout.simple_list_item_1, documents); 
    matList.setAdapter(adapter); 
. 
. 
. 
return view; 

數據加載罰款,它正確地顯示他們,但是當我嘗試搜索的微調,我的應用程序崩潰,我得到這個錯誤。

An exception occured during performFiltering() 
java.lang.IllegalStateException: Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created 
at io.realm.BaseRealm.checkIfValid(BaseRealm.java:383) 
at io.realm.MaterialDocRealmProxy.realmGet$document_number(MaterialDocRealmProxy.java:126) 
at com.my.application.test.Model.MaterialDoc.getDocumentNumber(MaterialDoc.java:29) 
at com.my.application.test.Model.MaterialDoc.toString(MaterialDoc.java:42) 
at android.widget.ArrayAdapter$ArrayFilter.performFiltering(ArrayAdapter.java:480) 
at android.widget.Filter$RequestHandler.handleMessage(Filter.java:234) 
at android.os.Handler.dispatchMessage(Handler.java:102) 
at android.os.Looper.loop(Looper.java:145) 
at android.os.HandlerThread.run(HandlerThread.java:61) 
01-09 12:13:06.649 18606-18606/com.my.application.test D/AndroidRuntime: Shutting down VM 
01-09 12:13:06.669 18606-18606/com.my.application.test E/AndroidRuntime: FATAL EXCEPTION: main 
Process: com.my.application.test, PID: 18606 
java.lang.NullPointerException: Attempt to invoke interface method 'int java.util.List.size()' on a null object reference 
at android.widget.ArrayAdapter.getCount(ArrayAdapter.java:330) 
at android.widget.AdapterView.checkFocus(AdapterView.java:947) 
at android.widget.AdapterView$AdapterDataSetObserver.onInvalidated(AdapterView.java:1070) 
at android.widget.AbsListView$AdapterDataSetObserver.onInvalidated(AbsListView.java:8297) 
at android.database.DataSetObservable.notifyInvalidated(DataSetObservable.java:50) 
at android.widget.BaseAdapter.notifyDataSetInvalidated(BaseAdapter.java:59) 
at android.widget.ArrayAdapter$ArrayFilter.publishResults(ArrayAdapter.java:513) 
at android.widget.Filter$ResultsHandler.handleMessage(Filter.java:282) 
at android.os.Handler.dispatchMessage(Handler.java:102) 
at android.os.Looper.loop(Looper.java:145) 
at android.app.ActivityThread.main(ActivityThread.java:6939) 
at java.lang.reflect.Method.invoke(Native Method) 
at java.lang.reflect.Method.invoke(Method.java:372) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1404) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199) 
+0

http://stackoverflow.com/questions/40630322/how-to-implement-filterable-in-realmrecyclerviewadapter – EpicPandaForce

回答

2

當你處理跨線程的實時結果時,你必須小心。領域來自一個你不能在其他領域使用的線程。因此,請嘗試獲取結果集的副本(與活動領域實例分離),並將此副本提供給適配器。 Realm擁有copyFromRealm這樣做的方法。

修改代碼,這樣

Realm realm = Realm.getDefaultInstance(); 
    RealmResults<MaterialDoc> realmResults = realm.where(MaterialDoc.class).findAll(); 
    List<MaterialDoc> documents = realm.copyFromRealm(realmResults); 
    ArrayAdapter<MaterialDoc> adapter = new ArrayAdapter<>(this.getContext(), android.R.layout.simple_list_item_1, documents); 
    matList.setAdapter(adapter); 
+0

非常高效!謝謝! – FiN

+1

錯誤,使用'copyFromRealm()'來「修復」非法線程訪問是不好的 – EpicPandaForce

3

通過@SarathKn接受的答案是沒有效率可言,複製/分離從境界的結果和你失去了自動更新和懶惰的評價,你複製出來整個結果集立即生效。

results.copyFromRealm()的用法,除了看不慣當你真正需要非託管對象,通常只有當你需要通過GSON發送的對象,或者如果你需要修改對象的可撤銷的方式(除/取消更改)。

使用realm.copyFromRealm()來「避免非法線程訪問」是一個錯誤答案。


的實際問題,而不是說砍修復是您正在使用ArrayAdapter的默認Filter實現執行在後臺線程的過濾,而你真正需要過濾的UI線程上RealmResults(因爲它是你在UI線程中顯示的結果集)。

如果您檢查ArrayAdapter的Filterable實現的源代碼,這是以下幾點:

@Override 
public @NonNull Filter getFilter() { 
    if (mFilter == null) { 
     mFilter = new ArrayFilter(); 
    } 
    return mFilter; 
} 

/** 
* <p>An array filter constrains the content of the array adapter with 
* a prefix. Each item that does not start with the supplied prefix 
* is removed from the list.</p> 
*/ 
private class ArrayFilter extends Filter { 
    @Override 
    protected FilterResults performFiltering(CharSequence prefix) { 
     final FilterResults results = new FilterResults(); 

     if (mOriginalValues == null) { 
      synchronized (mLock) { 
       mOriginalValues = new ArrayList<>(mObjects); 
      } 
     } 

     if (prefix == null || prefix.length() == 0) { 
      final ArrayList<T> list; 
      synchronized (mLock) { 
       list = new ArrayList<>(mOriginalValues); 
      } 
      results.values = list; 
      results.count = list.size(); 
     } else { 
      final String prefixString = prefix.toString().toLowerCase(); 

      final ArrayList<T> values; 
      synchronized (mLock) { 
       values = new ArrayList<>(mOriginalValues); 
      } 

      final int count = values.size(); 
      final ArrayList<T> newValues = new ArrayList<>(); 

      for (int i = 0; i < count; i++) { 
       final T value = values.get(i); 
       final String valueText = value.toString().toLowerCase(); 

       // First match against the whole, non-splitted value 
       if (valueText.startsWith(prefixString)) { 
        newValues.add(value); 
       } else { 
        final String[] words = valueText.split(" "); 
        for (String word : words) { 
         if (word.startsWith(prefixString)) { 
          newValues.add(value); 
          break; 
         } 
        } 
       } 
      } 

      results.values = newValues; 
      results.count = newValues.size(); 
     } 

     return results; 
    } 

    @Override 
    protected void publishResults(CharSequence constraint, FilterResults results) { 
     //noinspection unchecked 
     mObjects = (List<T>) results.values; 
     if (results.count > 0) { 
      notifyDataSetChanged(); 
     } else { 
      notifyDataSetInvalidated(); 
     } 
    } 
} 

performFiltering是在後臺線程執行,並publishResults在UI線程上執行。

實際上,您應該使用在UI線程上執行的Realm過濾器來替換此過濾器實現。

@Override 
public @NonNull Filter getFilter() { 
    if (mFilter == null) { 
     mFilter = new RealmFilter(); 
    } 
    return mFilter; 
} 

/** 
* <p>An array filter constrains the content of the array adapter with 
* a prefix. Each item that does not start with the supplied prefix 
* is removed from the list.</p> 
*/ 
private class RealmFilter extends Filter { 
    @Override 
    protected FilterResults performFiltering(CharSequence prefix) { 
     return new FilterResults(); 
    } 

    @Override 
    protected void publishResults(CharSequence constraint, FilterResults results) { 
     String prefix = constraint.toString().trim(); 
     //noinspection unchecked 
     RealmQuery<MaterialDoc> query = realm.where(MaterialDoc.class); 
     if(prefix == null || "".equals(prefix)) { 
      /* do nothing */ 
     } else { 
      if(prefix.contains(" ")) { 
       String[] words = prefix.split("\\s"); 
       boolean isFirst = true; 
       for(String word : words) { 
        if(!"".equals(word)) { 
         if(isFirst) { 
          isFirst = false; 
         } else { 
          query = query.or(); 
         } 
         query = query.beginsWith(/*enter query field name*/, word, Case.INSENSITIVE); 
        } 
       } 
      } else { 
       query = query.beginsWith(/* enter query field name*/, prefix, Case.INSENSITIVE); 
      } 
     } 
     mObjects = query.findAll(); 
     notifyDataSetChanged(); 
    } 
} 

但我這個here(和RecyclerView here)之前由域使用的角度鏈接到this Filter solution that isn't conceptually wrong回答。