博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Mms conversation部分学习总结
阅读量:7116 次
发布时间:2019-06-28

本文共 8048 字,大约阅读时间需要 26 分钟。

hot3.png

一、代码结构

Conversation中整体结构主要包括com..mms.data和com.android.mms.ui,如名字所示,大概就是数据处理部分和UI部分。数据部分主要是获取/缓存联系人信息、获取/缓存会话信息等。ui部分主要是convesation 的列表功能实现

ConversationList类是程序的主activity,派生于ListActivity,就是一个大的列表。  

ConversationListAdapter是这个ListView的adapter,派生于CursorAdapter实现了AbsListView.RecyclerListener(当一个view使用完,被放入 listview 回收堆时调用); 
ConversationListItemData 对应会话列表的每一个item中的多个元素所抽象出的类,管理item中的数据

ConversationListItem是一个自定义的ViewGroup,派生于RelativeLayout,用于表示会话列表的每一个item;将数据映射到item上。

Conversation表示一个会话数据;Contact表示一个联系人;ContactList维护一个联系人列表; 
RecipientIdCache用于开线程读取一个特殊的表,该表映射会话数据到联系人信息,也就是通过Recipient就可以获取联系人信息。 

二、UI结构

这里的UI主要就是ConversationList/ConversationListAdapter/ConversationListItem三者之间的交互。

在layout中,conversation_list_item.xml作为这个ListView(ConversationList)的item定义,直接使用了ConversationListItem这个view:

代码 

<com.android.mms.ui.ConversationListItem xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="?android:attr/listPreferredItemHeight"
    android:background="@drawable/conversation_item_background_read"
    android:paddingRight="10dip" >
    <android.widget.QuickContactBadge
        android:id="@+id/avatar"
        android:visibility="gone"
        android:layout_marginLeft="7dip"
        android:layout_centerVertical="true"
        style="?android:attr/quickContactBadgeStyleWindowSmall" />
    <ImageView
        android:id="@+id/presence"
        android:visibility="gone"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginRight="5dip"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:paddingBottom="20dip"
         />
    <TextView android:id="@+id/from"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceMediumInverse"
        android:singleLine="true"
        android:layout_marginTop="6dip"
        android:layout_marginRight="5dip"
        android:layout_marginLeft="7dip"
        android:layout_alignParentTop="true"
        android:layout_toRightOf="@id/avatar"
        android:layout_alignWithParentIfMissing="true"
        android:ellipsize="marquee"  />
    <TextView android:id="@+id/location"
        android:layout_marginTop="6dip"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:layout_toRightOf="@id/from"
        android:textAppearance="?android:attr/textAppearanceSmallInverse"
        android:singleLine="true"
        android:ellipsize="end"
        android:layout_toLeftOf="@id/presence"
        android:layout_alignWithParentIfMissing="true"
        android:gravity="right"
        android:layout_alignParentTop="true" />
    <TextView android:id="@+id/date"
        android:layout_marginTop="2dip"
        android:layout_marginBottom="10dip"
        android:layout_marginLeft="5dip"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:textAppearance="?android:attr/textAppearanceSmallInverse"
        android:singleLine="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentBottom="true" />
    <ImageView android:id="@+id/error"
        android:layout_marginLeft="3dip"
        android:visibility="invisible"
        android:layout_toLeftOf="@id/date"
        android:layout_alignBottom="@id/date"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:src="@drawable/ic_list_alert_sms_failed" />
    <ImageView android:id="@+id/attachment"
        android:layout_marginLeft="3dip"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:visibility="gone"
        android:layout_toLeftOf="@id/error"
        android:layout_alignBottom="@id/date"
        android:src="@drawable/ic_attachment_universal_small" />
    <TextView android:id="@+id/subject"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceSmallInverse"
        android:singleLine="true"
        android:layout_marginBottom="10dip"
        android:layout_marginLeft="7dip"
        android:layout_alignParentBottom="true"
        android:layout_toRightOf="@id/avatar"
        android:layout_alignWithParentIfMissing="true"
        android:layout_toLeftOf="@id/date"
        android:ellipsize="end" />
</com.android.mms.ui.ConversationListItem>
 

这个自定义item最重要的工作,就是将会话数据绑定到UI控件上,例如QuickContactBadge,subject ,date...   在ListView的使用中,要绑定数据,还有个方法就是自写adapter,在构造adapter时就传入所有数据。但是如你所见,这种方法需要先读取出所有的数据。

而这个系统自带的短信程序,则没有一次读入。这个自定义item还有个功能就是,作为一条联系人信息的更新监听器。 当联系人程序中数据发生变化时,mms中数据也要更新。

ConversationListAdapter中只实现了bindView和newView这两个函数,此外,它作为listView的AbsListView.RecyclerListener,还实现了onMovedToScrapHeap函数。

关于RecyclerListener,这里有篇文章从源码级角度分析了下,大概意思就是ListView在处理item时,有个缓存机制。 

三、数据与UI的映射

  

ConversationList的onStart中,开启了一个异步查询,查询所有的会话:

Java代码 @Override 

protected void onStart() {   

    super.onStart();   
  
     .......   
    startAsyncQuery(); 

   startyAsyncQuery调用了Conversation.startQueryForAll函数,该函数说白了还是调用AsyncQueryHandler.startQuery函数:

Java代码

public static void startQueryForAll(AsyncQueryHandler handler, int token) {   
    handler.cancelOperation(token);   
    handler.startQuery(token, null, sAllThreadsUri,   
            ALL_THREADS_PROJECTION, null, null, Conversations.DEFAULT_SORT_ORDER);   

   关于如何获取会话列表,其实就是个SQL的连表查询,可以参见这里:获取短信会话列表

当查询完后,android回调到自己实现的AsyncQueryHandler.onQueryComplete,该函数主要就是告诉adapter,we have done!:

Java代码

@Override 

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

    switch (token) {   
    case THREAD_LIST_QUERY_TOKEN:   
        mListAdapter.changeCursor(cursor);   //这里得到新的 查询 curser

       一旦adapter获得了一个cursor后, listview会通过适配器来显示各项数据。以上便是获取会话列表的大致流程。

接下来看看联系人获取的流程: 

adapter获得数据后,会调用bindView来绑定数据到UI的item:

Java代码 @Override 

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

    if (!(view instanceof ConversationListItem)) {   
        Log.e(TAG, "Unexpected bound view: " + view);   
        return;   
    }   
  
    ConversationListItem headerView = (ConversationListItem) view;   
    Conversation conv = Conversation.from(context, cursor);   
  
    ConversationListItemData ch = new ConversationListItemData(context, conv);   
    headerView.bind(context, ch);   

    Conversation.from函数会先检查Conversation缓存中是否有该cursor对应的数据,没有的话则会从cursor中取:

Java代码

public static Conversation from(Context context, Cursor cursor) {   
    // First look in the cache for the Conversation and return that one. That way, all the   
    // people that are looking at the cached copy will get updated when fillFromCursor() is   
    // called with this cursor.   
    long threadId = cursor.getLong(ID);   
    if (threadId > 0) {   
        Conversation conv = Cache.get(threadId);   
        if (conv != null) {   
            fillFromCursor(context, conv, cursor, false);   // update the existing conv in-place   
            return conv;   
        }   
    }   
    Conversation conv = new Conversation(context, cursor, false);   
    try {   
        Cache.put(conv);   
    } catch (IllegalStateException e) {   
        LogTag.error("Tried to add duplicate Conversation to Cache");   
    }   
    return conv;   

   然后主要是fillFromCursor函数(如果是创建新的Conversation,其构造函数中也是调用了该函数),该函数就是简单地从cursor中getXXXX获取各个数据,并且,最重要的,获取联系人信息:

Java代码     private static void fillFromCursor(Context context, Conversation conv,  

                                       Cursor c, boolean allowQuery) {   
  
...   
        ContactList recipients = ContactList.getByIds(recipientIds, allowQuery); 

    注意这里allowQuery参数为false。

ContactList.getByIds函数根据Conversation中recipientIds获取出对应的address,然后根据address从联系人URI中进一步获取联系人信息。

Java代码 public static ContactList getByIds(String spaceSepIds, boolean canBlock) {  

    ContactList list = new ContactList();   
    for (RecipientIdCache.Entry entry : RecipientIdCache.getAddresses(spaceSepIds)) {   
        if (entry != null && !TextUtils.isEmpty(entry.number)) {   
            Contact contact = Contact.get(entry.number, canBlock);   
            contact.setRecipientId(entry.id);   
            list.add(contact);   
        }   
    }   
    return list;   

   最终的contact被生成于Contact.get(entry.number, canBlock)中。该函数在canBlock为false的情况下,会push一个异步执行体(Runnable)到一个线程中。然后将contact返回。最终返回到adapter那一层的函数。

这个异步查询线程,会真正地去查询联系人信息。在此之前,外界获取出来的联系人不过是一个很简单的信息:只有电话号码。

adapter的bindView中,紧接着: 

Java代码     ConversationListItemData ch = new ConversationListItemData(context, conv);  

    headerView.bind(context, ch);   

        bind函数中很重要的操作,就是建立该会话对应的联系人对象的监听:当联系人发生变化时,会更新 mms中的contact缓存。

Java代码

ContactList contacts = ch.getContacts();   
 

if (DEBUG) Log.v(TAG, "bind: contacts.addListeners " + this);   

Contact.addListener(this);                               //者个监听器就是他自己 如上。

       以上过程,即展示了会话数据是如何映射到ListView的item,及联系人信息是如何与会话和listview item建立联系(即异步查询,然后同步)。

值得一提的是:查询联系人信息都是在bindView时发生,当一个listview被显示出来后,未显示的item是不会被触发bind的,也就是说:在listview显示时,不会触发查询整个会话对应的所有联系人,只有显示出来的item才会涉及到查询联系人。

转载于:https://my.oschina.net/bintojojo/blog/345984

你可能感兴趣的文章
B2B策划书:通过SWOT对B2B行业进行分析
查看>>
Linux-PAM认证模块
查看>>
python 定时器
查看>>
linux磁盘阵列实战
查看>>
页面状态还是组件?到底什么才是交互的中心?
查看>>
我的友情链接
查看>>
根据JSP中日期控件传过来的日期来查询Oracle数据库中的数据
查看>>
VMware虚拟机安装DOS6.22(一)
查看>>
Lucene文档
查看>>
java移位运算
查看>>
我的友情链接
查看>>
Chapter 5 OpenStack镜像服务(Image service)
查看>>
关于搭建harbor企业级访问docker仓库
查看>>
PBD加密专家V6.5.168实例
查看>>
mysql学习笔记1
查看>>
vim
查看>>
Web服务
查看>>
androidkit发布0.5.3alpha版
查看>>
错误java.lang.UnsupportedClassVersionError……Unsupported major.minor version 49.0
查看>>
apache服务器修改host在绑定多个域名
查看>>