Android11 FallbackHome启动和关闭流程分析
Android 7.0引入了新特性:Direct Boot Mode,设备启动后进入的一个新模式,直到用户解锁(unlock)设备此阶段结束。在这个模式下,系统调用 resolveHomeActivity 找到的是FallbackHome ,而不是我们的桌面应用。所以系统开始启动的是 FallbackHome 这个"桌面"。
03-13 16:58:41.359 431 431 D test10 : ===getDefaultTaskDisplayArea===
03-13 16:58:41.359 431 431 D test10 : comp:null
ResolveInfo:ResolveInfo{b15783 com.android.settings/.FallbackHome p=-1000 m=0x108000}
03-13 16:58:41.361 431 431 D test10 : bestChoice:ResolveInfo{b15783 com.android.settings/.FallbackHome p=-1000 m=0x108000}
03-13 16:58:41.361 431 431 D test10 : aInfo:ActivityInfo{67e5d00 com.android.settings.FallbackHome}
那为什么找到是FallbackHome 呢?FallbackHome 位于 com.android.settings这个包下,看一下主配置文件
//packages\apps\Settings\AndroidManifest.xml<activity android:name=".FallbackHome"//......<intent-filter android:priority="-1000"><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.HOME" /><category android:name="android.intent.category.DEFAULT" /></intent-filter></activity><application android:label="@string/settings_label"//......android:directBootAware="true"android:name=".SettingsApplication"android:appComponentFactory="androidx.core.app.CoreComponentFactory">
FallbackHome设置了HOME属性,且settings application配置了directBootAware属性,所以找到的是FallbackHome
FallbackHome的启动
FallbackHome的启动主要分为两个阶段
- systemserver告知Zygote要创建新进程
- 新进程通知AMS启动FallbackHome
systemserver告知Zygote要创建新进程
systemserver之间的调用这里就不详细分析,可以看一下调用的堆栈
startSpecificActivity r:ActivityRecord{eebff5 u0 com.android.settings/.FallbackHome t2}
03-13 16:58:41.400 431 431 D test10 : java.lang.Exception
03-13 16:58:41.400 431 431 D test10 : at com.android.server.wm.ActivityStackSupervisor.startSpecificActivity(ActivityStackSupervisor.java:979)
03-13 16:58:41.400 431 431 D test10 : at com.android.server.wm.ActivityStack.resumeTopActivityInnerLocked(ActivityStack.java:1970)
03-13 16:58:41.400 431 431 D test10 : at com.android.server.wm.ActivityStack.resumeTopActivityUncheckedLocked(ActivityStack.java:1516)
03-13 16:58:41.400 431 431 D test10 : at com.android.server.wm.RootWindowContainer.resumeFocusedStacksTopActivities(RootWindowContainer.java:2311)
03-13 16:58:41.400 431 431 D test10 : at com.android.server.wm.ActivityStarter.startActivityInner(ActivityStarter.java:1736)
03-13 16:58:41.400 431 431 D test10 : at com.android.server.wm.ActivityStarter.startActivityUnchecked(ActivityStarter.java:1525)
03-13 16:58:41.400 431 431 D test10 : at com.android.server.wm.ActivityStarter.executeRequest(ActivityStarter.java:1189)
03-13 16:58:41.400 431 431 D test10 : at com.android.server.wm.ActivityStarter.execute(ActivityStarter.java:670)
03-13 16:58:41.400 431 431 D test10 : at com.android.server.wm.ActivityStartController.startHomeActivity(ActivityStartController.java:207)
03-13 16:58:41.400 431 431 D test10 : at com.android.server.wm.RootWindowContainer.startHomeOnTaskDisplayArea(RootWindowContainer.java:1549)
03-13 16:58:41.400 431 431 D test10 : at com.android.server.wm.RootWindowContainer.startHomeOnDisplay(RootWindowContainer.java:1491)
03-13 16:58:41.400 431 431 D test10 : at com.android.server.wm.RootWindowContainer.startHomeOnDisplay(RootWindowContainer.java:1475)
03-13 16:58:41.400 431 431 D test10 : at com.android.server.wm.RootWindowContainer.startHomeOnAllDisplays(RootWindowContainer.java:1456)
03-13 16:58:41.400 431 431 D test10 : at com.android.server.wm.ActivityTaskManagerService$LocalService.startHomeOnAllDisplays(ActivityTaskManagerService.java:6727)
03-13 16:58:41.400 431 431 D test10 : at com.android.server.am.ActivityManagerService.systemReady(ActivityManagerService.java:9689)
03-13 16:58:41.400 431 431 D test10 : at com.android.server.SystemServer.startOtherServices(SystemServer.java:2257)
03-13 16:58:41.400 431 431 D test10 : at com.android.server.SystemServer.run(SystemServer.java:599)
03-13 16:58:41.400 431 431 D test10 : at com.android.server.SystemServer.main(SystemServer.java:415)
03-13 16:58:41.400 431 431 D test10 : at java.lang.reflect.Method.invoke(Native Method)
03-13 16:58:41.400 431 431 D test10 : at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
03-13 16:58:41.400 431 431 D test10 : at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:925)
可以看出,首先在ActivityManagerService的systemReady方法中调用,一直执行到ActivityStackSupervisor的startSpecificActivity方法
void startSpecificActivity(ActivityRecord r, boolean andResume, boolean checkConfig) {//......final boolean isTop = andResume && r.isTopRunningActivity();mService.startProcessAsync(r, knownToBeDead, isTop, isTop ? "top-activity" : "activity");}
通过 startProcessAsync请求 Zygote创建 FallbackHome进程。startProcessAsync经过一步步的调用,最后调用到ProcessList的start方法
//frameworks\base\services\core\java\com\android\server\am\ProcessList.java
@GuardedBy("mService")boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,int zygotePolicyFlags, boolean disableHiddenApiChecks, boolean disableTestApiChecks,boolean mountExtStorageFull, String abiOverride) {//......// Start the process. It will either succeed and return a result containing// the PID of the new process, or else throw a RuntimeException.final String entryPoint = "android.app.ActivityThread";return startProcessLocked(hostingRecord, entryPoint, app, uid, gids,runtimeFlags, zygotePolicyFlags, mountExternal, seInfo, requiredAbi,instructionSet, invokeWith, startTime);}
注意这里的一个参数entryPoint为 “android.app.ActivityThread”,调用其重载的方法,最后调用到Process的start方法
//frameworks\base\core\java\android\os\Process.javapublic static final ZygoteProcess ZYGOTE_PROCESS = new ZygoteProcess();public static ProcessStartResult start(......) {return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids,runtimeFlags, mountExternal, targetSdkVersion, seInfo,abi, instructionSet, appDataDir, invokeWith, packageName,zygotePolicyFlags, isTopApp, disabledCompatChanges,pkgDataInfoMap, whitelistedDataInfoMap, bindMountAppsData,
ZYGOTE_PROCESS是 ZygoteProcess类对象,调用其start方法,进而调用其startViaZygote方法
private Process.ProcessStartResult startViaZygote( ...... ){//一系列的参数设置,省略......//这里的processClass为android.app.ActivityThreadargsForZygote.add(processClass);if (extraArgs != null) {Collections.addAll(argsForZygote, extraArgs);}synchronized(mLock) {// The USAP pool can not be used if the application will not use the systems graphics// driver. If that driver is requested use the Zygote application start path.return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi),zygotePolicyFlags,argsForZygote);}}
1,调用openZygoteSocketIfNeeded 打开和 Zygote的链接
2,调用zygoteSendArgsAndGetResult发送参数并得到结果
先来看看openZygoteSocketIfNeeded 方法
@GuardedBy("mLock")private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {try {attemptConnectionToPrimaryZygote();if (primaryZygoteState.matches(abi)) {return primaryZygoteState;}//......}@GuardedBy("mLock")private void attemptConnectionToPrimaryZygote() throws IOException {if (primaryZygoteState == null || primaryZygoteState.isClosed()) {primaryZygoteState =ZygoteState.connect(mZygoteSocketAddress, mUsapPoolSocketAddress);//......}}
调用ZygoteState的connect
static ZygoteState connect(@NonNull LocalSocketAddress zygoteSocketAddress,@Nullable LocalSocketAddress usapSocketAddress)throws IOException {DataInputStream zygoteInputStream;BufferedWriter zygoteOutputWriter;final LocalSocket zygoteSessionSocket = new LocalSocket();if (zygoteSocketAddress == null) {throw new IllegalArgumentException("zygoteSocketAddress can't be null");}try {zygoteSessionSocket.connect(zygoteSocketAddress);zygoteInputStream = new DataInputStream(zygoteSessionSocket.getInputStream());zygoteOutputWriter =new BufferedWriter(new OutputStreamWriter(zygoteSessionSocket.getOutputStream()),Zygote.SOCKET_BUFFER_SIZE);} catch (IOException ex) {try {zygoteSessionSocket.close();} catch (IOException ignore) { }throw ex;}return new ZygoteState(zygoteSocketAddress, usapSocketAddress,zygoteSessionSocket, zygoteInputStream, zygoteOutputWriter,getAbiList(zygoteOutputWriter, zygoteInputStream));}
注意这里的 zygoteSocketAddress 即 Zygote.PRIMARY_SOCKET_NAME(zygote),调用connect连接名为zygote的socket服务,并初始化输入输出流,封装在ZygoteState对象中返回。该对象作为参数,传个zygoteSendArgsAndGetResult方法。 再来看一下zygoteSendArgsAndGetResult这个方法
@GuardedBy("mLock")private Process.ProcessStartResult zygoteSendArgsAndGetResult(ZygoteState zygoteState, int zygotePolicyFlags, @NonNull ArrayList<String> args)throws ZygoteStartFailedEx {//......if (shouldAttemptUsapLaunch(zygotePolicyFlags, args)) {try {return attemptUsapSendArgsAndGetResult(zygoteState, msgStr);} catch (IOException ex) {// If there was an IOException using the USAP pool we will log the error and// attempt to start the process through the Zygote.Log.e(LOG_TAG, "IO Exception while communicating with USAP pool - "+ ex.getMessage());}}return attemptZygoteSendArgsAndGetResult(zygoteState, msgStr);}private Process.ProcessStartResult attemptZygoteSendArgsAndGetResult(ZygoteState zygoteState, String msgStr) throws ZygoteStartFailedEx {try {final BufferedWriter zygoteWriter = zygoteState.mZygoteOutputWriter;final DataInputStream zygoteInputStream = zygoteState.mZygoteInputStream;zygoteWriter.write(msgStr);zygoteWriter.flush();// Always read the entire result from the input stream to avoid leaving// bytes in the stream for future process starts to accidentally stumble// upon.Process.ProcessStartResult result = new Process.ProcessStartResult();result.pid = zygoteInputStream.readInt();result.usingWrapper = zygoteInputStream.readBoolean();if (result.pid < 0) {throw new ZygoteStartFailedEx("fork() failed");}return result;} catch (IOException ex) {zygoteState.close();Log.e(LOG_TAG, "IO Exception while communicating with Zygote - "+ ex.toString());throw new ZygoteStartFailedEx(ex);}}
使用前面封装的ZygoteState 中的输入输出流,写入数据并返回结果。在 Android 11 Zygote启动流程 一文中提到,Zygote启动的时候,创建了Zygote服务端,并调用runSelectLoop,进入循环等待,等待客户端的请求。接收到请求调用processOneCommand方法。在processOneCommand方法中,先fork子进程,子进程fork成功,就在子进程中返回一个Runnable,并执行其run方法。这里的子进程是FallbackHome进程
新进程通知AMS启动FallbackHome
返回的Runnable是通过handleChildProc方法得到的
private Runnable handleChildProc(ZygoteArguments parsedArgs,FileDescriptor pipeFd, boolean isZygote) {//......Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);if (parsedArgs.mInvokeWith != null) {//} else {if (!isZygote) {return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,parsedArgs.mDisabledCompatChanges,parsedArgs.mRemainingArgs, null /* classLoader */);} else {return ZygoteInit.childZygoteInit(parsedArgs.mTargetSdkVersion,parsedArgs.mRemainingArgs, null /* classLoader */);}}}
剩下的就和systemserver启动流程一样,只不过这里是调用前面提到的android.app.ActivityThread类的main方法 。具体参考Android 11 SystemServer启动流程。来看看ActivityThread的main方法
public static void main(String[] args) {Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");//......Looper.prepareMainLooper();//......ActivityThread thread = new ActivityThread();thread.attach(false, startSeq);//......Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);Looper.loop();throw new RuntimeException("Main thread loop unexpectedly exited");}
创建 ActivityThread 对象并执行其attach方法
@UnsupportedAppUsageprivate void attach(boolean system, long startSeq) {sCurrentActivityThread = this;mSystemThread = system;if (!system) {android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",UserHandle.myUserId());RuntimeInit.setApplicationObject(mAppThread.asBinder());final IActivityManager mgr = ActivityManager.getService();try {mgr.attachApplication(mAppThread, startSeq);//......
获取AMS代理类对象,通过binder调用,调用AMS的attachApplication方法
@Overridepublic final void attachApplication(IApplicationThread thread, long startSeq) {if (thread == null) {throw new SecurityException("Invalid application interface");}synchronized (this) {int callingPid = Binder.getCallingPid();final int callingUid = Binder.getCallingUid();final long origId = Binder.clearCallingIdentity();attachApplicationLocked(thread, callingPid, callingUid, startSeq);Binder.restoreCallingIdentity(origId);}}
继续调用attachApplicationLocked方法
@GuardedBy("this")private boolean attachApplicationLocked(@NonNull IApplicationThread thread,int pid, int callingUid, long startSeq) {//......//创建Application并执行其oncreate方法thread.bindApplication(processName, appInfo, providerList,instr2.mClass,profilerInfo, instr2.mArguments,instr2.mWatcher,instr2.mUiAutomationConnection, testMode,mBinderTransactionTrackingEnabled, enableTrackAllocation,isRestrictedBackupMode || !normalMode, app.isPersistent(),new Configuration(app.getWindowProcessController().getConfiguration()),app.compat, getCommonServicesLocked(app.isolated),mCoreSettingsObserver.getCoreSettingsLocked(),buildSerial, autofillOptions, contentCaptureOptions,app.mDisabledCompatChanges);//......// See if the top visible activity is waiting to run in this process...if (normalMode) {try {didSomething = mAtmInternal.attachApplication(app.getWindowProcessController());//启动Activity} catch (Exception e) {Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);badApp = true;}}}
接下来就是AMS通知FallbackHome进程,执行FallbackHome的生命周期。FallbackHome就会启动起来
FallbackHome的退出
来看一下FallbackHome的onCreate方法
@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//......registerReceiver(mReceiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED));maybeFinish();}
注册了一个ACTION_USER_UNLOCKED的广播,并在这里调用了maybeFinish方法。接收到这个广播也是调用maybeFinish
private BroadcastReceiver mReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {maybeFinish();}};
所以接下来看看maybeFinish到底干了什么
private void maybeFinish() {if (getSystemService(UserManager.class).isUserUnlocked()) {final Intent homeIntent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME);final ResolveInfo homeInfo = getPackageManager().resolveActivity(homeIntent, 0);if (Objects.equals(getPackageName(), homeInfo.activityInfo.packageName)) {if (UserManager.isSplitSystemUser()&& UserHandle.myUserId() == UserHandle.USER_SYSTEM) {// This avoids the situation where the system user has no home activity after// SUW and this activity continues to throw out warnings. See b/28870689.return;}Log.d(TAG, "User unlocked but no home; let's hope someone enables one soon?");mHandler.sendEmptyMessageDelayed(0, 500);} else {Log.d(TAG, "User unlocked and real home found; let's go!");getSystemService(PowerManager.class).userActivity(SystemClock.uptimeMillis(), false);finish();}}}
如果系统已经解锁并且查找到的Launcher不是自己时就finish自己。所以可以理解为:FallbackHome注册了ACTION_USER_UNLOCKED这个广播,当接收到这个广播时,如果系统已经解锁并且查找到的Launcher不是自己时就finish自己。ACTION_USER_UNLOCKED这个广播是在哪里发出的呢?
在Android11开机动画退出流程分析一文中,有分析到,开机动画的退出是在performEnableScreen 方法中。退出开机动画后,调用AMS的bootAnimationComplete方法,然后调用到其finishBooting,我们从finishBooting开始分析
final void finishBooting() {//......mUserController.sendBootCompleted(new IIntentReceiver.Stub() {@Overridepublic void performReceive(Intent intent, int resultCode,String data, Bundle extras, boolean ordered,boolean sticky, int sendingUser) {synchronized (ActivityManagerService.this) {mOomAdjuster.mCachedAppOptimizer.compactAllSystem();requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false);}}});//......}
调用到UserController对象的sendBootCompleted方法,其源码路径为frameworks\base\services\core\java\com\android\server\am\UserController.java
void sendBootCompleted(IIntentReceiver resultTo) {final boolean systemUserFinishedBooting;// Get a copy of mStartedUsers to use outside of lockSparseArray<UserState> startedUsers;synchronized (mLock) {systemUserFinishedBooting = mCurrentUserId != UserHandle.USER_SYSTEM;startedUsers = mStartedUsers.clone();}for (int i = 0; i < startedUsers.size(); i++) {UserState uss = startedUsers.valueAt(i);if (systemUserFinishedBooting && uss.mHandle.isSystem()) {// On Automotive, at this point the system user has already been started and// unlocked, and some of the tasks we do here have already been done. So skip those// in that case.// TODO(b/132262830): this workdound shouldn't be necessary once we move the// headless-user start logic to UserManager-landSlog.d(TAG, "sendBootCompleted(): skipping on non-current system user");continue;}finishUserBoot(uss, resultTo);}}
继续调用finishUserBoot,最终调用到 finishUserUnlocking
private boolean finishUserUnlocking(final UserState uss) {//......mHandler.obtainMessage(USER_UNLOCK_MSG, userId, 0, uss).sendToTarget();//......
}
发送USER_UNLOCK_MSG消息,接收消息后,调用 finishUserUnlocked
void finishUserUnlocked(final UserState uss) {// Dispatch unlocked to external appsfinal Intent unlockedIntent = new Intent(Intent.ACTION_USER_UNLOCKED);unlockedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);unlockedIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);mInjector.broadcastIntent(unlockedIntent, null, null, 0, null,null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID,Binder.getCallingUid(), Binder.getCallingPid(), userId);//......finishUserUnlockedCompleted(uss);//发送开机广播}
可以看出,就是在这里发出解锁广播和开机广播的。
总结
本文简单的介绍了FallbackHome启动和关闭的代码调用流程。
启动流程主要分为以下几步
- Systemserver进程通过socket,通知Zygote创建新进程
- 新进程创建成功,新进程通知Systemserver可以启动FallbackHome
- Systemserver通知FallbackHome,执行其生命周期
FallbackHome退出的话,是接收到ACTION_USER_UNLOCKED广播,判断是否解锁并且查找到的Launcher不是自己时,就退出自己
相关文章:
Android11 FallbackHome启动和关闭流程分析
Android 7.0引入了新特性:Direct Boot Mode,设备启动后进入的一个新模式,直到用户解锁(unlock)设备此阶段结束。在这个模式下,系统调用 resolveHomeActivity 找到的是FallbackHome ,而不是我们的…...
elasticsearch-java api 8 升级
es client api 升级 背景 公司项目从sring-boot2 升级到了spring-boot3 ,es的服务端也跟着升级到了es8 ,而es的客户端7和服务端8 是不兼容的, 客户端es 7使用的是: elasticsearch-rest-high-level-client es 8 升级到…...
HCIA_IP路由基础问题?
目录 1. 什么是路由?2. 什么是路由器?3. 什么是路由信息?4. 路由器信息和路由表的区别?5. 路由表的生成方式?6.直连路由生效条件是什么?7.Inloopback0是什么接口?8.最优路由选择的原则ÿ…...
(黑马出品_高级篇_01)SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式
(黑马出品_高级篇_01)SpringCloudRabbitMQDockerRedis搜索分布式 微服务技术——保护 今日目标1.初识Sentinel1.1.雪崩问题及解决方案1.2.服务保护技术对比1.3.Sentinel介绍和安装1.3.1.初识Sentinel1.3.2.安装Sentinel 1.…...
高架学习笔记之信息系统分类概览
目录 零、前言 一、业务处理系统(TPS) 概念 功能 特点 二、管理信息系统(MIS) 概念 功能 组成 三、决策支持系统(DSS) 概念 功能 特点 组成 1. 数据仓库 2. 数据挖掘工具 3. 决策模型 4. 可视化界面 四、专家系统(ES) 概念 特点 组成 求解过程 专家系统…...
2023新版mapinfo美化电子地图 新版2013Arcgis shp电子地图 下载
2023新版MapInfo和电子地图美化,以及2013版ArcGIS的SHP电子地图设计,是地理信息系统(GIS)领域中的两个重要话题。下面将分别对这两个主题进行描述。 样图: 链接:https://pan.baidu.com/s/1WB4AGsycyBGagVq5…...
BUUCTF-Ezsql1
1.打开靶机 打开第一个链接 2.万能密码 使用万能密码:a or 1 # 密码为随意 第二个用kali打开 3.ssh连接靶机 ssh ctf284490d0-7600-4c65-9160-5ced02f45633.node5.buuoj.cn -p 28191 由题可知密码为123456 4.找到并修改index.php文件 找到index.php文件 #内容如…...
LiveGBS流媒体平台GB/T28181功能-大屏播放上大屏支持轮巡播放分屏轮巡值守播放监控视频轮播大屏轮询播放轮播
LiveGBS支持-大屏播放上大屏支持轮巡播放分屏轮巡值守播放监控视频轮播大屏轮询播放轮播 1、轮播功能2、分屏展示3、选择轮播通道4、配置轮播间隔(秒)5、点击开始轮播6、轮播停止及全屏7、搭建GB28181视频直播平台 1、轮播功能 视频监控项目使用过程中,有时需要大屏…...
npm和pnpm安装、更换镜像源
安装pnpm 1 wins 在系统中搜索框 输入“Windos PowerShell”右击“管理员身份运行” 2 输入“set-ExecutionPolicy RemoteSigned”回车,根据提示输入A,回车 3 输入 pnpm -v 查看版本 如果没有版本好就是没有安装 pnpm 输入安装命令 npm install -g pnpm 4 再次 …...
springcloud 复习day1~[自动装配]
package com.gavin.eureka_server;public class First {private String auto"自动装配";public String getAuto() {return auto;}public void setAuto(String auto) {this.auto auto;} }package com.gavin.eureka_server;public class Second { }装配:实现ImportSe…...
模块化开发在不同编程语言中的实现方式有何异同?并以LabVIEW为例进行说明
模块化开发是一种软件设计方法,它将一个大型程序分解成独立的、可以单独开发和测试的模块或组件。这种方法提高了代码的可重用性、可维护性和可测试性。不同编程语言实现模块化开发的方式各有特色,但都遵循基本的设计原则,如封装、接口抽象和…...
外贸网站文章批量生成器
随着全球贸易的不断发展,越来越多的企业开始关注外贸市场,而拥有高质量的内容是吸引潜在客户的关键之一。然而,为外贸网站生产大量优质的文章内容可能是一项耗时且繁琐的任务。因此,外贸网站文章批量生成软件成为了解决这一难题的…...
maven一点通
1.maven简介 Maven是一个基于Java的工程构建工具,用于管理和构建项目的依赖关系。它提供了一种标准的项目结构和一组约定,使得项目的开发、构建、部署和文档化更加容易和可靠。 Maven的主要功能包括: 依赖管理:Maven可以自动下载…...
超越标签的探索:K-means与DBSCAN在数据分析中的新视角
最近在苦恼为我的数据决定分组问题,在查找资料时,恰好看到机器学习中的无监督学习的聚类分析,正好适用于我的问题,但是我之前学机器学习时。正好没有学习无监督部分,因为我认为绝大多数问题都是有标签的监督学习&#…...
linux板子vscode gdb 远程调试
板子:hi3556v200 交叉编译工具:arm-himix200-linux 主机:win10虚拟机的ubuntu16.4 gdb:gdb-8.2.tar.gz 1.在ubuntu交叉编译gdb(Remote g packet reply is too long解决) 建议修改gdb8.2/gdb目录下面的remote.c解决…...
nginx代理服务器配置
nginx代理服务器配置 需要配置环境需求 1、一台1.1.1.1服务器,一台2.2.2.2服务器 前端包路径在1.1.1.1 /etc/dist 下 后端服务在2.2.2.2 上 暴露端口为9999 2、需求 现在需要访问 1.1.1.1:80访问到2.2.2.2 上面的9999后端服务 3、配置nginx ①:在…...
基于Matlab的视频人面检测识别,Matalb实现
博主简介: 专注、专一于Matlab图像处理学习、交流,matlab图像代码代做/项目合作可以联系(QQ:3249726188) 个人主页:Matlab_ImagePro-CSDN博客 原则:代码均由本人编写完成,非中介,提供…...
VSCode创建用户代码片段-案例demo
示例 - 在线生成代码片段 Vue3代码片段 {"vue3": {scope": "javascript,typescript,html,vue","prefix": "vue3","body": ["<template>","$1","</template>",""…...
河南大学-数字图像处理-图像变换
计算机与信息工程学院实验报告 序号:20 姓名:__杨馥瑞___ 学号:_2212080042_ 专业:__数据科学与大数据技术 年级:___2022级_____ 课程:数字图像处理 主讲教师:张延锋 辅导教师&#x…...
华为OD七日集训第3期 - 按算法分类,由易到难,循序渐进,玩转OD
目录 一、适合人群二、本期训练时间三、如何参加四、七日集训第 3 期五、精心挑选21道高频100分经典题目,作为入门。第1天、逻辑分析第2天、字符串处理第3天、矩阵第4天、深度优先搜索dfs算法第5天、回溯法第6天、二分查找第7天、图、正则表达式 大家好,…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...
leetcodeSQL解题:3564. 季节性销售分析
leetcodeSQL解题:3564. 季节性销售分析 题目: 表:sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...
Java + Spring Boot + Mybatis 实现批量插入
在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法:使用 MyBatis 的 <foreach> 标签和批处理模式(ExecutorType.BATCH)。 方法一:使用 XML 的 <foreach> 标签ÿ…...
【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...
【JVM】Java虚拟机(二)——垃圾回收
目录 一、如何判断对象可以回收 (一)引用计数法 (二)可达性分析算法 二、垃圾回收算法 (一)标记清除 (二)标记整理 (三)复制 (四ÿ…...
深入理解Optional:处理空指针异常
1. 使用Optional处理可能为空的集合 在Java开发中,集合判空是一个常见但容易出错的场景。传统方式虽然可行,但存在一些潜在问题: // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...
MySQL 主从同步异常处理
阅读原文:https://www.xiaozaoshu.top/articles/mysql-m-s-update-pk MySQL 做双主,遇到的这个错误: Could not execute Update_rows event on table ... Error_code: 1032是 MySQL 主从复制时的经典错误之一,通常表示ÿ…...
nnUNet V2修改网络——暴力替换网络为UNet++
更换前,要用nnUNet V2跑通所用数据集,证明nnUNet V2、数据集、运行环境等没有问题 阅读nnU-Net V2 的 U-Net结构,初步了解要修改的网络,知己知彼,修改起来才能游刃有余。 U-Net存在两个局限,一是网络的最佳深度因应用场景而异,这取决于任务的难度和可用于训练的标注数…...
