上一篇博文我介绍了账号与同步的同步实现过程,当中提供了一个工系统进程调用的服务,那么这个服务究竟是怎么被启动和使用的呢?这篇博文我就大体梳理一下启动过程。

事实上作为一个一般开发者,我们仅仅要知道要想知道被监听的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账号与同步之发起同步的更多相关文章

  1. android账号与同步之同步实现

    上一篇博文我先介绍了账号与同步的账号管理,这篇就介绍一下还有一部分.就是android给提供的sync同步机制的使用. 事实上sync机制的使用和上一篇博文中介绍的账号管理非常类似,也是基于binde ...

  2. [原]如何在Android用FFmpeg+SDL2.0之同步音频

    同步音频的原理可以参考:http://dranger.com/ffmpeg/tutorial05.html  本文是在 [原]如何在Android用FFmpeg+SDL2.0之同步视频 的基础上面继续 ...

  3. mysql对比表结构对比同步,sqlyog架构同步工具

    mysql对比表结构对比同步,sqlyog架构同步工具 对比后的结果示例: 执行后的结果示例: 点击:"另存为(S)" 按钮可以把更新sql导出来.

  4. 真正的inotify+rsync实时同步 彻底告别同步慢

    真正的inotify+rsync实时同步 彻底告别同步慢       http://www.ttlsa.com/web/let-infotify-rsync-fast/     背景 我们公司在用in ...

  5. MySQL和MsSQL实时自动同步---SyncNavigator 数据库同步软件

    需要MySQL数据库支持的狐友们有福了,MySQL和MsSQL实时自动同步---SyncNavigator 数据库同步软件   使用SyncNavigator轻松实现数据库异地同步.断点续传.异构同步 ...

  6. Windows下MySQL双向同步及环形同步的实现

    记录一下这次做的双向同步及环形同步吧,都是最简单的实现: 具体实现之前,先说些与之有关的内容吧,大部分内容都是网上的,操作步骤则是亲自测试之后记录下的: 一. 数据同步的几种方式: 1. 触发器,在数 ...

  7. JAVA之旅(十三)——线程的安全性,synchronized关键字,多线程同步代码块,同步函数,同步函数的锁是this

    JAVA之旅(十三)--线程的安全性,synchronized关键字,多线程同步代码块,同步函数,同步函数的锁是this 我们继续上个篇幅接着讲线程的知识点 一.线程的安全性 当我们开启四个窗口(线程 ...

  8. Oracle主从同步、双向同步的配置

    (本教程展示了Windows环境的oracle数据库主从同步,Linux环境一样也可以) (把主数据库obpm 和从数据库orcl 用实际的数据库名给替换掉) (配置主从同步后,再配置双向同步,可能会 ...

  9. socket的阻塞与非阻塞,同步与非同步

    网络编程中通常提到四种方式,同步/异步,阻塞/非阻塞.以下对它们的概念进行总结 1.同步/异步:主要针对C端 同步:所谓同步,就是在C端发出一个功能调用时,在没有得到结果之前,调用不返回,也就是必须一 ...

随机推荐

  1. go语言实现遍历目录,及查找特定的文件类型

    // filelist.go package main import ( //"flag" "fmt" "os" "path/fi ...

  2. memcache 集群

    memcache 是一个分布式的缓存系统,但是本身没有提供集群功能,在大型应用的情况下容易成为瓶颈.但是客户端这个时候可以自由扩展,分两阶段实现.第一阶段:key 要先根据一定的算法映射到一台memc ...

  3. 高质量c c++编程

    第1章 文件结构 每一个C++/C程序通常分为两个文件.一个文件用于保存程序的声明(declaration),称为头文件.还有一个文件用于保存程序的实现(implementation),称为定义(de ...

  4. unity3d插入Daikon Forge GUI 中国课程-7-高级控制slider采用

    (游戏开始的牛市)大家好我是孙广东.官方网站提供的是专业的视频教程http://www.daikonforge.com/dfgui/tutorials/,只是是在youtube上,要观看是须要FQ的. ...

  5. 返璞归真 asp.net mvc (9) - asp.net mvc 3.0 新特性之 View(Razor)

    原文:返璞归真 asp.net mvc (9) - asp.net mvc 3.0 新特性之 View(Razor) [索引页][源码下载] 返璞归真 asp.net mvc (9) - asp.ne ...

  6. 用PowerDesigner生成自定义建表语句

    原文:用PowerDesigner生成自定义建表语句 我们经常用PowerDesigner来进行数据库表结构的设计,并且设计出来的表比较直观的看出之间的相互关系,方便理解:但其自动生成的脚本并不一定符 ...

  7. Demo of Python &quot;Map Reduce Filter&quot;

    Here I share with you a demo for python map, reduce and filter functional programming thatowned by m ...

  8. PHP redis操作类 个人总结

    <pre name="code" class="php"><span style="font-size:18px;"> ...

  9. 生产都消费者模式的一个demo,消费者设置缓存

    package queue; import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlocki ...

  10. C# 字符串知识整理

    新知识点,只是对于本人来说而已. 系统处理文本的方式 [新知识点].NET Framework .NET Framework的定义:其包含了一个公共语言运行时(Common Language Runt ...