Keyguard分析
从Android 6.0开始,位于frameworks/bases/packages/Keyguard的Keyguard开始被编译为一个jar包,被SystemUI静态导入,相当于SystemUI的一个界面,这样Keyguard就可以复用SystemUI里关于通知的那一部分代码,这个在Keyuard的Makefile里可以看到
LOCAL_PATH:= $(call my-dir)
17include $(CLEAR_VARS) 19LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-subdir-Iaidl-files) 21LOCAL_MODULE := Keyguard
23LOCAL_CERTIFICATE := platform 25LOCAL_JAVA_LIBRARIES := SettingsLib 27LOCAL_PRIVILEGED_MODULE := true 29LOCAL_PROGUARD_FLAG_FILES := proguard.flags 31LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res 33include $(BUILD_STATIC_JAVA_LIBRARY) #include $(call all-makefiles-under,$(LOCAL_PATH))
Keyguard分为两个界面,不用输入密码的一级锁屏界面(SystemUI认为是PhoneStatusBar)与相应源码文件包含Security字样的二级锁屏界面(SystemUI认为是Bouncer)。
一级锁屏界面
二级锁屏界面
各个部件调用关系是下边这张图
可以看到,Keyguard第一个涉及到的是KeyguardDisplayManager,其由KeyguardViewMediator这个界面中介调用。
首先,由SystemServer的startSystemUi方法里的StartServiceAsUser连接到SystemUIService。再由SystemUIService(SystemUI/src/com/android/systemui/SystemUIService.java)里的onCreate函数调用(就这一个有用的函数)startServicesIfNeeded方法,开始SystemUI的初始化
1 static final void startSystemUi(Context context) {
2 Intent intent = new Intent();
3 intent.setComponent(new ComponentName("com.android.systemui",
4 "com.android.systemui.SystemUIService"));
5 intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
6 //Slog.d(TAG, "Starting service: " + intent);
7 context.startServiceAsUser(intent, UserHandle.SYSTEM);
8 }
1 public class SystemUIService extends Service {
2
3 @Override
4 public void onCreate() {
5 super.onCreate();
6 ((SystemUIApplication) getApplication()).startServicesIfNeeded();
7 }
KeyguardViewMediator是SystemUIApplication名为SERVICES数组里的一员,这个数组的东西都是SystemUI要load的Service的class,这个框架流程在start的时候,调用这些类的start方法,并在bootcompleted的时候调用这些类的onbootcompleted方法。
/**
44 * The classes of the stuff to start.
45 */
46 private final Class<?>[] SERVICES = new Class[] {
47 com.android.systemui.tuner.TunerService.class,
48 com.android.systemui.keyguard.KeyguardViewMediator.class,
49 com.android.systemui.recents.Recents.class,
50 com.android.systemui.volume.VolumeUI.class,
51 Divider.class,
52 com.android.systemui.statusbar.SystemBars.class,
53 com.android.systemui.usb.StorageNotification.class,
54 com.android.systemui.power.PowerUI.class,
55 com.android.systemui.media.RingtonePlayer.class,
56 com.android.systemui.keyboard.KeyboardUI.class,
57 com.android.systemui.tv.pip.PipUI.class,
58 com.android.systemui.shortcut.ShortcutKeyDispatcher.class,
59 com.android.systemui.VendorServices.class
60 };
可以看到,在SystemUIApplication这个类的startServicesIfNeeded里,会依次调用SERVICES里的start函数,这里会先调用com.android.systemui.keyguard.KeyguardViewMediator的start方法
final int N = services.length;
156 for (int i=0; i<N; i++) {
157 Class<?> cl = services[i];
158 if (DEBUG) Log.d(TAG, "loading: " + cl);
159 try {
160 Object newService = SystemUIFactory.getInstance().createInstance(cl);
161 mServices[i] = (SystemUI) ((newService == null) ? cl.newInstance() : newService);
162 } catch (IllegalAccessException ex) {
163 throw new RuntimeException(ex);
164 } catch (InstantiationException ex) {
165 throw new RuntimeException(ex);
166 }
167
168 mServices[i].mContext = this;
169 mServices[i].mComponents = mComponents;
170 if (DEBUG) Log.d(TAG, "running: " + mServices[i]);
171 mServices[i].start();
172
173 if (mBootCompleted) {
174 mServices[i].onBootCompleted();
175 }
176 }
177 mServicesStarted = true;
下面看下KeyguardViewMediator及其start方法
这个类位于SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java,比较长。第三方客户端也可以通过调用KeyguardManager这个类来获取和修改锁屏的信息、状态,这个类是锁屏操作的binder server基础类。
KeyguardViewMediator是抽象类SystemUI的一个具体实现子类,SystemUI这个类的主要方法是putComponent和getComponent,保存和获取相应类对应的实际组件的映射。还用mContext和mComponents保存相应的SystemUIApplication实例和其中名为component的hashmap。
KyeguardViewMediator总体负责所有的锁屏状态,并根据状态来决定调用哪些组件。
KeyguardViewMediator的start方法很简单,初始化锁屏状态,把KeyguardViewMediator的class和KeyguardViewMediator建立映射。
@Override
699 public void start() {
700 synchronized (this) {
701 setupLocked();
702 }
703 putComponent(KeyguardViewMediator.class, this);
704 }
初始化的过程在setupLocked方法里完成,首先获取系统的PowerManagerService,WindowManagerService,TrustManagerService并初始化一把partial wakelock锁
mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
635 mWM = WindowManagerGlobal.getWindowManagerService();
636 mTrustManager = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
637
638 mShowKeyguardWakeLock = mPM.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "show keyguard");
639 mShowKeyguardWakeLock.setReferenceCounted(false);
随后注册DELAYED_KEYGUARD_ACTION和DELAYED_LOCK_PROFILE_ACTION这两个Intent的broadcastreceiver
641 mContext.registerReceiver(mBroadcastReceiver, new IntentFilter(DELAYED_KEYGUARD_ACTION));
642 mContext.registerReceiver(
643 mBroadcastReceiver, new IntentFilter(DELAYED_LOCK_PROFILE_ACTION));
然后创建Keyguard包里的KeyguardDisplayManager和KeyguardUpdateMonitor,还有锁屏模式工具类,获取AlarmManagerService,给KeyguardUpdateMonitor设置当前的用户。
645 mKeyguardDisplayManager = new KeyguardDisplayManager(mContext);
646
647 mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
648
649 mUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
650
651 mLockPatternUtils = new LockPatternUtils(mContext);
652 KeyguardUpdateMonitor.setCurrentUser(ActivityManager.getCurrentUser());
然后设置锁屏状态的变量并调用锁屏状态改变回调函数表,通知TrustManager
654 // Assume keyguard is showing (unless it's disabled) until we know for sure...
655 setShowingLocked(!shouldWaitForProvisioning() && !mLockPatternUtils.isLockScreenDisabled(
656 KeyguardUpdateMonitor.getCurrentUser()));
657 updateInputRestrictedLocked();
658 mTrustManager.reportKeyguardShowingChanged();
然后设置视图显示,通过SystemUIFactory获取StatusBarKeyguardViewManager,并把视图中介回调(mViewMediatorCallback)和锁屏模式工具(mLockPatternUtils)传入。
660 mStatusBarKeyguardViewManager =
661 SystemUIFactory.getInstance().createStatusBarKeyguardViewManager(mContext,
662 mViewMediatorCallback, mLockPatternUtils);
SystemUIFactory里的code如下
84 public StatusBarKeyguardViewManager createStatusBarKeyguardViewManager(Context context,
85 ViewMediatorCallback viewMediatorCallback, LockPatternUtils lockPatternUtils) {
86 return new StatusBarKeyguardViewManager(context, viewMediatorCallback, lockPatternUtils);
87 }
StatusBarKeyguardViewManager是SystemUI中的一个状态栏组件,是锁屏的视图。
最后设置锁屏和解锁的声音的文件和音量,获取设备交互状态,加载锁屏隐藏的动画 com.android.internal.R.anim.lock_screen_behind_enter 。
663 final ContentResolver cr = mContext.getContentResolver();
664
665 mDeviceInteractive = mPM.isInteractive();
1 667 mLockSounds = new SoundPool(1, AudioManager.STREAM_SYSTEM, 0);
668 String soundPath = Settings.Global.getString(cr, Settings.Global.LOCK_SOUND);
669 if (soundPath != null) {
670 mLockSoundId = mLockSounds.load(soundPath, 1);
671 }
672 if (soundPath == null || mLockSoundId == 0) {
673 Log.w(TAG, "failed to load lock sound from " + soundPath);
674 }
675 soundPath = Settings.Global.getString(cr, Settings.Global.UNLOCK_SOUND);
676 if (soundPath != null) {
677 mUnlockSoundId = mLockSounds.load(soundPath, 1);
678 }
679 if (soundPath == null || mUnlockSoundId == 0) {
680 Log.w(TAG, "failed to load unlock sound from " + soundPath);
681 }
682 soundPath = Settings.Global.getString(cr, Settings.Global.TRUSTED_SOUND);
683 if (soundPath != null) {
684 mTrustedSoundId = mLockSounds.load(soundPath, 1);
685 }
686 if (soundPath == null || mTrustedSoundId == 0) {
687 Log.w(TAG, "failed to load trusted sound from " + soundPath);
688 }
689
690 int lockSoundDefaultAttenuation = mContext.getResources().getInteger(
691 com.android.internal.R.integer.config_lockSoundVolumeDb);
692 mLockSoundVolume = (float)Math.pow(10, (float)lockSoundDefaultAttenuation/20);
693
694 mHideAnimation = AnimationUtils.loadAnimation(mContext,
695 com.android.internal.R.anim.lock_screen_behind_enter);
696 }
然后SystemUIApplication会调用 mServices[i].onBootCompleted 方法,会在KeyguardViemMediator的start方法后调用,来发出Intent ACTION_BOOT_COMPLETED,通知其他组件锁屏初始化完成
上边是锁屏的初始化过程,然后就是锁屏的加载过程。锁屏界面的加载有两个地方,第一个是第一次开机的时候;第二个是在灭屏后,这个时候会预加载锁屏界面加速亮屏显示。
第一次开机时,,
在按住Power键灭屏的时候,流程如下
可以看到,KeyguardViewMediator里有两个回调函数被涉及
第一个是onStartedGoingToSleep。这个方法里做锁屏的一些预处理,并发出锁屏通知给KeyguardUpdateMonitor(这里的状态太多了)
723 /**
724 * Called to let us know the screen was turned off.
725 * @param why either {@link android.view.WindowManagerPolicy#OFF_BECAUSE_OF_USER} or
726 * {@link android.view.WindowManagerPolicy#OFF_BECAUSE_OF_TIMEOUT}.
727 */
728 public void onStartedGoingToSleep(int why) {
729 if (DEBUG) Log.d(TAG, "onStartedGoingToSleep(" + why + ")");
730 synchronized (this) {
731 mDeviceInteractive = false;
732 mGoingToSleep = true;
733
734 // Lock immediately based on setting if secure (user has a pin/pattern/password).
735 // This also "locks" the device when not secure to provide easy access to the
736 // camera while preventing unwanted input.
737 int currentUser = KeyguardUpdateMonitor.getCurrentUser();
738 final boolean lockImmediately =
739 mLockPatternUtils.getPowerButtonInstantlyLocks(currentUser)
740 || !mLockPatternUtils.isSecure(currentUser);
741 long timeout = getLockTimeout(KeyguardUpdateMonitor.getCurrentUser());
742 mLockLater = false;
743 if (mExitSecureCallback != null) {
744 if (DEBUG) Log.d(TAG, "pending exit secure callback cancelled");
745 try {
746 mExitSecureCallback.onKeyguardExitResult(false);
747 } catch (RemoteException e) {
748 Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e);
749 }
750 mExitSecureCallback = null;
751 if (!mExternallyEnabled) {
752 hideLocked();
753 }
754 } else if (mShowing) {
755 mPendingReset = true;
756 } else if ((why == WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT && timeout > 0)
757 || (why == WindowManagerPolicy.OFF_BECAUSE_OF_USER && !lockImmediately)) {
758 doKeyguardLaterLocked(timeout);
759 mLockLater = true;
760 } else if (!mLockPatternUtils.isLockScreenDisabled(currentUser)) {
761 mPendingLock = true;
762 }
763
764 if (mPendingLock) {
765 playSounds(true);
766 }
767 }
768 KeyguardUpdateMonitor.getInstance(mContext).dispatchStartedGoingToSleep(why);
769 notifyStartedGoingToSleep();
770 }
第二个是onFinishedGoingToSleep,可以看到核心方法是doKeyguardLocked和doKeyguardForChildProfilesLocked
772 public void onFinishedGoingToSleep(int why, boolean cameraGestureTriggered) {
773 if (DEBUG) Log.d(TAG, "onFinishedGoingToSleep(" + why + ")");
774 synchronized (this) {
775 mDeviceInteractive = false;
776 mGoingToSleep = false;
777
778 resetKeyguardDonePendingLocked();
779 mHideAnimationRun = false;
780
781 notifyFinishedGoingToSleep();
782
783 if (cameraGestureTriggered) {
784 Log.i(TAG, "Camera gesture was triggered, preventing Keyguard locking.");
785
786 // Just to make sure, make sure the device is awake.
787 mContext.getSystemService(PowerManager.class).wakeUp(SystemClock.uptimeMillis(),
788 "com.android.systemui:CAMERA_GESTURE_PREVENT_LOCK");
789 mPendingLock = false;
790 mPendingReset = false;
791 }
792
793 if (mPendingReset) {
794 resetStateLocked();
795 mPendingReset = false;
796 }
797
798 if (mPendingLock) {
799 doKeyguardLocked(null);
800 mPendingLock = false;
801 }
802
803 // We do not have timeout and power button instant lock setting for profile lock.
804 // So we use the personal setting if there is any. But if there is no device
805 // we need to make sure we lock it immediately when the screen is off.
806 if (!mLockLater && !cameraGestureTriggered) {
807 doKeyguardForChildProfilesLocked();
808 }
809
810 }
811 KeyguardUpdateMonitor.getInstance(mContext).dispatchFinishedGoingToSleep(why);
812 }
doKeyguardLocked 会先判断要不要锁屏,如果需要,则调用方法showLocked
1192 /**
1193 * Enable the keyguard if the settings are appropriate.
1194 */
1195 private void doKeyguardLocked(Bundle options) {
1196 // if another app is disabling us, don't show
1197 if (!mExternallyEnabled) {
1198 if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");
1199
1200 // note: we *should* set mNeedToReshowWhenReenabled=true here, but that makes
1201 // for an occasional ugly flicker in this situation:
1202 // 1) receive a call with the screen on (no keyguard) or make a call
1203 // 2) screen times out
1204 // 3) user hits key to turn screen back on
1205 // instead, we reenable the keyguard when we know the screen is off and the call
1206 // ends (see the broadcast receiver below)
1207 // TODO: clean this up when we have better support at the window manager level
1208 // for apps that wish to be on top of the keyguard
1209 return;
1210 }
1211
1212 // if the keyguard is already showing, don't bother
1213 if (mStatusBarKeyguardViewManager.isShowing()) {
1214 if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
1215 resetStateLocked();
1216 return;
1217 }
1218
1219 // In split system user mode, we never unlock system user.
1220 if (!mustNotUnlockCurrentUser()
1221 || !mUpdateMonitor.isDeviceProvisioned()) {
1222
1223 // if the setup wizard hasn't run yet, don't show
1224 final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim", false);
1225 final boolean absent = SubscriptionManager.isValidSubscriptionId(
1226 mUpdateMonitor.getNextSubIdForState(IccCardConstants.State.ABSENT));
1227 final boolean disabled = SubscriptionManager.isValidSubscriptionId(
1228 mUpdateMonitor.getNextSubIdForState(IccCardConstants.State.PERM_DISABLED));
1229 final boolean lockedOrMissing = mUpdateMonitor.isSimPinSecure()
1230 || ((absent || disabled) && requireSim);
1231
1232 if (!lockedOrMissing && shouldWaitForProvisioning()) {
1233 if (DEBUG) Log.d(TAG, "doKeyguard: not showing because device isn't provisioned"
1234 + " and the sim is not locked or missing");
1235 return;
1236 }
1237
1238 if (mLockPatternUtils.isLockScreenDisabled(KeyguardUpdateMonitor.getCurrentUser())
1239 && !lockedOrMissing) {
1240 if (DEBUG) Log.d(TAG, "doKeyguard: not showing because lockscreen is off");
1241 return;
1242 }
1243
1244 if (mLockPatternUtils.checkVoldPassword(KeyguardUpdateMonitor.getCurrentUser())) {
1245 if (DEBUG) Log.d(TAG, "Not showing lock screen since just decrypted");
1246 // Without this, settings is not enabled until the lock screen first appears
1247 setShowingLocked(false);
1248 hideLocked();
1249 mUpdateMonitor.reportSuccessfulStrongAuthUnlockAttempt();
1250 return;
1251 }
1252 }
1253
1254 if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen");
1255 showLocked(options);
1256 }
showLocked方法会发送SHOW消息
1332 /**
1333 * Send message to keyguard telling it to show itself
1334 * @see #handleShow
1335 */
1336 private void showLocked(Bundle options) {
1337 Trace.beginSection("KeyguardViewMediator#showLocked aqcuiring mShowKeyguardWakeLock");
1338 if (DEBUG) Log.d(TAG, "showLocked");
1339 // ensure we stay awake until we are finished displaying the keyguard
1340 mShowKeyguardWakeLock.acquire();
1341 Message msg = mHandler.obtainMessage(SHOW, options);
1342 mHandler.sendMessage(msg);
1343 Trace.endSection();
1344 }
handleShow方法就会被调用,显示mStatusBarKeyguardViewManager和mKeyguardDisplayManager的show方法。
1625 /**
1626 * Handle message sent by {@link #showLocked}.
1627 * @see #SHOW
1628 */
1629 private void handleShow(Bundle options) {
1630 Trace.beginSection("KeyguardViewMediator#handleShow");
1631 final int currentUser = KeyguardUpdateMonitor.getCurrentUser();
1632 if (mLockPatternUtils.isSecure(currentUser)) {
1633 mLockPatternUtils.getDevicePolicyManager().reportKeyguardSecured(currentUser);
1634 }
1635 synchronized (KeyguardViewMediator.this) {
1636 if (!mSystemReady) {
1637 if (DEBUG) Log.d(TAG, "ignoring handleShow because system is not ready.");
1638 return;
1639 } else {
1640 if (DEBUG) Log.d(TAG, "handleShow");
1641 }
1642
1643 setShowingLocked(true);
1644 mStatusBarKeyguardViewManager.show(options);
1645 mHiding = false;
1646 mWakeAndUnlocking = false;
1647 resetKeyguardDonePendingLocked();
1648 mHideAnimationRun = false;
1649 updateActivityLockScreenState();
1650 adjustStatusBarLocked();
1651 userActivity();
1652
1653 mShowKeyguardWakeLock.release();
1654 }
1655 mKeyguardDisplayManager.show();
1656 Trace.endSection();
1657 }
还有一种情况是超时灭屏,与上边的按住Power键灭屏流程基本一样
基本流程分析完了,下面看看Keyguard里的具体每个类
先看KeyguardDisplayManager这个类,这个类是控制手机远程显示的。如果手机远程连接上了电视这样的设备,就先一个一个KeyguardPresentation对话框,是个时钟
protected void updateDisplays(boolean showing) {
if (showing) {
MediaRouter.RouteInfo route = mMediaRouter.getSelectedRoute(
MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY);
boolean useDisplay = route != null
&& route.getPlaybackType() == MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE;
Display presentationDisplay = useDisplay ? route.getPresentationDisplay() : null; if (mPresentation != null && mPresentation.getDisplay() != presentationDisplay) {
if (DEBUG) Slog.v(TAG, "Display gone: " + mPresentation.getDisplay());
mPresentation.dismiss();
mPresentation = null;
} if (mPresentation == null && presentationDisplay != null) {
if (DEBUG) Slog.i(TAG, "Keyguard enabled on display: " + presentationDisplay);
mPresentation = new KeyguardPresentation(mContext, presentationDisplay,
R.style.keyguard_presentation_theme);
mPresentation.setOnDismissListener(mOnDismissListener);
try {
mPresentation.show();
} catch (WindowManager.InvalidDisplayException ex) {
Slog.w(TAG, "Invalid display:", ex);
mPresentation = null;
}
}
} else {
if (mPresentation != null) {
mPresentation.dismiss();
mPresentation = null;
}
}
}
Keyguard分析的更多相关文章
- SystemUI
1.Status bars(状态栏) 2.Navigation bars(导航栏) 3.Notification(通知) 4.Keyguard(锁屏) 5.Quick settings(快速设置) 6 ...
- Android框架浅析之锁屏(Keyguard)机制原理
最近终于成功的摆脱了FM收音机,迈向了新的模块:锁屏.状态栏.Launcher---姑且称之为“IDLE”小组,或许叫手机 美容小组,要是能施展下周星星同学的还我漂漂拳,岂不快哉. OK,闲话打住,咱 ...
- Android Framework------之Keyguard 简单分析
前面对于MediaPlayer的系统研究,刚刚开始,由于其他原因现在要先暂停一下.这次要看的模块是android 4.2 系统中的Keyguard模块.在接触之后才发现,android4.2的keyg ...
- Android 7.1 屏幕旋转流程分析
Android 7.1 屏幕旋转流程分析 一.概述 Android屏幕的旋转在framework主要涉及到三个类,结构如图 PhoneWindowManager:为屏幕的横竖屏转换的管理类. Wi ...
- Android startActivity原理分析(基于Android 8.1 AOSP)
应用进程内 如何使用Intent做Activity的跳转 Intnet intent = new Intent(MainActivity.this,TestActivity.class); start ...
- Android 5.0 Phone初始化分析
已经更新至个人blog:http://dxjia.cn/2015/07/android-5-0-phone-init-analysis/ persistent属性 要想了解phone的框架,首先需要了 ...
- Android之SystemUI载入流程和NavigationBar的分析
Android之SystemUI载入流程和NavigationBar的分析 本篇仅仅分析SystemUI的载入过程和SystemUI的当中的一个模块StatusBar的小模块NavigationBar ...
- android分析windowManager、window、viewGroup之间关系(二)
三.接上一节,分析windowManager中添加一个悬浮框的方式,首先看代码 WindowManager.LayoutParams params = new LayoutParams(); para ...
- Android的开机流程及对应源码位置分析
1.系统引导bootloader 1)源码:bootable/bootloader/* 2)说明:加电后,CPU将先执行bootloader程序,此处有三种选择 a)开机按Camera+Power启动 ...
随机推荐
- CSS - 初始值、指定值、计算值、应用值、实际值
初始值:未提供指定值且未从父元素指定值继承的 CSS 属性的值. 指定值:通过直接声明或 CSS 属性的值. 计算值:通过需要计算得到的值,如,继承和相对的尺寸.(注意:有些计算要等到布局确定才能进行 ...
- 用Vue来实现音乐播放器(二十):Vuex初始化及歌手数据的配置
state:所有组件的所有状态和数据 放入同一个内存空间去管理 我们把它称为state Vue Components:state里面的数据可以方便的映射到组件上 然后渲染组件 Actions:当组件 ...
- 【SVN】 一次SVN 修复笔记
同事乱提交了一个版本之后,SVN上最新版本出现了问题. 原本按照网上其他人的说法,可以手动到服务器端干掉最新版的存档,并修改版本记录到前一个版本号即可,但是这应该是个坑. 掉进这个坑后,需要解决,又不 ...
- tensorflow学习笔记二:入门基础 好教程 可用
http://www.cnblogs.com/denny402/p/5852083.html tensorflow学习笔记二:入门基础 TensorFlow用张量这种数据结构来表示所有的数据.用一 ...
- 2 Vue.js基础
1 简易计算器 <!DOCTYPE html> <html lang="en"> <head> <meta charset="U ...
- node.js—创建、删除、追加文件等方法汇总
使用Node.js的fs模块必须在electron项目里 /* 1. fs.stat 检测是文件还是目录(目录 文件是否存在) 2. fs.mkdir 创建目录 (创建之前先判断是否存在) 3. fs ...
- [Markdown] 03 进阶语法 第一弹
目录 1. YMAL 题头 2. 缩写 3. 强调 4. 自定义 <div> 标签 5. <cite> 标签 5. <code> 与 <br> 标签 6 ...
- [19/05/15-星期三] HTML_body标签(超链接标签和锚点)
一.超链接标签 <html> <head> <meta charset="UTF-8"> <title>04 body超链接标签学习 ...
- sde自动备份到文件gdb
本方法原理是使用python(以下简称py)调用arcmap的gp,在上再用bat调用py的方式实现.优点是能应用于所有数据库类型(包括pg,oracle等)的sde库 环境:arcmap 10.4, ...
- EasyUI之DataGrid分页
第一步创建分页DataGrid <table id="dg"> <thead> <tr> <th data-options="f ...