个人见解,欢迎交流。

联系人数据库设计,源代码下载请自行去android官网下载。

 

package com.android.providers.contacts;

import android.content.ContentProvider;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentValues;
import android.content.Context;
import android.content.OperationApplicationException;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteTransactionListener;
import android.net.Uri;

import java.util.ArrayList;

/**
* A common base class for the contacts and profile providers. This handles much of the same
* logic that SQLiteContentProvider does (i.e. starting transactions on the appropriate database),
* but exposes awareness of batch operations to the subclass so that cross-database operations
* can be supported.
* 用于联系人和配置信息的ContentProvider的公共基类。处理相同的数据库逻辑,同时暴露批处理处理的操作到子类中,从而实现
* 跨数据库操作支持
*
* 事务 详细请@百度百科
* 数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列操作。
* 事务处理可以确保除非事务性单元内的所有操作都成功完成,否则不会永久更新面向数据的资源。
* 通过将一组相关操作组合为一个要么全部成功要么全部失败的单元,可以简化错误恢复并使应用程序更加可靠。
* 一个逻辑工作单元要成为事务,必须满足所谓的ACID(原子性、一致性、隔离性和持久性)属性。
*/
public abstract class AbstractContactsProvider extends ContentProvider
implements SQLiteTransactionListener {//接口,用于transactionEvent。在ContactsTransaction中的监听

/**
* Duration in ms to sleep after successfully yielding the lock during a batch operation.
* 间隙。批处理操作成功释放lock锁,休眠时间
*/
protected static final int SLEEP_AFTER_YIELD_DELAY = 4000;

/**
* Maximum number of operations allowed in a batch between yield points.
* 批处理中允许的最大操作数。在一个yield points到另一个yield point之间
*/
private static final int MAX_OPERATIONS_PER_YIELD_POINT = 500;

/**
* Number of inserts performed in bulk to allow before yielding the transaction.
*/
private static final int BULK_INSERTS_PER_YIELD_POINT = 50;

/**
* The contacts transaction that is active in this thread.
* 关于ThreadLocal请查阅java编程思想 ThreadLocal=线程本地存储
*/
private ThreadLocal<ContactsTransaction> mTransactionHolder;

/**
* The DB helper to use for this content provider.
* 为此ContentProvider设计的SQLiteOpenHelper。
*/
private SQLiteOpenHelper mDbHelper;

/**
* The database helper to serialize all transactions on. If non-null, any new transaction
* created by this provider will automatically retrieve a writable database from this helper
* and initiate a transaction on that database. This should be used to ensure that operations
* across multiple databases are all blocked on a single DB lock (to prevent deadlock cases).
* 数据库助手用于序列话所有的事务(transaction)。如果不为null,所有由此ContentProvider创建的事务都会自动收到
* 一个可写的数据库,并且数据库上存在一个事务。
* 这主要用来保证所有的跨数据库操作都阻塞在一个单独的数据库锁上。从而杜绝死锁。
* 不错的设计,看看下面如何实现的。
*
* ---------------------附设计思想(个人见解)
* 正常情况下我们使用事务,首先得到一个db(database),开始事务调用db.beginTransaction。事务结束调用db.endTransaction。
* 但我们有时候要操作的数据库是对个数据表进行操作为了便于管理,所有的事务对象的db,由mSerializeOnDbHelper产生。
* -----------------------------
*/
private SQLiteOpenHelper mSerializeOnDbHelper;

/**
* The tag corresponding to the database used for serializing transactions.
* 序列话事务的tag,这个tag和数据库database相映射
*/
private String mSerializeDbTag;

@Override
public boolean onCreate() {
Context context = getContext();
mDbHelper = getDatabaseHelper(context);
mTransactionHolder = getTransactionHolder();
return true;
}

public SQLiteOpenHelper getDatabaseHelper() {
return mDbHelper;
}

/**
* Specifies a database helper (and corresponding tag) to serialize all transactions on.
* @param serializeOnDbHelper The database helper to use for serializing transactions.
* @param tag The tag for this database.
* 序列化事务的SQLiteOpenHelper以及tag
*/
public void setDbHelperToSerializeOn(SQLiteOpenHelper serializeOnDbHelper, String tag) {
mSerializeOnDbHelper = serializeOnDbHelper;
mSerializeDbTag = tag;
}

public ContactsTransaction getCurrentTransaction() {
return mTransactionHolder.get();
}

@Override
public Uri insert(Uri uri, ContentValues values) {
//添加ContactsTransaction到mTransactionHolder(线程本地存储)中。如果ContactsTrasanction不存在,创建一个。
//参数为由mSerializeOnDbHelper序列化的可写数据库,mSerializeDbTag,以及当前类的实现。
//false:代表当前为非批处理操作
ContactsTransaction transaction = startTransaction(false);
try {
Uri result = insertInTransaction(uri, values);//在子类中实现
if (result != null) {
transaction.markDirty();
}
transaction.markSuccessful(false);
return result;
} finally {
endTransaction(false);//结束当前事务,释放锁
}
}

@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
ContactsTransaction transaction = startTransaction(false);
try {
int deleted = deleteInTransaction(uri, selection, selectionArgs);
if (deleted > 0) {
transaction.markDirty();
}
transaction.markSuccessful(false);
return deleted;
} finally {
endTransaction(false);
}
}

@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
ContactsTransaction transaction = startTransaction(false);
try {
int updated = updateInTransaction(uri, values, selection, selectionArgs);
if (updated > 0) {
transaction.markDirty();
}
transaction.markSuccessful(false);
return updated;
} finally {
endTransaction(false);
}
}

@Override
public int bulkInsert(Uri uri, ContentValues[] values) {
ContactsTransaction transaction = startTransaction(true);//参数为true,代表批处理操作
int numValues = values.length;
int opCount = 0;
try {
for (int i = 0; i < numValues; i++) {
insert(uri, values[i]);
if (++opCount >= BULK_INSERTS_PER_YIELD_POINT) {
opCount = 0;
try {//如果批处理超过BULK_INSERTS_PER_YIELD_POINT(50),那么给其他线程请求让路。不要占用数据库太久
yield(transaction);//给其他线程让路,查看详细实现,查阅ContactProvider2中的yield方法实现。
} catch (RuntimeException re) {
transaction.markYieldFailed();
throw re;
}
}
}
transaction.markSuccessful(true);
} finally {
endTransaction(true);
}
return numValues;
}

@Override
public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
throws OperationApplicationException {
int ypCount = 0;
int opCount = 0;
ContactsTransaction transaction = startTransaction(true);//参数为true,代表批处理事务
try {
final int numOperations = operations.size();
final ContentProviderResult[] results = new ContentProviderResult[numOperations];
for (int i = 0; i < numOperations; i++) {
if (++opCount >= MAX_OPERATIONS_PER_YIELD_POINT) {//如果批处理个数太多,给其他线程让路
throw new OperationApplicationException(
"Too many content provider operations between yield points. "
+ "The maximum number of operations per yield point is "
+ MAX_OPERATIONS_PER_YIELD_POINT, ypCount);
}
final ContentProviderOperation operation = operations.get(i);
if (i > 0 && operation.isYieldAllowed()) {
opCount = 0;
try {
if (yield(transaction)) {
ypCount++;
}
} catch (RuntimeException re) {
transaction.markYieldFailed();
throw re;
}
}

results[i] = operation.apply(this, results, i);
}
transaction.markSuccessful(true);
return results;
} finally {
endTransaction(true);
}
}

/**
* If we are not yet already in a transaction, this starts one (on the DB to serialize on, if
* present) and sets the thread-local transaction variable for tracking. If we are already in
* a transaction, this returns that transaction, and the batch parameter is ignored.
* @param callerIsBatch Whether the caller is operating in batch mode.
* 添加ContactsTransaction到mTransactionHolder(线程本地存储)中。如果ContactsTrasanction不存在,创建一个。
* 参数为由mSerializeOnDbHelper序列化的可写数据库,mSerializeDbTag,以及当前类的实现。
* false:代表当前为非批处理操作
*/
private ContactsTransaction startTransaction(boolean callerIsBatch) {
ContactsTransaction transaction = mTransactionHolder.get();
if (transaction == null) {
transaction = new ContactsTransaction(callerIsBatch);
if (mSerializeOnDbHelper != null) {
transaction.startTransactionForDb(mSerializeOnDbHelper.getWritableDatabase(),
mSerializeDbTag, this);
}
mTransactionHolder.set(transaction);
}
return transaction;
}

/**
* Ends the current transaction and clears out the member variable. This does not set the
* transaction as being successful.
* @param callerIsBatch Whether the caller is operating in batch mode.
* 结束事务。清楚线程本地存储中的ContactTransaction引用。
*/
private void endTransaction(boolean callerIsBatch) {
ContactsTransaction transaction = mTransactionHolder.get();
if (transaction != null && (!transaction.isBatch() || callerIsBatch)) {
try {
if (transaction.isDirty()) {//如果数据库存在变化,notifyChange。
notifyChange();
}
transaction.finish(callerIsBatch);//关闭由序列化SQLiteHelper创建的数据库上的事务。
} finally {
// No matter what, make sure we clear out the thread-local transaction reference.
mTransactionHolder.set(null);
}
}
}

/**
* Gets the database helper for this contacts provider. This is called once, during onCreate().
*/
protected abstract SQLiteOpenHelper getDatabaseHelper(Context context);

/**
* Gets the thread-local transaction holder to use for keeping track of the transaction. This
* is called once, in onCreate(). If multiple classes are inheriting from this class that need
* to be kept in sync on the same transaction, they must all return the same thread-local.
* 返回线程本地存储,用于追踪事务。在onCreate中被调用一次。;如果是多继承设计中,必须保证多次调用返回的是同一个对象。
*/
protected abstract ThreadLocal<ContactsTransaction> getTransactionHolder();

protected abstract Uri insertInTransaction(Uri uri, ContentValues values);

protected abstract int deleteInTransaction(Uri uri, String selection, String[] selectionArgs);

protected abstract int updateInTransaction(Uri uri, ContentValues values, String selection,
String[] selectionArgs);

protected abstract boolean yield(ContactsTransaction transaction);

protected abstract void notifyChange();
}



 

 


联系人数据库设计之AbstractContactsProvider_sqlite