一 代码位置结构及相关类

..\packages\apps\Mms\src\com\android\mms\ui:存放界面显示相关的类

..\packages\apps\Mms\src\com\android\mms\data:存放界面显示需要的数据相关的类

主要的类:

ConversationList:信息对话界面——>ListActivity

ConversationListAdapter:适配器 ——>CursorAdapter

ConversationListItem:对话界面的ListItem View——>RelativeLayout

ConversationListItemData:对话界面列表显示需要的各项数据

Conversation:显示所需所有对话信息的所有数据

ContactList:每个Thread信息所对应的联系人

Contact:一个联系人数据信息


二 交互过程:

界面显示数据获取过程:

类交互过程:

二 界面数据查询更新流程图

三 代码实现过程分析

1 ConversationList中启动查询

onStart(){

……

startAsyncQuery();

}

实际上是:

startAsyncQuery() {

……

// mQueryHandler——>ThreadListQueryHandler ConversationList的内部类

//最终继承于AsyncQueryHandler

Conversation.startQueryForAll(mQueryHandler,THREAD_LIST_QUERY_TOKEN);

}

2 Conversation中调用异步查询线程

Conversation.startQueryForAll——>

public static voidstartQueryForAll(AsyncQueryHandler handler, int token) {

……

finalAsyncQueryHandlerqueryHandler = handler;

queryHandler.postDelayed(new Runnable() {

//匿名内部类

public void run() {

queryHandler.startQuery(

queryToken, null, sAllThreadsUri,

ALL_THREADS_PROJECTION, null, null, Conversations.DEFAULT_SORT_ORDER);

}

}, 10);

Conversations.DEFAULT_SORT_ORDER);

}

(AsyncQueryHandler使用条用者线程和工作线程组成)异步查询

此查询的是:所有Thread信息;

3 AsyncQueryHandler中查询过程

//各参数的含义

public voidstartQuery(int token, Object cookie, Uri uri,

String[] projection, String selection, String[] selectionArgs,

String orderBy) {

//启动一个工作者线程

mWorkerThreadHandler.sendMessage(msg);

}

——》工作线程查询完毕之后,返回到调用者线程;

——》执行AsyncQueryHandler的onQueryComplete函数;

——》 回到自行实现的继承于AsyncQueryHandler的类中 重写的onQueryComplete函数中;

——》执行到ThreadListQueryHandler的onQueryComplete函数中;

——》通知到ConversationList,至此查询Thread信息的过程结束;

数据据查询就是要使ContentProvider与数据库进行交互

AsyncQueryHandler的内部类工作者线程WorkerHandler的函数handleMessage中完成;

AsyncQueryhandler

A helper class to help make handling asynchronous ContentResolver queries easier.

AsyncQueryhandler中有两个handlerMessage,

一个是基于外部线程looper的,

一个是基于内部WorkerHandler实现的HandlerThread新线程的looper。

外部调用startQuery会通过mWorkerThreadHandler.sendMessage(msg)将查询发送给

WorkerHandler中处理,即在新线程中查询,

当WorkerHandler处理完后,把结果发送给AsyncQueryhandler的handlerMessage来调用对应的onXXXComplete函数。

这里就是把查询结果返回给原来线程来处理,这就通过两个handlerMessage实现了两个线程的消息交互。

AsyncQueryHandler实现步查询原理过程在此不作详细分析;

涉及到线程、Handle,Message等

ContentProvider如何与数据库进行交互在此不作详细分析;


4 ThreadListQueryHandler

属于ConversationList类的内部类:继承于AsyncQueryHandler

重写抽象函数,接收查询结果的反馈;

简单看一下这个函数:

@Override

protected void onQueryComplete(int token, Object cookie, Cursor cursor) {

switch (token) {

caseTHREAD_LIST_QUERY_TOKEN:

……

//mListAdapter属于ConversationListAdapter

mListAdapter.changeCursor(cursor); //更新UI数据

}

}

——》至此工作将转移到ConversationListAdapter中进行;

——》生成所需要ViewIten和绑定UI显示所需要的数据;

ConversationListAdapter继承于CursorAdapter

关于CursorAdapter功能及实现原理作用在此不作详细分析;

5 ConversationListAdapter

继承于:CursorAdapter;

简单看一下:ListView于Adapter以及Cursor的关系:

Adapter的作用就是ListView界面与数据之间的桥梁,

当列表里的每一项显示到页面时,都会调用Adapter的getView方法返回一个View

(对于CursorAdapter具体作用这里不作详细分析)


看一下CursorAdapter中的getView函数:

@Override

public ViewgetView(int position, View convertView, ViewGroup parent) {

……

View v;

//这里的作用很关键决定要不要新创建一个ViewItem

if (convertView == null) {

//创建一个ViewItem

v =newView(mContext, mCursor, parent);

} else {

//涉及到Recycler机制保证不会无限去创建Item,重复利用

v =convertView;

}

//将数据分配给所要显示的ViewItem

bindView(v, mContext, mCursor);

return v;

}

两个抽象函数abstract

newView:返回一个View,自定义ViewItem,需要重写;

bindView:绑定数据,需要重写;

下面看看ConversationListAdapter对这两个函数的实现:

newView:

@Override

public ViewnewView(Context context, Cursor cursor, ViewGroup parent) {

//LayoutInflater获取res\layout\下的布局文件xml,并且实例化;

//这里就是ListViewItem

return mFactory.inflate(R.layout.conversation_list_item, parent, false);

}

(具体LayoutInflater的作用在此不详细分析)

看看conversation_list_item的布局:

<com.android.mms.ui.ConversationListItem xmlns:android="http:.....">

//联系人快捷标识显示一张联系人图片点击弹出相关功能:tell,msg

<android.widget.QuickContactBadge android:id="@+id/avatar"/>

//ViewItem可以容纳的控件

<ImageView android:id="@+id/presence"/>

<TextView android:id="@+id/from"/>

<TextView android:id="@+id/date"/>

<ImageView android:id="@+id/error"/>

<ImageView android:id="@+id/attachment"/>

<TextView android:id="@+id/subject"/>

</com.android.mms.ui.ConversationListItem>

bindView:

@Override

public voidbindView(View view, Context context, Cursor cursor) {

//转化为信息列表的ListViewItem

ConversationListItem headerView = (ConversationListItem) view;

//使用cursor构建对话信息关联信息数据和联系人数据

Conversation conv = Conversation.from(context, cursor);

//构建单个对话信息数据

ConversationListItemData ch = new ConversationListItemData(context, conv);

//绑定数据

headerView.bind(context, ch);

}

根据cursor所获取到的数据个数 循环构建;



三 联系人数据的查询

  前面使用AsyncQueryHandler所获取到的cursor仅仅是查询了,所有对话信息数据;但是其中的联系人仅仅只是保存了其ID:recipientIds;

  还需要根据此recipientIds获取其联系人的信息;这个就是在包装信息数据给ListItem使用的时候获取的;

Conversation conv = Conversation.from(context, cursor);获取联系人相关信息;

下面看下这个过程:

先从Conversation中的Cache中查找是否当前cursor所对应的Thread信息已存在于缓存中,

若存在则将其更新并返回;否则新创建,并将其加入到缓存Cache中;

public static Conversation from(Context context, Cursor cursor) {

long threadId = cursor.getLong(ID);

if (threadId > 0) {

//从Conversation缓存中查找cursor所对应的Thread信息

Conversation conv = Cache.get(threadId);

if (conv != null) {

//已存在缓存中update the existing conv in-place

fillFromCursor(context, conv, cursor, false);

return conv;

}

}

//不存在于缓存中,新创建

Conversation conv = new Conversation(context, cursor, false);

Cache.put(conv);

return conv;

}

实际上不管是更新还是创建 都会走函数fillFromCursor();

那么下面看看这个函数都干了些什么事情;

private static void fillFromCursor(Context context, Conversation conv,

Cursor c, boolean allowQuery) {

synchronized(conv) {

//填写conv实例基本的对话信息如ThreadId,date,count,attach,type等;

}

//获取cursor中联系人Ids;

String recipientIds = c.getString(RECIPIENT_IDS);

//通过recipientIds获取对应的联系人数据:address,name……

ContactListrecipients = ContactList.getByIds(recipientIds, allowQuery);

synchronized(conv) {

//关联联系人数据

conv.mRecipients = recipients;

//计算未读信息条数

}

}

(注意这里的synchronized的用法,这个是线程相关,这里不详细分析)

——》ContactList.getByIds

这里就转到ContactList类里面操作去了()这里还是mms的data包下里面的类;

1 ContactList

到此查询过程如下:

下面几个部分就围绕这个流程图进行详细介绍;

ContactList

此类从ArrayList继承下来:public class ContactList extendsArrayList<Contact>{}

public static ContactListgetByIds(String spaceSepIds, boolean canBlock) {

  ContactList list = new ContactList();

  //foreach语句

  for (RecipientIdCache.Entry entry:RecipientIdCache

  .getAddresses(spaceSepIds)) { //根据Id获取号码访问数据库

  if (entry != null && !TextUtils.isEmpty(entry.number)) {

//根据号码获取联系人数据

  Contact contact = Contact.get(entry.number, canBlock);

  contact.setRecipientId(entry.id);

  list.add(contact);

  }

  }

  //返回联系人列表给Conversation;

  return list;

}

通过这里Contact contact = Contact.get(entry.number, canBlock); 传入号码

——》转到Contact里面执行;

下面看看这个类的get方法

2 Contact异步或者阻塞方式获取联系人数据

number:联系人的号码

canBlock:将决定是以阻塞的方式还是异步的方式获取联系人数据

public static Contactget(String number, boolean canBlock) {

//调用的是ContactsCache类实例的get方法

return sContactCache.get(number, canBlock);

}

下面看一下ContactsCache里面的get方法

ContactsCacheContact类的内部类;

public Contactget(String number, boolean canBlock) {

//返回一个contact不管数据库中是否存在 先从内部缓存中查找匹配号码的

//若不存在则直接将其返回,若不存在则返回新创建一个

  Contact contact =get(number); //内部查找

Runnable r = null;

synchronized(contact) {

  while (canBlock && contact.mQueryPending) { //是否阻塞方式

  contact.wait(); }

  final Contact c = contact; //匿名内部类实现线程

  r= new Runnable() {

  public voidrun() {

//仍然要在线程中更新,填充联系人数据 不管是否从缓存中取得

  updateContact(c); }

  }; }

  if (canBlock) {

  r.run(); //阻塞方式

  } else {

  pushTask(r); //异步方式 }

  return contact;

}

通过此方法异步或者阻塞方式获取到的联系人数据通过此get方法得到的联系人数据可能仅仅只是包含号码,而没有其他数据信息;

下面看一下updateContact方法

private voidupdateContact(final Contact c) {

Contact entry = getContactInfo(c.mNumber);

//从数据库中获取Contact数据

  Contact entry = getContactInfo(c.mNumber);

  //设置Contact实例c的数据信息:name id……

  //notify to update who?

  //who? Here It isConversationListItem

  UpdateListener l;//从Contact的UpdateListener队列中获取一个Listener对象

  //更新当前监听者所使用的Contact数据

  l.onUpdate(c)

}

3 Contact中updateListener的添加

这里存在一个updateListener对象就是ConversationListItem实例:什么时候传进去的呢?

看到 bind方法被调用时 也就是 上面所讲bindView时;

public final void bind(){

……

Contact.addListener(this); //添加UpdateListener

}

4联系人处理方式

Contact处理联系人数据有两种方式:异步和阻塞;具体这里不作详细分析;

那么这里有个点让我不明白!

1) 加载一个Thread ListItem对应的联系人数据 可能有多个 是在一个ContactList getByIds方法中for循环执行;

2) 异步方式单独处理每一个号码对应的联系人数据

3) 将pushTask(r);加入到TaskStack中之后,放弃对CPU的控制权;

4)TaskStack中线程mWorkerThread;异步执行存在很多的不确定性,怎么控制;

5) 一次异步方式执行updateContact会去通知ItemList更新数据,为什么不是选择一个Thread信息所有的号码处理完毕 之后再去更新ItemList;

是这么个道理: 加入TaskStack中时;执行:

public voidpush(Runnable r) {

synchronized(mThingsToLoad) {

mThingsToLoad.add(r);

mThingsToLoad.notify(); //放弃资源控制

}

}这里就等待getByIds将所有的号码处理加入到TaskStack中来处理;

但是仍然是每一次线程run方法都会去更新;也就是更新一个联系人就要更新UI一次。这样岂不浪费时间和资源;

这个mWorkerThread线程是在启动MmsApp时候就启动了

MmsApp.java中

@Override

public void onCreate(){

……

  //创建ContactsCache对象,ContactsCache创建TaskStack对象

  //TaskStack是ContactsCache内部类 其构造函数启动线程

  Contact.init(this);

}

这个Mms这个联系人管理是比较的复杂!这里所认识的可能并一定正确;待后续完善。

五 数据更新到界面更新

回到ConversationListAdapter的函数bindView函数中来

@Override

public void bindView(View view, Context context, Cursor cursor) {

ConversationListItem headerView = (ConversationListItem) view;

Conversation conv = Conversation.from(context, cursor);

ConversationListItemData ch = new ConversationListItemData(context, conv);

headerView.bind(context, ch);

}

需要看一下headerView.bind所执行的bind函数:

//更新ListViewItem中控件的相关内容

void bind(Context context, final ConversationListItemData ch) {

……

// Date

mDateView.setText(ch.getDate());

// From.

mFromView.setText(formatMessage(ch));

//Register for updates in changes of any of the contacts in this conversation.

ContactList contacts = ch.getContacts();

Contact.addListener(this); //有onUpdate函数

// Subject

mSubjectView.setText(ch.getSubject());

//avatar

updateAvatarView();

}

整个ConversationList界面到数据加载的主要过程便是这样。


更多相关文章

  1. Android中自制通讯录中显示出数据库中的姓名和电话号码进行打电
  2. 方法数据库android轻量型数据库sqlite的使用方法汇总
  3. Android(安卓)Ormlite 学习笔记1 -- 基础
  4. Android如何连接SQLServer数据库
  5. Android(安卓)RecyclerView更新某条/一条数据
  6. Android后台数据接口类型
  7. Android(安卓)application对象的使用 全局变量
  8. Android中sqllite存储海量数据解决办法
  9. Android面试题总结(六)Android源码篇

随机推荐

  1. Android文件操作总结
  2. 关于Qt和android ndk的兼容问题
  3. Android(安卓)Handler
  4. Android(安卓)避免内存泄露
  5. Android(安卓)利用getApplication() 共享
  6. Android(安卓)Support 包里究竟有什么
  7. 安卓混合开发——原生Java和H5交互,保证你
  8. Android获取系统隐藏服务实现锁屏
  9. Android利用Fiddler进行网络数据抓包
  10. Android的Activity切换动画特效库SwitchL