从 Android 12开始,如果是大屏底下导航栏会变成显示一个任务栏,从 NavigationBar 变成 Taskbar 。

注:CentralSurfacesImpl.java 与 原来的 statusbar.java 的作用一样。

CentralSurfacesImpl 的启动流程前面有说过,这里不在说明。

// CentralSurfacesImpl.java
public class CentralSurfacesImpl extends CoreStartable implementsCentralSurfaces {//省略其他代码....protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {//省略其他代码....createNavigationBar(result);//省略其他代码....}protected void createNavigationBar(@Nullable RegisterStatusBarResult result) {// 调用 NavigationBarController 的 createNavigationBars()方法创建 NavigationBarmNavigationBarController.createNavigationBars(true /* includeDefaultDisplay */, result);}}

接着往下看:

NavigationBarController#createNavigationBars()// NavigationBarController.javapublic void createNavigationBars(final boolean includeDefaultDisplay,RegisterStatusBarResult result) {// 如果需要,更新辅助功能按钮模式updateAccessibilityButtonModeIfNeeded();// 这里是重点,Android 12 之前版本的,没有这个判断。可以看下 initializeTaskbarIfNecessary() 方法。// 如果我们初始化TaskBar,则不需要在默认显示上创建导航栏final boolean shouldCreateDefaultNavbar = includeDefaultDisplay&& !initializeTaskbarIfNecessary();Display[] displays = mDisplayManager.getDisplays();for (Display display : displays) {if (shouldCreateDefaultNavbar || display.getDisplayId() != DEFAULT_DISPLAY) {createNavigationBar(display, null /* savedState */, result);}}}/** @return {@code true} if taskbar is enabled, false otherwise */private boolean initializeTaskbarIfNecessary() {// 这里会判断是否是平板。// 跟踪代码发现,如果屏幕的(最小边长度*160/dpi值)< 600,就会判断为设备是平板设备// system/vendor/mediatek/proprietary/packages/apps/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.javaif (mIsTablet) {Trace.beginSection("NavigationBarController#initializeTaskbarIfNecessary");// 移除导航栏removeNavigationBar(mContext.getDisplayId());// 加载任务栏mTaskbarDelegate.init(mContext.getDisplayId());Trace.endSection();} else {mTaskbarDelegate.destroy();}return mIsTablet;}

我们分析平板,所以这里直接看 mTaskbarDelegate.init(mContext.getDisplayId()) 方法;

TaskbarDelegate#init()// TaskbarDelegate.javapublic void init(int displayId) {if (mInitialized) {return;}//省略其他代码....// 注册监听,切换导航模式时接收回调。mEdgeBackGestureHandler.onNavigationModeChanged(mNavigationModeController.addListener(this));// 边缘手势处理,注意:这里只加载、处理手势。// 手势中左右两返回图标由这里加载,底部黑色小横条不在这里加载。mEdgeBackGestureHandler.onNavBarAttached();//省略其他代码....}// 导航模式改变回调,进行处理@Overridepublic void onNavigationModeChanged(int mode) {mNavigationMode = mode;mEdgeBackGestureHandler.onNavigationModeChanged(mode);}

1、手势加载

EdgeBackGestureHandler#onNavBarAttached()// EdgeBackGestureHandler.java/*** @see NavigationBarView#onAttachedToWindow()*/public void onNavBarAttached() {mIsAttached = true;mProtoTracer.add(this);mOverviewProxyService.addCallback(mQuickSwitchListener);mSysUiState.addCallback(mSysUiStateCallback);// 重点关注这里,更新视图updateIsEnabled();startTracking();}private void updateIsEnabled() {// mIsAttached 是否已经添加上了,mIsGesturalModeEnabled 是否启用手势模式boolean isEnabled = mIsAttached && mIsGesturalModeEnabled;if (isEnabled == mIsEnabled) {return;}mIsEnabled = isEnabled;// 如果无效的话结束监听 InputdisposeInputChannel();if (mEdgeBackPlugin != null) {// 边缘导航栏销毁mEdgeBackPlugin.onDestroy();mEdgeBackPlugin = null;}// 是否启用手势模式if (!mIsEnabled) {// 注销监听返回手势参数的设置变化mGestureNavigationSettingsObserver.unregister();if (DEBUG_MISSING_GESTURE) {Log.d(DEBUG_MISSING_GESTURE_TAG, "Unregister display listener");}mPluginManager.removePluginListener(this);TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);try {// 注销 WMS 里保存的除外区域监听mWindowManagerService.unregisterSystemGestureExclusionListener(mGestureExclusionListener, mDisplayId);} catch (RemoteException | IllegalArgumentException e) {Log.e(TAG, "Failed to unregister window manager callbacks", e);}} else {// 监听返回手势参数的设置变化mGestureNavigationSettingsObserver.register();updateDisplaySize();if (DEBUG_MISSING_GESTURE) {Log.d(DEBUG_MISSING_GESTURE_TAG, "Register display listener");}TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,mMainExecutor::execute, mOnPropertiesChangedListener);try {// 监听 WMS 里保存的除外区域mWindowManagerService.registerSystemGestureExclusionListener(mGestureExclusionListener, mDisplayId);} catch (RemoteException | IllegalArgumentException e) {Log.e(TAG, "Failed to register window manager callbacks", e);}//  注册名为 edge-swipe 的InputMonitormInputMonitor = InputManager.getInstance().monitorGestureInput("edge-swipe", mDisplayId);mInputEventReceiver = new InputChannelCompat.InputEventReceiver(mInputMonitor.getInputChannel(), Looper.getMainLooper(),Choreographer.getInstance(), this::onInputEvent);// 添加 NavigationBarEdgePanel 为 Edge Back 事件的处理实现setEdgeBackPlugin(new NavigationBarEdgePanel(mContext, mBackAnimation, mLatencyTracker));mPluginManager.addPluginListener(this, NavigationEdgeBackPlugin.class, /*allowMultiple=*/ false);}// 更新 ML 模型资源.updateMLModelState();}// 创建返回手势视图private void setEdgeBackPlugin(NavigationEdgeBackPlugin edgeBackPlugin) {if (mEdgeBackPlugin != null) {mEdgeBackPlugin.onDestroy();}// 缓存 NavigationEdgeBackPlugin 实现mEdgeBackPlugin = edgeBackPlugin;// 向 NavigationEdgeBackPlugin 注册 Back 手势的触发回调mEdgeBackPlugin.setBackCallback(mBackCallback);// 准备好手势视图的 Window 参数mEdgeBackPlugin.setLayoutParams(createLayoutParams());updateDisplaySize();}// 配置返回手势 Window 的参数// 包括 flag、type、title 等属性private WindowManager.LayoutParams createLayoutParams() {Resources resources = mContext.getResources();WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(resources.getDimensionPixelSize(R.dimen.navigation_edge_panel_width),resources.getDimensionPixelSize(R.dimen.navigation_edge_panel_height),WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,PixelFormat.TRANSLUCENT);...layoutParams.setTitle(TAG + mContext.getDisplayId());layoutParams.setFitInsetsTypes(0 /* types */);layoutParams.setTrustedOverlay();return layoutParams;}

到此手势导航创建完成,注意:此时 View 还是不可见的,后续事件产生的时候会进行展示和刷新。

下面粗略的看下手势相关的事件:

// EdgeBackGestureHandler.javaprivate void onMotionEvent(MotionEvent ev) {int action = ev.getActionMasked();if (action == MotionEvent.ACTION_DOWN) {mInputEventReceiver.setBatchingEnabled(false);mIsOnLeftEdge = ev.getX() <= mEdgeWidthLeft + mLeftInset;mMLResults = 0;mLogGesture = false;mInRejectedExclusion = false;boolean isWithinInsets = isWithinInsets((int) ev.getX(), (int) ev.getY());// 根据返回手势是否有效、// 点击区域是否是停用区域等条件// 得到当前是否允许视图处理该手势mAllowGesture = !mDisabledForQuickstep && mIsBackGestureAllowed && isWithinInsets&& !mGestureBlockingActivityRunning&& !QuickStepContract.isBackGestureDisabled(mSysUiFlags)&& isWithinTouchRegion((int) ev.getX(), (int) ev.getY());if (mAllowGesture) {// 更新当前是屏幕左侧还是右侧mEdgeBackPlugin.setIsLeftPanel(mIsOnLeftEdge);// 发射事件给视图mEdgeBackPlugin.onMotionEvent(ev);}} else if (mAllowGesture || mLogGesture) {if (!mThresholdCrossed) {mEndPoint.x = (int) ev.getX();mEndPoint.y = (int) ev.getY();// 多个手指按下的话取消事件处理if (action == MotionEvent.ACTION_POINTER_DOWN) {if (mAllowGesture) {logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_MULTI_TOUCH);cancelGesture(ev);}mLogGesture = false;return;} else if (action == MotionEvent.ACTION_MOVE) {// 手指移动超过长按阈值的话// 也要取消事件处理if ((ev.getEventTime() - ev.getDownTime()) > mLongPressTimeout) {if (mAllowGesture) {logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_LONG_PRESS);cancelGesture(ev);}mLogGesture = false;return;}...}}// 通过上述检查的话// 将 MOVE、UP 交给视图if (mAllowGesture) {// forward touchmEdgeBackPlugin.onMotionEvent(ev);}}mProtoTracer.scheduleFrameUpdate();}

事件间传递到 NavigationBarEdgePanel ,进行展示返回手势和触发返回。

NavigationBarEdgePanel 继续进行后面的工作:手势的判断、视图的刷新、动画的展示。

onMotionEvent() 的逻辑:

DOWN 的时候先让视图变为可见 VISIBLE
MOVE 的处理通过 handleMoveEvent() 判断距离,决定是否要更新赋予 mTriggerBack
UP 的时候将检查该变量决定是否触发返回动作即 triggerBack()

// NavigationBarEdgePanel.javapublic void onMotionEvent(MotionEvent event) {if (mVelocityTracker == null) {mVelocityTracker = VelocityTracker.obtain();}mVelocityTracker.addMovement(event);switch (event.getActionMasked()) {case MotionEvent.ACTION_DOWN:mDragSlopPassed = false;resetOnDown();mStartX = event.getX();mStartY = event.getY();setVisibility(VISIBLE);updatePosition(event.getY());mRegionSamplingHelper.start(mSamplingRect);mWindowManager.updateViewLayout(this, mLayoutParams);break;case MotionEvent.ACTION_MOVE:handleMoveEvent(event);break;case MotionEvent.ACTION_UP:if (mTriggerBack) {triggerBack();} else {cancelBack();}...}}

手势相关的事件流程到此打住了。

2、按钮导航
按钮导航是在 TouchInteractionService.java 中的 onCreate() 方法开始的。

// TouchInteractionService.java@Overridepublic void onCreate() {super.onCreate();//省略其他代码....mTaskbarManager = new TaskbarManager(this);//省略其他代码....}

用户配置完成后,根据配置,是否加载按钮导航。如果加载则会调用 TaskbarManager.java中的 recreateTaskbar() 方法。

TaskbarManager#recreateTaskbar()// TaskbarManager.javaprivate void recreateTaskbar() {// 销毁现有任务栏destroyExistingTaskbar();// 获取设备配置文件的DeviceProfile dp =mUserUnlocked ? LauncherAppState.getIDP(mContext).getDeviceProfile(mContext) : null;// 任务栏是否启用boolean isTaskBarEnabled = dp != null && dp.isTaskbarPresent;if(dp != null){Log.d("yexiao","yexiao"+(dp != null)+"----"+(dp.isTaskbarPresent));}if (!isTaskBarEnabled) {SystemUiProxy.INSTANCE.get(mContext).notifyTaskbarStatus(/* visible */ false, /* stashed */ false);return;}// TaskbarActivityContext 按钮导航的视图在里面加载,包括那个底部横线也在里面。mTaskbarActivityContext = new TaskbarActivityContext(mContext, dp, mNavButtonController,mUnfoldProgressProvider);// 初始化mTaskbarActivityContext.init(mSharedState);if (mActivity != null) {// 设置相关控制器mTaskbarActivityContext.setUIController(createTaskbarUIControllerForActivity(mActivity));}}

在 TaskbarManager 类中有个 mDispInfoChangeListener 的监听,当导航模式切换时,会在 DisplayController 类中 去回调这里来。

接着往下看TaskbarActivityContext的构造方法:

public TaskbarActivityContext(Context windowContext, DeviceProfile dp,
TaskbarNavButtonController buttonController, ScopedUnfoldTransitionProgressProvider
unfoldTransitionProgressProvider) {
super(windowContext);
mDeviceProfile = dp.copy(this);

final Resources resources = getResources();mNavMode = DisplayController.getNavigationMode(windowContext);
mImeDrawsImeNavBar = getBoolByName(IME_DRAWS_IME_NAV_BAR_RES_NAME, resources, false);
mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode",() -> getPackageManager().isSafeMode());
mIsUserSetupComplete = SettingsCache.INSTANCE.get(this).getValue(Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), 0);
mIsNavBarForceVisible = SettingsCache.INSTANCE.get(this).getValue(Settings.Secure.getUriFor(Settings.Secure.NAV_BAR_KIDS_MODE), 0);
mIsNavBarKidsMode = SettingsCache.INSTANCE.get(this).getValue(Settings.Secure.getUriFor(Settings.Secure.NAV_BAR_KIDS_MODE), 0);updateIconSize(resources);// 首先获取显示和焦点,因为视图可能会在构造函数中使用它们。
Display display = windowContext.getDisplay();
Context c = display.getDisplayId() == Display.DEFAULT_DISPLAY? windowContext.getApplicationContext(): windowContext.getApplicationContext().createDisplayContext(display);
mWindowManager = c.getSystemService(WindowManager.class);
mLeftCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT);
mRightCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT);// 初始化 views.
mDragLayer = (TaskbarDragLayer) mLayoutInflater.inflate(R.layout.taskbar, null, false);
TaskbarView taskbarView = mDragLayer.findViewById(R.id.taskbar_view);
TaskbarScrimView taskbarScrimView = mDragLayer.findViewById(R.id.taskbar_scrim);
FrameLayout navButtonsView = mDragLayer.findViewById(R.id.navbuttons_view);
StashedHandleView stashedHandleView = mDragLayer.findViewById(R.id.stashed_handle);mAccessibilityDelegate = new TaskbarShortcutMenuAccessibilityDelegate(this);// 构造控制器 controllers,将会在对应的控制添加对应的图标,以控制逻辑
mControllers = new TaskbarControllers(this,new TaskbarDragController(this),buttonController,getPackageManager().hasSystemFeature(FEATURE_PC)? new DesktopNavbarButtonsViewController(this, navButtonsView) :     new NavbarButtonsViewController(this, navButtonsView),new RotationButtonController(this,c.getColor(R.color.taskbar_nav_icon_light_color),c.getColor(R.color.taskbar_nav_icon_dark_color),R.drawable.ic_sysbar_rotate_button_ccw_start_0,R.drawable.ic_sysbar_rotate_button_ccw_start_90,R.drawable.ic_sysbar_rotate_button_cw_start_0,R.drawable.ic_sysbar_rotate_button_cw_start_90,() -> getDisplay().getRotation()),new TaskbarDragLayerController(this, mDragLayer),new TaskbarViewController(this, taskbarView),new TaskbarScrimViewController(this, taskbarScrimView),new TaskbarUnfoldAnimationController(this, unfoldTransitionProgressProvider,mWindowManager, WindowManagerGlobal.getWindowManagerService()),new TaskbarKeyguardController(this),new StashedHandleViewController(this, stashedHandleView),new TaskbarStashController(this),new TaskbarEduController(this),new TaskbarAutohideSuspendController(this),new TaskbarPopupController(this),new TaskbarForceVisibleImmersiveController(this),new TaskbarAllAppsController(this, dp),new TaskbarInsetsController(this));

}

public void init(@NonNull TaskbarSharedState sharedState) {
mLastRequestedNonFullscreenHeight = getDefaultTaskbarWindowHeight();
mWindowLayoutParams = createDefaultWindowLayoutParams();

//  在 TaskbarControllers控制器里,初始化前面构造方法中添加的各种控制器。
mControllers.init(sharedState);
updateSysuiStateFlags(sharedState.sysuiStateFlags, true /* fromInit */);
// 生成 LayoutParams 用于将视图作为新窗口直接添加到 WindowManager
mWindowManager.addView(mDragLayer, mWindowLayoutParams);

}

这里以 NavbarButtonsViewController 为例子分析:

// NavbarButtonsViewController.java/*** Initializes the controller*/public void init(TaskbarControllers controllers) {// 省略部分代码......// 强制导航按钮(特别是后退按钮)在设置向导期间可见。boolean isInSetup = !mContext.isUserSetupComplete();boolean isInKidsMode = mContext.isNavBarKidsModeActive();boolean alwaysShowButtons = isThreeButtonNav || isInSetup;// 省略部分代码......if (alwaysShowButtons) {// 初始化按钮initButtons(mNavButtonContainer, mEndContextualContainer,mControllers.navButtonController);} else {}// 省略部分代码......mSeparateWindowParent.recreateControllers();}private void initButtons(ViewGroup navContainer, ViewGroup endContainer,TaskbarNavButtonController navButtonController) {// back ButtonmBackButton = addButton(R.drawable.ic_sysbar_back, BUTTON_BACK,mNavButtonContainer, mControllers.navButtonController, R.id.back);mBackButtonAlpha = new MultiValueAlpha(mBackButton, NUM_ALPHA_CHANNELS);mBackButtonAlpha.setUpdateVisibility(true);mPropertyHolders.add(new StatePropertyHolder(mBackButtonAlpha.getProperty(ALPHA_INDEX_KEYGUARD_OR_DISABLE),flags -> {// Show only if not disabled, and if not on the keyguard or otherwise only when// the bouncer or a lockscreen app is showing above the keyguardboolean showingOnKeyguard = (flags & FLAG_KEYGUARD_VISIBLE) == 0 ||(flags & FLAG_ONLY_BACK_FOR_BOUNCER_VISIBLE) != 0 ||(flags & FLAG_KEYGUARD_OCCLUDED) != 0;return (flags & FLAG_DISABLE_BACK) == 0&& ((flags & FLAG_KEYGUARD_VISIBLE) == 0 || showingOnKeyguard);}));boolean isRtl = Utilities.isRtl(mContext.getResources());mPropertyHolders.add(new StatePropertyHolder(mBackButton,flags -> (flags & FLAG_IME_VISIBLE) != 0 && !mContext.isNavBarKidsModeActive(),View.ROTATION, isRtl ? 90 : -90, 0));// Translate back button to be at end/start of other buttons for keyguardint navButtonSize = mContext.getResources().getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size);mPropertyHolders.add(new StatePropertyHolder(mBackButton, flags -> (flags & FLAG_ONLY_BACK_FOR_BOUNCER_VISIBLE) != 0|| (flags & FLAG_KEYGUARD_VISIBLE) != 0,VIEW_TRANSLATE_X, navButtonSize * (isRtl ? -2 : 2), 0));// home buttonmHomeButton = addButton(R.drawable.ic_sysbar_home, BUTTON_HOME, navContainer,navButtonController, R.id.home);mHomeButtonAlpha = new MultiValueAlpha(mHomeButton, NUM_ALPHA_CHANNELS);mHomeButtonAlpha.setUpdateVisibility(true);mPropertyHolders.add(new StatePropertyHolder(mHomeButtonAlpha.getProperty(ALPHA_INDEX_KEYGUARD_OR_DISABLE),flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0 &&(flags & FLAG_DISABLE_HOME) == 0));// Recents buttonView recentsButton = addButton(R.drawable.ic_sysbar_recent, BUTTON_RECENTS,navContainer, navButtonController, R.id.recent_apps);mHitboxExtender.init(recentsButton, mNavButtonsView, mContext.getDeviceProfile(),() -> {float[] recentsCoords = new float[2];getDescendantCoordRelativeToAncestor(recentsButton, mNavButtonsView,recentsCoords, false);return recentsCoords;}, new Handler());recentsButton.setOnClickListener(v -> {navButtonController.onButtonClick(BUTTON_RECENTS);mHitboxExtender.onRecentsButtonClicked();});mPropertyHolders.add(new StatePropertyHolder(recentsButton,flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0 && (flags & FLAG_DISABLE_RECENTS) == 0&& !mContext.isNavBarKidsModeActive()));// A11y buttonmA11yButton =  (R.drawable.ic_sysbar_accessibility_button, BUTTON_A11Y,endContainer, navButtonController, R.id.accessibility_button,R.layout.taskbar_contextual_button);mPropertyHolders.add(new StatePropertyHolder(mA11yButton,flags -> (flags & FLAG_A11Y_VISIBLE) != 0&& (flags & FLAG_ROTATION_BUTTON_VISIBLE) == 0));}

事件的在 addButton() 的时候直接设置了,而且有特殊需求的,会单独设置。

至此简单分析完成

Android 13 平板Taskbar加载流程相关推荐

  1. Android SIM卡识别加载流程

    文章目录 总述 代码路径 UICC框架 SIM卡识别加载流程 日志分析举例 总述 本文基于Android N(Android 7) 首先要知道SIM卡一般是挂载在CP侧(MODEM侧)的,由MODEM ...

  2. android Q launcher 数据加载流程

    时间:2020/08/24 之前公司不允许csdn,笔记写在其它地方.最近整理过来 下一篇:launcher数据加载(二) 前言 androidQ和androidP上Launcher结构有很大区别. ...

  3. Android SIM 卡信息加载流程

    在PhoneAPP启动关系类初始化中,我们提到监听处理SIM卡状态的两个关键类UiccController和IccCardProxy UiccController:整个UICC事务处理的入口,负责对外 ...

  4. Android系统字体加载流程

    一.背景 视觉同学提了一个需求,要求手机中显示的字体可以支持medium字体,经过分析,android原生的字体库中并没有中文的medium字体,如果使用bold,显示又太粗,为满足需求,需要分析an ...

  5. Android 系统(169)---Android 7.0 插卡后APN信息的加载流程

    Android 7.0 插卡后APN信息的加载流程.UI界面编辑APN的流程及Android中APN配置相关的漏洞 终端中有一个apns-config.xml文件,负责定义各个运营商规定的默认APN参 ...

  6. Android 4.0 ICS SystemUI浅析——StatusBar加载流程分析

    前面两篇文章< Android 4.0 ICS SystemUI浅析--SystemUI启动流程>.< Android 4.0 ICS SystemUI浅析--StatusBar结构 ...

  7. 从源码分析Android的Glide库的图片加载流程及特点

    转载:http://m.aspku.com/view-141093.html 这篇文章主要介绍了从源码分析Android的Glide库的图片加载流程及特点,Glide库是Android下一款人气很高的 ...

  8. android wifi驱动加载流程

    本文基于android 7.0 海思 Hi3798MV200 平台 WiFi驱动加载流程: 一..进入高级设置,无线设置界面(WifiSettings.java)打开WiFi开关(SwitchBar) ...

  9. 【Android】恢复出厂后静态壁纸加载流程

    Android静态壁纸功能实现参与的类 /frameworks/base/core/java/android/app/WallpaperManager.java 给开发者提供方法调用.例:setBit ...

最新文章

  1. ng-repeat根据多个字段排序
  2. 宏基因组蚂蚁森林合种——胡杨专车
  3. Oracle学习笔记:创建logical standby
  4. 算法笔记之回溯法(2)
  5. android通过webservice验证用户 .
  6. String Table MFC
  7. Shell编程基础(1)
  8. 谭浩强C语言第四版第九章课后习题7--9题(建立,输出,删除,插入链表处理)...
  9. 《Ruby程序员修炼之道》(第2版)—第1章1.1节进入Ruby的世界
  10. python暴力破解
  11. C语言差分双向码编码,基于c语言的数字基带信号码型变换系统设计1.doc
  12. 2017腾讯校招面试回忆 成功拿到offer
  13. mysql cbrt函数_ES6 数值的扩展
  14. 『机器学习』入门教程汇总
  15. 用Java(APICloud)开发手机APP
  16. tmdb电影票房_TMDb Vue.js应用程序:电影数据库应用程序
  17. 安装kubectl失败:error: unpacking of archive failed on file /usr/bin/kubectl: cpio: rename
  18. Media.Metrics简介
  19. A Review on Deep Learning Applications in Prognostics and Health Management (翻译)
  20. UE4 Niagara扩散环效果及参数化配置(数字孪生用)

热门文章

  1. 电子商务物流解决方案
  2. ImageNet 数据集准备(用于分类)
  3. 迁移数据库报错解决方案
  4. 那些云服务器挂千牛方便
  5. 微信小程序开发与app开发的十大区别
  6. 电脑连接电视,将电视当做第2显示器(笔记本)
  7. MapKit 教程: 覆盖物
  8. 天津公务员 计算机水平,天津公务员考试报考这6类职位,上岸几率更大!
  9. RK3399平台开发系列讲解(USB设备驱动)5.28、USB 协议标准
  10. 关于Alpha Matte 和 Luma Matte