文档章节

ContentProvider数据更新

娶到笨笨
 娶到笨笨
发布于 2014/04/18 16:01
字数 3360
阅读 26
收藏 0
点赞 0
评论 0

Android学习 ContentProvider数据更新与Observer模式

一 Observer模式

意图:

  定义对象之间一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被更新。

  依赖(Dependents)、发布-订阅(Publish-Subscribe)。处理一对多情况下对象之间的依赖关系。

对象之间必然会存在依赖关系或者依赖关系会处于变动之中,如何解决依赖关系使他们之间的耦合性达到最小。

适用性:

  l  当一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将二者封装在独立的对象以使他们各自独立的改变和复用;

  l  当一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变。

  l  当一个对象必须通知其他对象,而它又不能假定其他对象是谁。

结构:

   

      

理解:

  Subject维护对象的状态,当状态改变需要立即通知Observer更新状态与Subject保持状态的一致;

使对象状态维护者Subject并不需要去关心有哪些Observer关注或者依赖于所维护的对象,而专心负责维护对象;

  同时如果某客户需要对某Subject所维护的对象感兴趣,可以通过对此Subject注册一个Observer来监听此对象的变化;

不需要监听的时候也可以注销掉监听;实现动态的关注对象状态的变化。

  Subject维护的对象状态谁会去改变呢?书上提到是某Observer去改变,但也不一定如此;

Subject维护某一对象,对象的具体如何改变可以和Subject是没有关系,仅仅是知道对象状态改变了,

需要通知其他对象;对象状态的改变是有很多层次或者方式进行,不限于由Observer来改变。

  更新由谁来触发呢?一是可以谁改变对象的状态谁来触发更新,这样各Observer状态的可以得到立即更新,

而且不需要客户端去负责更新,属于自动进行更新,但是每一次的对象状态变化都会执行更新,然而往往对象状态变化

是多个连续性的变化或者好几个变化间隔非常短,造成更新次数过多又非必要性的,降低效率;

二是由客户端负责更新,就可以控制何时进行更新最合适,避免整个状态变化中进行更新,而是整个连续性状态改变完成之后

一次性更新,但是此举将更新责任交给客户端,往往造成遗漏等不确定的缺陷。

  对象状态变化怎么传递?一是在执行更新Update时,将对象以参数的形式传递给Observer一步到位——推模型,

对象的类型如何?(Subject\State\…),提供太多数据和细节可能造成某些缺陷,而且提供这些数据并不总是需要;

二是提供接口供Observer访问获取状态或者变化细节——拉模型,这样造成双向的通信形式,且不能确定提供什么样信息合适,

依赖性太强耦合性高;三是通过其它方式渠道,主动的去查询获取对象的状态;所以这三种方式都有其优缺点,

对象状态变化会有多种情况,如何传递状态变化的信息,可能需要具体问题具体分析解决。

  对象变化了,会触发Subject的更新通知函数执行,完全不用关心谁关注此变化,有多少对象关注此变化;

而且都是通过抽象类实现,可以完全针对此接口进行编程,依赖于抽象而不是实现,降低Subject和Observer之间的耦合性。

  所以Subject可以不知道有何Observer和多少Observer的存在,Observer需要知道Subject的存在。

 

二 Android中信息列表数据更新流程

  在信息列表信息数据的变化需要及时反馈到界面上来,数据的存储是以SQLite数据库存储,

以ContentProvider形式访问;数据变化时是如何更新的呢?

  看一下面这个图:

   

    

  看到信息部分数据变化更新实现是在CursorAdapter中;

  ——>ContentObserver监听到数据变化消息之后;

  ——>通知CursorAdapter数据内容有变化;

  ——>通知信息相关的类进行数据更新重新进行查询;

  ContentObserver从名称看得出来这是一个Observer模式的应用;

那么ContentObserver是如何实现监听有数据变化的呢?

  ContentObserver在CursorAdapter中,肯定是CursorAdapter有关系,需要搞清楚ContentObserver与CursorAdapter之间的关系。

下面学习一下CursorAdapter中ContentObserver监听数据变化的流程;

 

三 Android中CursorAdapter的ContentObserver监听数据变化流程

  要搞清楚ContentObserver与CursorAdapter之间的关系,以及CursorAdapter与AbstractCursor之间更深层次之间的关系,

才能弄清楚谁是Subject,如何注册的Observer,在数据变化时,Subject是如何通知到Observer的。

  下面将采用倒推的方式一步一步的学习:

1 CursorAdapter中ContentObserver的注册到AbstractCursor过程

  ContentObserver(抽象类)就是用来接收数据变化时的观察者,能进行异步派发派发接收到变化的通知。

复制代码

public abstract class ContentObserver {  private Transport mTransport;   Handler mHandler;  public ContentObserver(Handler handler) {}  public IContentObserver getContentObserver() {}
  //需要重写onChange
  public void onChange(boolean selfChange) {}  public final void dispatchChange(boolean selfChange) {} }

复制代码

 

  在CursorAdapter初始化时:

void init(Context context, Cursor c, boolean autoRequery) {  // ChangeObserver 继承 ContentObserver  mChangeObserver = new ChangeObserver();  //交给Cursor注册Observer  c.registerContentObserver(mChangeObserver);
}

  

  Cursor中注册Observer过程:

Cursor是一个接口真正干活的是它实现者AbstractCursor

  

看下AbstractCursor 的registerContentObserver:void registerContentObserver(ContentObserver observer) {
        mContentObservable.registerObserver(observer);
}

 

ContentObservable又是什么呢?以及ContentObserver是怎么样一个类呢?

ContentObservable和前面ContentObserver不同:

 

  ContentObservable类:

复制代码

public class ContentObservable extends Observable<ContentObserver> {    public void registerObserver(ContentObserver observer) {}    public void dispatchChange(boolean selfChange) {}    public void notifyChange(boolean selfChange) {}
}public abstract class Observable<T> {    protected final ArrayList<T> mObservers = new ArrayList<T>();    public void registerObserver(T observer) {}    public void unregisterObserver(T observer) {}    public void unregisterAll() {
}

复制代码

 

    

  所以ContentObservable就是专门用来注册ContentObserver,负责管理AbstractCursor作为Subject时接收注册Observer的,

而AbstractCursor此处就是Subject;

       Observer——》ChangeObserverCursorAdapter内部类)

       Subject ——》AbstractCursor(成员ContentObservable负责管理Observer

 

触发更新Update

       既然AbstractCursor作为此处的Subject,那么触发Observer更新是在何时进行呢?

AbstractCursor有这样一个接口:

       

复制代码

protected void onChange(boolean selfChange) {
  //触发所有的Observer  mContentObservable.dispatchChange(selfChange);
  //这又是何缘故呢没搞懂 这里不会执行 下面再分析  if (mNotifyUri != null && selfChange) {
    mContentResolver.notifyChange(mNotifyUri, mSelfObserver);
  }
}

复制代码

 

ContentObservable类中:

复制代码

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

复制代码

 

ContentObserver类中:

复制代码

public final void dispatchChange(boolean selfChange) {  if (mHandler == null) {     onChange(selfChange);   } else {
    //异步的派发变化时通知     mHandler.post(
new NotificationRunnable(selfChange));   } }

复制代码

 

  这样就触发了所有注册到AbstarctCursor中ContentObserverable负责管理的所有ContentObserver;

此处便是CursorAdapter中的内部类ChangeObserver

  下面看一下这个层次上的结构类图:

      

    

       触发更新CursorAdapter中ChangeObserver中onChange函数执行;

这样就到了CursorAdapter更新流程上来了,这里属于Framework层;

  从CursorAdapter然后就到了Application层,如前面所述的信息列表的更新流程。

  这里的Observer模式中:

    Subject——>AbstractCursor

    Observer——>ChangeObserver(CursorAdapter内部类)

       那么到此AbstractCursor是如何改变其中对象的状态和何时使其执行通知所有的观察者的呢?

       作为Curosr虽然接口中提供了Update等操作来改变数据,但其实在AbstractCursor等并没有提供支持

来使用Update操作来改变数据,可以看到在4.0代码中已经将Update操作去掉;

       Cursor仅仅是作为database query返回的结果用于获取其中的数据,所以AbstractCursor及其子类维护

其中的某个数据对象,可以自己实现对象状态的改变维护如MatrixCursor,也可以仅仅负责某个数据对象的维护,

数据对象状态真正改变不用去负责,仅需要在数据有变化时得到通知即可如SQLiteCursor;

       所以还需要弄清楚Curosr作为Subject是维护的对象状态是如何变化且被通知到的。

       下面看一下AbstractCursor作为Subject其中的对象状态改变是如何进行和被通知到的。

 

四 Android中AbstractCursor对象状态变化通知

更新通知启动:

  从上面代码分析中可以知道AbstractCursor得到内容变化并通知其Observers是在下面函数中进行的:

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

 

顺着这条线找到onChange是何时被调用的:

       找到位置是在AbstractCursor内部类SelfContentObserver中有如下:

复制代码

protected static class SelfContentObserver extends ContentObserver {
  WeakReference<AbstractCursor> mCursor;  public void onChange(boolean selfChange) {
    AbstractCursor cursor = mCursor.get();    //这里调用其依赖类的上述的onChange类中    cursor.onChange(false);  //这里传递的false    }
}

复制代码

  

  这又是一个ContentObserver观察者类,这又是怎么回事呢?Go On……

SelfContentObserver作为观察者:

  找到SelfContentObserver使用方式:AbstractCursor中

复制代码

public void setNotificationUri(ContentResolver cr, Uri notifyUri) {
  mNotifyUri = notifyUri;
  mContentResolver = cr;
  mSelfObserver = new SelfContentObserver(this);  //进行Observer的注册  mContentResolver.registerContentObserver(
  mNotifyUri, true, mSelfObserver);
}

复制代码

 

  SelfContentObserver作为了ContentResolver的一个Observer;

  那函数setNotificationUri何时被调用呢?

答案是:ContentProvider中的Query()函数注释中有描述,派生类中需要调用这个c.setNotificationUri。

  那么现在注册任务交给了ContentResolver了。

 

ContentService登场:

       ContentResolver中注册Observer时

public final void registerContentObserver(Uri uri, boolean notifyForDescendents,
           ContentObserver observer)
{    getContentService().registerContentObserver(uri, notifyForDescendents,
    observer.getContentObserver());
}

  看到注册Observer使用的是ContentService进行的,这是个系统服务SystemService;

ContentService又是干什么的勾当的呢?

  从名称看到是内容服务,主要是数据库等提供解决方法的服务。因为数据库SQlite是一个C库,其中东东涉及很多需要。

 

ContentService中注册Observer函数

public void registerContentObserver(Uri uri, boolean notifyForDescendents,
              IContentObserver observer) {
  mRootNode.addObserverLocked(uri, observer,
    notifyForDescendents, mRootNode,
    Binder.getCallingUid(), Binder.getCallingPid());
}

 

看到此函数有三个参数分别代表什么意义呢:

  uri:针对对有变化的感兴趣进行监听的URI

  notifyForDescendents:true表示以uri前缀开始的任何变化都进行通知;false表示完全匹配才进行通知;

  observer:IContentObserver接口,提供了一个方法onChange,变化发生时Cursor需要更新时调用

  使用此ContentService的registerContentObserver接口注册Observer,

通过指定Uri可以仅对数据库中感兴趣的数据有变化时,进行监听。具体实现细节可以看下ContentService源代码

中是如何构建一个树形结构来管理观察者对感兴趣数据的监听,看到根节点就是上述的mRootNode构建了一颗大树。

  注册之后就是等待数据有变化时,进行监听了;此一布又是如何进行的呢?

 

触发数据更新通知:

  对数据库中数据的更改操作都是通过ContentResolver 中使用ContentProvider进行修改的,数据变化就来源于此;

看看ContentProvider中数据修改函数Insert中都干了些什么。

  ContentProvider是个abstract类,其中的数据更改操作的函数都是纯虚函数,但是看一下其中的Insert函数的注释:

    As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver)

    notifyChange()}after inserting.

    在使用此函数插入数据之后,需要调用类ContentResolver中函数notifyChange,所以其子类中需要做这个事情;

  可以看到在子类Insert函数中都执行了:

    getContext().getContentResolver().notifyChange(newUri, null)

  通过此到了ContentResolver的nofifyChange中,Go On……

  ContentResolver中函数:

void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
  getContentService().notifyChange(……);
}

 

  进入到ContentService中的数据变化通知更新策略中;注册Observer是在ContentService中,

数据变化通知Observer更新数据也必然是要在ContentService中。下面看看这是如何执行的:

 

ContentService通知Observer更新:

       ContentService函数中的notifyChange函数较为复杂,因为我们是注册对感兴趣的数据变化时才需要被通知到,

所以此处通过对树形结构存储的Observer,进行遍历查找到对变化感兴趣的Observer。

       

复制代码

public void notifyChange(Uri uri, IContentObserver observer,  boolean observerWantsSelfNotifications, boolean syncToNetwork) 
{   
//用于保存对此变化感兴趣的Observer  ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();  //收集对此变化感兴趣的Observer  mRootNode.collectObserversLocked(...,calls);  //发布数据变化通知到相应的Observer  final int numCalls = calls.size();  for (int i=0; i<numCalls; i++) {     ObserverCall oc = calls.get(i);     oc.mObserver.onChange(oc.mSelfNotify);   } }

复制代码

 

       此处就走到了哪里呢?必然正是我们前面在AbstractCursor中所注册的SelfContentObserver的onChange函数中,

然后就到了AbstractCursor作为Subject时这一层的Observer模式的通知机制中。

  这又是一个基于Observer模式的Subject——》Observer结构。

    Subject:ContentService;

    Observer:SelfContentObserver(AbstractCursor中内部类)

  上面所述数据改变时从ConentService通知其关注这一变化的Observer;这一过程的类结构图大致如下:

 

    

 

五 Android中ContentService数据变化通知更新流程

       从上面的整个代码流程可以看到这个过程中使用两个层次的Observer模式:

下面是注册Observer的时候的大致流程:

    

下面是数据变化时通知更新流程图:

 

    

 

  

  整个过程大致如上所述,有两个层次的Observer模式的应用。从中我们可以看到Subject可以不同,

Observer相同,使Observer可以被复用,Subject不用去关心Observer

Observer当然是要知道Subject存在的,Observer且能动态的添加删除。


本文转载自:http://www.cnblogs.com/bastard/archive/2012/06/02/2531663.html

共有 人打赏支持
娶到笨笨
粉丝 6
博文 51
码字总数 6482
作品 0
奉化
Android ContentProvider支持跨进程数据共享与"互斥、同步"杂谈

在开发中,假如,A、B进程有部分信息需要同步,这个时候怎么处理呢?设想这么一个场景,有个业务复杂的Activity非常占用内存,并引发OOM,所以,想要把这个Activity放到单独进程,以保证OOM...

看书的小蜗牛 ⋅ 2017/11/15 ⋅ 0

Content Provider使用

一、Content Provider基本概念 1、Content Provider提供为存储和获取数据提供了统一的接口 2、使用Content Provider可以再不同的应用程序间共享数据 3、Android为常见的一些数据提供了Conte...

小风89 ⋅ 2016/08/28 ⋅ 0

android Content Provider 详解

Android中的Contentprovider机制可支持在多个应用中存储和读取数据。这也是跨应用共享数据的唯一方式。在android系统中,没有一个公共的内存区域,供多个应用共享存储数据。 Android提供了一...

鉴客 ⋅ 2011/12/21 ⋅ 5

[Android] ContentProvider和Uri详解

一、使用ContentProvider(内容提供者)共享数据 ContentProvider在android中的作用是对外共享数据,也就是说你可以通过ContentProvider把应用中的数据共享给其他应用访问,其他应用可以通过...

枫兮兮 ⋅ 2014/03/03 ⋅ 0

Android使用内容提供者方式进行存储

内容提供者(ContentProvider)主要作用是对外共享数据,如果数据通过内容提供者对外共享了,那么其他应用就可以从内容提供者中查询到数据,并且可更新数据、删除数据、添加数据,如果采用文件...

IBMdW ⋅ 2011/09/26 ⋅ 0

SharedPreferences多进程解决方案

由于进程间是不能内存共享的,每个进程操作的SharedPreferences都是一个单独的实例,这导致了多进程间通过SharedPreferences来共享数据是不安全的,这个问题只能通过多进程间其它的通信方式或...

cjh94520 ⋅ 2017/04/27 ⋅ 0

Android Launcher开发之LiveFolder(实时文件夹) 完全解析

实时文件夹概述: 实时文件夹是在SDK1.5中引入的,支持开发人员在设备的默认打开屏幕(我们将其称为设备的主页)上公开 ContentProvider,如联系人信息、笔记和媒体。将ContentProvider(比如A...

长平狐 ⋅ 2012/09/03 ⋅ 0

Android读取系统联系人

使用 ContentProvider共享数据: 当应用继承 ContentProvider 类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据。虽 然使用其他方法也可以对外共享数据,但数据访问...

喜欢敲代码的感觉 ⋅ 2015/09/11 ⋅ 0

android数据存储与访问之使用ContentProvider

ContentProvider简介及其好处 简介: ContentProvider 在android中的作用是对外共享数据,也就是说你可以通过ContentProvider把应用中的数据共享给其他应用访问,其他应用可以通过ContentProv...

长平狐 ⋅ 2012/09/03 ⋅ 0

Android 四大组件(三)ContentProvider

转载请注明出处:http://blog.csdn.net/vnanyesheshou/article/details/75299025 这篇主要介绍下ContentProvider如何实现共享数据、及ContentResolver如何访问其他进程等数据。 ContentProvi...

VNanyesheshou ⋅ 2017/07/18 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

windows profesional 2017 build problem

.net framework .... https://stackoverflow.com/questions/43330915/could-not-load-file-or-assembly-microsoft-build-frameworkvs-2017...

机油战士 ⋅ 27分钟前 ⋅ 0

python3中报错的解决方法(长期更新)

1、ImportError: No module named ‘DjangoUeditor’ 出错原因:安装DjangoUeditor库适用于python2,需要下载适用python3的 下载地址:https://github.com/twz915/DjangoUeditor3 2、python3......

xiaoge2016 ⋅ 32分钟前 ⋅ 0

数据结构与算法之双向链表

一、双向链表 1.双向链表的结点结构 typedef struct DualNode{ ElemType data; struct DualNode *prior; // 前驱结点 struct DualNode *next; // 后继结点}DualNode, *DuL...

aibinxiao ⋅ 52分钟前 ⋅ 0

五大最核心的大数据技术

大数据技术有5个核心部分,数据采集、数据存储、数据清洗、数据挖掘、数据可视化。关于这5个部分,有哪些核心技术?这些技术有哪些潜在价值?看完今天的文章就知道了。 大数据学习群:7165810...

董黎明 ⋅ 53分钟前 ⋅ 0

PhpStorm 头部注释、类注释和函数注释的设置

首先,PhpStorm中文件、类、函数等注释的设置在:setting-》Editor-》FIle and Code Template-》Includes下设置即可,其中方法的默认是这样的: /**${PARAM_DOC}#if (${TYPE_HINT} != "v...

nsns ⋅ 53分钟前 ⋅ 0

spring.net AOP

http://www.springframework.net/doc-latest/reference/html/aop-quickstart.html https://www.cnblogs.com/wujy/archive/2013/04/06/3003120.html...

whoisliang ⋅ 58分钟前 ⋅ 0

【HAVENT原创】创建 Dockerfile 生成新的镜像,并发布到 DockerHub

注意:Win7 与 Win10 的版本存在差异,Win7 版本使用 Docker Quickstart Terminal 进入控制台,Win10下面直接用管理员权限打开控制台或者 PowerShell 即可;另外 Win7 下面只能访问 C盘,/ap...

HAVENT ⋅ 58分钟前 ⋅ 0

pom.xml出现web.xml is missing ...解决方案

提示信息应该能看懂。也就是缺少了web.xml文件,<failOnMissingWebXml>被设置成true了。 搜索了一下,Stack Overflow上的答案解决了问题,分享一下。 目前被顶次数最多的回答原文如下: This...

源哥L ⋅ 59分钟前 ⋅ 0

js时间戳与日期格式之间相互转换

1. 将时间戳转换成日期格式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 // 简单的一句代码 var date = new Date(时间戳); //获取一个时间对象 /** 1. 下面是获取时间日期的方法,需要什么样的格式自己...

Jack088 ⋅ 今天 ⋅ 0

web添加log4j

添加xml配置log4j.properties # Global logging configuration---root日志设置#log4j.rootLogger=info,dailyRollingFile,stdoutlog4j.rootLogger=debug,stdout,dailyRollingFile---......

黄柳淞 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部