2017-02-18 4 views
0

UPDATEАсинхронной Задача блокирующего потока пользовательского интерфейса

Благодаря @EmanuelSeibold я был в состоянии точно определить проблему с обновлением recyclerview. AsyncTask работает в фоновом режиме просто отлично, и только обновление адаптера recyclerview зависает от пользовательского интерфейса.

UPDATE2

Я нашел, что это было на самом деле моя установка макета. Я забыл удалить nestedScrollView вокруг RecyclerView. Это, казалось, вызывало конфликт рендеринга.

Я прокопал свой путь через ответы здесь и сообщения в блогах, но просто, похоже, не смог найти решение.

Я довольно новичок в разработке Android и пытаюсь получить представление о многопоточности.

Сценарий: У меня есть приложение, которое хранит базу данных SQLite с данными курса и реализует функцию поиска, которая запрашивает эту базу данных. Это блокирует UI-поток примерно через ~ 3 секунды. Поэтому я реализовал AsyncTask, чтобы поддерживать пользовательский интерфейс, но мой пользовательский интерфейс по-прежнему заблокирован во время поиска.

Заранее благодарен!

Вот код:

Поисковая активность

public class Activity_Search extends Activity_Base { 

    private RecyclerView rv; 


    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_search); 
     setActionBarTitle(R.string.title_search); 

     findViewById(R.id.searchCourseTitle).setOnKeyListener(new View.OnKeyListener() { 
      @Override 
      public boolean onKey(View v, int keyCode, KeyEvent event) { 
       switch(keyCode) { 
        case KeyEvent.KEYCODE_ENTER: 
         startSearch(); 
         break; 
        default: 
         return false; 
       } 
       return true; 
      } 
     }); 


     rv = (RecyclerView) findViewById(R.id.searchRecycler); 

     Adapter_Search adapter = new Adapter_Search(this, null); 
     rv.setAdapter(adapter); 
     rv.setLayoutManager(new LinearLayoutManager(this)); 

     FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.search_FAB); 
     fab.setOnClickListener(new View.OnClickListener() { 
      @Override 
      public void onClick(View view) { 
       startSearch(); 
      } 
     }); 
    } 

    private void startSearch() { 
     findViewById(R.id.searchCourseTitle).clearFocus(); 
     if (this.getCurrentFocus() != null) { 
      InputMethodManager imm = 
        (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); 
      imm.hideSoftInputFromWindow(this.getCurrentFocus().getWindowToken(), 0); 
     } 

     {getting input} 

     String[] columns = {...}; 


     String selection = {formatting input}; 

     Async_Search search = new Async_Search(this); 
     search.execute(columns, new String[]{selection}); 
    } 

    public void onSearchCompleted(Cursor results) { 
     ((Adapter_Search) rv.getAdapter()).changeCursor(results); 
    } 
} 

AsyncTask

public class Async_Search extends AsyncTask<String[], Void, Cursor> { 

    private Activity activity; 

    public Async_Search (Activity activity) { 
     this.activity = activity; 
    } 

    @Override 
    protected Cursor doInBackground(String[]... params) { 
     SQLiteDatabase db = SQL_Database.getInstance(activity).getWritableDatabase(); 

     String[] columns = params[0]; 
     String selection = params[1][0]; 

     return db.query(...) 
    } 

    @Override 
    protected void onPostExecute(Cursor results) { 
     ((Activity_Search) activity).onSearchCompleted(results); 
    } 
} 

Ресайклер адаптер

public class Adapter_Search extends RecyclerCursorAdapter<Adapter_Search.ViewHolder>{ 

public static class ViewHolder extends RecyclerView.ViewHolder { 
    public TextView name, ects, studipCode, termYear, lecturers, fields; 

    public ViewHolder (View view) { 
     super(view); 
     {id lookups} 
    } 
} 

public Adapter_Search(Context context, Cursor cursor) { 
    super(context, cursor); 
} 

@Override 
public ViewHolder onCreateViewHolder (ViewGroup parent, int ViewType) { 
    View view = LayoutInflater.from(parent.getContext()) 
      .inflate(R.layout.search_list_entry, parent, false); 
    ViewHolder viewHolder = new ViewHolder(view); 
    return viewHolder; 
} 

@Override 
public void onBindViewHolder (ViewHolder viewHolder, Cursor cursor) { 

    TextView name, ects, studipCode, termYear, lecturers, fields; 
    name = viewHolder.name; 
    ects = viewHolder.ects; 
    studipCode = viewHolder.studipCode; 
    termYear = viewHolder.termYear; 
    lecturers = viewHolder.lecturers; 
    fields = viewHolder.fields; 

    String termandyear = cursor.getString(cursor.getColumnIndexOrThrow(SQL_Database.COURSE_COLUMN_TERM)) + 
      String.format("%.2s",cursor.getString(cursor.getColumnIndexOrThrow(SQL_Database.COURSE_COLUMN_YEAR))); 

    name.setText(cursor.getString(cursor.getColumnIndexOrThrow(SQL_Database.COURSE_COLUMN_COURSE))); 
    String credits = cursor.getString(cursor.getColumnIndexOrThrow(SQL_Database.COURSE_COLUMN_ECTS)) + " ECTS"; 
    ects.setText(credits); 
    {and so on} 
} 

}

Базовый адаптер класса

public abstract class RecyclerCursorAdapter <ViewHolder extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<ViewHolder> { 

private Context mContext; 

private Cursor mCursor; 

private boolean mDataValid; 

private int mRowIdColumn; 

private DataSetObserver mDataSetObserver; 

public RecyclerCursorAdapter(Context context, Cursor cursor) { 
    mContext = context; 
    mCursor = cursor; 
    mDataValid = cursor != null; 
    mRowIdColumn = mDataValid ? mCursor.getColumnIndex("_id") : -1; 
    mDataSetObserver = new NotifyingDataSetObserver(); 
    if (mCursor != null) { 
     mCursor.registerDataSetObserver(mDataSetObserver); 
    } 
} 

public Cursor getCursor() { 
    return mCursor; 
} 

@Override 
public int getItemCount() { 
    if (mDataValid && mCursor != null) { 
     return mCursor.getCount(); 
    } 
    return 0; 
} 

@Override 
public long getItemId(int position) { 
    if (mDataValid && mCursor != null && mCursor.moveToPosition(position)) { 
     return mCursor.getLong(mRowIdColumn); 
    } 
    return 0; 
} 

@Override 
public void setHasStableIds(boolean hasStableIds) { 
    super.setHasStableIds(true); 
} 

public abstract void onBindViewHolder(ViewHolder viewHolder, Cursor cursor); 

@Override 
public void onBindViewHolder(ViewHolder viewHolder, int position) { 
    if (!mDataValid) { 
     throw new IllegalStateException("this should only be called when the cursor is valid"); 
    } 
    if (!mCursor.moveToPosition(position)) { 
     throw new IllegalStateException("couldn't move cursor to position " + position); 
    } 
    onBindViewHolder(viewHolder, mCursor); 
} 

/** 
* Change the underlying cursor to a new cursor. If there is an existing cursor it will be 
* closed. 
*/ 
public void changeCursor(Cursor cursor) { 
    Cursor old = swapCursor(cursor); 
    if (old != null) { 
     old.close(); 
    } 
} 

/** 
* Swap in a new Cursor, returning the old Cursor. Unlike 
* {@link #changeCursor(Cursor)}, the returned old Cursor is <em>not</em> 
* closed. 
*/ 
public Cursor swapCursor(Cursor newCursor) { 
    if (newCursor == mCursor) { 
     return null; 
    } 
    final Cursor oldCursor = mCursor; 
    if (oldCursor != null && mDataSetObserver != null) { 
     oldCursor.unregisterDataSetObserver(mDataSetObserver); 
    } 
    mCursor = newCursor; 
    if (mCursor != null) { 
     if (mDataSetObserver != null) { 
      mCursor.registerDataSetObserver(mDataSetObserver); 
     } 
     mRowIdColumn = newCursor.getColumnIndexOrThrow("_id"); 
     mDataValid = true; 
     notifyDataSetChanged(); 
    } else { 
     mRowIdColumn = -1; 
     mDataValid = false; 
     notifyDataSetChanged(); 
     //There is no notifyDataSetInvalidated() method in RecyclerView.Adapter 
    } 
    return oldCursor; 
} 

private class NotifyingDataSetObserver extends DataSetObserver { 
    @Override 
    public void onChanged() { 
     super.onChanged(); 
     mDataValid = true; 
     notifyDataSetChanged(); 
    } 

    @Override 
    public void onInvalidated() { 
     super.onInvalidated(); 
     mDataValid = false; 
     notifyDataSetChanged(); 
     //There is no notifyDataSetInvalidated() method in RecyclerView.Adapter 
    } 
} 

}

+0

Похоже, что вашему RecycleView требуется время для обновления данных. Это не AsyncTask. Можете ли вы опубликовать свой AdapterSearch? –

+0

@EmanuelSeibold Я отредактировал его. Не потому ли, что мне нужно перебирать весь курсор (который может содержать до 400 строк)? Если да, то как мне обойти это? И извините за длинный код, я нахожусь только на мобильном телефоне, и здесь очень сложно форматировать код. – Mipsh

+0

Курсор ленив, поэтому даже если он содержит 400 строк, его комбинация и переработка не должны повесить пользовательский интерфейс. И ты уверен, что он не заблокирован, а просто медленно? –

ответ

0

Может попробовать удалить эти строки из асинхронным поиска.

private Activity activity; 

    public Async_Search (Activity activity) { 
     this.activity = activity; 
    } 

Вместо называть так:

Async_Search search = new Async_Search(); 
    search.execute(columns, new String[]{selection}); 

Я не знаю, если это будет работать, но мой AsyncTask не использует материал активности вами есть.

+1

. Как мне получить читаемую базу данных? Ему нужен контекст для конструктора. И как мне взаимодействовать с пользовательским интерфейсом, а затем в onPostExecute? – Mipsh

+0

@Mipsh http://stackoverflow.com/questions/12575068/how-to-get-the-result-of-onpostexecute-to-main-activity-because-asynctask-is-a –

Смежные вопросы