下面是简单的流程图,从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. 【t084】数列

    Time Limit: 1 second Memory Limit: 128 MB [问题描述] 一个数列定义如下:f(1) = 1,f(2) = 1,f(n) = (A * f(n - 1) + B ...

  2. kindeditor4跨域上传图片解决

    项目中正在使用kindeditor, 版本号4.1.10 非常多公司的图片会走CDN,须要单独的一台图片上传服务如:(upload.268xue.com) kindeditor上传图片的简单内部流程: ...

  3. 矩阵分解(matrix factorization)

    1. 基本概念 针对高维空间中的数据集,矩阵分解通过寻找到一组基及每一个数据点在该基向量下的表示,可对原始高维空间中的数据集进行压缩表示. 令 X=[x1,⋯,xm]∈Rm×n 为数据矩阵,矩阵分解的 ...

  4. spark 2.0.2 集群搭建

    由于之前已经搭建过hadoop相关环境,现在搭建spark的预备工作只有scala环境了 一,配置scala环境 1.解压tar包后,编辑/etc/profile 2.source /etc/prof ...

  5. tensorflow 函数接口的理解

    1. tf.nn.softmax tf.nn.softmax(logits, dim=-1, name=None) w*x+b ⇒ logits softmax 函数执行的操作:exp(logits) ...

  6. ik_max_word ik_smart

    打开 ~/es_root/config/elasticsearch.yml 文件,加入以下配置: index: analysis: analyzer: ik_syno: type: custom to ...

  7. IdentityServer4实战 - 谈谈 JWT Token 的安全策略

    原文:IdentityServer4实战 - 谈谈 JWT Token 的安全策略 一.前言 众所周知,IdentityServer4 默认支持两种类型的 Token,一种是 Reference To ...

  8. 【转】Mysql rownum 实现 及应用

    Mysql rownum 实现 转自:http://blog.csdn.net/saydo/article/details/22725953   SELECT @rownum:=@rownum+1 A ...

  9. hadoop学习笔记(四)——eclipse+maven+hadoop2.5.2源代码

    Eclipse同maven进口hadoop源代码 1)  安装和配置maven环境变量 M2_HOME: D:\profession\hadoop\apache-maven-3.3.3 PATH: % ...

  10. 创建可按比例调整的布局的 Windows 窗体

    能够正确调整大小的窗体可以提高您的用户界面的易用性. 此演练演示了如何创建当用户调整窗体大小时按比例调整的布局. 您将使用 TableLayoutPanel 控件实现一个接收联系人信息的数据输入窗体. ...