忽然有一天,我想要做一件事:去代码中去验证那些曾经被“灌输”的理论。
– 服装学院的IT男
本篇已收录于Activity短暂的一生系列
接待一起学习讨论Android应用开发或者WMS
V:WJB6995
Q:707409815
正文
Activity 生命周期是学习 Android 必定要把握的知识点,但是刚入行的同学对于这个概念根本上是靠死记硬背,有一些实际工作履历的同学,在工作中通过实现业务需求或者解决一些BUG根本上是可以知道哪些生命周期对应用户操作的哪一步。
固然触发生命周期的场景很多,当前只还是以在桌面启动应用来分析,但是完整的跟过这一流程的代码逻辑,就能加深对生命周期本质的理解,其他场景的生命周期切换也不是啥题目。
生命周期系列:
- Activity生命周期之onPause
- onCreate,onStart,onResume-1
- onCreate,onStart,onResume-2
- Activity生命周期之onStop-1
- Activity生命周期之onStop-2
- Activity生命周期之onDestory
本篇会介绍生命周期的实行顺序和 onPause 的实行逻辑
1. 生命周期介绍
回想一下2个 Activity 的界说:
- SourceActivity:实行 startActivity 方法的 Activity,也就是发起请求的Activity,当前就是 Launcher 的 Activity
- TargetActivity:需要被启动的 Activity,当前就是“电话”应用在清单文件配置的 MainActivity
在 Activity.java 中,每个生个周期的实行都有对应的log打印,比如 onPause
- # Activity
- // 默认为false,需要收到打开
- private static final boolean DEBUG_LIFECYCLE = true;
- protected void onPause() {
- if (DEBUG_LIFECYCLE) Slog.v(TAG, "onPause " + this);
- ......
- }
复制代码 打开log开关后,操作一遍就得到到了下面的生命周期日记:
根据这里的日记打印就可以得出以下结论:
- 先实行 SourceActivity 的 onPause
- 然后依次实行 TargetActivity onCreate,onStart,onResume 三个生命周期
- 最后实行 SourceActivity 的 onStop
-
如果没有条件编译AOSP的同学,搞个Activity在各自的生命周期加上log也能得到如许的一份log
根据之前【Activity启动流程】的知识和这份生命周期的log,可以得到下面这个图:

SystemService 会先触发 SourceActivity 的 onPause,然后才允许启动新的 TargetActivity 。(鲁迅曾说过:俗话说旧的不去新的不来)
SourceActivity 实行了 onPause 的时候,TargetActivity 还没起来,以是说 onPause 的时候,Activity也是可见的,只有知道到 onStop才不可见,因为 TargetActivity 已经显示了。
知道这几个生命周期的实行顺序其实就已经可以开始反向推到调用流程了,但是可以通过 events 日记,把流程详细化,完整流程对应的 events 日记如下:

只有分析流程的时候,按顺序像连线一样找到各个日记打印的地方,形成调用链,那整个生命周期的源码分析就完成了。
这里把 events 日记做了一些简化和加上了注释,如果以后遇到流程异常的,就可以和这份正常的日记对比一下,哪一步开始出题目了,然后去定位题目缘故起因。
- // SystemService端创建TargetActivity (writeWmCreateActivity)
- 03-27 14:41:06.428 27889 28629 I wm_create_activity: [0,253598020,21,com.google.android.dialer/.extensions.GoogleDialtactsActivity,android.intent.action.MAIN,NULL,NULL,270532608]
- // SystemService端触发SourceActivity的Pause (writeWmPauseActivity)
- 03-27 14:41:06.431 27889 28629 I wm_pause_activity: [0,51114540,com.android.launcher3/.uioverrides.QuickstepLauncher,userLeaving=true,pauseBackTasks]
- // SourceActivity应用端将执行onPause (writeWmOnPausedCalled)
- 03-27 14:41:06.448 28606 28606 I wm_on_paused_called: [51114540,com.android.launcher3.uioverrides.QuickstepLauncher,performPause]
- // SystemService端把SourceActivity添加进需要stop的集合 (writeWmAddToStopping)
- 03-27 14:41:06.459 27889 28630 I wm_add_to_stopping: [0,51114540,com.android.launcher3/.uioverrides.QuickstepLauncher,makeInvisible]
- // SystemService端要真正触发TargetActivity启动 (writeWmRestartActivity)
- 03-27 14:41:06.487 27889 28630 I wm_restart_activity: [0,253598020,21,com.google.android.dialer/.extensions.GoogleDialtactsActivity]
- // TargetActivity端将执行onCreate (writeWmOnCreateCalled)
- 03-27 14:41:06.769 3401 3401 I wm_on_create_called: [253598020,com.google.android.dialer.extensions.GoogleDialtactsActivity,performCreate]
- // TargetActivity端将执行onStart (writeWmOnStartCalled)
- 03-27 14:41:06.900 3401 3401 I wm_on_start_called: [253598020,com.google.android.dialer.extensions.GoogleDialtactsActivity,handleStartActivity]
- // TargetActivity端将执行Resume (writeWmOnResumeCalled)
- 03-27 14:41:06.911 3401 3401 I wm_on_resume_called: [253598020,com.google.android.dialer.extensions.GoogleDialtactsActivity,RESUME_ACTIVITY]
- // SystemService端触发SourceActivity的stop (writeWmStopActivity)
- 03-27 14:41:07.097 27889 30491 I wm_stop_activity: [0,51114540,com.android.launcher3/.uioverrides.QuickstepLauncher]
- // SourceActivity应用端将执行onStop (writeWmStopActivity)
- 03-27 14:41:07.119 28606 28606 I wm_on_stop_called: [51114540,com.android.launcher3.uioverrides.QuickstepLauncher,STOP_ACTIVITY_ITEM]
复制代码 1.1 生命周期事件
Activity各个生命周期的实行被封装成了一个事件,详细的映射关系如下:
- LaunchActivityItem onCreate
- StartActivityItem Start
- ResumeActivityItem onResume
- PauseActivityItem Pause
- StopActivityItem Stop
- ActivityRelaunchItem relaunch
- ActivityResultItem result
复制代码 各个类被界说在 frameworks/base/core/java/android/app/servertransaction/ 路径下:
这些类都实现了 BaseClientRequest 接口,以是都有 preExecute ,execute ,postExecute 3个方法。
其实 execute 表现真正触发应用端实行对应的生命周期,而 preExecute,,postExecute 分别对应实行生命周期前后该做的事,这3个方法的实行循序如下:
- preExecute -》execute -》postExecute
复制代码 这些后续在代码中也能论证。
2. onPause
根据之前的分析知道 SourceActivity 是最先实行的,这个时候 TargetActivity 是还没有启动的。
pause流程对应的事件是 PauseActivityItem ,以是构建这个事件之前的调用逻辑就是 pause 流程在 SystemService 的处理,而 PauseActivityItem::execute 后的调用逻辑就是 pause 流程在应用端的处理。
目标明确,开始分析看代码。
2.1 SystemService 触发 pause 流程
构建 PauseActivityItem 的地方在 TaskFragment::schedulePauseActivity 方法中,在【Activity启动流程1】中知道在流程早起会实行下面这段调用链:
- ActivityTaskManagerService::startActivity
- ActivityTaskManagerService::startActivityAsUser
- ActivityTaskManagerService::startActivityAsUser
- ActivityStartController::obtainStarter
- ActivityStarter::execute
- ActivityStarter::executeRequest -- 构建 ActivityRecord --2.1 创建ActivityRecord
- ActivityStarter::startActivityUnchecked
- ActivityStarter::startActivityInner -- 2.2 关键函数startActivityInner
- ActivityStarter::getOrCreateRootTask -- 2.2.1 创建或者拿到Task
- ActivityStarter::setNewTask -- 2.2.2 将task与activityRecord 绑定
- RootWindowContainer::resumeFocusedTasksTopActivities --2.2.3 处理需要显示的Activity
复制代码 RootWindowContainer::resumeFocusedTasksTopActivities 方法后续的逻辑如下:
- RootWindowContainer::resumeFocusedTasksTopActivities
- Task::resumeTopActivityUncheckedLocked
- Task::resumeTopActivityInnerLocked
- TaskFragment::resumeTopActivity
- TaskDisplayArea::pauseBackTasks -- Pause SourceActivity 逻辑 (第一次执行)
- WindowContainer::forAllLeafTask
- TaskFragment::forAllLeafTaskFragments
- TaskFragment::startPausing
- TaskFragment::startPausing
- TaskFragment::schedulePauseActivity --构建 PauseActivityItem,这里是触发SourceActivity的pause
- ActivityTaskManagerService::startProcessAsync -- 创建TargetActivity所在的进程 (第一次执行,且需要启动目标进程)
- ActivityTaskSupervisor::startSpecificActivity -- 启动TargetActivity(第二次执行,现在有个印象)
复制代码 需要注意的是,上面的 RootWindowContainer::resumeFocusedTasksTopActivities 触发的逻辑是会实行2次的
第一次调用就是启动流程 startActivity 触发的,实行到 TaskFragment::resumeTopActivity 方法时,触发 SourceActivity 的 pause 流程和创建 TargetActivity 的进程。(应用内启动Activity肯定就不会触发进程创建了)
第二次触发是 SourceActivity 实行完 pause 后,就会触发 completePause 流程,这次实行到 TaskFragment::resumeTopActivity 方法时走的是触发启动 TargetActivity 流程。完整调用链如下:
- ActivityClientController::activityPaused
- ActivityRecord::activityPaused
- TaskFragment::completePause
- RootWindowContainer::resumeFocusedTasksTopActivities --分支1 再次执行 resumeFocusedTasksTopActivities
- RootWindowContainer::resumeFocusedTasksTopActivities
- Task::resumeTopActivityUncheckedLocked
- Task::resumeTopActivityInnerLocked
- TaskFragment::resumeTopActivity
- ActivityTaskSupervisor::startSpecificActivity -- 启动TargetActivity
- RootWindowContainer::ensureActivitiesVisible --分支2 确保有Activity显示流程
复制代码 这里需要注意 completePause 流程再次实行 RootWindowContainer::resumeFocusedTasksTopActivities 方法时内部还会触发 TaskFragment::resumeTopActivity 方法,而这一次触发的是启动 TargetActivity 的流程。
同一个方法因为实行机遇不同,走不同的逻辑,这种环境在分析源码的时候非常让人头疼,上面做了一些说明而且提供了调用链。
本篇其实需要注意的只有第一次实行的逻辑处理,因为第二次是 completePause 流程了,触发的是TargetActivity 流程也就是 TargetActivity 的onCreate,onStart,onResume 三个生命周期实行,是下一篇的触发逻辑,当前有个印象即可。
其他的地方在【Activity启动流程】系列都分析过来,如今只关注 pause 逻辑即可,看一下 TaskFragment::resumeTopActivity 方法的代码。
2.1.1 TaskFragment::resumeTopActivity 方法
- # TaskFragment
- final boolean resumeTopActivity(ActivityRecord prev, ActivityOptions options,
- boolean deferPause) {
- // 这里的next返回下一个需要显示的Activity
- ActivityRecord next = topRunningActivity(true /* focusableOnly */);
- ...... // 如果跳过的这些逻辑都没执行return,则开始执行resume流程,如果被return了也会打印日志。
-
- // 打印日志,需要显示哪个Activity(也就是SourceActivity)
- if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next);
- ......
- // 重点* 1. 第一次执行走这,执行SourceActivity的pause流程
- boolean pausing = !deferPause && taskDisplayArea.pauseBackTasks(next);
- ......
- if (pausing) {
- ProtoLog.v(WM_DEBUG_STATES, "resumeTopActivity: Skip resume: need to"+ " start pausing");
- if (next.attachedToProcess()) {
- ......
- } else if (!next.isProcessRunning()) {
- // 进程没有运行,则触发异步创建进程。 当前逻辑肯定是执行
- final boolean isTop = this == taskDisplayArea.getFocusedRootTask();
- mAtmService.startProcessAsync(next, false /* knownToBeDead */, isTop,
- isTop ? HostingRecord.HOSTING_TYPE_NEXT_TOP_ACTIVITY
- : HostingRecord.HOSTING_TYPE_NEXT_ACTIVITY);
- }
- ......
- // 注意,这里会return,所以第一次执行到这就结束了。
- return true;
- }
- ......
- ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Restarting %s", next);
- // 重点*2. 第二次执行的时候走这,SourceActivity已经执行完pause流程了,所以走这启动Activity走这
- mTaskSupervisor.startSpecificActivity(next, true, true);
- }
复制代码 这个方法是极其复杂的, 但是基于前面的铺垫后再看这个方法,我觉得应该目标明确了,由于当前是分析的是 onPause流程,以是只看第一次实行的调用链即可,也就是 pauseBackTasks 方法。
TaskDisplayArea::pauseBackTasks 方法的逻辑从前面的调用链知道会实行 TaskFragment::schedulePauseActivity 方法,这中心的调用逻辑在【Activity启动流程-2】中有详细分析,当前直接看 schedulePauseActivity 方法。
- # TaskFragment
- void schedulePauseActivity(ActivityRecord prev, boolean userLeaving,
- boolean pauseImmediately, String reason) {
- // Proto日志
- ProtoLog.v(WM_DEBUG_STATES, "Enqueueing pending pause: %s", prev);
- try {
- // 输出events 日志 wm_pause_activity
- EventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev),
- prev.shortComponentName, "userLeaving=" + userLeaving, reason);
- // 重点* 构建并执行PauseActivityItem
- mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
- prev.token, PauseActivityItem.obtain(prev.finishing, userLeaving,
- prev.configChangeFlags, pauseImmediately));
- } catch (Exception e) {
- ......
- }
- }
复制代码
- 打印第一个 events 日记:wm_pause_activity
- 构建实行 PauseActivityItem 事件
SystemService 触发 pause 的逻辑就分析完了,PauseActivityItem 是承上启下的关键线索,看看这个事件做了些什么。
2.2 PauseActivityItem
- # PauseActivityItem
- @Override
- public void execute(ClientTransactionHandler client, ActivityClientRecord r,
- PendingTransactionActions pendingActions) {
- // trace
- Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
- // 触发应用进程pause
- client.handlePauseActivity(r, mFinished, mUserLeaving, mConfigChanges, pendingActions,
- "PAUSE_ACTIVITY_ITEM");
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
- }
- @Override
- public int getTargetState() {
- return ON_PAUSE;
- }
- @Override
- public void postExecute(ClientTransactionHandler client, IBinder token,
- PendingTransactionActions pendingActions) {
- if (mDontReport) {
- return;
- }
- // 自己加的Trace
- Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityPaused");
- // TODO(lifecycler): Use interface callback instead of actual implementation.
- // 触发 completePause 流程
- ActivityClient.getInstance().activityPaused(token);
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
- }
复制代码
- 先实行 execute ,终极触发应用端 Activity 的 onPause
- 然后实行 postExecute ,这里的逻辑是由 SystemService 实行 completePause 流程
这2个发生是同步触发,但是详细触发逻辑所在的进程都不一样,以是肯定是异步实行的,没有强绑定关系, 换句话说就是 应用端的 Activity 是不是真的顺遂实行完了 onPause,和 completePause 流程没有必然关系。
这点很重要,背面看 onStop 流程的时候还会提到。
activityPaused 触发的后续逻辑就是 2.1 末节提到的第二次实行 TaskFragment::resumeTopActivity 方法来启动 Activity 的触发点。
当然本篇还是看 pause 逻辑,以是直接看 execute 方法触发的应用端是怎样处理的。
2.3 应用端实行pause
应用端调用链如下:
- ActivityThread::handlePauseActivity
- ActivityThread::performPauseActivity
- ActivityThread::performPauseActivityIfNeeded
- InstrumentationcallActivityOnPause
- Activity::performPause
- Activity::onPause -- onPause
- ActivityClientRecord::setState -- 设置状态ON_PAUSE(4)
复制代码 代码流程也很简朴,看一下。
- # ActivityThread
- @Override
- public void handlePauseActivity(ActivityClientRecord r, boolean finished, boolean userLeaving,
- int configChanges, PendingTransactionActions pendingActions, String reason) {
- ......
- r.activity.mConfigChangeFlags |= configChanges;
- performPauseActivity(r, finished, reason, pendingActions);
- ......
- }
- private Bundle performPauseActivity(ActivityClientRecord r, boolean finished, String reason,
- PendingTransactionActions pendingActions) {
- ......
- // Pre-Honeycomb apps always save their state before pausing
- final boolean shouldSaveState = !r.activity.mFinished && r.isPreHoneycomb();
- if (shouldSaveState) {
- // 安卓3之前的版本是在onPause执行执行OnSaveInstanceState的
- callActivityOnSaveInstanceState(r);
- }
- // onPause 逻辑
- performPauseActivityIfNeeded(r, reason);
- ......
- }
复制代码 轻微提到了一下 OnSaveInstanceState 的调用机遇,Honeycomb 是安卓3, 如今根本上没有这种手机了,相识一下即可,继续看主流程。
- # ActivityThread
- Instrumentation mInstrumentation;
- private void performPauseActivityIfNeeded(ActivityClientRecord r, String reason) {
- // 如果已经 paused 就不许要处理了
- if (r.paused) {
- // You are already paused silly...
- return;
- }
- try {
- r.activity.mCalled = false;
- // 主流程
- mInstrumentation.callActivityOnPause(r.activity);
- if (!r.activity.mCalled) {
- throw new SuperNotCalledException("Activity " + safeToComponentShortString(r.intent)
- + " did not call through to super.onPause()");
- }
- } catch ......
- // 需要留意一下 这里的状态是生命周期,不是ActivityRecord的那个状态
- r.setState(ON_PAUSE);
- }
复制代码 又看到了 Instrumentation
- # Instrumentation
- public void callActivityOnPause(Activity activity) {
- activity.performPause();
- }
- <!---->
- # Activity
- final void performPause() {
- // 1. Trace begin
- if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
- Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "performPause:"
- + mComponent.getClassName());
- }
- dispatchActivityPrePaused();
- mDoReportFullyDrawn = false;
- // 2. Fragments处理
- mFragments.dispatchPause();
- mCalled = false;
- // 3. 重点* onPause
- onPause();
- // 4. events日志 wm_on_paused_called
- EventLogTags.writeWmOnPausedCalled(mIdent, getComponentName().getClassName(),
- "performPause");
- mResumed = false;
- ......
- // Trace end
- Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
- }
复制代码 到这里 onPause 流程就分析完了, 这里有几个需要注意的点
-
-
- 最重要的触发了 Activity 的 onPause
- 打印日记 wm_on_paused_called ,表现应用端已经pause了
到这里 SourceActivity 的 Pause 流程已经完全竣事了。
3 ActivityClientRecord 是什么
在 ActivityThread::performPauseActivityIfNeeded 中看到在触发 onPause 实行后还会调用 ActivityClientRecord::setState 方法通报了一个 ON_PAUSE 进去。
这句代码大概意思猜着就是设置一个 ON_PAUSE 的状态生存起来。
但是 SystemService 端有个 ActivityRecord 来记载 Activity的一些信息,那这个 ActivityClientRecord 又是什么呢?
从命名上来看,好像和 ActivityRecord 雷同,是 Client(客户端)这边记载 Activity 的一个类,实际上也确实如此,ActivityClientRecord 内部也生存了很多与 Activity 启动和运行相干的各种参数,如Intent、token、binder引用等。以后遇到再说,不过如今我们关注生命周期相干的,先看看 setState 这个方法。
- # ActivityThread$ActivityClientRecord
- // 是否paused
- @UnsupportedAppUsage
- boolean paused;
- // 是否stoped
- @UnsupportedAppUsage
- boolean stopped;
- // 生命周期状态
- @LifecycleState
- private int mLifecycleState = PRE_ON_CREATE;
- /** Update the current lifecycle state for internal bookkeeping. */
- // 更新内部保存的生命周期状态
- public void setState(@LifecycleState int newLifecycleState) {
- mLifecycleState = newLifecycleState;
- switch (mLifecycleState) {
- case ON_CREATE:
- // 刚刚创建,不可见,不可交互
- paused = true;
- stopped = true;
- break;
- case ON_START:
- // 还没完全进入前台,但已经可见,不可交互
- paused = true;
- stopped = false;
- break;
- case ON_RESUME:
- // 可见,可交互
- paused = false;
- stopped = false;
- break;
- case ON_PAUSE:
- // 未被完全遮挡,可见,不可交互
- paused = true;
- stopped = false;
- break;
- case ON_STOP:
- // 被完全遮挡,所以不可见,也不可交互
- paused = true;
- stopped = true;
- break;
- }
- }
复制代码 这段代码有3个变量:
- paused: 表现当前Activity 是否处于暂停(pause)状态,也就是能不能和用户交互
- stopped : 表现当前Activity是否处于 停止(stop)状态,也就是能不能被用户可见
- mLifecycleState :这是一个被LifecycleState注解的变量,说明取值只能是内部界说的几个生命周期
关于pause和stop的区别大概有些小伙伴还很迷糊,这里表达一下我的理解:
onPause和onStop都是不能和用户交互的,但是 onPause 的时候,Activity 还是可见的,比如出现一个dialog主题的Activity,这个时候下面的 Activity 就是pause,但不是stop,因为它还能被用户看见。
从log来看,SourceActivity 的onPause是最先实行的,但是onStop却要等 TargetActivity 实行完 onResume 再实行
抛开代码从实际环境来说,SourceActivity 要不可见,就只有等 TargetActivity 完全显示(onResume)的时候才可以算 onStop 了
@LifecycleState 注解是界说在 ActivityLifecycleItem下的
- # ActivityLifecycleItem
- @Retention(RetentionPolicy.SOURCE)
- public @interface LifecycleState{}
- public static final int UNDEFINED = -1;
- public static final int PRE_ON_CREATE = 0;
- public static final int ON_CREATE = 1;
- public static final int ON_START = 2;
- public static final int ON_RESUME = 3;
- public static final int ON_PAUSE = 4;
- public static final int ON_STOP = 5;
- public static final int ON_DESTROY = 6;
- public static final int ON_RESTART = 7;
复制代码 可以看到 从 1-7 刚好是和 7个生命周期对应的,这里需要注意对应的几个int值,背面的代码还要用到。
当前 pause 流程通报了一个 ON_PAUSE 进去 那当前 mLifecycleState 的值就是 ON_PAUSE(4)。
4. 总结
本篇首先明确了启动 Activity 生命周期的先后实行顺序,然后介绍了完整流程的 events 日记。
然后知道了 startActivity 流程最开始会实行到 TaskFragment::resumeTopActivity 方法,在这里会触发 SourceActivity 的 pause 流程,同时还会创建 TargetActivity 所在的进程(有必要的话)
我对关键方法加上了一个 Trace 后,当前桌面冷启动应用场景得到的Trace如下:
- 可以看到 TaskFragment::resumeTopActivity 实行了2次,上图是第一次
-
- 实行了 pauseBackTasks 流程,也就是 SourceActivity 的 pause
- 触发了TargetActivity 所在的进程创建
pauseBackTasks 方法背面会实行 TaskFragment::schedulePauseActivity 方法,在这个方法中有2个点:
而 PauseActivityItem 也做了2件事:
- 先实行 execute ,终极触发应用端 Activity 的 onPause
- 然后实行 postExecute ,这里的逻辑是由 SystemService 实行 completePause 流程。
本篇关心的是 execute 方法除非的逻辑,在应用段实行到 onPause 的逻辑,打印了 wm_on_paused_called 日记,流程也就分析完了。
最后再增补一下 postExecute 方法触
-
- 这里的 resumeTopActivity 上面是 activityPaused,也就是我在 PauseActivityItem::postExecute 加是 trace
- 固然还会实行 pauseBackTasks 但是下面没有构建 pause 事件的逻辑了
- 这一次实行还会触发prepareAppTransition方法
- 终极要的是要创建 TargetActivity 了,下一篇是基于这个根本
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |