swapCursor() медленный: приложение может выполнять слишком много работы в своем основном потоке.

Я продолжаю видеть «Приложение может выполнять слишком много работы в своем основном потоке».

Это вызвано swapCursor() в моем коде ниже? Выходит так: если я его удалю, вышеуказанное предупреждение пропадает.

Я до сих пор не понимаю, почему он утверждает, что является основным потоком, вызывающим проблему. Я перемещал swapCursor() в разные места, такие как onLoadFinished() и loadInBackground(), но получаю тот же результат.

Как я могу вызвать swapCursor(), чтобы это предупреждение не появлялось? Вся причина, по которой я использовал SimpleCursorLoader, заключалась в том, чтобы не задерживать основной поток, но это все еще происходит.

package com.example.sqlitetest;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import android.app.Activity;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
import android.support.v4.content.CursorLoader;
import android.support.v4.widget.SimpleCursorAdapter;
import android.support.v4.widget.SimpleCursorAdapter.ViewBinder;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.ListView;
import android.widget.TextView;

public class MainActivity extends FragmentActivity implements LoaderManager.LoaderCallbacks<Cursor> 
{
  public static Context mContext;

  private LoaderManager.LoaderCallbacks<Cursor> mLoaderCallbacks;

  public static SQLiteDatabase      db;
  public static SimpleCursorAdapter cAdapter;
  public static ListView            lvCustomList;


  public static final class MyCursorLoader extends SimpleCursorLoader 
  {
    public MyCursorLoader( Context context )
    {
      super( context );
    }

    @Override
    public Cursor loadInBackground()
    {
      Cursor cursor = null;

      cursor = db.rawQuery( "SELECT rowid _id, Name, Rating FROM Tune ORDER BY Name", null );

      cAdapter.swapCursor( cursor );

      return cursor;
    }
  }  

  @Override
  public Loader<Cursor> onCreateLoader(int id, Bundle args) 
  {
    return new MyCursorLoader( this );
  }

  @Override
  public void onLoadFinished( Loader<Cursor> loader, Cursor cursor ) 
  {
//    cAdapter.swapCursor( cursor );
  }

  @Override
  public void onLoaderReset( Loader<Cursor> loader ) 
  {
    cAdapter.swapCursor( null );
  }

  @Override
  public void onCreate( Bundle savedInstanceState )
  {
    super.onCreate( savedInstanceState );
    setContentView( R.layout.tune_artist_album_view );

    mContext = this;

    String path = "/sdcard/MyDb.sqlite";

    db = SQLiteDatabase.openDatabase( path, null, 0 );

    lvCustomList = (ListView) findViewById( R.id.lv_custom_list );

    String[] columns = new String[] { "Name", "Rating" };

    int[] to = new int[] { R.id.lv_tune };  

    cAdapter = new SimpleCursorAdapter( mContext, R.layout.like_hate_row, null, columns, to, 0 );

    lvCustomList.setAdapter( cAdapter );

    mLoaderCallbacks = this;

    LoaderManager lm = getSupportLoaderManager();

    lm.initLoader( 0, null, mLoaderCallbacks );
  }
}

Вот SimpleCursorLoader:

package com.example.sqlitetest;

import android.content.Context;
import android.database.Cursor;
import android.support.v4.content.AsyncTaskLoader;

/**
 * Used to write apps that run on platforms prior to Android 3.0. When running
 * on Android 3.0 or above, this implementation is still used; it does not try
 * to switch to the framework's implementation. See the framework SDK
 * documentation for a class overview.
 * 
 * This was based on the CursorLoader class
 */
public abstract class SimpleCursorLoader extends AsyncTaskLoader<Cursor>
{
  private Cursor mCursor;

  public SimpleCursorLoader( Context context )
  {
    super( context );
  }

  /* Runs on a worker thread */
  @Override
  public abstract Cursor loadInBackground();

  /* Runs on the UI thread */
  @Override
  public void deliverResult( Cursor cursor )
  {
    if( isReset() )
    {
      // An async query came in while the loader is stopped
      if( cursor != null )
      {
        cursor.close();
      }
      return;
    }

    Cursor oldCursor = mCursor;
    mCursor = cursor;

    if( isStarted() )
    {
      super.deliverResult( cursor );
    }

    if( oldCursor != null && oldCursor != cursor && !oldCursor.isClosed() )
    {
      oldCursor.close();
    }
  }

  /**
   * Starts an asynchronous load of the contacts list data. When the result is
   * ready the callbacks will be called on the UI thread. If a previous load has
   * been completed and is still valid the result may be passed to the callbacks
   * immediately.
   * <p/>
   * Must be called from the UI thread
   */
  @Override
  protected void onStartLoading()
  {
    if( mCursor != null )
    {
      deliverResult( mCursor );
    }
    if( takeContentChanged() || mCursor == null )
    {
      forceLoad();
    }
  }

  /**
   * Must be called from the UI thread
   */
  @Override
  protected void onStopLoading()
  {
    // Attempt to cancel the current load task if possible.
    cancelLoad();
  }

  @Override
  public void onCanceled( Cursor cursor )
  {
    if( cursor != null && !cursor.isClosed() )
    {
      cursor.close();
    }
  }

  @Override
  protected void onReset()
  {
    super.onReset();

    // Ensure the loader is stopped
    onStopLoading();

    if( mCursor != null && !mCursor.isClosed() )
    {
      mCursor.close();
    }
    mCursor = null;
  }
}

comment
Используйте Traceview и узнайте, на что тратится ваше время, а не гадайте.   -  person CommonsWare    schedule 07.07.2014
comment
@CommonsWare: Спасибо, я попробую и посмотрю, что он покажет. Пробовал на телефоне в обеденное время, никаких предупреждений не появилось. Проблема может быть только на моем планшете.   -  person SparkyNZ    schedule 08.07.2014
comment
@CommonsWare: Хорошо, поэтому TraceView сообщает мне, что мое предположение было правильным - 1,5 секунды тратится на выполнение nativeExecuteForCursorWindow() в основном потоке. Почему в основной теме? Я, очевидно, не достиг своей цели, чтобы поиск базы данных происходил в фоновом режиме.   -  person SparkyNZ    schedule 08.07.2014


Ответы (1)


Такие методы, как query() и rawQuery(), делают не то, что вы думаете. Примечательно, что они фактически не запрашивают базу данных. Они просто настраивают SQLiteCursor, которые они возвращают. Когда вы пытаетесь использовать этот Cursor, он выполняет запрос.

Следовательно, в вашем методе loadInBackground() между rawQuery() и swapCursor() вызовите getCount() для Cursor, которое вы получили от rawQuery(). Этого достаточно, чтобы заставить Cursor действительно выполнять работу по запросу, и, следовательно, эта работа выполняется в фоновом потоке.

person CommonsWare    schedule 08.07.2014
comment
Ага! Спасибо за это. Я видел вызов getCount() в некоторых примерах. Возвращаемое значение было проигнорировано, и я подумал, что это просто оставшийся случайный код — это объясняет его присутствие! Я попробую. Если это поможет, я мог бы принять ваш ответ. - person SparkyNZ; 08.07.2014
comment
@SparkyNZ: Увы, метода yoYouLazyObjectDoTheQueryAlready() не существует, иначе мы бы использовали его для этого варианта использования. :-) - person CommonsWare; 08.07.2014
comment
Да, это помогло. Вместо того, чтобы видеть 1,5-секундный блок в основном потоке, я вижу, что ModernAsyncTask выполняет такой же объем работы... и никаких предупреждений о пропущенных кадрах. Я принял ваш ответ - большое спасибо за это ... и интересное путешествие с TraceView. - person SparkyNZ; 08.07.2014
comment
Сейчас март 2016 года, и это все еще происходит для CursorLoader. повезло что нашел - person nbtk; 21.03.2016