Android系统的启动主要包括三个阶段:

①BootLoader启动

②Linux Kernel启动

③Android系统启动

前面我们大致分析了前面两个步骤,即u-boot和内核的启动流程(内核启动流程待完善-_-!!),这次就来分析以下Linux内核启动之后是怎样挂载起Android这个“根文件系统”的。Android系统在Linux内核上运行了一系列的系统服务。

和Linux系统类似,Android系统中第一个被启动的进程也是init进程,它的PID为0。Android系统的init进程启动的过程中会解析init.rc文件,以此来构建出系统的初始运行形态。其他Android系统服务,比如装载Android的文件系统、创建系统目录、初始化属性系统、启动Android系统重要的守护进程,这些进程包括USB守护进程,adb守护进程,vold守护进程,rild守护进程、mediaserver和ServiceManager等,大多都是在这个脚本中被相继启动的。

init进程启动后会执行system/core/init/init.c 中的 main 函数,并且在main函数中调用init_parse_config_file函数来解析init.rc脚本。下面就分析以下这个main函数究竟做了那些事。

//  system/core/init/init.c
int main(int argc, char **argv)
{
....
//对umask进行清零
umask();
//为rootfs建立必要的文件夹,并挂载适当的分区
mkdir("/dev", );
mkdir("/proc", );
mkdir("/sys", );
mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
mkdir("/dev/pts", );
mkdir("/dev/socket", );
mount("devpts", "/dev/pts", "devpts", , NULL);
mount("proc", "/proc", "proc", , NULL);
mount("sysfs", "/sys", "sysfs", , NULL);
/* indicate that booting is in progress to background fw loaders, etc */
close(open("/dev/.booting", O_WRONLY | O_CREAT, ));
//调用dup函数把标准输入,输出,错误输出都重定位到/dev/__null__
open_devnull_stdio();
//初始化kernel log,创建/dev/kmsg设备节点
klog_init();
//属性初始化,读取default.prop
property_init();
//获取硬件信息/proc/cpuinfo
get_hardware_name(hardware, &revision);
//获取命linux内核传来命令行信息
process_kernel_cmdline();
union selinux_callback cb;
cb.func_log = log_callback;
selinux_set_callback(SELINUX_CB_LOG, cb);
cb.func_audit = audit_callback;
selinux_set_callback(SELINUX_CB_AUDIT, cb);
selinux_initialize();
/* These directories were necessarily created before initial policy load
* and therefore need their security context restored to the proper value.
* This must happen before /dev is populated by ueventd.
*/
restorecon("/dev");
restorecon("/dev/socket");
restorecon("/dev/__properties__");
restorecon_recursive("/sys");
is_charger = !strcmp(bootmode, "charger");
INFO("property init\n");
property_load_boot_defaults();
INFO("reading config file\n");
///解析init.rc脚本
init_parse_config_file("/init.rc");
//执行rc文件中触发器为 on early-init的语句
action_for_each_trigger("early-init", action_add_queue_tail);
/*
解析完init.rc配置文件后,会得到一系列的Action,action_for_each_trigger函数用来执行early-init阶段的Action。
init将动作执行的时间划分为4个阶段:early-init、init、early-boot、boot。由于有些动作必须要在其他动作完成后才能执行,
所以就有了先后之分。哪些动作属于哪个阶段由配置文件决定。
*/
queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
queue_builtin_action(keychord_init_action, "keychord_init");
queue_builtin_action(console_init_action, "console_init");
// 执行init阶段的动作
/* execute all the boot actions to get us started */
action_for_each_trigger("init", action_add_queue_tail);
/* Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
* wasn't ready immediately after wait_for_coldboot_done
*/
queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
queue_builtin_action(property_service_init_action, "property_service_init");
queue_builtin_action(signal_init_action, "signal_init");
/* Don't mount filesystems or start core system services if in charger mode. */
if (is_charger) {
action_for_each_trigger("charger", action_add_queue_tail);
} else {
action_for_each_trigger("late-init", action_add_queue_tail);
}
/* run all property triggers based on current state of the properties */
queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
#if BOOTCHART
queue_builtin_action(bootchart_init_action, "bootchart_init");
#endif
// 无限循环,用来处理各种消息
for(;;) {
int nr, i, timeout = -;
execute_one_command();
// 重启那些已经死去的进程,启动所有init脚本中声明的service
restart_processes();
// 用来监听属性设置服务的事件
if (!property_set_fd_init && get_property_set_fd() > ) {
ufds[fd_count].fd = get_property_set_fd();
ufds[fd_count].events = POLLIN;
ufds[fd_count].revents = ;
fd_count++;
property_set_fd_init = ;
}
if (!signal_fd_init && get_signal_fd() > ) {
ufds[fd_count].fd = get_signal_fd();
ufds[fd_count].events = POLLIN;
ufds[fd_count].revents = ;
fd_count++;
signal_fd_init = ;
}
if (!keychord_fd_init && get_keychord_fd() > ) {
ufds[fd_count].fd = get_keychord_fd();
ufds[fd_count].events = POLLIN;
ufds[fd_count].revents = ;
fd_count++;
keychord_fd_init = ;
}
if (process_needs_restart) {
timeout = (process_needs_restart - gettime()) * ;
if (timeout < )
timeout = ;
}
if (!action_queue_empty() || cur_action)
timeout = ;
#if BOOTCHART
if (bootchart_count > ) {
if (timeout < || timeout > BOOTCHART_POLLING_MS)
timeout = BOOTCHART_POLLING_MS;
if (bootchart_step() < || --bootchart_count == ) {
bootchart_finish();
bootchart_count = ;
}
}
#endif
nr = poll(ufds, fd_count, timeout);
if (nr <= )
continue;
// 处理具体的消息
for (i = ; i < fd_count; i++) {
if (ufds[i].revents & POLLIN) {
if (ufds[i].fd == get_property_set_fd())
handle_property_set_fd();
else if (ufds[i].fd == get_keychord_fd())
handle_keychord();
else if (ufds[i].fd == get_signal_fd())
handle_signal();
}
}
}
return ;
}

在 restart_processes 函数中回去解析init.rc中的Service标号,启动Service

//system/core/init/init.c
static void restart_processes()
{
process_needs_restart = ;
//调用service_for_each_flags
service_for_each_flags(SVC_RESTARTING,
restart_service_if_needed);
}

在restart_processes调用了 service_for_each_flags

// system/core/init/init_parser.c
void service_for_each_flags(unsigned matchflags,
void (*func)(struct service *svc))
{
struct listnode *node;
struct service *svc;
//遍历service_list链表里的每一个节点
list_for_each(node, &service_list) {
svc = node_to_item(node, struct service, slist);
if (svc->flags & matchflags) {
//调用传进来的func函数处理svc
func(svc);
}
}
}

所以最终我们是调用了 restart_service_if_needed 来处理 SVC_RESTARTING链表里的每一个service节点。看一下这个restart_service_if_needed函数

static void restart_service_if_needed(struct service *svc)
{
time_t next_start_time = svc->time_started + ;
if (next_start_time <= gettime()) {
svc->flags &= (~SVC_RESTARTING);
//启动service
service_start(svc, NULL);
return;
}
if ((next_start_time < process_needs_restart) ||
(process_needs_restart == )) {
process_needs_restart = next_start_time;
}
}

接着分析一下这个service_start函数

//   system/core/init/init.c
void service_start(struct service *svc, const char *dynamic_args)
{
....
NOTICE("starting '%s'\n", svc->name);
//创建一个子线程,启动service
pid = fork();
....
}

init.rc语法

以行尾单位,以空格间隔的语法,以#开始代表注释行。rc文件主要包含Action、Service、Command、Options,其中对于Action和Service的名称都是唯一的,对于重复的命名视为无效。在android 5.0.2源码system/core/init/readme.txt中有init.rc语法的帮助文档。

①Action(动作)

Action: 通过trigger,即以 on开头的语句,决定何时执行相应的service。

on early-init; 在初始化早期阶段触发;

on init; 在初始化阶段触发;

on late-init; 在初始化晚期阶段触发;

on boot/charger: 当系统启动/充电时触发,还包含其他情况,此处不一一列举;

on property:<key>=<value>: 当属性值满足条件时触发;

②Commands(命令)

下面列举常用的命令

class_start <service_class_name>: 启动属于同一个class的所有服务;

start <service_name>: 启动指定的服务,若已启动则跳过;

stop <service_name>: 停止正在运行的服务;

setprop <name> <value>:设置属性值;

mkdir <path>:创建指定目录;

symlink <target> <sym_link>: 创建连接到<target>的<sym_link>符号链接;

write <path> <string>: 向文件path中写入字符串;

exec: fork并执行,会阻塞init进程直到程序完毕;

exprot <name> <name>:设定环境变量;

loglevel <level>:设置log级别;

③Service(服务)

服务Service,以 service开头,由init进程启动,一般运行于另外一个init的子进程,所以启动service前需要判断对应的可执行文件是否存在。init生成的子进程,定义在rc文件,其中每一个service,在启动时会通过fork方式生成子进程。

例如: service servicemanager /system/bin/servicemanager代表的是服务名为servicemanager,服务的路径,也就是服务执行操作时运行/system/bin/servicemanager。

④Options(选项)

Options是Services的可选项,与service配合使用

disabled: 不随class自动启动,只有根据service名才启动;

oneshot: service退出后不再重启;

user/group: 设置执行服务的用户/用户组,默认都是root;

class:设置所属的类名,当所属类启动/退出时,服务也启动/停止,默认为default;

onrestart:当服务重启时执行相应命令;

socket: 创建名为/dev/socket/<name>的socket

critical: 在规定时间内该service不断重启,则系统会重启并进入恢复模式

default: 意味着disabled=false,oneshot=false,critical=false。

所有的Service里面只有servicemanager ,zygote ,surfaceflinger这3个service有onrestart关键字来触发其他service启动过程。

系统启动流程

本地系统服务:在init.rc初始化一个server入口,然后通过这个server去启动其他service,比如mediaserver

Java系统服务启动:

 1):init启动service manager,这个进程主要负责系统服务的注册管理,包括“java系统服务”“本地系统服务”

 2):init启动Media server,这个进程负责启动C/C++的“本地系统服务”。

 3):init启动Zygote,这个进程启动System server进程,这个进程启动"Java系统服务"---[包括power manager    service,sensor service]

 4):另外init启动system/bin下面的各种守护进程

首先看一下init.rc里面是如何启动这些系统服务的

## Daemon processes to be run by init.
##
service ueventd /sbin/ueventd
class core
critical
seclabel u:r:ueventd:s0
service logd /system/bin/logd
class core
socket logd stream logd logd
socket logdr seqpacket logd logd
socket logdw dgram logd logd
seclabel u:r:logd:s0
service healthd /sbin/healthd
class core
critical
seclabel u:r:healthd:s0
service console /system/bin/sh
class core
console
disabled
user shell
group shell log
seclabel u:r:shell:s0
on property:ro.debuggable=
start console
# adbd is controlled via property triggers in init.<platform>.usb.rc
service adbd /sbin/adbd --root_seclabel=u:r:su:s0
class core
socket adbd stream system system
disabled
seclabel u:r:adbd:s0
# adbd on at boot in emulator
on property:ro.kernel.qemu=
start adbd
service lmkd /system/bin/lmkd
class core
critical
socket lmkd seqpacket system system
service servicemanager /system/bin/servicemanager
class core
user system
group system
critical
onrestart restart healthd
onrestart restart zygote
onrestart restart media
onrestart restart surfaceflinger
onrestart restart drm
service vold /system/bin/vold
class core
socket vold stream root mount
ioprio be
service netd /system/bin/netd
class main
socket netd stream root system
socket dnsproxyd stream root inet
socket mdns stream root system
socket fwmarkd stream root inet
service debuggerd /system/bin/debuggerd
class main
# for ZTE 3G
service ril-daemon /system/bin/rild -l /system/lib/libzte-ril.so -- -d /dev/ttyUSB1 -u /dev/ttyUSB3
class main
socket rild stream root radio
socket rild-debug stream radio system
user root
group radio cache inet misc audio sdcard_r sdcard_rw log net_admin net_raw
on property:ril.reset.rild=
stop ril-daemon
start ril-daemon
setprop ril.reset.rild
service pppd_gprs /system/etc/ppp/init.gprs-pppd
user root
group radio cache inet misc log net_admin net_raw
disabled
oneshot
service surfaceflinger /system/bin/surfaceflinger
class core
user system
group graphics drmrpc
#重启时会触发zygote重启
onrestart restart zygote
service drm /system/bin/drmserver
class main
user drm
group drm system inet drmrpc
service media /system/bin/mediaserver
class main
user media
group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm
ioprio rt
# One shot invocation to deal with encrypted volume.
service defaultcrypto /system/bin/vdc --wait cryptfs mountdefaultencrypted
disabled
oneshot
# vold will set vold.decrypt to trigger_restart_framework (default
# encryption) or trigger_restart_min_framework (other encryption)
# One shot invocation to encrypt unencrypted volumes
service encrypt /system/bin/vdc --wait cryptfs enablecrypto inplace default
disabled
oneshot
# vold will set vold.decrypt to trigger_restart_framework (default
# encryption)
service bootanim /system/bin/bootanimation
class core
user graphics
group graphics audio
disabled
oneshot
service installd /system/bin/installd
class main
socket installd stream system system
service flash_recovery /system/bin/install-recovery.sh
class main
seclabel u:r:install_recovery:s0
oneshot
service racoon /system/bin/racoon
class main
socket racoon stream system system
# IKE uses UDP port . Racoon will setuid to vpn after binding the port.
group vpn net_admin inet
disabled
oneshot
service mtpd /system/bin/mtpd
class main
socket mtpd stream system system
user vpn
group vpn net_admin inet net_raw
disabled
oneshot
service keystore /system/bin/keystore /data/misc/keystore
class main
user keystore
group keystore drmrpc
service dumpstate /system/bin/dumpstate -s
class main
socket dumpstate stream shell log
disabled
oneshot
service mdnsd /system/bin/mdnsd
class main
user mdnsr
group inet net_raw
socket mdnsd stream mdnsr inet
disabled
oneshot
service pre-recovery /system/bin/uncrypt
class main
disabled
oneshot

init进程通过init.rc脚本启动servicemanager

service servicemanager /system/bin/servicemanager
class core
user system
group system
critical
#重启时会触发下列进程重启
onrestart restart healthd
onrestart restart zygote
onrestart restart media
onrestart restart surfaceflinger
onrestart restart drm

个servicemanager是Android系统Binder进程间通信的重要的守护进程

这个先放一下,之后学习Binder系统的时候再深入分析。

init进程又启动了zygote进程

init.rc脚本在开是引入了zygote的启动脚本

import /init.${ro.zygote}.rc
#system/core/rootdir/init.zygote32.rc
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
socket zygote stream root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd

zygote进程是java环境的入口

// frameworks/base/cmds/app_process/app_main.cpp
int main(int argc, char* const argv[])
{
....
if (!niceName.isEmpty()) {
runtime.setArgv0(niceName.string());
set_process_name(niceName.string());
}
if (zygote) {
//启动zygote进程
runtime.start("com.android.internal.os.ZygoteInit", args);
} else if (className) {
runtime.start("com.android.internal.os.RuntimeInit", args);
} else {
fprintf(stderr, "Error: no class name or --zygote supplied.\n");
app_usage();
LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
return ;
}
}

看一下系统是怎样进入java环境的

// frameworks/base/core/jni/AndroidRuntime.cpp
void AndroidRuntime::start(const char* className, const Vector<String8>& options)
{
ALOGD("\n>>>>>> AndroidRuntime START %s <<<<<<\n",
....
//启动systemserver
static const String8 startSystemServer("start-system-server");
....
//启动java进程
char* slashClassName = toSlashClassName(className);
jclass startClass = env->FindClass(slashClassName);
if (startClass == NULL) {
ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
/* keep going */
} else {
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
if (startMeth == NULL) {
ALOGE("JavaVM unable to find main() in '%s'\n", className);
/* keep going */
} else {
env->CallStaticVoidMethod(startClass, startMeth, strArray);
....
}

接着就是启动systemserver了,它是由zygote启动的

// frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static void main(String argv[]) {
....
registerZygoteSocket(socketName);//注册socket
....
if (startSystemServer) {
//启动SystemServer
startSystemServer(abiList, socketName);
}
....
}

systemserver启动了一票的Java层服务

//frameworks/base/services/java/com/android/server/SystemServer.java
public static void main(String[] args) {
//new一个SystemServer对象并调用其run函数
new SystemServer().run();
}
private void run() {
....
// Here we go!
Slog.i(TAG, "Entered the Android system server!");
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_SYSTEM_RUN, SystemClock.uptimeMillis());
....
// Initialize native services.
//加载本地库并初始化
System.loadLibrary("android_servers");
nativeInit();
....
// Start services.
//启动一票的Java层系统服务
try {
startBootstrapServices();
startCoreServices();
startOtherServices();
} catch (Throwable ex) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting system services", ex);
throw ex;
}

下面看一下这些系统服务的启动过程,注释很详细

 private void startBootstrapServices() {
// Wait for installd to finish starting up so that it has a chance to
// create critical directories such as /data/user with the appropriate
// permissions. We need this to complete before we initialize other services.
mInstaller = mSystemServiceManager.startService(Installer.class);
// Activity manager runs the show.
mActivityManagerService = mSystemServiceManager.startService(
ActivityManagerService.Lifecycle.class).getService();
mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
// Power manager needs to be started early because other services need it.
// Native daemons may be watching for it to be registered so it must be ready
// to handle incoming binder calls immediately (including being able to verify
// the permissions for those calls).
mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);
// Now that the power manager has been started, let the activity manager
// initialize power management features.
mActivityManagerService.initPowerManagement();
// Display manager is needed to provide display metrics before package manager
// starts up.
mDisplayManagerService = mSystemServiceManager.startService(DisplayManagerService.class);
// We need the default display before we can initialize the package manager.
mSystemServiceManager.startBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);
// Only run "core" apps if we're encrypting the device.
String cryptState = SystemProperties.get("vold.decrypt");
if (ENCRYPTING_STATE.equals(cryptState)) {
Slog.w(TAG, "Detected encryption in progress - only parsing core apps");
mOnlyCore = true;
} else if (ENCRYPTED_STATE.equals(cryptState)) {
Slog.w(TAG, "Device encrypted - only parsing core apps");
mOnlyCore = true;
}
// Start the package manager.
Slog.i(TAG, "Package Manager");
mPackageManagerService = PackageManagerService.main(mSystemContext, mInstaller,
mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
mFirstBoot = mPackageManagerService.isFirstBoot();
mPackageManager = mSystemContext.getPackageManager();
Slog.i(TAG, "User Service");
ServiceManager.addService(Context.USER_SERVICE, UserManagerService.getInstance());
// Initialize attribute cache used to cache resources from packages.
AttributeCache.init(mSystemContext);
// Set up the Application instance for the system process and get started.
mActivityManagerService.setSystemProcess();
}
private void startCoreServices() {
// Manages LEDs and display backlight.
mSystemServiceManager.startService(LightsService.class);
// Tracks the battery level. Requires LightService.
mSystemServiceManager.startService(BatteryService.class);
// Tracks application usage stats.
mSystemServiceManager.startService(UsageStatsService.class);
mActivityManagerService.setUsageStatsManager(
LocalServices.getService(UsageStatsManagerInternal.class));
// Tracks whether the updatable WebView is in a ready state and watches for update installs.
mSystemServiceManager.startService(WebViewUpdateService.class);
}
private void startOtherServices() {
....
Slog.i(TAG, "Reading configuration...");
SystemConfig.getInstance();
Slog.i(TAG, "Scheduling Policy");
ServiceManager.addService("scheduling_policy", new SchedulingPolicyService());
Slog.i(TAG, "Telephony Registry");
telephonyRegistry = new TelephonyRegistry(context);
ServiceManager.addService("telephony.registry", telephonyRegistry);
Slog.i(TAG, "Entropy Mixer");
ServiceManager.addService("entropy", new EntropyMixer(context));
Slog.i(TAG, "Account Manager");
accountManager = new AccountManagerService(context);
ServiceManager.addService(Context.ACCOUNT_SERVICE, accountManager);
//下面还有很多,具体用到哪些东西的时候再回过头来分析
....

再往下就是开机进入锁屏界面了,这部分内容还不熟悉,之后再分析。。。。。-_-!!!

多谢下面两位前辈的无私分享

参考文章:

http://gityuan.com/

http://blog.csdn.net/hovan/article/details/9896751

Tiny4412 Android 启动流程的更多相关文章

  1. Android 启动流程分析

    原文:https://www.jianshu.com/p/a5532ecc8377 作者曾经在高通的Android性能组工作,主要工作是优化Android Application的启动时间. APP基 ...

  2. Android启动流程

    Android是一个基于Linux的开源操作系统.x86(x86是一系列的基于intel 8086 CPU的计算机微处理器指令集架构)是linux内核部署最常见的系统.然而,所有的Android设备都 ...

  3. android 启动流程 相关 杂项记录

    Android原生流程 Init进程 主要流程及分支梳理 ueventd_main()watchdogd_main()主要流程a) 公共部分 增加PATH 环境变量初始化内核日志,打开/dev/kms ...

  4. 深入浅出 - Android系统移植与平台开发(四)- Android启动流程

    作者:唐老师,华清远见嵌入式学院讲师. 一.Android init进程启动 还是从Linux的启动开始吧.Linux被bootloader加载到了内存之后,开始运行,在初始化完 Linux运行环境之 ...

  5. android 启动流程

    韩梦飞沙  韩亚飞  313134555@qq.com  yue31313  han_meng_fei_sha adb shell 后  用 ps 命令 回车 可以看到 运行的进程. 如下结果: ct ...

  6. android 启动流程 相关2 init进程 属性服务

    Init属性服务 系统属性服务 属性梳理 来源和读取时机 来源:内核参数 ro.kernel.*   代表有qemu内核参数才会设置(在虚拟机中) ro.boot.*     1.内核设备树相关的设备 ...

  7. 【转】android SystemUI 流程分析

    android4 SystemUI 流程分析 什么是SystemUI? 对于Phone来说SystemUI指的是:StatusBar(状态栏).NavigationBar(导航栏).而对于Tablet ...

  8. Android进阶系列之源码分析Activity的启动流程

    美女镇楼,辟邪! 源码,是一个程序猿前进路上一个大的而又不得不去翻越障碍,我讨厌源码,看着一大堆.5000多行,要看完得啥时候去了啊.不过做安卓的总有这一天,自从踏上这条不归路,我就认命了.好吧,我慢 ...

  9. Android FM模块学习之一 FM启动流程

    最近在学习FM模块,FM是一个值得学习的模块,可以从上层看到底层. 上层就是FM的按扭操作和界面显示,从而调用到FM底层驱动来实现广播收听的功能. FM启动流程:如下图: 先进入FMRadio.jav ...

随机推荐

  1. hdu 5876 Sparse Graph 无权图bfs求最短路

    Sparse Graph Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others) P ...

  2. Lightoj 1370 素数打表 +二分

    1370 - Bi-shoe and Phi-shoe   PDF (English) Statistics   Time Limit: 2 second(s) Memory Limit: 32 MB ...

  3. 分享知识-快乐自己:Java中的经典算法之冒泡排序(Bubble Sort)

    原理:比较两个相邻的元素,将值大的元素交换至右端. 思路:依次比较相邻的两个数,将小数放在前面,大数放在后面.即在第一趟:首先比较第1个和第2个数,将小数放前,大数放后.然后比较第2个数和第3个数,将 ...

  4. 解决:创建Android模拟器时提示“No system images installed for target”

    今天在Eclipse上创建安卓模拟器,但发现CPU/ABI一项显示为“No system images installed for target”: 在网上搜索答案,在叶超Luka的博客中找到了答案, ...

  5. 1D/1D优化dp之利用决策点的凸性优化

    关于dp的优化之前做过一些简单的利用优先队列或者单调队列维护一个值就ok了,但有时候给出的方程很难直接用单调队列维护,需要转化一下思路. 这种优化方式利用数形结合,根据比较斜率来抛去一些非最优解,能将 ...

  6. Android中如何禁止音量调节至静音

    Android中音量按键在调低音量时,如果一直按住Down按钮不放,则系统将音量跳到最小后,又自动调节到静音状态.这个机制和iPhone是不同的,iPhone中无论你怎么按Volume-按钮,只能调到 ...

  7. SEM

    SEM是Search Engine Marketing的缩写,中文意思是搜索引擎营销.SEM是一种新的网络营销形式.SEM所做的就是全面而有效的利用搜索引擎来进行网络营销和推广.SEM追求最高的性价比 ...

  8. win32下开发hadoop

    转载自:http://my.oschina.net/muou/blog/408543[木偶:Windows下使用Hadoop2.6.0-ecli­p­s­e­-­p­­lugin插件] 对于一些细节地 ...

  9. 熟练使用IDT

    一些很基础的操作,现在基本都了然于胸了.主要还是多练习.多去写,多去看别人的.后来才能更加的熟悉.工具这东西,越是经常使用,越是得心应手.虽然现在还是很菜.好歹也会了不少了.勤能补拙,不要太看低别人, ...

  10. Element 'beans' cannot have character [children], because the type's content type is element-only

    这个小问题快搞死我了,找了大半个小时. Element 'beans' cannot have character [children], because the type's content typ ...