下面是简单的流程图,从Java到kernel层。

ShutdownThread.java文件

stop playing music,因为后面可能要playing shutdown music.

代码如下:(我在Android6.0上沒有看到調用requestAudioFocus的代碼)

    private static void beginShutdownSequence(Context context) {
....
//acquire audio focus to make the other apps to stop playing muisc
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
mAudioManager.requestAudioFocus(null,
AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);

show system dialog to indicate phone is shutting down,如果没有关机动画的话,要show一个关机提示出来。

代码如下:

 1     if (!checkAnimationFileExist()) {
2 // throw up an indeterminate system dialog to indicate radio is
3 // shutting down.
4 ProgressDialog pd = new ProgressDialog(context);
5 pd.setTitle(context.getText(com.android.internal.R.string.power_off));
6 pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
7 pd.setIndeterminate(true);
8 pd.setCancelable(false);
9 pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
10
11 pd.show();
12 }

Hold the wakelock,make sure we never fall asleep again,抓锁防止机器关机过程中休眠

代码如下:

1     sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(
2 PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu");//这个只是锁住cpu不进入休眠,但screen是off的,需full锁来保证screen常亮
3 sInstance.mCpuWakeLock.setReferenceCounted(false);
4 sInstance.mCpuWakeLock.acquire();

make sure the screen stays on,再抓一个full锁,防止屏幕半暗

代码如下:

1     sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
2 PowerManager.FULL_WAKE_LOCK, TAG + "-screen");//保持srceen常亮
3 sInstance.mScreenWakeLock.setReferenceCounted(false);
4 sInstance.mScreenWakeLock.acquire();

sending shutdown broadcast,发出广播,通知各app该保存数据赶紧的,我要关机了

代码如下:

1     Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
2 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
3 mContext.sendOrderedBroadcastAsUser(intent,//发广播
4 UserHandle.ALL, null, br, mHandler, 0, null, null);

shutdown activity manager,关闭activity manager,即关闭AppOpsService,UsageStatsService,BatteryStatsService

注意:Android L 与KK在关闭UsageStatsService上有所区别

代码如下:

[ActivityManagerService.java]

1     final IActivityManager am =
2 ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
3 if (am != null) {
4 try {
5 am.shutdown(MAX_BROADCAST_TIME);
6 } catch (RemoteException e) {
7 }
8 }

shutdown package manager,保存app使用时间到 disk里,这是android L新增的功能。

代码如下:

[PackageManagerService.java]

1     final PackageManagerService pm = (PackageManagerService)
2 ServiceManager.getService("package");
3 if (pm != null) {
4 pm.shutdown();
5 }

show shutdown animation,播放关机动画了

代码如下:

 1     private static void showShutdownAnimation() {
2 /*
3 * When boot completed, "service.bootanim.exit" property is set to 1.
4 * Bootanimation checks this property to stop showing the boot animation.
5 * Since we use the same code for shutdown animation, we
6 * need to reset this property to 0. If this is not set to 0 then shutdown
7 * will stop and exit after displaying the first frame of the animation
8 */
9 SystemProperties.set("service.bootanim.exit", "0");
10
11 SystemProperties.set("ctl.start", "bootanim");//也是用bootanim进程,跟开关动画一样的方式。
12 }

shutdown radio[NFC,BT,MODEM],注意这里关闭modem这块与andorid KK的不一样。

代码如下:

shutdownRadios(MAX_RADIO_WAIT_TIME);

shutdown MountService,特别这里会导致关机失败。

代码如下:

 1     // Set initial variables and time out time.
2 mActionDone = false;
3 final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME;
4 synchronized (mActionDoneSync) {
5 try {
6 final IMountService mount = IMountService.Stub.asInterface(
7 ServiceManager.checkService("mount"));
8 if (mount != null) {
9 mount.shutdown(observer);
10 } else {
11 Log.w(TAG, "MountService unavailable for shutdown");
12 }
13 } catch (Exception e) {
14 Log.e(TAG, "Exception during MountService shutdown", e);
15 }
16 while (!mActionDone) {
17 long delay = endShutTime - SystemClock.elapsedRealtime();
18 if (delay <= 0) {
19 Log.w(TAG, "Shutdown wait timed out");
20 break;
21 }
22 try {
23 mActionDoneSync.wait(delay);
24 } catch (InterruptedException e) {
25 }
26 }
27 }

走完上层关机流程,下面就要执行关机动作了。

代码如下:

 1     public static void rebootOrShutdown(boolean reboot, String reason) {
2 deviceRebootOrShutdown(reboot, reason);
3 if (reboot) {
4 Log.i(TAG, "Rebooting, reason: " + reason);
5 PowerManagerService.lowLevelReboot(reason);//重启, 其中reason字符串可以爲空、“bootloader”、“recovery”, 在手機下次啓動中LK會根據這些值使手機進入不同的模式
6 Log.e(TAG, "Reboot failed, will attempt shutdown instead");
7 } else if (SHUTDOWN_VIBRATE_MS > 0) {
8 // vibrate before shutting down
9 Vibrator vibrator = new SystemVibrator();
10 try {
11 vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES);
12 } catch (Exception e) {
13 // Failure to vibrate shouldn't interrupt shutdown. Just log it.
14 Log.w(TAG, "Failed to vibrate during shutdown.", e);
15 }
16
17 // vibrator is asynchronous so we need to wait to avoid shutting down too soon.
18 try {
19 Thread.sleep(SHUTDOWN_VIBRATE_MS);
20 } catch (InterruptedException unused) {
21 }
22 }
23
24 // Shutdown power
25 Log.i(TAG, "Performing low-level shutdown...");
26 PowerManagerService.lowLevelShutdown();//关机
27 }

从代码上看始终会走到lowLevelShutdown(),但如果是重启就不会,lowLevelReboot()就停止了。

lowLevelShutdown()与lowLevelReboot()都在PowerManagerService.java实现,其实都只是设置一个属性:SystemProperties.set("sys.powerctl", "xxx");

正是这个动作触发关机流程往下走,这涉及到init进程的4大功能,请参考我的另一篇文章Android的init进程

sys.powerctl属性触发开关在init.rc定义

    on property:sys.powerctl=*
powerctl ${sys.powerctl}

我们来解读这句话,on property:sys.powerctl=*表示当属性sys.powerctl设置为任何值是都会跑到这里,触发动作是powerctl ${sys.powerctl},这个动作的意思是调用powerctl指令,并把sys.powerctl的值传给它。powerctl指令在init 进程会执行。

从下面的表可知,powerctl对应的操作是do_powerctl

[system/core/init/keywords.h]

KEYWORD(powerctl,    COMMAND, 1, do_powerctl)  

do_powerctl的实现

代码如下:

[system/core/init/builtins.c]

 1 int do_powerctl(int nargs, char **args)
2 {
3 char command[PROP_VALUE_MAX];
4 int res;
5 int len = 0;
6 int cmd = 0;
7 const char *reboot_target;
8
9 res = expand_props(command, args[1], sizeof(command)); //args中存放的是: shutdown 或者 reboot 或者 reboot,bootloader 或者 reboot,recovery
10 if (res) {
11 ERROR("powerctl: cannot expand '%s'\n", args[1]);
12 return -EINVAL;
13 }
14
15 if (strncmp(command, "shutdown", 8) == 0) {
16 cmd = ANDROID_RB_POWEROFF;
17 len = 8;
18 } else if (strncmp(command, "reboot", 6) == 0) {
19 cmd = ANDROID_RB_RESTART2;
20 len = 6;
21 } else {
22 ERROR("powerctl: unrecognized command '%s'\n", command);
23 return -EINVAL;
24 }
25
26 if (command[len] == ',') {
27 char prop_value[PROP_VALUE_MAX] = {0};
28 reboot_target = &command[len + 1]; // 存放reboot的reason,也就是下次手機重啓將要進入的模式
29
30 if ((property_get("init.svc.recovery", prop_value) == 0) &&
31 (strncmp(reboot_target, "keys", 4) == 0)) {
32 ERROR("powerctl: permission denied\n");
33 return -EINVAL;
34 }
35 } else if (command[len] == '\0') { // 如果reason爲空,對於reboot來說,下次手機會正常啓機
36 reboot_target = "";
37 } else {
38 ERROR("powerctl: unrecognized reboot target '%s'\n", &command[len]);
39 return -EINVAL;
40 }
41
42 return android_reboot(cmd, 0, reboot_target);
43 }

它调用android_reboot()函数,实现如下:

[system/core/libcutils/android_reboot.c]

 1 int android_reboot(int cmd, int flags UNUSED, const char *arg)
2 {
3 int ret;
4
5 sync();
6 remount_ro();
7
8 switch (cmd) {
9 case ANDROID_RB_RESTART:
10 ret = reboot(RB_AUTOBOOT);
11 break;
12
13 case ANDROID_RB_POWEROFF:
14 ret = reboot(RB_POWER_OFF);
15 break;
16
17 case ANDROID_RB_RESTART2: // arg中存放的是reboot的reason,如bootloader、recovery
18 ret = syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
19 LINUX_REBOOT_CMD_RESTART2, arg);
20 break;
21
22 default:
23 ret = -1;
24 }
25
26 return ret;
27 }

从这里看出它的主要工作:

sync() 回写block设备的内容,这是阻塞型操作。

remount_ro() 把block设备remount成ro,这里有个关键LOG:SysRq : Emergency Remount R/O,这是在logkit所能看到的最后一句LOG,因为remount成ro了,后面的LOG要通过last kmsg技术导出来。

reboot()或者syscall(__NR_reboot....,这点与android KK不同,这边直接用syscall功能,KK则通过汇编。

后面syscall(__NR_reboot...知道,直接调用了linux的__NR_reboot系统调用,这个系统调用会跑哪里?后面会讲。

reboot()这个函数实现如下:

[bionic/libc/bionic/reboot.cpp]

1     int reboot(int mode) {
2 return __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, mode, NULL);
3 }

调用了__reboot,它在汇编实现 如下:

[bionic/libc/arch-arm/syscalls/__reboot.S]

 1     ENTRY(__reboot)
2 mov ip, r7
3 ldr r7, =__NR_reboot //也跑到__NR_reboot系统调用
4 swi #0
5 mov r7, ip
6 cmn r0, #(MAX_ERRNO + 1)
7 bxls lr
8 neg r0, r0
9 b __set_errno_internal
10 END(__reboot)

__NR_reboot对应的内核入口在哪里?

如下:

[bionic/libc/kernel/uapi/asm-generic/unistd.h]

    #define __NR_reboot 142  

它在内核入口如下:

注:bionic/libc/kernel/uapi/asm-generic/unistd.h与kernel/include/uapi/asm-generic/unistd.h是对应的,方便以后代码追踪

[kernel/include/uapi/asm-generic/unistd.h]

1     #define __NR_reboot 142
2 __SYSCALL(__NR_reboot, sys_reboot)

__NR_reboot 映射到 sys_reboot

grep 下sys_reboot 找不到,其实在这里

用SYSCALL_DEFINE定义

[kernel/kernel/reboot.c]

 1 /*
2 * Reboot system call: for obvious reasons only root may call it,
3 * and even root needs to set up some magic numbers in the registers
4 * so that some mistake won't make this reboot the whole machine.
5 * You can also set the meaning of the ctrl-alt-del-key here.
6 *
7 * reboot doesn't sync: do that yourself before calling this.
8 */
9 SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
10 void __user *, arg)
11 {
12 struct pid_namespace *pid_ns = task_active_pid_ns(current);
13 char buffer[256];
14 int ret = 0;
15
16 /* We only trust the superuser with rebooting the system. */
17 if (!ns_capable(pid_ns->user_ns, CAP_SYS_BOOT))
18 return -EPERM;
19
20 /* For safety, we require "magic" arguments. */
21 if (magic1 != LINUX_REBOOT_MAGIC1 ||
22 (magic2 != LINUX_REBOOT_MAGIC2 &&
23 magic2 != LINUX_REBOOT_MAGIC2A &&
24 magic2 != LINUX_REBOOT_MAGIC2B &&
25 magic2 != LINUX_REBOOT_MAGIC2C))
26 return -EINVAL;
27
28 /*
29 * If pid namespaces are enabled and the current task is in a child
30 * pid_namespace, the command is handled by reboot_pid_ns() which will
31 * call do_exit().
32 */
33 ret = reboot_pid_ns(pid_ns, cmd);
34 if (ret)
35 return ret;
36
37 /* Instead of trying to make the power_off code look like
38 * halt when pm_power_off is not set do it the easy way.
39 */
40 if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)
41 cmd = LINUX_REBOOT_CMD_HALT;
42
43 mutex_lock(&reboot_mutex);
44 switch (cmd) {
45 case LINUX_REBOOT_CMD_RESTART:
46 kernel_restart(NULL);
47 break;
48
49 case LINUX_REBOOT_CMD_CAD_ON:
50 C_A_D = 1;
51 break;
52
53 case LINUX_REBOOT_CMD_CAD_OFF:
54 C_A_D = 0;
55 break;
56
57 case LINUX_REBOOT_CMD_HALT:
58 kernel_halt();
59 do_exit(0);
60 panic("cannot halt");
61
62 case LINUX_REBOOT_CMD_POWER_OFF:
63 kernel_power_off();
64 do_exit(0);
65 break;
66
67 case LINUX_REBOOT_CMD_RESTART2:
68 ret = strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1);
69 if (ret < 0) {
70 ret = -EFAULT;
71 break;
72 }
73 buffer[sizeof(buffer) - 1] = '\0';
74
75 kernel_restart(buffer);
76 break;
77
78 #ifdef CONFIG_KEXEC
79 case LINUX_REBOOT_CMD_KEXEC:
80 ret = kernel_kexec();
81 break;
82 #endif
83
84 #ifdef CONFIG_HIBERNATION
85 case LINUX_REBOOT_CMD_SW_SUSPEND:
86 ret = hibernate();
87 break;
88 #endif
89
90 default:
91 ret = -EINVAL;
92 break;
93 }
94 mutex_unlock(&reboot_mutex);
95 return ret;
96 }

有很多分支,我们只关心kernel_power_off()和kernel_restart()两函数就行

如下:

kernel_power_off:

 1     void kernel_power_off(void)
2 {
3 kernel_shutdown_prepare(SYSTEM_POWER_OFF);//关闭外设
4 if (pm_power_off_prepare)
5 pm_power_off_prepare();
6 migrate_to_reboot_cpu(); // 比如執行reboot命令的進程運行在CPU1上,而將來CPU1會先於CPU0被停止,故需要將當前進程轉移至CPU0上
7 syscore_shutdown();//关闭syscore
8 printk(KERN_EMERG "Power down.\n");//关键打印
9 kmsg_dump(KMSG_DUMP_POWEROFF);
10 machine_power_off();
11 }

kernel_restart:

 1     void kernel_restart(char *cmd)
2 {
3 kernel_restart_prepare(cmd);//关闭外设
4 migrate_to_reboot_cpu();
5 syscore_shutdown();//关闭syscore
6 if (!cmd)
7 printk(KERN_EMERG "Restarting system.\n");//关键打印
8 else
9 printk(KERN_EMERG "Restarting system with command '%s'.\n", cmd);
10 kmsg_dump(KMSG_DUMP_RESTART);
11 machine_restart(cmd);
12 }

都执行XX_prepare()函数

1     static void kernel_shutdown_prepare(enum system_states state)
2 {
3 blocking_notifier_call_chain(&reboot_notifier_list,
4 (state == SYSTEM_HALT)?SYS_HALT:SYS_POWER_OFF, NULL);
5 system_state = state;
6 usermodehelper_disable();
7 device_shutdown();
8 }

上面的第3行會遍歷reboot_notifier_list鏈表,向其中註冊的每一個notifier_block發送SYS_POWER_OFF事件。

在我們的驅動程序中可以調用如下函數將notifier_block註冊到系統中:

 1 /**
2 * register_reboot_notifier - Register function to be called at reboot time
3 * @nb: Info about notifier function to be called
4 *
5 * Registers a function with the list of functions
6 * to be called at reboot time.
7 *
8 * Currently always returns zero, as blocking_notifier_chain_register()
9 * always returns zero.
10 */
11 int register_reboot_notifier(struct notifier_block *nb)
12 {
13 return blocking_notifier_chain_register(&reboot_notifier_list, nb);
14 }
15 EXPORT_SYMBOL(register_reboot_notifier);

在第7行中調用了device_shutdown()函數,如下:

 1 /**
2 * device_shutdown - call ->shutdown() on each device to shutdown.
3 */
4 void device_shutdown(void)
5 {
6 struct device *dev, *parent;
7
8 spin_lock(&devices_kset->list_lock);
9 /*
10 * Walk the devices list backward, shutting down each in turn.
11 * Beware that device unplug events may also start pulling
12 * devices offline, even as the system is shutting down.
13 */
14 while (!list_empty(&devices_kset->list)) {
15 dev = list_entry(devices_kset->list.prev, struct device,
16 kobj.entry);
17 ......
18
19 if (dev->bus && dev->bus->shutdown) { // 如 i2c_bus_type
20 if (initcall_debug)
21 dev_info(dev, "shutdown\n");
22 dev->bus->shutdown(dev);
23 } else if (dev->driver && dev->driver->shutdown) { // 如spi_bus_type、usb_bus_type
24 if (initcall_debug)
25 dev_info(dev, "shutdown\n");
26 dev->driver->shutdown(dev);
27 }
28 ......
29 }
30 spin_unlock(&devices_kset->list_lock);
31 }
32

kernel_restart_prepare:

1     void kernel_restart_prepare(char *cmd)
2 {
3 blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd);
4 system_state = SYSTEM_RESTART;
5 usermodehelper_disable();
6 device_shutdown();
7 }

除了前面不同,都调用了device_shutdown()函数,关闭外设。

machine_power_off() machine_resestart()函数实现

machine_power_off:

1     void machine_power_off(void)
2 {
3 preempt_disable();
4 smp_send_stop();
5
6 if (pm_power_off)
7 pm_power_off();//关机
8 }

其中,第4行的smp_send_stop會將除CPU0之外的CPU全部stop。

machine_restart:

 1     void machine_restart(char *cmd)
2 {
3 preempt_disable();
4 smp_send_stop();
5
6 /* Flush the console to make sure all the relevant messages make it
7 * out to the console drivers */
8 arm_machine_flush_console();
9
10 arm_pm_restart(reboot_mode, cmd);//重启
11
12 /* Give a grace period for failure to restart of 1s */
13 mdelay(1000);
14
15 /* Whoops - the platform was unable to reboot. Tell the user! */
16 printk("Reboot failed -- System halted\n");
17 local_irq_disable();
18 while (1);
19 }

pm_power_offf() arm_pm_restart()都是一个函数指针

赋值如下:

[kernel/drivers/power/reset/msm-poweroff.c]

1     pm_power_off = do_msm_poweroff;
2 arm_pm_restart = do_msm_restart;

高通平台的关机代码与之前有所不同,现在文件msm-poweroff.c以前是restart.c。

do_msm_poweroff()与do_msm_restart()实现如下:

do_msm_poweroff:

 1     static void do_msm_poweroff(void)
2 {
3 ....
4 pr_notice("Powering off the SoC\n");//关键打印
5 #ifdef CONFIG_MSM_DLOAD_MODE
6 set_dload_mode(0);//关机,所以dloadmode是0
7 #endif
8 qpnp_pon_system_pwr_off(PON_POWER_OFF_SHUTDOWN);//配置PMIC,是关机
9 .....
10 /* MSM initiated power off, lower ps_hold */
11 __raw_writel(0, msm_ps_hold);//拉 PS_HOLD,执行关机动作。
12
13 mdelay(10000);
14 pr_err("Powering off has failed\n");
15 return;
16 }

do_msm_restart:

 
 1     static void msm_restart_prepare(const char *cmd)
2 {
3 #ifdef CONFIG_MSM_DLOAD_MODE
4
5 /* Write download mode flags if we're panic'ing
6 * Write download mode flags if restart_mode says so
7 * Kill download mode if master-kill switch is set
8 */
9
10 set_dload_mode(download_mode &&
11 (in_panic || restart_mode == RESTART_DLOAD));//设置dload
12 #endif
13
14 /* Hard reset the PMIC unless memory contents must be maintained. */
15 if (get_dload_mode() || (cmd != NULL && cmd[0] != '\0'))
16 qpnp_pon_system_pwr_off(PON_POWER_OFF_WARM_RESET);//设置PIMC为热重启
17 else
18 qpnp_pon_system_pwr_off(PON_POWER_OFF_HARD_RESET);//设置PIMC为硬重启
19
20 if (cmd != NULL) {
21 if (!strncmp(cmd, "bootloader", 10)) {
22 __raw_writel(0x77665500, restart_reason);//写一些东东到IMEM,用于bootloader,recovery等
23 } else if (!strncmp(cmd, "recovery", 8)) {
24 __raw_writel(0x77665502, restart_reason);
25 } else if (!strcmp(cmd, "rtc")) {
26 __raw_writel(0x77665503, restart_reason);
27 } else if (!strncmp(cmd, "oem-", 4)) {
28 unsigned long code;
29 int ret;
30 ret = kstrtoul(cmd + 4, 16, &code);
31 if (!ret)
32 __raw_writel(0x6f656d00 | (code & 0xff),
33 restart_reason);
34 } else if (!strncmp(cmd, "edl", 3)) {
35 enable_emergency_dload_mode();
36 } else {
37 __raw_writel(0x77665501, restart_reason);
38 }
39 }
40
41 .....
42
43 }

do_msm_poweroff()与do_msm_restart()都设置了dload,PMIC,唯一不同的是do_msm_restart()里 多了一个__raw_writel的动作,即reason写入IMEM,目的在于重启进入sbl1时判断应该进入那种模式,如我们开发用的 bootloader模式,恢复出厂设置的recovery模式等。

完了

android L 关机流程图的更多相关文章

  1. ANDROID L——Material Design详解(UI控件)

    转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! Android L: Google已经确认Android L就是Android Lolli ...

  2. nexus7 二代 升级 android L

    折腾了半天 ,最后发现其实很简单... 1.装好windows下gdb和bootloader的驱动,注意打开usb debug,另外进入bootloader是开机按电源键和音量减小键,至于要解锁这个想 ...

  3. Ubuntu 试用Android L版本

    Android L是最近google一个大更新的版本,目前google开发了android L的开发者预览版本,对于一个android 开发者来说很定是要下载下来体验一把,顺便也要了解一下Androi ...

  4. [Android L]SEAndroid开放设备文件结点权限(读或写)方法(涵盖常用操作:sys/xxx、proc/xxx、SystemProperties)

    温馨提示      建议你先了解一下上一篇博文([Android L]SEAndroid增强Androd安全性背景概要及带来的影响)所讲的内容,先对SEAndroid窥个全貌,然后再继续本节内容.   ...

  5. [Android L]SEAndroid增强Androd安全性背景概要及带来的影响

    1  SEAndroid背景   Android对于操作系统安全性方面的增强一直沿用Linux内核所提供的MAC强制访问控制套件SELinux,对权限进行了更为深度的管理,有效地控制着进程对资源的访问 ...

  6. tcpdump for android L 5.x with pie support

    由于使用了NDK编译的可执行文件在应用中调用,在4.4及之前的版本上一直没出问题. 最近由于要测试在Android L上的运行情况发现,当运行该可执行文件时,报如下错误: error: only po ...

  7. [转]ANDROID L——Material Design详解(动画篇)

    转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! 转自:http://blog.csdn.net/a396901990/article/de ...

  8. Android L Camera2 API 使用实例程序汇总

    在网上发现几个使用Camera API2开发的实例程序,总结一下方便后续参考: 1.Camera2 Basic : https://github.com/googlesamples/android-C ...

  9. Android L 使用ART能提高多少性能?

    点击打开链接 刚刚结束的 Google I/O 大会上,Android 下一代操作系统「L」带来不少惊喜.新系统运行更快.更省电. 然而开发者对这个新系统也有颇多疑问,比如新的运行模式 ART 对开发 ...

随机推荐

  1. PatentTips - Well bias control circuit

    BACKGROUND OF THE INVENTION The present invention relates to a semiconductor integrated circuit devi ...

  2. 2016.3.14__CSS 定位__第六天

    假设您认为这篇文章还不错.能够去H5专题介绍中查看很多其它相关文章. CSS 定位机制 CSS中一共同拥有三种基本定位机制:普通流.浮动.绝对定位. 假设不进行专门指定.全部的标签都在普通流中定位. ...

  3. C++ 程序延时处理的几种方法

    (—)使用_sleep()函数 例如:_sleep(200);//延时200毫秒 (二)使用delay(int time)函数 (需要自己实现,编译器里面没有) /// @brief      程序延 ...

  4. [React Router v4] Use the React Router v4 Link Component for Navigation Between Routes

    If you’ve created several Routes within your application, you will also want to be able to navigate ...

  5. 5.7-GTID复制搭建

    基本环境   Master Slave MySQL版本 MySQL-5.7.16-X86_64 MySQL-5.7.16-X86_64 IP 192.168.56.156 192.168.56.157 ...

  6. SpringMVC中支持多视图解析

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/suo082407128/article/details/70173301 在SpringMVC模式当 ...

  7. 小强的HTML5移动开发之路(39)——jqMobi插件json格式ActionSheet

    在上一篇中我们学会了ActionSheet的使用,细心的朋友可能会发现其中创建列表的格式是HTML的,代码如下: function showCustomHtmlSheet() { $("#a ...

  8. QQ欢乐豆斗地主心得体会(四):合谋赢豆

    刚刚又在玩QQ欢乐斗地主,只可惜,这次不够欢乐. 本金,300万豆,运气比较好,赢到将近400万.突然,形势急转直下,一直输,一直到180多万豆.本来这一局,有硬炸在手,但是没有癞子,基本被吊打的节奏 ...

  9. Vuex的一个易错点

    好长时间不用Vuex,发现有些东西记模糊了. 在对Vuex进行模块化开发的时候, const store = new Vuex.Store({ modules: { a: moduleA, b: mo ...

  10. TCP基础

    TCP基础知识 复习   前言 说来惭愧,大二时候学的计算机网络好多都不太记得了,不过还好有认真学过,捡起来也挺快的,就是对于现在业界中使用的网络算法的不是很懂: 1 TCP报文段结构 1.1 序号和 ...