What are Loaders? How to implement a Loader to load from the database without content Provider and how to implement a custom Loader?

Asked

Viewed 2,958 times

11

On the Internet, at least in my research, I found little material on Loader in Portuguese. That is why I record here this question, to have documented answers to the android programming community as a source of research, consultation and study.

  1. What are Loaders?
  2. How to implement a Loader to load data directly from database without the need to create a Contentprovider?
  3. How to implement a Custom Loader to load data that is used in Custom Adapter and Custom Lists?

2 answers

15


Attention, this answer is indicated for programmers who have at least a basic knowledge of:

  1. Activity
  2. Fragment
  3. Lists and Adapters
  4. Custom Lists and Custom Adapters

Loaders

Introduced in Android 3.0, loaders make it easier to load data asynchronously in Activitys and Fragments. Loaders has the following features:

  • They are available in each Activity and Fragment.
  • They provide data loading asynchronously .
  • They monitor the source of your data and deliver new results when content is modified.
  • They automatically reconnect to the last slider cursor when they are being recreated after a configuration change. Therefore, they do not need to re-query your data.

Summary of Loader API

These are the multiple classes and interfaces that are involved in using loaders in an application. They are summarized in this table:

Classes envolvidas com Loader

NOTE: This table was mounted from the table that is on the official website of the Android documentation on Download. I took their table, translated it, mounted it in a text editor, took a print and then edited the image. References to the sites are at the end of the post.

References for classes / interfaces:

The classes and interfaces in the table above are the essential components that you will use to implement a Loader in your application. You won’t need all of them for each Loader you create, but you’ll always need a reference to the Loadermanager to initialize a Loader and an implementation of a Loader class like Cursorloader.

Using Loaders in your Application

An application that uses Loaders will typically include the following: NOTE: PAY ATTENTION TO ITEM 3 BELOW, THE PART IN BOLD.

  1. An Activity or Fragment.
  2. An instance of Loadermanager.
  3. A Cursorloader to upload data supported by a Contentprovider. Alternatively, you can implement your own subclass of loader or Asynctaskloader to load data from some other source.
  4. An implementation of Loadermanager.LoaderCallbacks. This is where you create new Loaders and manage your references to existing Loaders.

We saw a bit about Loader and the classes that are involved in its implementation. Did you notice item 3 above? I asked to repair. In the documentation, where the above text was taken from, it says to use a Cursorloader to upload data from a Contentprovider, but I don’t want that.

I will teach how to load data directly from an Sqlite database without creating a Contentprovider. We will do this from a publicly available library. However, remember, we could implement our own Custom Loader if we did not want to use the library. But stay calm, I’ll still teach you how to implement a Custom Loader.

cwac-loaderex library

Regardless of how you create your database, the library needs to use a reference to Sqliteopenhelper, so have a method where it is possible to return such an object. With the library imported into your project and assuming you already have a reference to Sqliteopenhelper, let’s use as an example a Listfragment that wants to display a list of manufacturers that is stored in the database and wants to use a Simplecursoradapter to link this data to the list.

Class Fabricantelistfragment This class presents only the methods and commands needed for the implementation, ignoring the rest.

/**
 * Classe que exibe uma lista de fabricantes que estao cadastradas no banco de dados.
 * O usuario pode cadastrar, editar e deletar um fabricante. Esses fabricantes sao utilizados somente para selecionar o fabricante do instrumento.
 *
 * @author Lucas Santos
 *         Date: 28/03/2014
 *         Time: 09:11:20
 */
public class FabricanteListFragment extends ListFragment implements LoaderCallbacks<Cursor> {

    /** O ID unico do Loader. ID's dos Loaders sao especificos para a Activity ou Fragment em que eles residem. */
    private static final int LOADER_ID = 1;

    /** Referencia a interface callback no qual iremos interagir com o LoaderManager. */
    private LoaderCallbacks<Cursor> loaderCallbacks;
    /** O adapter que vincula nossos dados para o ListFragment. */
    private SimpleCursorAdapter mAdapter;
    /** Um Loader para um cursor que carrega dados diretamente de um banco de dados ao invés de utilizar Content Provider. */
    private SQLiteCursorLoader loader;

    @Override
    public void onCreate( Bundle savedInstanceState ) {
        super.onCreate( savedInstanceState );

        String[] projection = { Fabricante.COLUNA_NOME.texto };
        int[] to = { android.R.id.text1 };

        /*
         * Inicializa o adapter. O cursor do adapter e null. Nos iremos passar o cursor somente quando tiverem seu carregamento finalizado pela primeira vez.
         * Exemplo: quando LoaderManager entrega os dados para onLoadFinished. Passango a flag 0 como ultimo argumento do adapter, isto previne que o adapter
         * registre um ContentObserver para o Cursor (CursorLoader ira se encarregar disto para nos).
         */
        mAdapter = new SimpleCursorAdapter( getActivity().getApplicationContext(), R.layout.list_fragment_item_drawer, null, projection, to, 0 );

        // Associa o adapter, agora vazio, com o ListView
        setListAdapter( mAdapter );

        /*
         * A Activity ou Fragment (que implementa a interfce LoaderCallbacks<D>) e o objeto callback do qual iremos interagir com o LoaderManager.
         * O LoaderManager usa este objeto para instanciar um Loader e para notificar o cliente quando os dados estao disponiveis/indisponiveis.
         */
        loaderCallbacks = this;

        // Indica que este Fragmento gostaria de participar no preenchimento do menu de opcoes recebendo uma chamada para onCreateOptionsMenu e metodos relacionados.
        setHasOptionsMenu( true );
    }

    @Override
    public void onActivityCreated( Bundle savedInstanceState ) {
        super.onActivityCreated( savedInstanceState );

        /*
         * Inicializa o Loader com o id "LOADER_ID" e callback 'loaderCallbacks'. Se o Loader nao existe, um e criado. Caso contrario, o Loader ja existente e reutilizado.
         * O chamada ao initLoader assegura que o loader está inicializado e ativo. São duas as respostas a essa chamada:
         * Se o loader especificado já existe, o último loader criado será reutilizado.
         * Se o loader especificado não existe, o initLoader inicia o método onCreateLoader dentro de LoaderManager.LoaderCallbacks.
         * Aqui é onde você implementa código para instanciar e retornar um novo loader.
         * Em ambos os casos, o LoaderManager ira gerenciar o Loader sobre o ciclo de vida da Activity/Fragment, ira receber qualquer novos Loaders uma vez que estiverem completados,
         * e ira relatar estes novos dados de volta para o objeto 'loaderCallbacks'.
         */
        getLoaderManager().initLoader( LOADER_ID, null, loaderCallbacks );
    }

    /*
     * Instancia e retorna um novo Loader para um dado ID.
     * É um método de fábrica que simplesmente retorna um novo Loader. O LoaderManager vai chamar esse método quando ele for o Loader pela primeira vez.
     */
    @Override
    public Loader<Cursor> onCreateLoader( int id, Bundle args ) {
        // AQUI eu obtenho referência para um objeto `SQLiteOpenHelper` através da minha classe de banco de dados.
        DatabaseHelper databaseHelper = DatabaseHelper.getInstance( getActivity().getApplicationContext() );

        // Criamos o Loader através da biblioteca sem a menor dificuldade, apenas passando o `context`, objeto `SQLiteOpenHelper`, uma `Query` e os argumentos null.
        // `Fabricante.GET_ALL_FABRICANTES.texto` é uma `Query` em forma de String que fará a consulta ao banco de dados. Como eu não utilizo a clausula WHERE, então o quarto parâmetro é null.
        loader = new SQLiteCursorLoader( getActivity().getApplicationContext(), databaseHelper, Fabricante.GET_ALL_FABRICANTES.texto, null );

        return loader;
    }

    /*
     * Chamado quando um loader criado préviamente tem seu carregamento finalizado.
     * Este método é geralmente onde o cliente irá atualizar a interface do usuário do aplicativo com os dados carregados.
     * O cliente pode (e deve) assumir que novos dados serão retornados a este método cada vez que novos dados são disponibilizados.
     * Lembre-se que este é o trabalho do Loader, monitorar a fonte de dados e realizar os carregamentos assíncronos reais.
     * O LoaderManager receberá esses carregamentos uma vez que tenham concluído, em seguida, passar o resultado para o método onLoadFinished do objeto callback
     * para o cliente (ou seja, a Activity/Fragment) para usar.
     */
    @Override
    public void onLoadFinished( Loader<Cursor> loader, Cursor data ) {
        this.loader = (SQLiteCursorLoader) loader;

        // O carregamento assincrono esta completo e os dados agora estao disponiveis para uso. Somente agora podemos associar o cursor consultado com o Adapter.
        mAdapter.swapCursor( data );
        // O listview agora exibe os dados consultados.
    }

    /*
     * Chamado quando um loader criado préviamente está sendo resetado, fazendo com que seus dados fiquem indisponíveis.
     * É chamado quando os dados do Loader está prestes a ser resetado. Este método dá a você a oportunidade para remover
     * quaisquer referências a dados antigos que podem não estar mais disponíveis.
     */
    @Override
    public void onLoaderReset( Loader<Cursor> loader ) {
        // Por algume razao, os dados dos Loader's estao agora indisponiveis. Remova qualquer referencia para os dados antigos substituindo ele com um Cursor null.
        mAdapter.swapCursor( null );
    }
}

As the class is ALL commented, I will not comment on the class here, scan the code.

Using this library is really very easy. Now after you have used and implemented, test how the Loader automatically loads when a change is detected. Try putting a method that deletes a manufacturer from the list or add, but do it without leaving the Fragment from the list. But how? For example, put to when press an item from the list, pop up a Dialog asking if you want to delete, you confirm saying yes and then perform the deletion in the database. The screen will automatically update.

Take a look at the library documentation, it provides methods for your Loader created from Sqlitecursorloader to insert and delete from the database without having to create an Asynctask for it. The creation of Asynctask is encapsulated within the method provided by the library. For example, in the list above if you wanted to delete a record in the database using your Loader just do this:

// Usando este metodo com um SQLiteCursorLoader ele automaticamente e executado em uma AsyncTask, ou seja, fora da Thread Main.
loader.delete( Fabricante.NOME_TABELA.texto, Fabricante.COLUNA_ID.texto + " = " + idItemSelecionado, null );

Simple no? :). I’ll be explaining in another answer how to implement a Custom Loader to load data from a class and return it to display in a Custom List that uses a Custom Adapter.


References:

  • 2

    Note: language name/frameworks/technologies (Android, Sqlite) should not be highlighted as a code; I imagine that the table is a scan of some book, so it is important to quote the source.

  • @brasofilo sorry, I did not know that it should not be highlighted as code. I’ve always done this to highlight different from bold. But I will change this habit and put highlights in bold only. As for the table I have already put the source, which was the site google. I took their table, translated it into a libre office document, took a print, edited the image and put it here.

  • 1

    No problem, were only editorial remarks (usually, the origin of an image is cited just below it, but if it is its production, all blue). Excellent response by the way :)

  • Thank you. I will edit the question after anyway, removing the highlights.

  • Congratulations on your answers. They were very helpful to me! Thank you very much!

5

Now let’s see how to use a custom Loader to load data from a custom Adapter.

Attention, this answer is indicated for programmers who have at least a basic knowledge of:

  1. Activity
  2. Fragment
  3. Lists and Adapters
  4. Custom Lists and Custom Adapters

To understand the answer I believe you have to have the following classes:

  1. Model Class - Entity (For example, a Person class that contains nothing LESS than just getters and setters attributes and methods.
  2. A custom Adapter, perhaps implementing a Viewholder standard.
  3. Activity or Fragment you use to implement screen logic and load the data. In this example a Fragment will be used.

Let’s start with the model class. Example of a model class with getters and Setter attributes and methods. It is the entity that contains the data that is stored on the Adapter and in the database:

Class Instrument:

/**
 * Classe que representa um instrumento.
 *
 * @author Lucas Santos
 *         Date: 29/04/2014
 *         Time: 11:24:20
 */
public class Instrumento {
    /** ID do instrumento no banco de dados. */
    private long id;
    /** Nome do instrumento. */
    private String nome;
    /** O numero de serie deste instrumento, unico para cada um. */
    private String numeroSerie;

    // ... Aqui teriam muitos outros atributos.


    public Instrumento( long id, String nome, String numeroSerie ) {
        this.id = id;
        this.nome = nome;
        this.numeroSerie = numeroSerie;
    }

    // ---------------------------------- INICIO dos getters e setters ----------------------------------
    public long getId() {
        return id;
    }

    public void setId( long id ) {
        this.id = id;
    }


    public String getNome() {
        return nome;
    }

    public void setNome( String nome ) {
        this.nome = nome;
    }

    public String getNumeroSerie() {
        return numeroSerie;
    }

    public void setNumeroSerie( String numeroSerie ) {
        this.numeroSerie = numeroSerie;
    }

    // ... Aqui teriam muitos outros getters e setters.
    // ---------------------------------- FIM dos getters e setters ----------------------------------
}

Now imagining that you have your custom Adapter there implementing Viewholder, which gets a list of instruments in the constructor and which extends Basedapter, you will add the following method if you do not have one:

/**
 * Define uma lista para este adapter.
 *
 * @param data A lista será utilizada pelo adapter.
 */
public void setList( List<Instrumento> data ) {
    listInstrumentos = data;
}

I will explain why this method, you have to understand why to add because sometimes you may not be extending from Basedapter but from Arrayadapter for example and Arrayadapter already provides you a method to perform something similar. The function of this method I told you to add is to set a list for your Adapter. It will serve to define null when the Loader is reset. It will not be used to define the Adapter list itself because Adapter in my case is passed by constructor.

Now let’s go to the Fragment which will create custom Adapter, objects of the instrument class and will create and implement Loader. Please review the code as it is ALL commented. I will dispense with explanations as they are in the code.

Class Instrumentolistfragment

public class InstrumentoListFragment extends ListFragment implements LoaderManager.LoaderCallbacks<List<Instrumento>> {
    /** String usada ao se registrar mensagens de log. */
    private static final String TAG = "InstrumentoTcTpListFragment";
    /** O ID unico do Loader. ID's dos Loaders sao especificos para a Activity ou Fragment em que eles residem. */
    private static final int LOADER_ID = 1;

    /** Referencia para a classe que contem a logica do banco de dados. */
    private static SQLiteDatabase bancoDeDados;
    /** Referencia a interface callback no qual iremos interagir com o LoaderManager. */
    private LoaderManager.LoaderCallbacks<List<Instrumento>> loaderCallbacks;

    @Override
    public void onCreate( Bundle savedInstanceState ) {
        super.onCreate( savedInstanceState );

        /*
         * A Activity ou Fragment (que implementa a interfce LoaderCallbacks<D>) e o objeto callback do qual iremos interagir com o LoaderManager.
         * O LoaderManager usa a implementação de LoaderManager.LoaderCallbacks para notificar o cliente dos eventos do Loader.
         */
        loaderCallbacks = this;
    }

    @Override
    public void onActivityCreated( Bundle savedInstanceState ) {
        super.onActivityCreated( savedInstanceState );


        /*
         * Inicializa o Loader com o id "LOADER_ID" e callback 'loaderCallbacks'. Se o Loader nao existe, um e criado. Caso contrario, o Loader ja existente e reutilizado.
         * O chamada ao initLoader assegura que o loader está inicializado e ativo. São duas as respostas a essa chamada:
         * Se o loader especificado já existe, o último loader criado será reutilizado.
         * Se o loader especificado não existe, o initLoader inicia o método onCreateLoader dentro de LoaderManager.LoaderCallbacks.
         * Aqui é onde você implementa código para instanciar e retornar um novo loader.
         * Em ambos os casos, o LoaderManager ira gerenciar o Loader sobre o ciclo de vida da Activity/Fragment, ira receber qualquer novos Loaders uma vez que estiverem completados,
         * e ira relatar estes novos dados de volta para o objeto 'loaderCallbacks'.
         * OBS: Deveríamos utilizar "initLoader()", mas como queremos que o Loader seja recarregado caso volte a partir de outro Fragment, então mudamos para "restartLoader()".
         */
        getLoaderManager().restartLoader( LOADER_ID, null, loaderCallbacks );
    }

    // ---------------------------------- INICIO dos metodos das interfaces implementadas ----------------------------------
    /*
     * Instancia e retorna um novo Loader para um dado ID.
     * É um método de fábrica que simplesmente retorna um novo Loader. O LoaderManager vai chamar esse método quando ele for o Loader pela primeira vez.
     */
    @Override
    public Loader<List<Instrumento>> onCreateLoader( int id, Bundle args ) {
        Log.d( TAG, "onCreateLoader()!" );

        return new IntrumentoTcTpLoader( getActivity().getApplicationContext() );
    }

    /*
     * Chamado quando um loader criado préviamente tem seu carregamento finalizado.
     * Este método é geralmente onde o cliente irá atualizar a interface do usuário do aplicativo com os dados carregados.
     * O cliente pode (e deve) assumir que novos dados serão retornados a este método cada vez que novos dados são disponibilizados.
     * Lembre-se que este é o trabalho do Loader, monitorar a fonte de dados e realizar os carregamentos assíncronos reais.
     * O LoaderManager receberá esses carregamentos uma vez que tenham concluído, em seguida, passar o resultado para o método onLoadFinished do objeto callback
     * para o cliente (ou seja, a Activity/Fragment) para usar.
     */
    @Override
    public void onLoadFinished( Loader<List<Instrumento>> loader, List<Instrumento> data ) {
        Log.d( TAG, "onLoadFinished()!" );
        InstrumentoAdapter instrumentoAdapter = new InstrumentoAdapter( getActivity().getApplicationContext(), data );
        setListAdapter( instrumentoAdapter );
    }

    /*
     * Chamado quando um loader criado préviamente está sendo resetado, fazendo com que seus dados fiquem indisponíveis.
     * É chamado quando os dados do Loader está prestes a ser resetado. Este método dá a você a oportunidade para remover
     * quaisquer referências a dados antigos que podem não estar mais disponíveis.
     */
    @Override
    public void onLoaderReset( Loader<List<Instrumento>> loader ) {
        Log.d( TAG, "onLoaderReset()!" );

        ((InstrumentoAdapter) getListAdapter()).setList( null );
        setListAdapter( null );
    }
    // ---------------------------------- FIM dos metodos das interfaces implementadas ----------------------------------

    // ---------------------------------- INICIO das classe privadas que estende AsyncTask ----------------------------------
    /**
     * Loaders é uma classe abstrata que executa o carregamento assíncrono de dados. Enquanto Loaders são ativos eles devem monitorar a fonte de seus dados e entregar
     * novos resultados quando o conteúdo mudar.
     * Clientes de Loaders devem, em regra, realizar qualquer chamada para um Loader a partir da thread principal do seu processo (isto é, a thread principal).
     * As subclasses de Loader (como AsyncTaskLoader), irá muitas vezes, realizar o seu trabalho em uma thread separada, mas ao entregar seus resultados este também deve ser feito na thread principal.
     * <br><br> Subclasses geralmente deve implementar pelo menos onStartLoading (), onStopLoading (), onForceLoad (), e onreset ().
     * <br><br> A maioria das implementações não deve derivar diretamente dessa classe, mas em vez disso, herdar AsyncTaskLoader.
     * <br><br>
     * <b>Uma aplicação que usa loaders tipicamente incluirá o seguinte:</b><br>
     * • Uma atividade ou fragmento <br>
     * • Uma instância do LoaderManager <br>
     * • Um CursorLoader para carregar dados guardados por um ContentProvider. Alternativamente, você pode implementar sua própria subclasse de Loader ou AsyncTaskLoader para carregar dados de outra fonte. <br>
     * • Uma implementação de LoaderManager.LoaderCallbacks. Isso é necessário para que seja criado novos loaders e gerenciar suas referências para loaders existentes. <br>
     * • Uma forma de mostrar os dados do loader, como um SimpleCursorAdapter. <br>
     * • Um data source, como um ContentProvider quando usando o CursorLoader. <br>
     * <br>
     * O LoaderManager gerencia um ou mais instâncias de Loader dentro da atividade ou fragmento. Existe apenas um LoaderManager por atividade ou fragmento.
     * Você tipicamente inicializará o Loader dentro do onCreate() da Activity ou dentro do onActivityCreated() dentro do Fragment.
     */
    private static class IntrumentoTcTpLoader extends AsyncTaskLoader<List<Instrumento>> {

        // Referência para os dados dos Loader's aqui.
        private List<Instrumento> mData;

        // (1°) Chamado depois de onCreateLoader().
        public IntrumentoTcTpLoader( Context ctx ) {
            /*
             * Loader's podem ser usados em várias Activitys (assumindo que eles não estão vinculados a um LoaderManager), por isso nunca mantenha referencia para o contexto diretamente.
             * Isso fará com que cause um vazamento no contexto da Activity inteira. O construtor da superclasse irá armazenar uma referência para o contexto da aplicação ao invés disso e,
             * pode recebê-lo com uma chamada a getContext().
             */
            super( ctx );

            Log.d( TAG, "Construtor do Loader!" );
        }

        /**
         * (3°) Chamado depois de onStartLoading(). <br><br>
         * Este método é chamado em uma thread de background que realiza o carregamento assíncrono dos dados.
         *
         * @return Os dados que serão entregue ao cliente
         */
        @Override
        public List<Instrumento> loadInBackground() {
            Log.d( TAG, "loadInBackground()!" );
            List<Instrumento> dados = new ArrayList<Instrumento>();

            // TODO: Realiza uma consulta aqui e adiciona os resultados para "dados"
            Cursor result = bancoDeDados.query( InstrumentoTcTp.NOME_TABELA.texto, null, "Clausula WHERE com ?", "Argumentos da Clausula WHERE", null, null, null );

            // Percorre o cursor obtendo todos os dados e criando um objeto com os dados retornados.
            while ( result.moveToNext() ) {
                long id = result.getLong( result.getColumnIndex( InstrumentoTcTp.COLUNA_ID.texto ) );
                String nome = result.getString( result.getColumnIndex( InstrumentoTcTp.COLUNA_NOME.texto ) );
                String numeroSerie = result.getString( result.getColumnIndex( InstrumentoTcTp.COLUNA_NUMERO_SERIE.texto ) );

                // Cria um objeto instrumento com os dados armazenados no banco de dados.
                Instrumento instrumento = new Instrumento( id, nome, numeroSerie );
                // Adiciona o objeto instrumento a lista de instrumentos
                dados.add( instrumento );
            }

            return dados;
        }

        /**********************************************************/
        /**  Entrega os resultados para o  **/
        /** ****************************************************** */

        /**
         * (4°) Chamado depois de loadInBackground(). Após finalizar chama onLoadFinished(). <br><br>
         * Chamado quando há novos dados para entregar para o cliente (listener registrado).
         * A superclasse vai cuidar de entregá-lo; a implementação aqui apenas acrescenta um pouco mais de lógica.
         *
         * @param data Os dados que serão entregues ao cliente
         */
        @Override
        public void deliverResult( List<Instrumento> data ) {
            Log.d( TAG, "deliverResult()!" );
            // Verifica se o Loader foi redefinido. Isto é, ou o Loader não foi iniciado pela primeira vez, ou seu método reset() foi chamado.
            if ( isReset() ) {
                if ( data != null ) {
                    // Ignora o resultado e invalida os dados.
                    releaseResources( data );
                }
            }

            // Mantem uma referência para os dados antigos para que ele não coletado pelo "Garbage collector".
            // Devemos protegê-lo até que os novos dados sejam entregues.
            List<Instrumento> oldData = data;
            mData = data;

            if ( isStarted() ) {
                // Se o Loader está em um estado iniciado, entregamos imediatamente o resultado para o cliente. O método de superclasse fará isso para nós.
                super.deliverResult( data );
            }

            // Neste ponto, podemos liberar os recursos associados a "oldData", se necessário; agora que o novo resultado foi entregue sabemos que não precisamos mais dele.
            if ( oldData != null ) {
                releaseResources( oldData );
            }
        }

        /*****************************************************************/
        /** Implementação dos comportamentos dos estados do Loader *******/
        /** ************************************************************ */

        // (2°) Chamado depois depois do construtor do Loader
        @Override
        protected void onStartLoading() {
            Log.d( TAG, "onStartLoading()!" );
            // Se atualmente temos um resultado disponível, entregá-lo imediatamente.
            if ( mData != null ) {
                deliverResult( mData );
            }

            // Se os dados foram alterados desde a última vez que foi carregado ou não está disponível, inicie uma carga.
            if ( takeContentChanged() || mData == null ) {
                // Quando o observer detectar uma alteração, ele deve chamar onContentChanged() no Loader, que fará com que a próxima chamada para takeContentChanged()
                // retorne verdadeiro. Se este é sempre o caso (ou se os dados atual é nulo), forçamos uma nova carga.
                forceLoad();
            }
        }

        // Chamado quando a Activity/Fragment não está mais vísivel, por exemplo.
        @Override
        protected void onStopLoading() {
            Log.d( TAG, "onStopLoading()!" );
            // O Loader está em um estado parado, então nós devemos tentar cancelar o atual Loader (se houver).
            cancelLoad();
        }

        @Override
        protected void onReset() {
            Log.d( TAG, "onReset()!" );
            // Assegura que o Loader tenha sido parado.
            onStopLoading();

            // Neste ponto podemos liberar os recursos associados com "mData" se necessário.
            if ( mData != null ) {
                releaseResources( mData );
                mData = null;
            }
        }

        @Override
        public void onCanceled( List<Instrumento> data ) {
            Log.d( TAG, "onCanceled()!" );
            // Tentativa de cancelar o carregamento assíncrono atual.
            super.onCanceled( data );

            // O carregamento foi cancelado, nós devemos liberar os recursos associados com "data".
            releaseResources( data );
        }

        /**
         * Função auxiliar para cuidar de liberar recursos associados a um conjunto de dados ativamente carregado.
         * Para uma List simples, não há nada o que fazer aqui. Para algo como um Cursor, nós teríamos que fechá-lo neste método.
         * Todos os recursos associados com o Loader devem ser liberados aqui.
         *
         * @param data Os dados ativamente carregado.
         */
        private void releaseResources( List<Instrumento> data ) {
            Log.d( TAG, "releaseResources()!" );
        }
    }
    // ---------------------------------- FIM das classe privadas que estende AsyncTask ----------------------------------
}
  • 1

    Why this answer needs to be separate?

  • 3

    @bigown put in an answer only does not give, because the number of characters limits is exceeded.

  • 3

    Probably because it’s too long. You should wipe the answer.

  • @Mustache impossible to dry the answer in this case, I know it comes out a little of the patterns of Stack Overflow, but it is the only way to help in this case.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.