通过Google语音操作搜索操作过滤列表时,Android应用会崩溃

时间:2016-02-10 09:03:46

标签: java android listview android-intent google-voice-actions

我在使用基于this tutorial的Volley解析的JSON数据过滤列表时遇到问题,此时列表是通过Google App的Google系统语音操作中的Search in App意图过滤的。

遇到的确切问题如下:

  1. 该应用最初根本没有运行(如果应用正在运行或在后台,搜索效果非常好。)

  2. 通过adb:

  3. 触发意图

    cd C:...\android-sdk\platform-tools
    adb shell am start -a "com.google.android.gms.actions.SEARCH_ACTION" --es query "[query keyword]" -n "com.testapp/.MainActivity"

    1. 打开正确的应用,但列表为空,即没有过滤结果。

    2. 该应用程序随后崩溃。

    3. 下面是stacktrace:

      02-15 17:54:03.331: D/AndroidRuntime(31982): Shutting down VM
      02-15 17:54:03.341: E/AndroidRuntime(31982): FATAL EXCEPTION: main
      02-15 17:54:03.341: E/AndroidRuntime(31982): Process: com.test.app, PID: 31982
      02-15 17:54:03.341: E/AndroidRuntime(31982): java.lang.IndexOutOfBoundsException: Invalid index 0, size is 0
      02-15 17:54:03.341: E/AndroidRuntime(31982):    at java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:255)
      02-15 17:54:03.341: E/AndroidRuntime(31982):    at java.util.ArrayList.get(ArrayList.java:308)
      02-15 17:54:03.341: E/AndroidRuntime(31982):    at com.test.app.adapter.CustomListAdapter.getView(CustomListAdapter.java:92)
      02-15 17:54:03.341: E/AndroidRuntime(31982):    at android.database.DataSetObservable.notifyChanged(DataSetObservable.java:37)
      02-15 17:54:03.341: E/AndroidRuntime(31982):    at android.widget.BaseAdapter.notifyDataSetChanged(BaseAdapter.java:50)
      02-15 17:54:03.341: E/AndroidRuntime(31982):    at com.test.app$1.onResponse(MainActivity.java:152)
      02-15 17:54:03.341: E/AndroidRuntime(31982):    at com.test.app$1.onResponse(MainActivity.java:1)
      02-15 17:54:03.341: E/AndroidRuntime(31982):    at com.android.volley.toolbox.JsonRequest.deliverResponse(JsonRequest.java:65)
      02-15 17:54:03.341: E/AndroidRuntime(31982):    at com.android.volley.ExecutorDelivery$ResponseDeliveryRunnable.run(ExecutorDelivery.java:99)
      02-15 17:54:03.341: E/AndroidRuntime(31982):    at android.os.Handler.handleCallback(Handler.java:739)
      02-15 17:54:03.341: E/AndroidRuntime(31982):    at android.os.Handler.dispatchMessage(Handler.java:95)
      02-15 17:54:03.341: E/AndroidRuntime(31982):    at android.os.Looper.loop(Looper.java:145)
      02-15 17:54:03.341: E/AndroidRuntime(31982):    at android.app.ActivityThread.main(ActivityThread.java:6843)
      02-15 17:54:03.341: E/AndroidRuntime(31982):    at java.lang.reflect.Method.invoke(Native Method)
      02-15 17:54:03.341: E/AndroidRuntime(31982):    at java.lang.reflect.Method.invoke(Method.java:372)
      02-15 17:54:03.341: E/AndroidRuntime(31982):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1404)
      02-15 17:54:03.341: E/AndroidRuntime(31982):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199)
      

      我的应用程序包含一个MainActivity,它是一个可搜索的活动,显示列表,模型类,适配器类和列表项的控制器类(类似于现有的Android ListView教程)。

      下面是代码(省略了一些代码):

      MainActivity

      //[…]
      public class MainActivity extends AppCompatActivity {
          //[…]
          private List<Item> itemList = new ArrayList<Item>();
          public static ListView listView;
          private CustomListAdapter adapter;
          private static final String GMS_SEARCH_ACTION = "com.google.android.gms.actions.SEARCH_ACTION";
          private String qq;
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              //[…]
              onNewIntent(getIntent());
          }
          public void onNewIntent(Intent intent) {
              super.onNewIntent(intent);
              String action = intent.getAction();
              if (action!= null && (action.equals(Intent.ACTION_SEARCH)||action.equals(GMS_SEARCH_ACTION))) {
                  qq = intent.getStringExtra(SearchManager.QUERY);
                  doSearch(qq);
              }
          }
          /* FIXME - Current problem: From adb command, ListView can only be filtered successfully only if app is still running in background (i.e. onPause()). ListView filtering works ok if searching within the app. If app is not running, sending the search command causes the app to open but nothing is filtered, and then crashes shortly after.*/
          private void doSearch(String qq) {
              CharSequence query = qq.toUpperCase(Locale.getDefault());
              MainActivity.this.adapter.getFilter().filter(query);
              }
          }
          @Override
          public boolean onCreateOptionsMenu(Menu menu) {
              MenuInflater inflater = getMenuInflater();
              inflater.inflate(R.menu.menu, menu);
              // […]
              MenuItem searchItem = menu.findItem(R.id.menu_search);
              SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
              SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
              SearchableInfo searchableInfo = searchManager.getSearchableInfo(getComponentName());
              searchView.setSearchableInfo(searchableInfo);
              searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
                  @Override
                  public boolean onQueryTextSubmit(String qq) {
                      doSearch(qq);
                      return false;
                  }
                  @Override
                  public boolean onQueryTextChange(String qq) {
                      doSearch(qq);
                      return false;
                  }
              });
              return super.onCreateOptionsMenu(menu);
          }
      }
      

      适配器类

      // […]
      public class CustomListAdapter extends BaseAdapter {
          private Activity activity;
          private LayoutInflater inflater;
          private List<Item> items, default_items;
          private Filter myFilter;
          ImageLoader imageLoader = AppController.getInstance().getImageLoader();
          public CustomListAdapter(Activity activity, List<Item> items) {
              this.activity = activity;
              this.items = items;
              this.default_items = items;
          }
          //[…]
          @Override
          public View getView(int position, View convertView, ViewGroup parent) {
      //…
          }
          public Filter getFilter() {
              if (myFilter == null) { myFilter = new MYFilter();}
              return myFilter;
          }
          private class MYFilter extends Filter {
              @Override
              protected FilterResults performFiltering(CharSequence query) {
                  FilterResults results = new FilterResults();
                  // If no filter implemented, return the whole list
                  if (query == null || query.length() == 0) {
                      results.values = default_ items;
                      results.count = default_ items.size();
                  }
                  else {
                      List<Item> nitems = new ArrayList<Item>();
                      items = default_nitems;
                      for (Item t : items) {
                          // Filter logic implemented here, items are added to nitems if it meets conditions
      }
                      results.values = nitems;
                      results.count = nitems.size();
                  }
                  return results;
              }
              @Override
              protected void publishResults(CharSequence query, FilterResults results) {
                  if (results.count == 0){
                      items = (List<Item>) results.values;
                      MainActivity.emptyView.setVisibility(View.VISIBLE);
                      MainActivity.listView.setVisibility(View.GONE);
                  }
                  else {
                      items = (List< Item >) results.values;
                      MainActivity.listView.setVisibility(View.VISIBLE);
                      MainActivity.emptyView.setVisibility(View.GONE);
                      notifyDataSetChanged();
                  }
              }
          }
      }
      

      清单

      <?xml version="1.0" encoding="utf-8"?>
      <manifest ... >
      
          <application
              android:name=".app.AppController"
              android:allowBackup="true"
              android:icon="@drawable/ic_launcher"
              android:label="@string/app_name"
              android:theme="@style/AppTheme" android:allowTaskReparenting="true">
      
              <activity
                  android:name=".MainActivity"
                  android:label="@string/app_name"
                  android:screenOrientation="portrait"
                  android:exported="true"
                  android:launchMode="singleTop" >
      
                  <intent-filter>
                      <action android:name="android.intent.action.MAIN" />
                      <category android:name="android.intent.category.LAUNCHER" />
                  </intent-filter>
      
                  <intent-filter>
                      <action android:name="android.intent.action.VIEW" />
                      <category android:name="android.intent.category.DEFAULT" />
                      <category android:name="android.intent.category.BROWSABLE" />
                  </intent-filter>
      
                  <intent-filter>
                      <action android:name="android.intent.action.SEARCH" />
                      <category android:name="android.intent.category.DEFAULT" />
                      <category android:name="android.intent.category.BROWSABLE" />
                  </intent-filter>
      
                  <meta-data
                      android:name="android.app.searchable"
                      android:resource="@xml/searchable" />
      
                  <intent-filter>
                      <action android:name="com.google.android.gms.actions.SEARCH_ACTION" />
                      <category android:name="android.intent.category.DEFAULT" />
                      <category android:name="android.intent.category.BROWSABLE" />
                  </intent-filter>
      
                  <meta-data
                      android:name="android.app.default_searchable"
                      android:value=".MainActivity" />
      
              </activity>
      <!-- ... -->
      </application>
      </manifest>
      

      感谢您解决此问题的任何帮助。

1 个答案:

答案 0 :(得分:0)

如果将来任何人使用基于另一个线程或AsyncTask中填充的数据集的ListView遇到类似问题(例如通过Volley in this tutorial进行JSON解析),则会出现此问题,因为JSON解析会导致此问题与onNewIntent(getIntent());方法同时完成。 因此,在过滤操作MYFilter期间可能只有一个空的ArrayList可以过滤,从而产生IndexOutOfBoundsException

在这种特定情况下(使用Volley),解决方案是在onResponse方法结束时调用onNewIntent(getIntent());(有关更多信息,请参阅this SO question)。对我来说,onCreate()内的排球请求后没有更多的代码,所以我很安全。

我意识到这是问题所在,因为这个问题已经发布,我创建了一个&#34;离线模式&#34;对于在主线程中进行简单JSON解析的应用程序,通过Google Voice Actions进行的过滤非常有效。此外,我碰巧在多个设备上测试了这个代码,其中一些设备在接收到应用程序中的搜索(com.google.android.gms.actions.SEARCH_ACTION)意图之前完成了解析JSON数据,而其他设备没有。