Сбой приложения Android при фильтрации списка с помощью действия поиска в Google Voice.

У меня возникла проблема с фильтрацией списка с использованием данных JSON, проанализированных с помощью Volley, на основе этого учебного пособия, когда список фильтруется с помощью функции " Поиск в приложении" в Системных голосовых действиях Google из Google App.

Точная возникшая проблема указана ниже:

  1. Приложение изначально вообще не работает (поиск работает отлично, если приложение работает или в фоновом режиме).

  2. Огонь намерения через ADB:

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. Приложение вылетает.

Ниже приведена трассировка стека:

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).

Ниже приведен код (часть кода пропущена):

Основная деятельность

//[…]
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 ответ

Решение

В случае, если кто-то в будущем столкнется с подобной проблемой, используя ListView, основанный на наборе данных, заполненном в другом потоке, или AsyncTask (например, анализ JSON через Volley в этом учебном пособии), эта проблема вызвана тем, что синтаксический анализ JSON выполняется одновременно с onNewIntent(getIntent()); метод. Следовательно, для фильтрации во время операции фильтрации может использоваться только пустой ArrayList. MYFilter что приводит к IndexOutOfBoundsException,

В этом конкретном случае (используя залп), решение было бы вызвать onNewIntent(getIntent()); в конце метода onResponse (обратитесь к этому вопросу SO для получения дополнительной информации). Для меня не было больше кода после моего запроса залпа в течение onCreate() так что я в безопасности.

Я понял, что это проблема, потому что с тех пор, как этот вопрос был опубликован, я создал "автономный режим" для приложения, который выполняет простой анализ JSON в основном потоке, и фильтрация с помощью Google Voice Actions отлично работает. Кроме того, мне довелось протестировать этот код на нескольких устройствах, некоторые из которых завершили анализ данных JSON перед получением поиска в приложении ( com.google.android.gms.actions.SEARCH_ACTION ) намерение, а другие нет.

Другие вопросы по тегам