android账号与同步之发起同步

摘要:
在上一篇博客文章中,我介绍了帐户和同步的同步过程,它提供了工程系统过程调用的服务。此服务是如何启动和使用的?在这篇博客文章中,我将整理启动过程。事实上,作为一个普通的开发人员,我们只需要知道,要知道被监控的ContentProvider已经更改,ContentProvider必须首先使用ContentResolver。notifyChange(android.net.Uri,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账号与同步之发起同步》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇submit提交表单后,不刷新当前页面全屏轮播插件 fullPage.js应用(基础版兼容IE7, 背景图版兼容IE8)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

使用springcloud gateway搭建网关(分流,限流,熔断)

Spring Cloud Gateway Spring Cloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。 Spring Cloud Gatewa...

.NET CORE 对接天翼云 OOS

最近,因公司项目需要对接天翼云OOS,在百度多次折腾后,大部分的都是基于java、php 等其他语言,很少基于C#语言的相关资料,即使有也是基于.NET Framwork开发的SDK,内容几乎是千篇一律,很少基于.NET CORE的开发。在官网上也很少发现基于C#语言的身影,最终在网上找寻到OOS相关的SDK集合中找到基于.NET(C#) SDK开发包 。...

HLS -- m3u8档案格式解析

1. Playlist file 一个M3U的 Playlist 就是一个由多个独立行组成的文本文件,每行由回车/换行区分。每一行可以是一个URI、空白行或是以”#“号开头的字符串,并且空格只能存在于一行中不同元素间的分隔。一个URI 表示一个媒体段或是”variant Playlist file“(最多支持一层嵌套,即一个m3u8文件中嵌套另一个m3u...

ftp操作方法整理

1.整理简化了下C#的ftp操作,方便使用    1.支持创建多级目录    2.批量删除    3.整个目录上传    4.整个目录删除    5.整个目录下载 2.调用方法展示, var ftp = new FtpHelper("10.136.12.11", "qdx1213123", "123ddddf");//初始化ftp,...

Python的几个爬虫代码整理(网易云、微信、淘宝、今日头条)

整理了一下网易云歌曲评论抓取、分析好友信息抓取、淘宝宝贝抓取、今日头条美图抓取的一些代码 抓取网易云评论 进入歌曲界面: http://music.163.com/#/song?id=453185824 找到如下的数据源: 贴一段Lyrichu的代码: (运行环境为P2.7) # -*- coding: utf-8 -*- # @Time   : ...

HDFS的Java客户端操作代码(查看HDFS下所有的文件或目录)

1.查看HDFS下所有的文件或目录 1 packageHdfs; 2 3 importjava.io.IOException; 4 importjava.net.URI; 5 importorg.apache.hadoop.conf.Configuration; 6 importorg.apache.hadoop.fs.FileStatus; 7 im...