android账号与同步之发起同步
上一篇博文我介绍了账号与同步的同步实现过程,当中提供了一个工系统进程调用的服务,那么这个服务究竟是怎么被启动和使用的呢?这篇博文我就大体梳理一下启动过程。
事实上作为一个一般开发者,我们仅仅要知道要想知道被监听的ContentProvider有变动,首先那个ContentProvider必须使用ContentResolver.notifyChange(android.net.Uri,
android.database.ContentObserver, boolean)这种方法来通知我们。我们知道这种方法会通知监听这个ContentProvider的ContentObserver数据有变化,可是ContentObserver须要在一个执行这个的进程中注冊,假设这个进程死掉了ContentObserver也须要取消注冊监听,这样就没法监听了。只是值得注意的是,这个notifyChange方法还有另外一个作用,就是配合实现android的同步框架,通知SyncManager启动对应账号的同步。
ContentResolver.java
首先我们来看看ContentResolver的notifyChange源代码,例如以下:
public void notifyChange(Uri uri, ContentObserver observer) {
notifyChange(uri, observer, true /* sync to network */);
}
public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
notifyChange(uri, observer, syncToNetwork, UserHandle.getCallingUserId());
}
public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork,
int userHandle) {
try {
getContentService().notifyChange(
uri, observer == null ? null : observer.getContentObserver(),
observer != null && observer.deliverSelfNotifications(), syncToNetwork,
userHandle);
} catch (RemoteException e) {
}
}
上面重载的三个notifyChange方法,终于调用的都是ContentService中的notifyChange方法,getContentService()方法例如以下:
public static IContentService getContentService() {
if (sContentService != null) {
return sContentService;
}
IBinder b = ServiceManager.getService(CONTENT_SERVICE_NAME);
if (false) Log.v("ContentService", "default service binder = " + b);
sContentService = IContentService.Stub.asInterface(b);
if (false) Log.v("ContentService", "default service = " + sContentService);
return sContentService;
}
ContentService.java
ContentService是对存根类IContentService.Stub的实现,它提供一系列数据同步及数据訪问等相关的操作,源代码例如以下:
public final class ContentService extends IContentService.Stub
此类中的两个重载的notifyChange方法例如以下:
public void notifyChange(Uri uri, IContentObserver observer,
boolean observerWantsSelfNotifications, boolean syncToNetwork) {
notifyChange(uri, observer, observerWantsSelfNotifications, syncToNetwork,
UserHandle.getCallingUserId());
}
@Override
public void notifyChange(Uri uri, IContentObserver observer,
boolean observerWantsSelfNotifications, boolean syncToNetwork,
int userHandle) {
...
最重要的一句,就是启动ContentObserver的onChange方法
oc.mObserver.onChange(oc.mSelfChange, uri);
...
最重要的还有一句,就是启动同步
if (syncToNetwork) {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, uid,
uri.getAuthority());
}
}
} finally {
restoreCallingIdentity(identityToken);
}
}
//获取SyncManager 的方法
private SyncManager getSyncManager() {
if (SystemProperties.getBoolean("config.disable_network", false)) {
return null;
}
synchronized(mSyncManagerLock) {
try {
// Try to create the SyncManager, return null if it fails (e.g. the disk is full).
if (mSyncManager == null) mSyncManager = new SyncManager(mContext, mFactoryTest);
} catch (SQLiteException e) {
Log.e(TAG, "Can't create SyncManager", e);
}
return mSyncManager;
}
}
SyncManager.java
这是一个同步机制中最核心的类,承载了基本的功能实现,本人仅仅大体介绍一下主要代码,以求打通整个流程。首先来看上面那个类调用的方法scheduleLocalSync:
public void scheduleLocalSync(Account account, int userId, int reason, String authority) {
final Bundle extras = new Bundle();
extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
scheduleSync(account, userId, reason, authority, extras,
LOCAL_SYNC_DELAY /* earliest run time */,
2 * LOCAL_SYNC_DELAY /* latest sync time. */,
false /* onlyThoseWithUnkownSyncableState */);
}
scheduleLocalSync方法主要调用了scheduleSync方法,其大体内容例如以下:
public void scheduleSync(Account requestedAccount, int userId, int reason,
String requestedAuthority, Bundle extras, long beforeRuntimeMillis,
long runtimeMillis, boolean onlyThoseWithUnkownSyncableState) {
boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
final boolean backgroundDataUsageAllowed = !mBootCompleted ||
getConnectivityManager().getBackgroundDataSetting();
if (extras == null) {
extras = new Bundle();
}
if (isLoggable) {
Log.d(TAG, "one-time sync for: " + requestedAccount + " " + extras.toString() + " "
+ requestedAuthority);
}
//假设是加速模式则把延迟时间设置为-1,runtimeMillis在较早的版本号中是叫做delay的,就是当你调用这种方法后多久启动同步任务。
Boolean expedited = extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false);
if (expedited) {
runtimeMillis = -1; // this means schedule at the front of the queue
}
//获取手机中全部加入过的账号信息
AccountAndUser[] accounts;
if (requestedAccount != null && userId != UserHandle.USER_ALL) {
accounts = new AccountAndUser[] { new AccountAndUser(requestedAccount, userId) };
} else {
// if the accounts aren't configured yet then we can't support an account-less
// sync request
accounts = mRunningAccounts;
if (accounts.length == 0) {
if (isLoggable) {
Log.v(TAG, "scheduleSync: no accounts configured, dropping");
}
return;
}
}
//
final boolean uploadOnly = extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false);
final boolean manualSync = extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
if (manualSync) {
extras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, true);
extras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true);
}
final boolean ignoreSettings =
extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false);
int source;
if (uploadOnly) {
source = SyncStorageEngine.SOURCE_LOCAL;
} else if (manualSync) {
source = SyncStorageEngine.SOURCE_USER;
} else if (requestedAuthority == null) {
source = SyncStorageEngine.SOURCE_POLL;
} else {
// this isn't strictly server, since arbitrary callers can (and do) request
// a non-forced two-way sync on a specific url
source = SyncStorageEngine.SOURCE_SERVER;
}
for (AccountAndUser account : accounts) {
// Compile a list of authorities that have sync adapters. For each authority sync each account that matches a sync adapter.
final HashSet<String> syncableAuthorities = new HashSet<String>();
for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapter :
mSyncAdapters.getAllServices(account.userId)) {
syncableAuthorities.add(syncAdapter.type.authority);
}
// if the url was specified then replace the list of authorities with just this authority or clear it if this authority isn't syncable
if (requestedAuthority != null) {
final boolean hasSyncAdapter = syncableAuthorities.contains(requestedAuthority);
syncableAuthorities.clear();
if (hasSyncAdapter) syncableAuthorities.add(requestedAuthority);
}
for (String authority : syncableAuthorities) {
//account账号是否声明同步authority
int isSyncable = getIsSyncable(account.account, account.userId,
authority);
if (isSyncable == 0) {
continue;
}
//一系列的推断,排除一起异常的情况
final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo;
syncAdapterInfo = mSyncAdapters.getServiceInfo(
SyncAdapterType.newKey(authority, account.account.type), account.userId);
if (syncAdapterInfo == null) {
continue;
}
final boolean allowParallelSyncs = syncAdapterInfo.type.allowParallelSyncs();
final boolean isAlwaysSyncable = syncAdapterInfo.type.isAlwaysSyncable();
if (isSyncable < 0 && isAlwaysSyncable) {
mSyncStorageEngine.setIsSyncable(account.account, account.userId, authority, 1);
isSyncable = 1;
}
if (onlyThoseWithUnkownSyncableState && isSyncable >= 0) {
continue;
}
if (!syncAdapterInfo.type.supportsUploading() && uploadOnly) {
continue;
}
// always allow if the isSyncable state is unknown
boolean syncAllowed =
(isSyncable < 0)
|| ignoreSettings
|| (backgroundDataUsageAllowed
&& mSyncStorageEngine.getMasterSyncAutomatically(account.userId)
&& mSyncStorageEngine.getSyncAutomatically(account.account,
account.userId, authority));
if (!syncAllowed) {
if (isLoggable) {
Log.d(TAG, "scheduleSync: sync of " + account + ", " + authority
+ " is not allowed, dropping request");
}
continue;
}
//最后进入两种须要通知同步的情况,都调用scheduleSyncOperation方法实现
Pair<Long, Long> backoff = mSyncStorageEngine
.getBackoff(account.account, account.userId, authority);
long delayUntil = mSyncStorageEngine.getDelayUntilTime(account.account,
account.userId, authority);
final long backoffTime = backoff != null ? backoff.first : 0;
if (isSyncable < 0) {
// Initialisation sync.
Bundle newExtras = new Bundle();
newExtras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true);
if (isLoggable) {
Log.v(TAG, "schedule initialisation Sync:"
+ ", delay until " + delayUntil
+ ", run by " + 0
+ ", source " + source
+ ", account " + account
+ ", authority " + authority
+ ", extras " + newExtras);
}
scheduleSyncOperation(
new SyncOperation(account.account, account.userId, reason, source,
authority, newExtras, 0 /* immediate */, 0 /* No flex time*/,
backoffTime, delayUntil, allowParallelSyncs));
}
if (!onlyThoseWithUnkownSyncableState) {
if (isLoggable) {
Log.v(TAG, "scheduleSync:"
+ " delay until " + delayUntil
+ " run by " + runtimeMillis
+ " flex " + beforeRuntimeMillis
+ ", source " + source
+ ", account " + account
+ ", authority " + authority
+ ", extras " + extras);
}
scheduleSyncOperation(
new SyncOperation(account.account, account.userId, reason, source,
authority, extras, runtimeMillis, beforeRuntimeMillis,
backoffTime, delayUntil, allowParallelSyncs));
}
}
}
}
//此方法主要做两件是,一是把要同步的操作加入到队列中,二是发消息
public void scheduleSyncOperation(SyncOperation syncOperation) {
boolean queueChanged;
synchronized (mSyncQueue) {
queueChanged = mSyncQueue.add(syncOperation);
}
if (queueChanged) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "scheduleSyncOperation: enqueued " + syncOperation);
}
sendCheckAlarmsMessage();
} else {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "scheduleSyncOperation: dropping duplicate sync operation "
+ syncOperation);
}
}
}
//发出MESSAGE_CHECK_ALARMS消息
private void sendCheckAlarmsMessage() {
if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_CHECK_ALARMS");
mSyncHandler.removeMessages(SyncHandler.MESSAGE_CHECK_ALARMS);
mSyncHandler.sendEmptyMessage(SyncHandler.MESSAGE_CHECK_ALARMS);
}
//接收消息并处理
@Override
public void handleMessage(Message msg) {
if (tryEnqueueMessageUntilReadyToRun(msg)) {
return;
}
switch (msg.what) {
case SyncHandler.MESSAGE_CANCEL: {
...
case SyncHandler.MESSAGE_SYNC_FINISHED:
...
case SyncHandler.MESSAGE_SERVICE_CONNECTED: {
ServiceConnectionData msgData = (ServiceConnectionData)msg.obj;
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CONNECTED: "
+ msgData.activeSyncContext);
}
// check that this isn't an old message
if (isSyncStillActive(msgData.activeSyncContext)) {
runBoundToSyncAdapter(msgData.activeSyncContext, msgData.syncAdapter);
}
break;
}
case SyncHandler.MESSAGE_SERVICE_DISCONNECTED: {
...
case SyncHandler.MESSAGE_SYNC_ALARM: {
boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
if (isLoggable) {
Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_SYNC_ALARM");
}
mAlarmScheduleTime = null;
try {
nextPendingSyncTime = maybeStartNextSyncLocked();
} finally {
mHandleAlarmWakeLock.release();
}
break;
}
//须要检查时钟任务,有须要通知的同步操作
case SyncHandler.MESSAGE_CHECK_ALARMS:
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_CHECK_ALARMS");
}
nextPendingSyncTime = maybeStartNextSyncLocked();
break;
}
} finally {
manageSyncNotificationLocked();
manageSyncAlarmLocked(earliestFuturePollTime, nextPendingSyncTime);
mSyncTimeTracker.update();
mSyncManagerWakeLock.release();
}
}
//检查可能有下一个通知操作
private long maybeStartNextSyncLocked() {
...一堆的推断
for (int i = 0, N = operations.size(); i < N; i++) {
...一堆的推断
// If the next run time is in the future, even given the flexible scheduling, return the time.
if (op.effectiveRunTime - op.flexTime > now) {
if (nextReadyToRunTime > op.effectiveRunTime) {
nextReadyToRunTime = op.effectiveRunTime;
}
if (isLoggable) {
Log.v(TAG, " Dropping sync operation: Sync too far in future.");
}
continue;
}
...一堆的推断
if (toReschedule != null) {
runSyncFinishedOrCanceledLocked(null, toReschedule);
scheduleSyncOperation(toReschedule.mSyncOperation);
}
synchronized (mSyncQueue) {
mSyncQueue.remove(candidate);
}
dispatchSyncOperation(candidate);
}
return nextReadyToRunTime;
}
//正式分发通知同步操作
private boolean dispatchSyncOperation(SyncOperation op) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "dispatchSyncOperation: we are going to sync " + op);
Log.v(TAG, "num active syncs: " + mActiveSyncContexts.size());
for (ActiveSyncContext syncContext : mActiveSyncContexts) {
Log.v(TAG, syncContext.toString());
}
}
// connect to the sync adapter
SyncAdapterType syncAdapterType = SyncAdapterType.newKey(op.authority, op.account.type);
final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo;
syncAdapterInfo = mSyncAdapters.getServiceInfo(syncAdapterType, op.userId);
if (syncAdapterInfo == null) {
Log.d(TAG, "can't find a sync adapter for " + syncAdapterType
+ ", removing settings for it");
mSyncStorageEngine.removeAuthority(op.account, op.userId, op.authority);
return false;
}
//一个SyncManager的内部类
ActiveSyncContext activeSyncContext =
new ActiveSyncContext(op, insertStartSyncEvent(op), syncAdapterInfo.uid);
activeSyncContext.mSyncInfo = mSyncStorageEngine.addActiveSync(activeSyncContext);
mActiveSyncContexts.add(activeSyncContext);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "dispatchSyncOperation: starting " + activeSyncContext);
}
//绑定服务端
if (!activeSyncContext.bindToSyncAdapter(syncAdapterInfo, op.userId)) {
Log.e(TAG, "Bind attempt failed to " + syncAdapterInfo);
closeActiveSyncContext(activeSyncContext);
return false;
}
return true;
}
ActiveSyncContext.java
ActiveSyncContext是SyncManager的内部类,它实现了ServiceConnection,当中的两个重要的方法例如以下:
boolean bindToSyncAdapter(RegisteredServicesCache.ServiceInfo info, int userId) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.d(TAG, "bindToSyncAdapter: " + info.componentName + ", connection " + this);
}
Intent intent = new Intent();
//还记得上一篇博文中的这个Action吗?android.content.SyncAdapter
intent.setAction("android.content.SyncAdapter");
intent.setComponent(info.componentName);
intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
com.android.internal.R.string.sync_binding_label);
intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser(
mContext, 0, new Intent(Settings.ACTION_SYNC_SETTINGS), 0,
null, new UserHandle(userId)));
mBound = true;
final boolean bindResult = mContext.bindServiceAsUser(intent, this,
Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
| Context.BIND_ALLOW_OOM_MANAGEMENT,
new UserHandle(mSyncOperation.userId));
if (!bindResult) {
mBound = false;
}
return bindResult;
}
public void onServiceConnected(ComponentName name, IBinder service) {
Message msg = mSyncHandler.obtainMessage();
msg.what = SyncHandler.MESSAGE_SERVICE_CONNECTED;
msg.obj = new ServiceConnectionData(this, ISyncAdapter.Stub.asInterface(service));
mSyncHandler.sendMessage(msg);
}
//在上面的接收MESSAGE_SERVICE_CONNECTED消息后,运行runBoundToSyncAdapter
private void runBoundToSyncAdapter(final ActiveSyncContext activeSyncContext,
ISyncAdapter syncAdapter) {
activeSyncContext.mSyncAdapter = syncAdapter;
final SyncOperation syncOperation = activeSyncContext.mSyncOperation;
try {
activeSyncContext.mIsLinkedToDeath = true;
syncAdapter.asBinder().linkToDeath(activeSyncContext, 0);
//还记得上一篇博文中有关startSync的介绍吗?
syncAdapter.startSync(activeSyncContext, syncOperation.authority,
syncOperation.account, syncOperation.extras);
} catch (RemoteException remoteExc) {
Log.d(TAG, "maybeStartNextSync: caught a RemoteException, rescheduling", remoteExc);
closeActiveSyncContext(activeSyncContext);
increaseBackoffSetting(syncOperation);
scheduleSyncOperation(new SyncOperation(syncOperation));
} catch (RuntimeException exc) {
closeActiveSyncContext(activeSyncContext);
Log.e(TAG, "Caught RuntimeException while starting the sync " + syncOperation, exc);
}
}
注意,本博文仅仅是简单地抽取了notifyChange分支的流程,给大家看,有兴趣的童鞋能够研究一下requestSync流程。
android账号与同步之发起同步的更多相关文章
- android账号与同步之同步实现
上一篇博文我先介绍了账号与同步的账号管理,这篇就介绍一下还有一部分.就是android给提供的sync同步机制的使用. 事实上sync机制的使用和上一篇博文中介绍的账号管理非常类似,也是基于binde ...
- [原]如何在Android用FFmpeg+SDL2.0之同步音频
同步音频的原理可以参考:http://dranger.com/ffmpeg/tutorial05.html 本文是在 [原]如何在Android用FFmpeg+SDL2.0之同步视频 的基础上面继续 ...
- mysql对比表结构对比同步,sqlyog架构同步工具
mysql对比表结构对比同步,sqlyog架构同步工具 对比后的结果示例: 执行后的结果示例: 点击:"另存为(S)" 按钮可以把更新sql导出来.
- 真正的inotify+rsync实时同步 彻底告别同步慢
真正的inotify+rsync实时同步 彻底告别同步慢 http://www.ttlsa.com/web/let-infotify-rsync-fast/ 背景 我们公司在用in ...
- MySQL和MsSQL实时自动同步---SyncNavigator 数据库同步软件
需要MySQL数据库支持的狐友们有福了,MySQL和MsSQL实时自动同步---SyncNavigator 数据库同步软件 使用SyncNavigator轻松实现数据库异地同步.断点续传.异构同步 ...
- Windows下MySQL双向同步及环形同步的实现
记录一下这次做的双向同步及环形同步吧,都是最简单的实现: 具体实现之前,先说些与之有关的内容吧,大部分内容都是网上的,操作步骤则是亲自测试之后记录下的: 一. 数据同步的几种方式: 1. 触发器,在数 ...
- JAVA之旅(十三)——线程的安全性,synchronized关键字,多线程同步代码块,同步函数,同步函数的锁是this
JAVA之旅(十三)--线程的安全性,synchronized关键字,多线程同步代码块,同步函数,同步函数的锁是this 我们继续上个篇幅接着讲线程的知识点 一.线程的安全性 当我们开启四个窗口(线程 ...
- Oracle主从同步、双向同步的配置
(本教程展示了Windows环境的oracle数据库主从同步,Linux环境一样也可以) (把主数据库obpm 和从数据库orcl 用实际的数据库名给替换掉) (配置主从同步后,再配置双向同步,可能会 ...
- socket的阻塞与非阻塞,同步与非同步
网络编程中通常提到四种方式,同步/异步,阻塞/非阻塞.以下对它们的概念进行总结 1.同步/异步:主要针对C端 同步:所谓同步,就是在C端发出一个功能调用时,在没有得到结果之前,调用不返回,也就是必须一 ...
随机推荐
- WPF 图片浏览 伪3D效果
原文:WPF 图片浏览 伪3D效果 首先上效果图: 因项目要求,需要把图片以"好看"."炫"的效果展示出来,特地研究了一下WPF关于3D方面的制作,奈何最终成果 ...
- [原创] linux 下上传 datapoint数据到yeelink 【golang版本】
package main /* Create by sndnvaps<sndnvaps@gmail.com> * date : 2015-04-05 * upload datapoint ...
- C# WinForm多线程(一)Thread类库
Windows是一个多任务的系统,如果你使用的是windows 2000及其以上版本,你可以通过任务管理器查看当前系统运行的程序和进程.什么是进程呢?当一个程序开始运行时,它就是一个进程,进程所指包括 ...
- 座IO理解力
一般堵塞IO服务器通信,通常有一个单独的Acceptor线程负责监控client联系,它接收client对于每个请求连接后client分配用于处理一个新的线程,处理后.返回应答给client.线程才销 ...
- Idea开发环境
Idea开发环境中搭建Maven 1.配置Maven的环境变量 a.首先我们去maven官网下载Maven程序,解压到安装目录,如图所示: b.配置M2_HOME的环境变量,然后将该变量添加到Path ...
- 了解ASP.NET5 Web应用程序结构
本文参考ASP.NET5 官方文档 Understanding ASP.NET 5 Web Apps,加入了一些个人理解,理解不对的地方希望大家能指出,互相学习. ASP.NET 5 针对WEB编程引 ...
- 开源Math.NET基础数学类库使用(02)矩阵向量计算
原文:[原创]开源Math.NET基础数学类库使用(02)矩阵向量计算 开源Math.NET基础数学类库使用系列文章总目录: 1.开源.NET基础数学计算组件Math.NET(一)综合介绍 ...
- Snail—ORACLE基础之事务学习(五)
---------------事务---当运行到commit时 事务才算是完毕,不然 会运行rollback操作. declare v_money acount.money%type:=1223; e ...
- S性能 Sigmoid Function or Logistic Function
S性能 Sigmoid Function or Logistic Function octave码 x = -10:0.1:10; y = zeros(length(x), 1); for i = 1 ...
- java IdentityHashMap 与HashMap
这两个map的主要区别在于,比较key值什么时候: IdentityHashMap我觉得当k1 == k2 时刻key值一样的 HaspMap觉得k1 == null ? k2 == null:k1. ...