Search code examples
androidasynctaskloader

Populating a ListView using AsyncTaskLoader


I'm having problems using AsyncTaskLoader. This is my first attempt populating a ListView from a SQLite database using a loader.

Everything seems ok, when I rotate the screen the data is cached and no query is done again. But when I press the home button and launch my app again, the data is loaded again.

Note: Usuario means User, so I'm populating the ListView with a list of users.

public class Main extends SherlockFragmentActivity
        implements LoaderManager.LoaderCallbacks<ArrayList<Usuario>> {
    UsuarioAdapter adapter;
    ListView listView;
    Database db;

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

        listView = (ListView) findViewById(R.id.lista);
        db       = new Database(this);
        adapter  = new UsuarioAdapter(this, new ArrayList<Usuario>());
        listView.setAdapter(adapter);
        getSupportLoaderManager().initLoader(0, null, this);
    }

    @Override
    public Loader<ArrayList<Usuario>> onCreateLoader(int id, Bundle args) {
        return new UsuariosLoader(this, db);
    }

    @Override
    public void onLoadFinished(Loader<ArrayList<Usuario>> loader,
                           ArrayList<Usuario> usuarios) {
       //adapter.notifyDataSetChanged();
      listView.setAdapter(new UsuarioAdapter(this, usuarios));
      // ((BaseAdapter) listView.getAdapter()).notifyDataSetChanged();
    }

    @Override
    public void onLoaderReset(Loader<ArrayList<Usuario>> loader) {
        listView.setAdapter(null);
    }
}

// THE LOADER
class UsuariosLoader extends AsyncTaskLoader<ArrayList<Usuario>> {
    private ArrayList<Usuario> usuarios;
    private Database db;

    public UsuariosLoader(Context context, Database db) {
        super(context);
        this.db = db;
    }

    @Override
    protected void onStartLoading() {
        if (usuarios != null) {
          deliverResult(usuarios); // Use the cache
        }
       forceLoad();
    }

    @Override
    protected void onStopLoading() {
        // The Loader is in a stopped state, so we should attempt to cancel the
        // current load (if there is one).
        cancelLoad();
    }

    @Override
    public ArrayList<Usuario> loadInBackground() {              
        db.open();  // Query the database
        ArrayList<Usuario> usuarios = db.getUsuarios();
        db.close();
        return usuarios;
    }

    @Override
    public void deliverResult(ArrayList<Usuario> data) {
        usuarios = data; // Caching
        super.deliverResult(data);
    }

    @Override
    protected void onReset() {
       super.onReset();
       // Stop the loader if it is currently running
       onStopLoading();
       // Get rid of our cache if it exists
       usuarios = null;
    }

    @Override
    public void onCanceled(ArrayList<Usuario> data) {
       // Attempt to cancel the current async load
       super.onCanceled(data);
       usuarios = null;
   }
}

And I think this snippet is not well done. I'm creating a new Adapter instead of updating the data.

    @Override
    public void onLoadFinished(Loader<ArrayList<Usuario>> loader,
                           ArrayList<Usuario> usuarios) {
        //adapter.notifyDataSetChanged();
        listView.setAdapter(new UsuarioAdapter(this, usuarios));
        //((BaseAdapter) listView.getAdapter()).notifyDataSetChanged();
    }

Why adapter.notifyDataSetChanged() does not work?

So, basically, my app does not crash but all my data is reloaded again every time I restart the app.

Edit: This is my Adapter code:

    class UsuarioAdapter extends BaseAdapter {
    private ArrayList<Usuario> usuarios;
    private LayoutInflater inflater;

    public UsuarioAdapter(Context context, ArrayList<Usuario> usuarios) {
        this.usuarios = usuarios;
        this.inflater = LayoutInflater.from(context);
    }

    @Override
    public int getCount() { return usuarios.size(); }
    @Override
    public Object getItem(int pos) { return usuarios.get(pos); }
    @Override
    public long getItemId(int pos) { return pos; }

    @Override
    public View getView(int pos, View convertView, ViewGroup arg) {
        LinearLayout itemView;
        if (convertView == null) {
            itemView = (LinearLayout) inflater.inflate(R.layout.list_item, null);
        } else {
            itemView = (LinearLayout) convertView;
        }

        ImageView avatar = (ImageView) itemView.findViewById(R.id.avatar);
        TextView nombre  = (TextView) itemView.findViewById(R.id.nombre);
        TextView edad    = (TextView)itemView.findViewById(R.id.edad);

        // Set the image ... TODO
        nombre.setText(usuarios.get(pos).getNombre());
        edad.setText(String.valueOf(usuarios.get(pos).getEdad()));
        return itemView;
    }
}

Solution

  • I've found the solution. The onStartLoading was the guilty:

        @Override
        protected void onStartLoading() {
            if (usuarios != null) {
                deliverResult(usuarios); // Use cache
            } else {
                forceLoad();
            }
        }
    

    In my original post forceLoad was always called. It must be in the else branch.