Autore Topic: startManagingCursor deprecato obbliga ad usare ContentProvider?  (Letto 608 volte)

Offline arlabs

  • Utente normale
  • ***
  • Post: 430
  • Respect: +49
    • Mostra profilo
  • Dispositivo Android:
    GalaxyS6, Nexus5
  • Play Store ID:
    AR Labs
  • Sistema operativo:
    Windows 10
startManagingCursor deprecato obbliga ad usare ContentProvider?
« il: 12 Febbraio 2015, 17:56:30 CET »
0
Ciao a tutti,
non sono molto ferrato nella gestione dei dati ed ho bisogno di un consiglio.

Ho un piccolo DB a cui faccio una query, il cui risultato visualizzo in una ListView, specializzando un CursorAdapter.
Non sto a mettere il codice perchè non è funzionale alla domanda.

La domanda è, come gestisco il ciclo di vita del Cursor?
startManagingCursor è deprecato, secondo la documentazione è consigliato usare un CursorLoader.

Bene, significa che devo crearmi un ContentResolver ed un CursorLoader per fare una banale query ad un DB?
La query è un po' complessa ma il DB è piccolissimo (e sempre lo sarà, vista la sua funzione) e la query non può durare più di pochi ms, non ho un vero interesse a rendere asincrono il tutto.
Non è come sparare con un cannone ad un uccellino?

E' veramente questa l'unica strada o ho qualche alternativa?

Grazie.

Offline Blodhgard

  • Utente junior
  • **
  • Post: 53
  • Respect: +3
    • Mostra profilo
    • Budget Veloce
  • Dispositivo Android:
    Nexus 5, S4 mini, Galaxy Tab S
  • Play Store ID:
    blodhgard
  • Sistema operativo:
    Windows 8.1
Re:startManagingCursor deprecato obbliga ad usare ContentProvider?
« Risposta #1 il: 13 Febbraio 2015, 00:37:17 CET »
0
Io ho risolto gestendo il Cursor da codice.

Codice (Java): [Seleziona]
Cursor c = ....
if(c.moveToFirst)
{
     do
     {
          .....
     }while(c.moveToNext());
}
c.close();

All'inizio usavo anchio startManagingCursor ma ora sono passato a questo metodo.
Se devo fare grosse query metto tutto in un AsyncTask

Offline arlabs

  • Utente normale
  • ***
  • Post: 430
  • Respect: +49
    • Mostra profilo
  • Dispositivo Android:
    GalaxyS6, Nexus5
  • Play Store ID:
    AR Labs
  • Sistema operativo:
    Windows 10
Re:startManagingCursor deprecato obbliga ad usare ContentProvider?
« Risposta #2 il: 13 Febbraio 2015, 10:15:27 CET »
0
Blodhgard, ma io non devo scandirlo, devo solo passarlo ad una ListaView tramite Adapter, quindi deve rimaner vivo finché sono nel Fragment e chiudersi all'uscita.
Potrei usare il ciclo che dici tu, copiandomi i dati in un Array e usando un ArrayAdapter. Probabilmente sarebbe la cosa più safe.

Al momento però ho ovviato creando un CursorLoader Fake (che non carica il Cursor, ma passa sottobanco uno già pronto:

Codice (Java): [Seleziona]
public class FakeCursorLoader extends AsyncTaskLoader<Cursor>
{
    private Cursor mCursor;

    public FakeCursorLoader( Context context, Cursor cursor )
    {
        super(context);
        mCursor = cursor;
    }


    @Override
    public Cursor loadInBackground()
    {
        return mCursor;
    }

    @Override protected void onStartLoading()
    {
        forceLoad();
    }

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

        mCursor.close();
    }
}

Qualcuno di più esperto, mi può dire se vede qualche incongruenza in questo? Qualche consiglio?

Ciao.

Post unito: 13 Febbraio 2015, 10:16:28 CET
P.S. Blodhgard, quando fai così ti consiglio però di mettere la close in un costrutto finally

Offline Blodhgard

  • Utente junior
  • **
  • Post: 53
  • Respect: +3
    • Mostra profilo
    • Budget Veloce
  • Dispositivo Android:
    Nexus 5, S4 mini, Galaxy Tab S
  • Play Store ID:
    blodhgard
  • Sistema operativo:
    Windows 8.1
Re:startManagingCursor deprecato obbliga ad usare ContentProvider?
« Risposta #3 il: 13 Febbraio 2015, 11:52:07 CET »
0
Ops sorry non avevo fatto caso al fatto che utilizzassi una listview.
Beh se usi un CursorLoader fa tutto lui.
Non hai bisogno di gestire il ciclo di vita del cursor.

Codice (Java): [Seleziona]
protected static class YourCursorAdapter extends CursorAdapter
        {  
                public TransactionsCursorAdapter(Context context, Cursor c, int flag)
                {
                super(context, c, flag);
                }
               
                private final class ViewHolder
                {
                        TextView textView;
                }
             
            @Override
            public View newView(Context context, Cursor cursor, ViewGroup parent)
            {
                LayoutInflater inflater = LayoutInflater.from(parent.getContext());
                View view = inflater.inflate(R.layout.item_income_expense, parent, false);
             
                ViewHolder holder = new ViewHolder();
            holder.textView = (TextView) view.findViewById(R.id.textview_item_ie_category);        
                view.setTag(holder);
               
                return view;
            }
             
                @Override
            public void bindView(View view, Context context, Cursor cursor)
            {
                        ViewHolder holder = (ViewHolder) view.getTag();
                       
                holder.textView.setText(cursor.getString(cursor.getColumnIndex(Database.IEMetaData.IE_ACCOUNT)));
               
            }
        }

Lo crei assegnandogli il cursor nel doInBackround di un AsyncTask e lo assegni alla listview nell'onPostExecute.
Io faccio così poi non so se ci sono modi migliori/efficienti di farlo.

Offline arlabs

  • Utente normale
  • ***
  • Post: 430
  • Respect: +49
    • Mostra profilo
  • Dispositivo Android:
    GalaxyS6, Nexus5
  • Play Store ID:
    AR Labs
  • Sistema operativo:
    Windows 10
Re:startManagingCursor deprecato obbliga ad usare ContentProvider?
« Risposta #4 il: 13 Febbraio 2015, 12:07:12 CET »
0
Sì il CursorAdapter l'avevo fatto.

Il dubbio era che se uso un CursorLoader sono però obbligato anche a crearmi un ContentProvider.

Era questa la domanda iniziale (e titolo del thread). Mi pareva un esagerazione usare un ContentProvider per riempire un paio di righe di una ListView con una query.

Post unito: 13 Febbraio 2015, 12:09:50 CET
Lo crei assegnandogli il cursor nel doInBackround di un AsyncTask e lo assegni alla listview nell'onPostExecute.

C'è un modo di assegnare un Cursor ad un CursorLoader senza usare un ContentProvider?
Scusa, mi faresti vedere come si fa? Io mi son fatto il FakeCursorLoader apposta per fare questo.
« Ultima modifica: 13 Febbraio 2015, 12:09:50 CET da arlabs, Reason: Merged DoublePost »

Offline Blodhgard

  • Utente junior
  • **
  • Post: 53
  • Respect: +3
    • Mostra profilo
    • Budget Veloce
  • Dispositivo Android:
    Nexus 5, S4 mini, Galaxy Tab S
  • Play Store ID:
    blodhgard
  • Sistema operativo:
    Windows 8.1
Re:startManagingCursor deprecato obbliga ad usare ContentProvider?
« Risposta #5 il: 13 Febbraio 2015, 12:25:29 CET »
0
Nel doInBackground dell'asynctask

Codice (Java): [Seleziona]
customAdapter = new YourCursorAdapter (context, your_cursor, 0);
e nell'onPostExecute

Codice (Java): [Seleziona]
listView.setAdapter(customAdapter);

Offline arlabs

  • Utente normale
  • ***
  • Post: 430
  • Respect: +49
    • Mostra profilo
  • Dispositivo Android:
    GalaxyS6, Nexus5
  • Play Store ID:
    AR Labs
  • Sistema operativo:
    Windows 10
Re:startManagingCursor deprecato obbliga ad usare ContentProvider?
« Risposta #6 il: 13 Febbraio 2015, 14:22:36 CET »
0
Scusa Blodhgard, ma stiamo parlando di cose differenti.
Lasciamo perdere gil Adapter che non sono il problema.

Il problema è il ciclo di vita del Cursor (il CursorLoader ne tiene la ownership e lo chiude, non l'Adapter)
Dalla tua frase "Lo crei assegnandogli il cursor nel doInBackround di un AsyncTask e lo assegni alla listview nell'onPostExecute" avevo inteso che potessi assegnare un Cursor ad un CursorLoader senza ContentProvider, che era la cosa che volevo evitare. Invece probabilmente il soggetto di tale frase era l'adapter.

Diciamo che come ho fatto (il FakeCursorAdapter) funziona... ma sono timoroso di side-effect.

Ciao.

Post unito: 13 Febbraio 2015, 15:44:11 CET
Se a qualcuno può interessare ho provato anche questa soluzione.
E' un CursorLoader asincrono ma che non usa un ContentProvider, ma chiama una callback per fare la query.

Qualcuno potrebbe chiedermi: "Ma cosa ti hanno fatto i ContentProvider per odiarli così tanto?" :D
Ed avrebbe ragione...

Codice (Java): [Seleziona]
public class QueryCursorLoader extends AsyncTaskLoader<Cursor>
{
    private Cursor          mCursor;
    private QueryCallback   mCallback;

    public interface QueryCallback
    {
        Cursor query();
    }

    public QueryCursorLoader(Context ctx, QueryCallback callback) {
        super(ctx);
        mCallback = callback;
    }

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

        if( mCallback != null )
            cursor = mCallback.query();

        return cursor;
    }

    @Override
    public void deliverResult(Cursor data)
    {
        if (isReset())
        {
            // The Loader has been reset; ignore the result and invalidate the data.
            releaseResources(data);
            return;
        }

        // Hold a reference to the old data so it doesn't get garbage collected.
        // We must protect it until the new data has been delivered.
        Cursor oldData = mCursor;
        mCursor = data;

        if (isStarted())
        {   // If the Loader is in a started state, deliver the results to the
            // client. The superclass method does this for us.
            super.deliverResult(data);
        }

        // Invalidate the old data as we don't need it any more.
        if (oldData != null && oldData != data)
        {
            releaseResources(oldData);
        }
    }

    @Override
    protected void onStartLoading() {
        if (mCursor != null)
        {   // Deliver any previously loaded data immediately.
            deliverResult(mCursor);
        }

        if (takeContentChanged() || mCursor == null) {
            forceLoad();
        }
    }

    @Override
    protected void onStopLoading() {
        cancelLoad();
    }

    @Override
    protected void onReset() {
        // Ensure the loader has been stopped.
        onStopLoading();

        // At this point we can release the resources associated with 'mCursor'.
        if (mCursor != null) {
            releaseResources(mCursor);
                  mCursor = null;
        }
    }

    @Override
    public void onCanceled(Cursor data) {
        // Attempt to cancel the current asynchronous load.
        super.onCanceled(data);

        // The load has been canceled, so we should release the resources
        // associated with 'data'.
        releaseResources(data);
    }

    private void releaseResources(Cursor data)
    {
        if( data != null )
            data.close();
    }
}

Dq chiamare così dall'activity:
Codice (Java): [Seleziona]
public class MyActivity extends Activity implements LoaderManager.LoaderCallbacks<Cursor>
{

    @Override
    public void onCreate(Bundle savedInstanceState) {
       
        mAdapter = new MyAdapter( ... );

        ListView listView = (ListView)view.findViewById( R.id.listView );
        listView.setAdapter( mAdapter );

        ...
        getLoaderManager().initLoader(0, null, this);
        ...
    }


    public Loader<Cursor> onCreateLoader(int id, Bundle args)
    {
        return new QueryCursorLoader( getActivity(), new QueryCursorLoader.QueryCallback()
        {
            @Override
            public Cursor query()
            {
                return mDq.query_che_voglio( ... );
            }
        } );
    }

    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        mAdapter.swapCursor(data);
    }

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

    ...
}
« Ultima modifica: 13 Febbraio 2015, 15:44:11 CET da arlabs, Reason: Merged DoublePost »

Offline Nicola_D

  • Moderatore
  • Utente storico
  • *****
  • Post: 2479
  • SBAGLIATO!
  • Respect: +323
    • Github
    • Google+
    • nicoladorigatti
    • Mostra profilo
  • Dispositivo Android:
    Nexus 6p, Nexus 4, Nexus S, Nexus 7(2012)
  • Sistema operativo:
    Windows 7
Re:startManagingCursor deprecato obbliga ad usare ContentProvider?
« Risposta #7 il: 13 Febbraio 2015, 15:59:00 CET »
+1
personalmente preferisco farmi una classe "di utilità" che tira su i dati dal db sotto forma di oggetti e poi uso questi oggetti. Niente cursor adapter ne startManagingCursor
IMPORTANTE:NON RISPONDO A PROBLEMI VIA MESSAGGIO PRIVATO
LOGCAT: Non sai cos'è? -> Android Debug Bridge | Android Developers
               Dov'è in Eclipse? -> Window -> Open Prospective -> DDMS e guarda in basso!
[Obbligatorio] Logcat, questo sconosciuto! (Gruppo AndDev.it LOGTFO) - Android Developers Italia

Offline bradipao

  • Moderatore globale
  • Utente storico
  • *****
  • Post: 4043
  • keep it simple
  • Respect: +567
    • Github
    • Google+
    • bradipao
    • Mostra profilo
  • Dispositivo Android:
    Nexus 5
  • Play Store ID:
    Bradipao
  • Sistema operativo:
    W7
Re:startManagingCursor deprecato obbliga ad usare ContentProvider?
« Risposta #8 il: 13 Febbraio 2015, 16:01:50 CET »
0
personalmente preferisco farmi una classe "di utilità" che tira su i dati dal db sotto forma di oggetti e poi uso questi oggetti. Niente cursor adapter ne startManagingCursor

Concordo! Principalmente perchè così disaccoppio al massimo la base dati e il loro utilizzo.
NON rispondo a domande nei messaggi privati
Bradipao @ Play Store

Offline arlabs

  • Utente normale
  • ***
  • Post: 430
  • Respect: +49
    • Mostra profilo
  • Dispositivo Android:
    GalaxyS6, Nexus5
  • Play Store ID:
    AR Labs
  • Sistema operativo:
    Windows 10
Re:startManagingCursor deprecato obbliga ad usare ContentProvider?
« Risposta #9 il: 15 Febbraio 2015, 10:24:58 CET »
0
Grazie Nicola e Bradipao, in effetti se le righe sono poche non ha un utilità utilizzare il CursorAdapter.
Quando ho cominciato ad usarlo non mi ero reso conto che il modo 'ufficiale' di usarlo mi avrebbe portato così "in là"

Ciao