Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use two Cursors and CursorJoiner in LoaderManager in android

I have a ContentProvider, it's having two tables 1. OnlineContacts 2. AllContacts. Then i have a method in which i am querying both the tables and getting their resultant cursors separately. And then joining them using CursorJoiner and making a list of Contacts. Passing this list to my CustomAdapter extending BaseAdapter, i am populating my listview. Like :

public static List<Contact> getContacts(Context context){
    List<Contact> contactList = new ArrayList<Contact>(); 

// Getting First Cursor
    String URL = xyz;
    Uri baseUri1 = Uri.parse(URL);
    String[] select = xyz; 
    String where =xyz; 
    Cursor cursor =  context.getContentResolver().query(baseUri1, select, where, null, "pid");

// Getting 2nd Cursor
    Uri baseUri = xyz; 
    String[] projection =xyz; 
    String selection =xyz; 
    String[] selectionArgs = null;
    String sortOrder = xyz; 

    Cursor mCursor= context.getContentResolver().query(baseUri, projection, selection, selectionArgs, sortOrder);

    // Joinging Both Cursors

    CursorJoiner joiner = new CursorJoiner(cursor, new String[] {MyContentProvider.PHONE_ID} , mCursor, new String[] {MyContentProvider.Phone._ID});
    for (CursorJoiner.Result joinerResult : joiner) {
        Contact cont = new Contact();

        switch (joinerResult) {
        case LEFT:
            // handle case where a row in cursorA is unique
            break;
        case RIGHT:
            // handle case where a row in cursorB is unique

        case BOTH:
            // handle case where a row with the same key is in both cursors
            cont.setID(xyz);
            cont.setName(xyz);
            cont.setPhoneNumber(xyz);
            cont.setStatus("0");
            contactList.add(cont);
            break;
        }
    }
    mCursor.close();
    cursor.close();
    return contactList;
}   

And here is my CustomAdapter :

private class CustomAdapter extends BaseAdapter {

        List<Contact> contactsList ;
        public CustomAdapter(List<Contact> contactsList){
            this.contactsList = contactsList;
        }

        public List<Contact> contacts() {
            return this.contactsList;    
        }

        @Override
        public int getCount() {
            return contactsList.size();
        }

        @Override
        public Contact getItem(int arg0) {
            return contactsList.get(arg0);
        }

        @Override
        public long getItemId(int arg0) {
            return arg0;
        }

        @Override
        public View getView(int position, View view, ViewGroup viewGroup) {

            SimpleViewHolder viewHolder;
            if(view==null)
            {
                LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                view = inflater.inflate(R.layout.list_item, viewGroup,false);
                viewHolder = new SimpleViewHolder(view);
                view.setTag(viewHolder);
            }

            viewHolder = (SimpleViewHolder) view.getTag();

            TextView contName = (TextView) viewHolder.get(R.id.nameText);
            ImageView image = (ImageView) viewHolder.get(R.id.contact_image);

            Contact contact = contactsList.get(position);
            image.setBackgroundResource(R.drawable.person_empty_offline);

            contName.setText(contact.getName());
            return view;
        }

    }   

Now , i need to do it using LoaderManager. I know , to some extent, the implementation of it. I know , the onCreateLoader acts like:

    public Loader<Cursor> onCreateLoader(int id, Bundle args) {

        Uri baseUri = xyz;
        String[] projection =  xyz;
        String selection =  xyz;
        String[] selectionArgs = null;
        String sortOrder =  xyz;
        return  new CursorLoader(getActivity(), baseUri, projection, selection, selectionArgs, sortOrder);
}

And in OnCreate , if i use MyCursorAdapter extending CursorAdapter, we do something like :

mAdapter = new MyCursorAdapter(getActivity(), null, 0);
        setListAdapter(mAdapter);
        getLoaderManager().initLoader(0, null, this);

Now, i need to do is how can i achive the above implementation using LoaderManager. I didn't know how to ask thats why its too explanatory.

like image 412
BST Kaal Avatar asked Jul 14 '14 09:07

BST Kaal


2 Answers

Use two loaders, one for each cursor. When either one finishes loading, call another method that will join them if both have loaded.

// Loader IDs. You could also generate unique R.id values via XML
private static final int LOADER_ID_CURSOR_1 = 1;
private static final int LOADER_ID_CURSOR_2 = 2;

private Cursor cursor1 = null;
private Cursor cursor2 = null;

// return loader for cursor 1
private CusorLoader getCursor1Loader() {
    Uri uri = Uri.parse(abc);
    String[] select = abc; 
    String where = abc;
    String[] whereArgs = abc;
    String sortOrder = abc;
    return new CursorLoader(uri, select, where, whereArgs, sortOrder);
}

// return loader for cursor 2
private CusorLoader getCursor2Loader() {
    // same as above but with different values
    return new CursorLoader(uri, select, where, whereArgs, sortOrder);
}

// to start loading, ...
LoaderManager lm = getLoaderManager();
lm.initLoader(LOADER_ID_CURSOR_1, null, this);
lm.initLoader(LOADER_ID_CURSOR_2, null, this);

// LoaderCallbacks implementations
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    switch(id) {
    case LOADER_ID_CURSOR_1:
        return getCursor1Loader();
    case LOADER_ID_CURSOR_2:
        return getCursor2Loader();
    }
}

@override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    switch(loader.getId()) {
    case LOADER_ID_CURSOR_1:
        cursor1 = data;
        joinCursors();
        break;
    case LOADER_ID_CURSOR_2:
        cursor2 = data;
        joinCursors();
        break;
    }
}

private void joinCursors() {
    if (cursor1 != null && cursor2 != null) {
        // use CursorJoiner here
    }
}
like image 113
Karakuri Avatar answered Sep 22 '22 13:09

Karakuri


I have written a class which loads two different Cursors using the LoaderManager and returns the CursorJoiner.Result objects so you can handle the join. There is not much to say about this code, if something is unclear or you have any questions just ask in the comments!

public class JoinLoader {

    public interface JoinHandler {
        public void onHandleJoin(CursorJoiner.Result result);
    }

    private static final int LOADER_ONE = 0;
    private static final int LOADER_TWO = 1;

    private final LoaderCallbackImpl callbackOne;
    private final LoaderCallbackImpl callbackTwo;

    private final Context context;
    private final LoaderManager loaderManager;

    private Cursor cursorOne;
    private Cursor cursorTwo;

    private String[] leftColumns;
    private String[] rightColumns;

    private JoinHandler joinHandler;

    private JoinLoader(Activity activity) {
        this.context = activity;
        this.loaderManager = activity.getLoaderManager();
        this.callbackOne = new LoaderCallbackImpl(activity, new LoaderCallbackImpl.FinishedListener() {
            @Override
            public void onFinished(Cursor data) {
                cursorOne = data;
                handleSuccess();
            }
        });
        this.callbackTwo = new LoaderCallbackImpl(activity, new LoaderCallbackImpl.FinishedListener() {
            @Override
            public void onFinished(Cursor data) {
                cursorTwo = data;
                handleSuccess();
            }
        });
    }

    public void start() {
        this.cursorOne = null;
        this.cursorTwo = null;
        this.loaderManager.initLoader(LOADER_ONE, null, this.callbackOne);
        this.loaderManager.initLoader(LOADER_TWO, null, this.callbackTwo);
    }

    public void setJoinOn(String[] leftColumns, String[] rightColumns) {
        this.leftColumns = leftColumns;
        this.rightColumns = rightColumns;
    }

    private void handleSuccess() {
        if(this.joinHandler != null && this.cursorOne != null && this.cursorTwo != null) {
            CursorJoiner joiner = new CursorJoiner(this.cursorOne, this.leftColumns, this.cursorTwo, this.rightColumns);
            for (CursorJoiner.Result result : joiner) {
                this.joinHandler.onHandleJoin(result);
            }
            this.cursorOne.close();
            this.cursorTwo.close();
        }
    }

    public void setJoinHandler(JoinHandler joinHandler) {
        this.joinHandler = joinHandler;
    }

    public void setFirstQuery(Uri uri, String[] projection, String selection, String[] selectionArgs, String orderBy) {
        this.callbackOne.setQuery(uri, projection, selection, selectionArgs, orderBy);
    }

    public void setSecondQuery(Uri uri, String[] projection, String selection, String[] selectionArgs, String orderBy) {
        this.callbackTwo.setQuery(uri, projection, selection, selectionArgs, orderBy);
    }

    private static class LoaderCallbackImpl implements LoaderManager.LoaderCallbacks<Cursor> {

        public interface FinishedListener {
            public void onFinished(Cursor data);
        }

        private final Context context;
        private final FinishedListener finishedListener;

        private Uri uri;
        private String[] projection;
        private String selection;
        private String[] selectionArgs;
        private String orderBy;

        private boolean finished = false;

        private LoaderCallbackImpl(Context context, FinishedListener listener) {
            this.context = context;
            this.finishedListener = listener;
        }

        @Override
        public Loader<Cursor> onCreateLoader(int id, Bundle args) {
            this.finished = false;
            return new CursorLoader(context, uri, projection, selection, selectionArgs, orderBy);
        }

        @Override
        public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
            this.finished = true;
            if(this.finishedListener != null) {
                this.finishedListener.onFinished(data);
            }
        }

        @Override
        public void onLoaderReset(Loader<Cursor> loader) {

        }

        public void setQuery(Uri uri, String[] projection, String selection, String[] selectionArgs, String orderBy) {
            this.uri = uri;
            this.projection = projection;
            this.selection = selection;
            this.selectionArgs = selectionArgs;
            this.orderBy = orderBy;
        }

        public boolean isFinished() {
            return finished;
        }
    }
}

I tested the class and it seems to work as expected. You can use it like this:

JoinLoader loader = new JoinLoader(activity);
loader.setFirstQuery(firstUri, firstProjection, firstSelection, firstSelectionArgs, firstOrderBy);
loader.setSecondQuery(secondUri, secondProjection, secondSelection, secondSelectionArgs, secondOrderBy);
loader.setJoinOn(leftColumns, rightColumns);
loader.setJoinHandler(new JoinLoader.JoinHandler() {
    @Override
    public void onHandleJoin(CursorJoiner.Result result) {
        switch (result) {
            case LEFT:
                ...
                break;

            case RIGHT:
                ...
                break;

            case BOTH:
                ...
                break;
        }
    }
});
loader.start();

I hope I could help you, if you have any further questions please feel free to ask!

like image 42
Xaver Kapeller Avatar answered Sep 24 '22 13:09

Xaver Kapeller