zoukankan      html  css  js  c++  java
  • ContentProvider和Cursor以及CursorAdapter三者之间内部链接实现原理 解析

        最近 在学习Android3.0中推出的 Loader 机制,其中CursorLoader 这个加载器说是可以实时监测数据和更新数据,为了一探究竟,就连带的将 ContentProvider和Cursor以及CursorAdapter三者间的内部交互分析了下,然而本章内容主要就是将这一块,至于Loader机制准备,下一篇来具体分析。

        对于这三个类我们知道,Contentprovider就是一个Android中进程间的内容共享机制,我们可以使用ContentResolver这个工具嫁接 目标 URI 来访问对应的Contentprovider,从而获取目标Cursor数据,Android中 使用Sqliet就是这样一个机制。然而在这三个类之间其实存在了两处的观测者模式的运用。第一处在于Cursor  和 Contentprovider 之间,第二处在于 Cursor 和 CursorAdapter 之间,下面我们先来看一张时序图大致的了解下。Ps: 时序图 有哪里不对的还请及时指出啊。

                    

       上面说到观察者模式的运用 第一处在于Cursor  和 Contentprovider 之间,我们可以通过上面的时序图来加以分析,当我们通过 ContentResolver 对目标ContentProvider的数据进行CRUD(增删改查)操作时,在返回目标Cursor数据之前,我们发现在每个CRUD操作中有一个setNotifycationUri()这个方法,那么这个方法里到底做了什么呢,我们可以看看。

    public void setNotificationUri(ContentResolver cr, Uri notifyUri) {  
            synchronized (mSelfObserverLock) {  
                mNotifyUri = notifyUri;  
                mContentResolver = cr;  
                if (mSelfObserver != null) {  
                    mContentResolver.unregisterContentObserver(mSelfObserver);  
                }  
                mSelfObserver = new SelfContentObserver(this);  
                mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver);  
                mSelfObserverRegistered = true;  
            }  
        }  

    我们可以发现,这里它创建了一个SelfContentObserver的对象并且给它注册了Uri监听。这里SelfContentObserver看起源码知道了它继承了ContentObserver,就是一个Observer,这样一来当Uri变动时,我们就可以通知它了。注意了在我们进行CRUD操作时,我们经常会加一句 :getContext().getContentResolver().notifyChange(XXX.CONTENT_URI,null),那么这样一来Cursor类中的mSelfObserver就会收到通知并且回调onChange方法,到这里我们是不是可以看出来了这就是观察者模式的运用呢。

        至于第二处则在于 Cursor 和 CursorAdapter 之间,同样的 我们也可从上面的时序图中发现。CursorAdapter中持有两个观察者:mChangeObserver和mDataSetObserver.这两个Observer在 CursorAdapter初始化时或者调用其changeCursor(Cursor c)或swapCursor(Cursor c)方法时,就被注册到Cursor中了,三种方式的代码依次如下:

     void init(Context context, Cursor c, int flags) {
           ...省略
            mCursor = c;
             ...省略
            if ((flags & FLAG_REGISTER_CONTENT_OBSERVER) == FLAG_REGISTER_CONTENT_OBSERVER) {
                mChangeObserver = new ChangeObserver();
                mDataSetObserver = new MyDataSetObserver();
            } else {
                mChangeObserver = null;
                mDataSetObserver = null;
            }
    
            if (cursorPresent) {
                if (mChangeObserver != null) c.registerContentObserver(mChangeObserver);
                if (mDataSetObserver != null) c.registerDataSetObserver(mDataSetObserver);
            }
        }
      public void changeCursor(Cursor cursor) {
            Cursor old = swapCursor(cursor);
            if (old != null) {
                old.close();
            }
        }
     public Cursor swapCursor(Cursor newCursor) {
            if (newCursor == mCursor) {
                return null;
            }
            Cursor oldCursor = mCursor;
            if (oldCursor != null) {
                if (mChangeObserver != null) oldCursor.unregisterContentObserver(mChangeObserver);
                if (mDataSetObserver != null) oldCursor.unregisterDataSetObserver(mDataSetObserver);
            }
            mCursor = newCursor;
            if (newCursor != null) {
                if (mChangeObserver != null) newCursor.registerContentObserver(mChangeObserver);
                if (mDataSetObserver != null) newCursor.registerDataSetObserver(mDataSetObserver);
                mRowIDColumn = newCursor.getColumnIndexOrThrow("_id");
                mDataValid = true;
                // notify the observers about the new cursor
                notifyDataSetChanged();
            } else {
                mRowIDColumn = -1;
                mDataValid = false;
                // notify the observers about the lack of a data set
                notifyDataSetInvalidated();
            }
            return oldCursor;
        }

    我们可以看到,这两个Observer在初始化Adapter的时候被创建,而后会在不同情况下注册到Cursor中。这里是因为Cursor中持有两个目标对象:mContentObservable和mDataSetObservable 这两个类就继承了Observable接口。所以其实是它们两分别接受了Observer的注册。代码如下:

    public void registerContentObserver(ContentObserver observer) {  
            mContentObservable.registerObserver(observer);  
    }  
    public void registerDataSetObserver(DataSetObserver observer) {  
            mDataSetObservable.registerObserver(observer);  
    }  

    到这里第二处观察者模式运用就显示出来啦!

        从时序图中,我们可以看到当Cursor类中的mSelfObserver收到通知后就会调用onChange方法

    protected void onChange(boolean selfChange) {  
            synchronized (mSelfObserverLock) {  
                mContentObservable.dispatchChange(selfChange);  
                if (mNotifyUri != null && selfChange) {  
                    mContentResolver.notifyChange(mNotifyUri, mSelfObserver);  
                }  
            }  
    }  

    我们可以看到 它会触发mContentObservable这个目标对象去调用dispatchChange()方法

    public void dispatchChange(boolean selfChange) {  
            synchronized(mObservers) {  
                for (ContentObserver observer : mObservers) {  
                    if (!selfChange || observer.deliverSelfNotifications()) {  
                        observer.dispatchChange(selfChange);  
                    }  
                }  
     }  

    到这里,它接着就通知其注册的各个Observer去执行dispatchChange()方法,前面我们已经知道了mContentObservable了被注册了ChangeObserver 的实例 mChangeObserver,这里呢首先会执行ChangeObserver的父类ContentObserver的dispatchChange(false)方法:

    public final void dispatchChange(boolean selfChange) {  
            if (mHandler == null) {  
                onChange(selfChange);  
            } else {  
                mHandler.post(new NotificationRunnable(selfChange));  
            }  
    }  

    接着就来到其子类实例mChangeObserver的dispatchChange()方法:

    private class ChangeObserver extends ContentObserver {  
            public ChangeObserver() {  
                super(new Handler());  
            }  
      
            @Override  
            public boolean deliverSelfNotifications() {  
                return true;  
            }  
      
            @Override  
            public void onChange(boolean selfChange) {  
                onContentChanged();  
            }  
    }  

    在其Onchange()方法中调用了onContentChanged()方法:

    protected void onContentChanged() {  
            if (mAutoRequery && mCursor != null && !mCursor.isClosed()) {  
                mDataValid = mCursor.requery();  
            }  
    }  

    到这里 我们是不是恍然大悟了,mCursor.requery()则就会重新刷新并填充mCursor对象。然后还没有结束:我们的cursor重新填充了,但是不会告诉Adapter执行notifyDataSetChanged()方法,因为只有执行了这个方法,我们的界面才会刷新。

    所以我们接着看下mCursor.requery()的内部做了些什么:

    public boolean requery() {  
            if (mSelfObserver != null && mSelfObserverRegistered == false) {  
                mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver);  
                mSelfObserverRegistered = true;  
            }  
            mDataSetObservable.notifyChanged();  
            return true;  
    }  

    我们可以看到,mDataSetObservable.notifyChanged();这个就会 会触发mDataSetObservable去通知其内部注册的observer,前面我们也讲了mDataSetObservable被注册了 CursorAdapter中的 MyDataSetObserver的实例 mDataSetObserver,所以我们接着看下mDataSetObserver的onchange()方法的实现:

    private class MyDataSetObserver extends DataSetObserver {  
            @Override  
            public void onChanged() {  
                mDataValid = true;  
                notifyDataSetChanged();  
            }  
      
            @Override  
            public void onInvalidated() {  
                mDataValid = false;  
                notifyDataSetInvalidated();  
            }  
    }  

    在该方法中调用了 notifyDataSetChanged();  这个方法干嘛了呢,我们不仅要问,是不是它就是用来刷新界面呢?这个方法用的是子父类Baseadapter的,

    BaseAdapetr:

    public void notifyDataSetChanged() {
            mDataSetObservable.notifyChanged();
        }

    我们可以看到,在其源码中,它会调用其父类BaseAdapetr中mDataSetObservable去通知其中被注册的Observer,那这个observer到底在哪里被注册的呢,这里呢 也就不饶弯子了一步到位,回到我们使用CursorAdapter的最初,但我们初始化完成它的时候,我们是不是接着会调用setAdapter()方法,将该Adapter设置到目标列表中,那么这里又做了什么呢?

     public void setAdapter(ListAdapter adapter) {
    
           ...省略
    
            if (mAdapter != null) {
               
           ...省略
    
                mDataSetObserver = new AdapterDataSetObserver();
                mAdapter.registerDataSetObserver(mDataSetObserver);
    
                 ...省略
    
    
            requestLayout();
        }

    在这里我们找到了我们的答案,原来这个被注册的observer就是AdapterDataSetObserver,那这下就好啦,我们转到其内部的onchange()去一探究竟:

     public void onChanged() {
                mDataChanged = true;
                mOldItemCount = mItemCount;
                mItemCount = getAdapter().getCount();
    
                // Detect the case where a cursor that was previously invalidated has
                // been repopulated with new data.
                if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
                        && mOldItemCount == 0 && mItemCount > 0) {
                    AdapterView.this.onRestoreInstanceState(mInstanceState);
                    mInstanceState = null;
                } else {
                    rememberSyncState();
                }
                checkFocus();
                requestLayout();
            }

    果不其然,原来它通过 requestLayout();来完成接下来的操作了去刷新界面,其内部就是Android中View的绘制机制了,感兴趣的话可以去了解哦!

     到这里,本章内容就全部讲完啦!嘎嘎~  Ps: 有不对的还请及时指出哦!

     
  • 相关阅读:
    Get-CrmSetting返回Unable to connect to the remote server的解决办法
    Dynamics 365中的常用Associate和Disassociate消息汇总
    Dynamics 365 Customer Engagement V9 活动源功能报错的解决方法
    Dynamics Customer Engagement V9版本配置面向Internet的部署时候下一步按钮不可点击的解决办法
    Dynamics 365检查工作流、SDK插件步骤是否选中运行成功后自动删除系统作业记录
    注意,更改团队所属业务部门用Update消息无效!
    Dynamics 365的审核日志分区删除超时报错怎么办?
    Dynamics 365使用Execute Multiple Request删除系统作业实体记录
    Dynamics 365的系统作业实体记录增长太快怎么回事?
    Dynamics CRM日期字段查询使用时分秒的方法
  • 原文地址:https://www.cnblogs.com/kevinOne/p/4306154.html
Copyright © 2011-2022 走看看