Android ContactsProvider源码分析

        Android源码目录packages\providers下的应用是下载,通话等内置基本应用提供数据存储和操作的provider应用,本文章将针对ContactsProvider源码的架构和实现展开分析。(注:本文使用使用android4.0版本进行分析)

1、架构设计

      ContactsProvider中数据操作基类是AbstractContactsProvider.java(参见frameworks\ex\common\java\com\android\common\content\SQLiteContentProvider.java),它继承ContentProvider.java实现SQLiteTransactionListener.java,类结构如下图所示:



图 1 provider结构图

        该类是抽象基类,在里面实现了父类的insert、delete和update三个抽象方法,在这三个方法中在其中使用了事务对数据库进行操作。该类设计时,使用了模板模式模板方法为insertInTransaction,updateInTransaction和deleteInTransaction。该类在对数据库进行事务操作的同时,对子类开放了onRollback,onCommit等事物回调方法,子类可以根据自己的业务特点进行扩展。增删改查的调用过程如下图2 3 4所示:


图2  插入操作

图 3 更新操作


图 4 删除操作

  在整个设计中有两个类继承了AbstractContactsProvider.java,一个是ContactsProvider2.java,我们在调用系统的联系人数据时基本都是调用该类,里面封装了所有联系人的数据操作。第二个是ProfileProvider.java,该类是ContactsProvider2.java的委托类。这三个类的类间关系如下图5所示:


图 5 类间关系图

  类ContactsTransaction.java是对事务的管理类,主要对进行的事务进行管理,类似一个事务池。是AbstractContactsProvider.java中事务处理的核心类。

         DataRowHandler.java是数据处理抽象类,实现了对数据的增删改操作,子类有如下几个:

DataRowHandlerForCommonDataKind.java
DataRowHandlerForCustomMimetype.java
DataRowHandlerForEmail.java
DataRowHandlerForGroupMembership.java
DataRowHandlerForIm.java
DataRowHandlerForNickname.java
DataRowHandlerForNote.java
DataRowHandlerForOrganization.java
DataRowHandlerForPhoneNumber.java
DataRowHandlerForPhoto.java
DataRowHandlerForStructuredName.java
DataRowHandlerForStructuredPostal.java

这些子类在ContactsProvider2#initDataRowHandlers中初始化

    private void initDataRowHandlers(Map<String, DataRowHandler> handlerMap,
            ContactsDatabaseHelper dbHelper, ContactAggregator contactAggregator,
            PhotoStore photoStore) {
        Context context = getContext();
        handlerMap.put(Email.CONTENT_ITEM_TYPE,
                new DataRowHandlerForEmail(context, dbHelper, contactAggregator));
        handlerMap.put(Im.CONTENT_ITEM_TYPE,
                new DataRowHandlerForIm(context, dbHelper, contactAggregator));
        handlerMap.put(Organization.CONTENT_ITEM_TYPE,
                new DataRowHandlerForOrganization(context, dbHelper, contactAggregator));
        handlerMap.put(Phone.CONTENT_ITEM_TYPE,
                new DataRowHandlerForPhoneNumber(context, dbHelper, contactAggregator));
        handlerMap.put(Nickname.CONTENT_ITEM_TYPE,
                new DataRowHandlerForNickname(context, dbHelper, contactAggregator));
        handlerMap.put(StructuredName.CONTENT_ITEM_TYPE,
                new DataRowHandlerForStructuredName(context, dbHelper, contactAggregator,
                        mNameSplitter, mNameLookupBuilder));
        handlerMap.put(StructuredPostal.CONTENT_ITEM_TYPE,
                new DataRowHandlerForStructuredPostal(context, dbHelper, contactAggregator,
                        mPostalSplitter));
        handlerMap.put(GroupMembership.CONTENT_ITEM_TYPE,
                new DataRowHandlerForGroupMembership(context, dbHelper, contactAggregator,
                        mGroupIdCache));
        handlerMap.put(Photo.CONTENT_ITEM_TYPE,
                new DataRowHandlerForPhoto(context, dbHelper, contactAggregator, photoStore));
        handlerMap.put(Note.CONTENT_ITEM_TYPE,
                new DataRowHandlerForNote(context, dbHelper, contactAggregator));
    }

这些子类在getDataRowHandler方法中中通过mimetype进行调用:

    public DataRowHandler getDataRowHandler(final String mimeType) {
        if (inProfileMode()) {
            return getDataRowHandlerForProfile(mimeType);
        }
        DataRowHandler handler = mDataRowHandlers.get(mimeType);
        if (handler == null) {
            handler = new DataRowHandlerForCustomMimetype(
                    getContext(), mContactsHelper, mContactAggregator, mimeType);
            mDataRowHandlers.put(mimeType, handler);
        }
        return handler;
    }

getDataRowHandler方法在insertData,deleteData和updateData方法中被调用。

 /**
     * Inserts an item in the data table
     *
     * @param values the values for the new row
     * @return the row ID of the newly created row
     */
    private long insertData(ContentValues values, boolean callerIsSyncAdapter) {
        long id = 0;
        mValues.clear();
        mValues.putAll(values);

        long rawContactId = mValues.getAsLong(Data.RAW_CONTACT_ID);

        // Replace package with internal mapping
        final String packageName = mValues.getAsString(Data.RES_PACKAGE);
        if (packageName != null) {
            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.get().getPackageId(packageName));
        }
        mValues.remove(Data.RES_PACKAGE);

        // Replace mimetype with internal mapping
        final String mimeType = mValues.getAsString(Data.MIMETYPE);
        if (TextUtils.isEmpty(mimeType)) {
            throw new IllegalArgumentException(Data.MIMETYPE + " is required");
        }

        mValues.put(DataColumns.MIMETYPE_ID, mDbHelper.get().getMimeTypeId(mimeType));
        mValues.remove(Data.MIMETYPE);

        DataRowHandler rowHandler = getDataRowHandler(mimeType);
        id = rowHandler.insert(mActiveDb.get(), mTransactionContext.get(), rawContactId, mValues);
        if (!callerIsSyncAdapter) {
            mTransactionContext.get().markRawContactDirty(rawContactId);
        }
        mTransactionContext.get().rawContactUpdated(rawContactId);
        return id;
    }

所有数据的mimetype都被存储在表Tables.MIMETYPES中,该只有两个字段_id和mimetype。

  db.execSQL("CREATE TABLE " + Tables.MIMETYPES + " ("
                + MimetypesColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
                + MimetypesColumns.MIMETYPE + " TEXT NOT NULL" + ");");

Tables.MIMETYPES表数据的存储是在ContactsDatabaseHelper#lookupAndCacheId中进行的,具体调用过程如下图 6 所示:


图 6 mimetype数据调用流程图

2、表结构

        ContactsProvider 中共创建了25张表,

        public static final String CONTACTS = "contacts";
        public static final String RAW_CONTACTS = "raw_contacts";
        public static final String STREAM_ITEMS = "stream_items";
        public static final String STREAM_ITEM_PHOTOS = "stream_item_photos";
        public static final String PHOTO_FILES = "photo_files";
        public static final String PACKAGES = "packages";
        public static final String MIMETYPES = "mimetypes";
        public static final String PHONE_LOOKUP = "phone_lookup";
        public static final String NAME_LOOKUP = "name_lookup";
        public static final String AGGREGATION_EXCEPTIONS = "agg_exceptions";
        public static final String SETTINGS = "settings";
        public static final String DATA = "data";
        public static final String GROUPS = "groups";
        public static final String PRESENCE = "presence";
        public static final String AGGREGATED_PRESENCE = "agg_presence";
        public static final String NICKNAME_LOOKUP = "nickname_lookup";
        public static final String CALLS = "calls";
        public static final String STATUS_UPDATES = "status_updates";
        public static final String PROPERTIES = "properties";
        public static final String ACCOUNTS = "accounts";
        public static final String VISIBLE_CONTACTS = "visible_contacts";
        public static final String DIRECTORIES = "directories";
        public static final String DEFAULT_DIRECTORY = "default_directory";
        public static final String SEARCH_INDEX = "search_index";
        public static final String VOICEMAIL_STATUS = "voicemail_status";

这些表数据对开发者开放的api在 \frameworks\base\core\java\android\provider\文件夹下,

CallLog.java
Contacts.java
ContactsContract.java
SocialContract.java
SyncConstValue.java
VoicemailContract.java

由于数据查询涉及多个表间关系,大量数据的查询都是通过视图来完成的,创建了如下8个视图,而这几个视图是大家在调用api进行查询时显示的数据:

    public interface Views {
        public static final String DATA = "view_data";
        public static final String RAW_CONTACTS = "view_raw_contacts";
        public static final String CONTACTS = "view_contacts";
        public static final String ENTITIES = "view_entities";
        public static final String RAW_ENTITIES = "view_raw_entities";
        public static final String GROUPS = "view_groups";
        public static final String DATA_USAGE_STAT = "view_data_usage_stat";
        public static final String STREAM_ITEMS = "view_stream_items";
    }

4、总结

      ContactsProvder是provider源码中数据处理和架构比较全面的一个应用,可以将它的架构核心抽离出来供大家参考和借鉴,以下类构成了整个架构的核心类,可以重点研究:

AbstractContactsProvider.java
ContactAggregator.java
ContactsDatabaseHelper.java
ContactsProvider2.java
ContactsTransaction.java
TransactionContext.java




评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值