AMS对象随系统进程启动而构建,随着系统进程退出而消亡,可以说,AMS与系统进程共存亡。

先上一张总的启动时序图:

上图分为三个步骤:

  1. 初始化系统进程的运行环境;
  2. 初始化AMS对象;
  3. AMS对象启动的配套工作。

1.初始化系统进程的运行环境

SystemServer是我们理解Android系统进程的入口,它的初始化是从Native层开始的:Zygote从Native层调用SystemServer的main()函数,便开始了SystemServer的初始化。初始化很重要的一个步骤就是创建系统进程的运行环境,即创建一个SystemContext,调用关系如下所示:

SystemServer.main()      // Zygote会从Native层调用该方法,进入SystemServer的执行代码
└── SystemServer.run() // SystemServer一旦运行起来,就一直循环处理消息队列中的消息
└── SystemServer.createSystemContext() // 创建SystemContext

SystemContext到底是什么呢?说到底,它还是一个Context类型的对象,需要借助于ActivityThread才能获取到:

// SystemServer.createSystemContext()
private void createSystemContext() {
ActivityThread activityThread = ActivityThread.systemMain();
mSystemContext = activityThread.getSystemContext();
mSystemContext.setTheme(android.R.style.Theme_DeviceDefault_Light_DarkActionBar);
}

ActivityThread.systemMain()函数用于创建系统进程的主线程,方法主体很简单,创建了一个新的ActivityThread对象,并调用了attach()方法,输入参数是true,表示需要将其绑定到系统进程。

应用进程创建ActivityThread对象是通过ActivityThread.main()函数完成的。由于系统进程的特殊性,专辟了一个systemMain()函数给系统进程。

public static ActivityThread systemMain() {
... // 省略硬件渲染相关配置的代码
ActivityThread thread = new ActivityThread();
thread.attach(true);
return thread;
}

接下来,我们来着重分析一下ActivityThread.attach()函数:

private void attach(boolean system) {
sCurrentActivityThread = this;
mSystemThread = system;
if (!system) {
... // 绑定应用进程,本例仅讨论绑定系统进程,故省略之
} else {
// 设置进程名为system_process
android.ddm.DdmHandleAppName.setAppName("system_process", UserHandle.myUserId());
try {
mInstrumentation = new Instrumentation();
ContextImpl context = ContextImpl.createAppContext(
this, getSystemContext().mPackageInfo);
mInitialApplication = context.mPackageInfo.makeApplication(true, null);
mInitialApplication.onCreate();
} catch (Exception e) { ... // 省略抛出异常的代码 }
} // 设置与输出日志相关的Dropbox
DropBox.setReporter(new DropBoxReporter());
ViewRootImpl.addConfigCallback( ... // );
}

在上面的函数实现中,会创建几个很重要的对象:

  • mInstrumentation: 工具类,主要用于测试,与AndroidManifest.xml中<instrumentation>对应;

  • context: Application的运行环境,创建它的目的是为了创建下面的Application对象。创建这个context需要传入一个LoadedApk类型的对象,通过ActivityThread.getSystemContext()函数便可获取:

public ContextImpl getSystemContext() {
synchronized (this) {
if (mSystemContext == null) {
mSystemContext = ContextImpl.createSystemContext(this);
}
return mSystemContext;
}
}

这里是第一次调用getSystemContext()函数,所以mSystemContext为null,进而会通过ContextImpl.createSystemContext()函数创建一个新的对象:

// ContextImpl.createSystemContext()
static ContextImpl createSystemContext(ActivityThread mainThread) {
LoadedApk packageInfo = new LoadedApk(mainThread);
ContextImpl context = new ContextImpl(null, mainThread,
packageInfo, null, null, false, null, null, Display.INVALID_DISPLAY);
context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(),
context.mResourcesManager.getDisplayMetricsLocked());
return context;
}

上述函数首先会创建一个LoadedApk的对象,LoadedApk表示一个装载到内存的Apk,对于SystemContext,这个Apk就是framework-res.apk; 然后,创建一个ContextImpl的对象,进一步初始化资源相关的配置; 最终,返回的context就是SystemContext。

  • mInitialApplication: Application对象,由于是通过framework-res.apk这个APK的Context创建而来,所以这个Application对象就对应到了framework-res.apk,这是系统中第一个运行的APK,所以叫做InitialApplication,它是运行在系统进程中。

兜兜转转弄了一圈,终于把SystemContext给创建好了,系统进程好好运行就好了,为什么要大费周张的搞一个运行环境Context出来呢? 操作系统运行基本单位是进程,我们写的任何Android代码最终都要落到一个实际的进程中去执行。然而,Android有意弱化了进程的概念,我们在写Android应用程序的时候,基础的概念都是运行环境(Context),而不是去考虑进程中有什么可以使用。 Android对待系统进程,也像对待普通的应用进程一样,都需要构建一个运行环境,就是Context。在构建AMS对象时,会将SystemContext传入,通过这个特殊的Context,AMS就能获取运行时所需要各种信息。

我们通过一张类图来总结一下,与系统进程运行环境初始化相关的各个类之间的关系:

 

总结初始化环境的三个步骤:

  1. Android要为应用进程创造一个运行环境,同样也需要为系统进程创造一个运行环境,在系统进程启动伊始,这个运行环境就需要创建完毕,这个环境就是我们所说的SystemContext;

  2. SystemServer会创建一个ActivityThread对象,代表系统进程的主线程,系统进程的入口为SysMain(),在ActivityThread对象构建的过程中,又会创建Instrumentation对象和framework-res.apk的LoadedApk对象,再通过LoadedApk创建Application对象;

  3. 在ActivityThread对象构建完毕后,SystemServer便可获取到系统进程的运行环境了,即SystemContext,这是ActivityThread通过ContextImpl创建而成的。

2.初始化ActivityManagerService对象

在Android起机的过程中,系统服务是相互影响的,所以有启动顺序的先后之分,譬如BatteryService,WindowManagerService就需要在ActivityManagerService创建之后才能创建。还有一些系统事件,譬如BOOT_COMPLETED广播,需要在系统完全启动之后才能发出。 Android为系统服务封装了SystemService类,设计了系统服务的生命周期:

public abstract class SystemService {
// 阶段 1: 等待显示设备准备完毕
// 这个阶段,Installer, AMS, PowerManagerService, DisplayManagerService已经构建完毕
public static final int PHASE_WAIT_FOR_DEFAULT_DISPLAY = 100;
// 阶段 2: 锁屏服务已经准备完毕
// 这个阶段,大部分系统服务已经构建完毕。其他服务可以获取锁屏相关的数据了,譬如锁屏密码等
public static final int PHASE_LOCK_SETTINGS_READY = 480;
// 阶段 3: 系统服务已经准备完毕
// 这个阶段,大部分系统服务已经构建完毕,PackageManagerService,PowerManagerService已经能够提供服务
public static final int PHASE_SYSTEM_SERVICES_READY = 500;
// 阶段 4: ActivityManagerService已经能够提供服务了
// 这个阶段,获取ContentProvider、发送广播等AMS相关的功能已经可以用了
public static final int PHASE_ACTIVITY_MANAGER_READY = 550;
// 阶段 5: 已经可以启动应用程序了
// 这个阶段,用户界面已经启动,数据连接、音频等服务都已可用
public static final int PHASE_THIRD_PARTY_APPS_CAN_START = 600;
// 阶段 6: 系统起机完成
public static final int PHASE_BOOT_COMPLETED = 1000; public abstract void onStart();
public void onBootPhase(int phase) {} // ... 省略其他函数
}

部分系统服务的初始化依赖于当前系统已经起机到什么阶段,当系统服务启动时,onStart()函数会被调用; 系统启动到一定的阶段,onBootPhase()函数会被调用。所有系统服务的创建和生命周期的管理都是由SystemServiceManager这个类完成的。

本文中要分析的AMS,它并没有直接继承SystemService,而是通过内部类Lifecycle来继承实现的, AMS.Lifecycle非常简单,就是调用了AMS的构造函数和start()函数,是一个间接初始化AMS的过程。

public static final class Lifecycle extends SystemService {
private final ActivityManagerService mService; // 构造函数会被SystemServiceManager反射调用
public Lifecycle(Context context) {
super(context);
mService = new ActivityManagerService(context);
} // 在Lifecycle对象构造完成后,这个函数会被回调
@Override
public void onStart() {
mService.start();
} public ActivityManagerService getService() {
return mService;
}
}
我们通过类图来总结一下SystemServiceManager, SystemService以及AMS三者之间的关系:

  1. SystemService是系统服务的抽象类,封装了onStart()和onBootPhase()等生命周期函数供SystemServiceManager回掉;

  2. AMS并不是直接继承SystemService,而是通过内部类Lifecycle间接完成了系统服务启动的生命周期;

  3. SystemServiceManager管理了多个SystemService。

SystemServer在完成一些简单的初始化后,就需要启动系统服务了,最重要的一系列服务是在SystemServer.startBootstrapServices() 这个函数中启动的,BootstrapServices的命名也很贴切,表示要启动一些”引导服务”,这些服务直接影响到其他服务的启动。

  1. 启动AMS之前,需要先连接installd进程。在系统进程(system_process)中,对应的服务就Installer, 通过Installer这个服务,就能完成一些重要目录的创建,譬如/data/user。

  2. 启动AMS。由此可见AMS的重要性,Android第二个启动的系统服务就是AMS。 实际上,这里通过SystemServcieManager来启动的是AMS.Lifecycle,AMS的真正初始化工作是由AMS.Lifecyle间接完成的。

  3. 启动PowerManagerService,这也是非常重要的一个系统服务。AMS也需要使用PowerManagerService的服务, 譬如,在启动Activity时,要避免系统进入休眠状态,就需要获取WakeLock。

  4. 这一步启动Lights、 DisplayManager、 PackageManager这些系统服务。

  5. 调用AMS.setSystemProcess()将当前进程设置为系统进程。为什么在SystemServer中需要调用AMS的方法来设置当前进程的信息呢? 因为AMS的职责之一就是维护所有进程的状态,不管是应用进程还是系统进程,都是AMS的管辖范围。

上面第2条AMS对象构建时,要初始化的内容非常多,大致可以分成两类:

  • 监测统计: Watchdog,CPU、内存、电量的使用统计
  • 组件管理: broadcastQueues, services, providers, statckSupervisor,recentTasks, Android四大组件的相关信息都由AMS统一维护

在第5步中, 通过AMS对象调用了setSystemProcess()函数,目的是为了将当前进程(system_process)设置为系统进程:

  1. 通过ServiceManager向系统注册一些重要的服务。诸如meminfo、gfxinfo、dbinfo等信息都是由系统进程维护的, 可以通过adb shell dumpsys命令输出。

  2. ApplicationInfo包含了一个应用程序的信息,这些信息从AndroidManifest.xml的<application>标签中解析出来,譬如进程名、版本号、使用的主题等,那么,通过android这个包名,获取的ApplicationInfo自然就是Android系统这个应用程序的信息。

    每一个普通的应用程序都会有一个AndroidManifest.xml文件,这个应用程序运行的环境,就是我们所说的”应用进程”; 有一个特殊的应用程序,具备更多的特权,这个应用程序的运行环境就是”系统进程”。 android就是这个特殊应用程序的包名,其所对应的<application>标签定义在frameworks/base/core/res/AndroidManifest.xml中,最终会编译在framework-res.apk中。

    不论是应用进程还是系统进程,都有主线程,ActivityThread就是Android定义的主线程类。 AMS中的mSystemThread对象就是ActivityThread的实例,表示这是系统进程的主线程。 通过mSystemThread.installSystemApplicationInfo()这个函数调用,ApplicationInfo和ClassLoader就被设置到了 LoadedApk中,ApplicationInfo与LoadedApk的关系我们后文再描述。

  3. ProcessRecord这个类用于描述一个运行的进程,AMS管理着所有ProcessRecord的状态,包括系统进程的ProcessRecord。 系统进程的ProcessRecord几个重要的属性值:

    • persistent=true: 系统进程的ProcessRecord是常驻内存的
    • maxAdj=SYSTEM_ADJ(-16): 在内存不足时,这个值越小,存活的几率就越大。SYSTEM_ADJ已经是倒数第二小了,可见系统进程在内存不足时被杀的可能性极小。
    • active: 在上文中,我们介绍过ProcessRecord有Active和InActive两种状态,所谓”激活”,就是将应用程序的信息(IApplicationThread)绑定到进程,这样就能够通过ProcessRecord间接完成对进程的调度。

    AMS通过mPidSelfLocked这个映射表来记录所有的ProcessRecord,(键 => 值)关系是(PID => ProcessRecord)。 在创建一个ProcessRecord之后,就需要集中对进程的信息进行调整了,AMS中管理进程的函数就两类:updateLruProcessLocked()用于更新最近使用进程列表,updateOomAdjLocked()用户更新进程的OOM ADJ。

3.AMS启动的配套工作

在引导服务(BootstrapServices)启动完毕后,SystemServer就开始启动核心服务(CoreServices),包括电池服务(BatteryService),用户行为统计服务(UsageStatsService)等; 最后,就是启动其他服务了,非常之多,不再此列举。部分服务在启动时,仍需要与AMS关联,譬如: AMS需要与WindowManagerService关联。AMS在这个过程之中有两个关键的步骤:

  1. AMS.installSystemProviders(): 表示要装载SettingsProvider, 很多系统服务都需要从这个数据库中读取配置信息;
  2. AMS.systemReady(): 表示当前SystemServer已经启动完毕, AMS仍需要做一些准备就绪工作。

3.1 对于installSystemProviders的分析

关键函数 generateApplicationProvidersLocked()

该函数基于AndroidManifest.xml文件的定义, 生成一个应用程序的Provider信息, 以方便AMS对Provider进行管理,

AMS对Provider的管理方式:

  • AMS中使用ContentProviderRecord来管理一个ContentProvider。ProviderInfo, ApplicationInfo, 运行ContentProvider的ProcessRecord等信息都保存在ContentProviderRecord中。

  • AMS维护了一个ProviderMap, 支持从Authority或CompnentName到ContentProviderRecord的映射; AMS也维护了各种各样的ProcessRecord, 譬如: 前台进程, 内存常驻进程, 最近使用的进程等。 当一个ContentProvider绑定到具体的进程时, 就会添加到ProcessRecord中维护的mPubProviders的映射表中。所以, ProcessRecord.mPubProviders就表示进程所拥有的ContentProvider。

现在,我们再来看这块函数的逻辑:

  • 首先, 需要确保ProcessRecord能够容纳一定数量的Provider, 前面通过PackageManager找到的ProviderInfo可能会关联到ProcessRecord中,所以, 在mPubProvider上已有容量的基础上, 再扩容的大小为找到的ProviderInfo的数量。

  • 然后, 对找到的ProviderInfo列表进行遍历, 如有需要, 则新建一个ContentProviderRecord对象, 将其添加到mProviderMap中以方便AMS管理;同时, 也需要将其添加到PrcoessRecord.mPubProviders中。

  • 最后, 由于可能新增其他APK中的ProviderInfo, 所以需要确保对APK进行dex优化。

关键函数: ActivityThread.installSystemProviders()

将一个ProviderInfo绑定到ProcessRecord后, AMS中就有了Provider的信息了, 但这时Provider还不能工作, 因为真正的ContentProvider还未创建, 该函数的就是将上面找到的ProviderInfo装载到系统进程之中

所有ContentProvider的创建都需要经过installContentProvider()函数, 它接收两个参数, 一个是进程的运行环境Context, 一个是 ProviderInfo列表; 对系统进程而言, 运行环境就是mInitialApplication。该函数中使用了ContentProviderHodler这个类,它实现了Parcelable接口, 通常表示这类数据结构需要跨进程传递, 应用进程中生成的ContentProvider需要向系统进程注册后才能使用, 所以, 需要将ContentProvider的信息从应用进程传递到系统进程, 这就用到了ContentProviderHolder进行数据封装。

基于ProviderInfo生成的ContentProviderHolder的函数实现是installProvider():

3.2对于systemReady的分析

  1. mSystemReady表示系统进程是否准备完毕, 由于systemReady()函数会被多次调用到,而且多线程并行,所以一旦mSystemReady为true,表示不再需要执行下面的逻辑了,直接回调函数入参goingCallback,这个回调函数我们放到后面分析;

  2. 进入这一步,表示mSystemReady为false,第一次进入systemReady()函数时,就是这种场景。这里会恢复最近任务列表;

  3. 涉及到PRE_BOOT_COMPLETED广播的处理。这个广播在BOOT_COMPLETED广播之前发送,而且只发给系统应用。系统应用收到该广播后,也应该标注已经处理过该广播,下次不用再派发过来。设计PRE_BOOT_COMPLETED广播的目的,是为了应对系统升级的场景:当从旧的版本升级时,系统应用可能有一些清除数据的需要,系统升级后的第一次起机时,就会向接收者派发这个广播。

PRE_BOOT_COMPLETED的派发实现在deliverPreBootCompleted()函数中,本文不展开分析。需要知道这里有两个控制变量:

  • mDidUpdate: 默认为false; 如果为true,表示已PRE_BOOT_COMPLETED已经处理完毕,确切的说是已经检查完毕,因为在派发该广播之前,要检查是否已经向接收者派发过一次该广播了; 之所以该变量取名为update,是因为该广播的设计与系统升级后的操作有关;
  • mWaitingUpdate: 默认为false; 如果有PRE_BOOT_COMPLETED的接收者,而且之前没有处理过该广播,则这个变量会被true,直到广播处理完成后才被重新置成false。

经过这3步mSystemReady就被设置为true了,再次调用该systemReady()函数,就会进入第1步的逻辑

在AMS完全准备就绪之前,就可能有一些进程已经启动,在这里需要进行一个检查,如果非peristent的进程先于AMS启动,那么就需要杀掉这些进程。 注意,这里所指的进程是AMS管理的进程(系统进程和应用进程),Native进程并不在AMS的管辖范围。

在该代码片段的最后,输出了一行Event Log:

boot_process_ams_ready: xxx

进行日志分析时,可以根据这行日志信息判定AMS已经准备就绪,其他应用进程可以启动了。因为只有当AMS就绪后,才能开始管理应用进程。

AMS准备就绪是个关键节点,在此之后,很多其他服务就可以开始进入准备就绪的状态了,其实就是调用这些系统服务的systemReady()函数。

systemReady()函数的最后部分,一部分工作是发送通知:“当前用户已经进入使用状态了”; 另一部分工作就是启动桌面,有两处关键调用: startHomeActivityLocked()和mStackSupervisor.resumeTopActivitiesLocked()

至此,systemReady()函数的执行逻辑已经分析完了,一共经历了4个步骤

  • 片段1: 主要处理PRE_BOOT_COMPLETED广播;
  • 片段2: 杀掉在AMS准备就绪之前就已经启动的进程。因为这些进程要被AMS管理起来,需要在AMS准备就绪之后才启动;
  • 片段3: 主要任务是通知其他系统服务进入就绪状态。在AMS就绪完毕后,从SettingsProvider和本地文件中(urigrants.xml)读取一些配置信息。通知其他系统服务进入就绪状态,启动SystemUi;
  • 片段4: 主要任务是启动桌面。

systemReady()函数调用完成之后,桌面就可见了,用户就真正见到了Android系统的可操作界面

3.4 发送ACTION_BOOT_COMPLETED广播

在桌面启动后,桌面进程主线程的消息队列进入空闲状态,此时会发起跨进程调用AMS.activityIdle(),紧接着会引发下面的调用关系:

AMS.activityIdle()
└── ActivityStackSupervisor.activityIdleInternalLocked()
└── ActivityStackSupervisor.checkFinishBootingLocked()
└── AMS.postFinishBooting() // 向主线程抛出FINISH_BOOTING_MSG消息
└── AMS.MainHandler.handleMessage(FINISH_BOOTING_MSG)
└── AMS.finishBooting()
AMS.finishBooting()函数主要进行ABI检查,注册一些系统广播,启动之前被抑制启动的进程,发送ACTION_BOOT_COMPLETED广播,调度性能统计功能。 总结:

系统进程启动过程中与AMS相关的逻辑,总体而言,可以分为三步:

  1. 初始化系统进程的运行环境。所谓运行环境,就是指的Context,Context蕴含了代码执行过程中所需要的信息,包括进程信息、包信息、资源信息等等。Android有意弱化进程的概念,强化Context的概念,在Android编程时,一定避免不了与Context打交道。对于系统进程而言,Context有一定的特殊性,所以单独造了一个SystemContext。

  2. 初始化AMS对象。AMS对象在系统进程构建,作为最重要的系统服务,AMS初始化要做的事情非常多。由于各种系统服务耦合在一块,相互影响,Android设计了“系统进程启动阶段”这个概念,就像一个简单的状态机,只有进入的某个阶段,才能做某些操作。譬如,只有进入PHASE_ACTIVITY_MANAGER_READY,AMS才能正常工作,这时才可以进行派发广播、管理进程等操作。

    因为系统进程也在AMS的管辖范围之内,所以,AMS对象构建后有一个重要的任务,就是设置系统进程的一些属性。这时,会将第一个启动的应用frameworks-res.apk的信息装载到系统进程中,创建一个系统进程的ProcessRecord对象以便AMS管理。

  3. AMS初始化的配套工作。这里所谓配套工作是指,系统要完全运行起来,还需要经由AMS进行一系列的运作:系统设置SettingsProvider会经由AMS装载到系统进程中;其他系统服务在AMS准备就绪后,也会进入就绪状态,表示可以正常工作;桌面会经由AMS启动,最终ACTION_BOOT_COMPLETED广播发出。

参考文章:http://duanqz.github.io/2016-07-15-AMS-LaunchProcess#3-%E6%80%BB%E7%BB%93

ActivityManagerService的启动过程的更多相关文章

  1. Android源码——Activity组件的启动过程

    根Activity启动过程 Launcher启动MainActivity的过程主要分为6个步骤: 一.Launcher向ActivityManagerService发送一个启动MainActivity ...

  2. App的启动过程

    App的启动过程 所有的app都是通过launcher去启动的 launcher自己也是一个app,一个系统级别的app,放在asystem/app/下,使用系统签名. 对代码进行分析

  3. 【凯子哥带你学Framework】Activity启动过程全解析

    It’s right time to learn Android’s Framework ! 前言 学习目标 写作方式 主要对象功能介绍 主要流程介绍 zygote是什么有什么作用 SystemSer ...

  4. Android 启动过程简析

    首先我们先来看android构架图: android系统是构建在linux系统上面的. 所以android设备启动经历3个过程. Boot Loader,Linux Kernel & Andr ...

  5. Android 核心分析 之八Android 启动过程详解

    Android 启动过程详解 Android从Linux系统启动有4个步骤: (1) init进程启动 (2) Native服务启动 (3) System Server,Android服务启动 (4) ...

  6. Android 启动过程总结

    SystemServer的启动 frameworks/base/services/java/com/android/server/SystemServer.java: run() 其中调用Activi ...

  7. Android系统进程Zygote启动过程的源代码分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6768304 在Android系统中,所有的应用 ...

  8. Android系统默认Home应用程序(Launcher)的启动过程源代码分析

    在前面一篇文章中,我们分析了Android系统在启动时安装应用程序的过程,这些应用程序安装好之后,还需要有一个 Home应用程序来负责把它们在桌面上展示出来,在Android系统中,这个默认的Home ...

  9. Android应用程序进程启动过程的源代码分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址: http://blog.csdn.net/luoshengyang/article/details/6747696 Android 应用程序框架层创 ...

随机推荐

  1. AVCaptureSession音频视频采集

    // // AudioVideoCaptureViewController.m // live // // Created by lujunjie on 2016/10/31. // Copyrigh ...

  2. python内存增长问题

    如果你的程序没有调用什么特殊的库, 只是用了很平常的库, 而且使再循环很多的情况下, 那么建议你把循环里的程序拆出来,写成一子函数,循环子函数. 如下面格式: for   (循环) 子函数 这样程序每 ...

  3. MySQL主从复制之Mycat简单配置和高可用

    什么是Mycat 1.Mycat就是MySQL Server,而Mycat后面连接的MySQL Server,就好象是MySQL的存储引擎,如InnoDB,MyISAM等.因此,Mycat本身并不存储 ...

  4. LeetCode_Construct Binary Tree from Preorder and Inorder Traversal

    一.题目 Construct Binary Tree from Preorder and Inorder Traversal My Submissions Given preorder and ino ...

  5. Dcloud课程4 如何进行APP接口开发

    Dcloud课程4 如何进行APP接口开发 一.总结 一句话总结:通过json或者xml. 1.APP如何进行通信? 通过在地址上接参数指明传递的数据的类型.而数据传递的类型一般是XML和json. ...

  6. echarts3.0 仪表盘实例更改完成占用率实例

    需要完成的项目效果 官方实例效果 基本思路: 首先引入jquery和echarts3.0库. 需要两个仪表盘,一个仪表盘是纯色灰色,在底部.startAngle 和endAngle永远是最大值,默认为 ...

  7. 【Codeforces Round #431 (Div. 1) B】

    [链接]h在这里写链接 [题意] 场上有 n 个点,它们分别向上与向右在不同时刻开始运动,相遇则改变移动方向,求最终这些点到达的坐标. [题解] 先把每个点的坐标都往它本该移动的方向相反的方向退ti个 ...

  8. TCP的可靠传输机制(简单好理解:分段与流,滑窗,连接,流量控制,重新发送,堵塞控制)

    TCP的几大模块:分段与流,滑窗,连接,流量控制,重新发送,堵塞控制. 1.checksum:在发送TCP报文的时候,里面的信息可能会因为环境的问题,发送变化,这时,接收信号的时候就需要通过check ...

  9. 使用 JS 关闭警告框及监听自定义事件(amaze ui)

    使用 JS 关闭警告框及监听自定义事件(amaze ui) 一.总结 1.jquery匿名函数:第8行,jquery匿名函数,$(function(){});,有没有很简单,只是少了jquery的前面 ...

  10. UVA 10970 - Big Chocolate 洪水@。@

    先横着切m-1刀,矩形巧克力就变成了1*n (有m个)然后每个都要切n-1下,所以有 m*(n-1) +(m-1)= n*m-1 #include<cstdio> int main() { ...