如何在android的LoaderManager中使用两个Cursor和CursorJoiner

Posted

技术标签:

【中文标题】如何在android的LoaderManager中使用两个Cursor和CursorJoiner【英文标题】:How to use two Cursors and CursorJoiner in LoaderManager in android 【发布时间】:2014-09-04 04:29:32 【问题描述】:

我有一个 ContentProvider,它有两个表 1. OnlineContacts 2. AllContacts。然后我有一个方法,我在其中查询两个表并分别获取它们的结果cursors。然后使用 CursorJoiner加入他们并列出 Contacts。将此列表传递给我的 CustomAdapter extending BaseAdapter,我正在填充我的 listview。喜欢:

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;
   

这是我的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;
        

       

现在,我需要使用 LoaderManager 来完成。我知道,在某种程度上,它的执行。我知道,onCreateLoader 的行为类似于:

    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);

OnCreate 中,如果我使用 MyCursorAdapter extending CursorAdapter,我们会执行以下操作:

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

现在,我需要做的是如何使用 LoaderManager 实现上述实现。我不知道怎么问这就是为什么它太解释了。

【问题讨论】:

您可以使用两个加载器进行两个不同的查询,然后将结果连接起来(检查此gist.github.com/luksprog/7ec8cd3fcdea97b5839c),或者您在ContentProvider 级别实现连接并返回MatrixCursor from连接的结果。 【参考方案1】:

使用两个加载器,每个游标一个。当任何一个完成加载时,调用另一个方法,如果两者都已加载,将加入它们。

// 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
    

【讨论】:

Thnx alot @KaraKuri,你的回答正是我想要的。我在对我有用的joinCursor 方法中使用了 1MatrixCursor`。 你好@KaraKuri,你可以看看***.com/questions/25196894/…【参考方案2】:

我编写了一个类,它使用LoaderManager 加载两个不同的Cursors 并返回CursorJoiner.Result 对象,以便您可以处理连接。这段代码没什么好说的,如果有什么不清楚或者有什么问题可以在cmets中提问!

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;
        
    

我测试了这个类,它似乎按预期工作。你可以这样使用它:

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();

希望能帮到你,如果还有什么问题欢迎追问!

【讨论】:

Thnx @Xaver Kapeller 兄弟,我实现了 Karakuri 给出的上述答案,这对我来说更容易理解......现在我尝试用你的方式来查看哪个行为有效。非常感谢。我肯定会问你很多关于很多事情的问题。你很棒。 你好@Xaver Kapeller,你可以看看***.com/questions/25196894/… @Xaver Kapeller 是否可以为左连接和右连接加入光标?

以上是关于如何在android的LoaderManager中使用两个Cursor和CursorJoiner的主要内容,如果未能解决你的问题,请参考以下文章

Android LoaderManager原理剖析

Android 兼容包和 getLoaderManager()

Android LoaderManager原理详解

android Loader机制

Android - 尝试重新打开一个已经关闭的对象:使用 loaderManager 的 SQLiteQuery

LoaderManager与Loader使用理解