Как ускорить загрузку Android-приложения ArrayList из 140 000 элементов из актива

У меня есть программа на Java, которую я только что превратил в Android-приложение, которое медленно загружается. Проблема: он имеет дело со «словарем» из 140 000 слов (хранящимся в файле Asset), в котором нужно искать слова, соответствующие шаблонам «подстановочных знаков Windows»: например, S???CK* будет соответствовать STICKS, SHACK, STACK, , STACKOVERFLOW и т. д. Это ОЧЕНЬ быстро в Windows 7. Не так на телефоне.

Одна вещь, которую я сделал, это прочитал все 140 000 слов в ArrayList (я был шокирован тем, что он скомпилировался и запустился), после чего, если шаблон не начинается с подстановочного знака, Collections.binarySearch(...) делает поиск практически немедленным.

Но чтение его в список массивов занимает 60 секунд, а пользовательский ввод блокируется. И это происходит каждый раз, когда нужно запускать onCreate, т. е. неприемлемо часто.

Я хочу ускорить это.

Вот SSCCE того, что работает отлично, но слишком медленно:

MainActivity.java

public class MainActivity extends Activity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    FragmentTransaction
        ft;
        ft = getFragmentManager().beginTransaction();
        ft.replace(R.id.layout_container, new OutputFragment());
        ft.commit();
  };
}

OutputFragment.java

public class          OutputFragment          extends Fragment
{
  @Override
  public void onCreate(Bundle savedInstanceState)
  {
    super.onCreate(savedInstanceState);
  }

  @Override
  public View onCreateView(LayoutInflater _layoutInflater,
                           ViewGroup      _sourceOfLayoutParams,
                           Bundle         savedInstanceState)
  {


    View v = _layoutInflater.inflate(R.layout.fragment_output,_sourceOfLayoutParams, false);

    EditText et = (EditText)v.findViewById(R.id.txaOutput);

    Matcher matcher = new Matcher(getActivity().getAssets());

    for (int i = 0; i < 9; i++)
       et.append("\n" + matcher.get(i));

   return v;
  }
} 

Matcher.java

public class Matcher extends ArrayList<String> {

  Matcher(AssetManager assets) {

    Scanner scDict = null;
    try { scDict = new Scanner(assets.open("dictionary.dic")); }
    catch (IOException e) { e.printStackTrace(); }

    int k = 0;

    while(scDict.hasNext())// && ++k<10)
      add(scDict.next());
  }
}

activity_main.xml

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
          android:layout_width    ="match_parent"
          android:layout_height   ="match_parent"

    xmlns:tools  ="http://schemas.android.com/tools"
          tools:context           =".MainActivity"
    >

   <LinearLayout
       android:id           ="@+id/layout_container"
       android:orientation  ="vertical"
       android:layout_width ="match_parent"
       android:layout_height="match_parent">
   </LinearLayout>

</RelativeLayout>

fragment_output.xml

<GridLayout
      xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width ="match_parent"
            android:layout_height="match_parent"
      android:rowCount="33"
      android:columnCount="2">

    <TextView
        android:id              ="@+id/txvOutput"
        android:text            ="Output shown below"
        android:layout_width    ="wrap_content"
        android:layout_height   ="wrap_content"
        android:textAppearance  ="?android:attr/textAppearanceLarge"
        android:layout_row="0"
        android:layout_column="0">
    </TextView>

        <EditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="New Text"
            android:id="@+id/txaOutput"
            android:layout_row="2"
            android:inputType="textMultiLine"
            android:layout_column="0"
            android:maxLines="100"/>


</GridLayout>

Так что я хочу, чтобы ускорить его. Я прочитал "Поддержание отклика приложения для Android". Я не знаю, смогу ли я сделать это в соответствии с моей ситуацией. Я взял пример оттуда и скорректировал его как мог:

  private class LoadWords extends AsyncTask<Scanner, Integer, Long> 
  {
    @Override
    protected Long doInBackground(Scanner... params) { // variable arg list required (??)
      while(params[0].hasNext()) //// no way this could work...
        add(params[0].next());
      return 0L;
    };
  }

Я не ожидал, что это сработает, как только я набрал params[0].hasNext(), но, похоже, требуется список переменных аргументов.

Вот как я пытался это реализовать:

    LoadWords loadWords = new LoadWords(); /////////////////////////

    InputStream stream = null;
    Scanner     scDict = null;
    ...
                stream = assets.open("dictionary.dic");
    ...    

    scDict = new Scanner(stream);

    loadWords.execute(scDict); ///////////////////// What should I pass?????

Думаю, мне следует отказаться от этого подхода и попытаться использовать Thread, которым мне придется управлять. Меня это не устраивает.

Любые предложения о том, как действовать, приветствуются.


person DSlomer64    schedule 03.09.2015    source источник
comment
Или, по крайней мере, AsyncTask для чтения в...   -  person Buddy    schedule 04.09.2015
comment
Зачем загружать все это в память? Просто используйте базу данных   -  person Ed George    schedule 04.09.2015
comment
Загрузить в память: это просто! И я не ожидал, что это вообще сработает, не говоря уже о плавности. Вот почему я ищу совета, который я получил. Я никогда не использовал базу данных или AsyncTask, но у меня есть мотивация. Я задам новый вопрос о том, как реализовать AsyncTask.   -  person DSlomer64    schedule 04.09.2015
comment
@Selvin-- (1) Что даст использование базы данных с точки зрения ускорения загрузки? Будет ли это однократная загрузка, то есть однократное долгое ожидание, или загрузка будет происходить всякий раз, когда выполняется onCreate? (2) Ускорит ли это доступ, если есть начальный подстановочный знак? Я считаю, что ведущий подстановочный знак обречен быть очень медленным. Поэтому мне нужно знать, какой выгоды ожидать, прежде чем я войду в эту новую область.   -  person DSlomer64    schedule 04.09.2015
comment
Я спрашиваю, потому что в моей базе данных будет одна таблица. База данных кажется излишним тому, кто не использовал ее в таком ограниченном контексте. Повышение производительности превзойдет все сомнения.   -  person DSlomer64    schedule 04.09.2015
comment
Преимущество использования базы данных заключается в том, что вы сохраняете только Cursor в памяти и ResultSet вашего запроса. Кроме того, вы можете индексировать свое поле слова, и это ускорит работу.   -  person Paulo Avelar    schedule 04.09.2015


Ответы (2)


У меня есть частичное решение, использующее AsyncTask. Загрузка 140 000 слов происходит в фоновом режиме; Графический интерфейс реагирует немедленно, НО не все слова загружаются вовремя для возврата всех совпадений.

public class ListMaker extends ArrayList<String> 
{
  Scanner scDict;
  InputStream stream = null;

  public Matcher(AssetManager assets) 
  {
    LoadWords loadWords = new LoadWords();
    stream = assets.open("dictionary.dic");
    loadWords.execute((Object[]) null);
  }

  private class LoadWords extends AsyncTask<Object, Integer, ArrayList<String>> {
    @Override
    protected ArrayList<String> doInBackground(Object... params) 
    {
      scDict = new Scanner(stream).useDelimiter("\r\n");
      while (scDict.hasNext())
        add(scDict.next());
      return null;
    }

    @Override
    protected void onPostExecute(ArrayList<String> result) {
      MainActivity.setLoaded(true);
    }
person DSlomer64    schedule 04.09.2015

Из-за того, что Пауло Авелар упомянул индекс, я, наконец, отказался от советов Селвина и Эда Джорджа использовать SQLite database вместо загрузки в память всех 140 000 слов, что я никогда не считал хорошей идеей, но это сработало. достаточно, чтобы начать. Но очень плохо, оказывается.

С базой данных улучшение заметно.

Индексирование единственного столбца (т. е. без излишеств) сделало поиск по подстановочным знакам мгновенным с использованием "where word like ?", где "?" — это шаблон пользователя.

Загрузка 140 000 слов в базу данных занимает минуту (одноразовая задача, при условии, что данные приложения не очищаются через Settings), но использование database.beginTransaction и endTransaction сделало загрузку достаточно быстрой. Подробнее см. здесь.

person DSlomer64    schedule 20.09.2015