当前位置: 首页 > news >正文

Android系统揭秘(一)-Activity启动流程(上)

public ActivityResult execStartActivity(

Context who, IBinder contextThread, IBinder token, Activity target,

Intent intent, int requestCode, Bundle options) {

IApplicationThread whoThread = (IApplicationThread) contextThread;

try {

int result = ActivityManager.getService()

.startActivity(whoThread, who.getBasePackageName(), intent,

intent.resolveTypeIfNeeded(who.getContentResolver()),

token, target != null ? target.mEmbeddedID : null,

requestCode, 0, null, options);

checkStartActivityResult(result, intent);

} catch (RemoteException e) {

throw new RuntimeException(“Failure from system”, e);

}

return null;

}

首先调用ActivityManager的getService方法来获取AMS的代理对象,接着调用它的startActivity方法

这里需要注意的是,Android8.0之前是通过ActivityManagerNative的getDefault未获取AMS的代理对象的, 现在这个逻辑封装到了ActivityManager中而不是ActivityManagerNative中。

frameworks/base/core/java/android/app/ActivityManager.java

@SystemService(Context.ACTIVITY_SERVICE)

public class ActivityManager {

public static IActivityManager getService() {

return IActivityManagerSingleton.get();

}

private static final Singleton IActivityManagerSingleton =

new Singleton() {

@Override

protected IActivityManager create() {

// Context.ACTIVITY_SERVICE值为"activity"

final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);

final IActivityManager am = IActivityManager.Stub.asInterface(b);

return am;

}

};

}

getService方法调用了IActivityManagerSingleton的get方法,IActivityManagerSingleton是一个Singleton类。

这段代码采用的是AIDL,IActivityManager.java类是由AIDL工具在编译时自动生成的, 要实现进程间通信,服务器端也就是AMS只需要继承IActivityManager.Stub类并实现相应的方法就可以了。

注意Android8.0之前并没有采用AIDL,而是采用了类似AIDL的形式, 用AMS的代理对象ActivityManagerProxy来与AMS进行进程间通信, Android8.0去除了ActivityManagerNative的内部类ActivityManagerProxy, 代替它的是IActivityManager,它是AMS在本地的代理。 回到Instrumentation类的execStartActivity方法中, 从上面得知execStartActivity方法最终调用的是AMS的startActivity方法。

AMS到ApplicationThread的调用过程


我们先看看AMS里面的代码

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

@Override

public final int startActivity(IApplicationThread caller, String callingPackage,

Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,

int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {

return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,

resultWho, requestCode, startFlags, profilerInfo, bOptions,

UserHandle.getCallingUserId());

}

@Override

public final int startActivityAsUser(IApplicationThread caller, String callingPackage,

Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,

int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {

// 判断调用者进程是否被隔离,如果被隔离则抛出SecurityException异常

enforceNotIsolatedCaller(“startActivity”);

// 检查调用者是否有权限,如果没有权限也会抛出SecurityException异常

userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),

userId, false, ALLOW_FULL_ONLY, “startActivity”, null);

// TODO: Switch to user app stacks here.

return mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent,

resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,

profilerInfo, null, null, bOptions, false, userId, null, “startActivityAsUser”);

}

可知,AMS使用ActivityStarter来启动Activity

frameworks/base/services/core/java/com/android/server/am/ActivityStarter.java

final int startActivityMayWait(IApplicationThread caller, int callingUid,

String callingPackage, Intent intent, String resolvedType,

IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,

IBinder resultTo, String resultWho, int requestCode, int startFlags,

ProfilerInfo profilerInfo, WaitResult outResult,

Configuration globalConfig, Bundle bOptions, boolean ignoreTargetSecurity, int userId,

TaskRecord inTask, String reason) {

final ActivityRecord[] outRecord = new ActivityRecord[1];

int res = startActivityLocked(caller, intent, ephemeralIntent, resolvedType,

aInfo, rInfo, voiceSession, voiceInteractor,

resultTo, resultWho, requestCode, callingPid,

callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,

options, ignoreTargetSecurity, componentSpecified, outRecord, inTask,

reason);

return res;

}

}

我们看下startActivityLocked方法,这个方法在ActivityStarter.startActivity前调用,会做一些校验

int startActivityLocked(IApplicationThread caller, Intent intent, Intent ephemeralIntent,

String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,

IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,

IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,

String callingPackage, int realCallingPid, int realCallingUid, int startFlags,

ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,

ActivityRecord[] outActivity, TaskRecord inTask, String reason) {

// 不会直接调用startActivity,要先做一些校验

if (TextUtils.isEmpty(reason)) {

throw new IllegalArgumentException(“Need to specify a reason.”);

}

mLastStartReason = reason;

mLastStartActivityTimeMs = System.currentTimeMillis();

mLastStartActivityRecord[0] = null;

mLastStartActivityResult = startActivity(caller, intent, ephemeralIntent, resolvedType,

aInfo, rInfo, voiceSession, voiceInteractor, resultTo, resultWho, requestCode,

callingPid, callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,

options, ignoreTargetSecurity, componentSpecified, mLastStartActivityRecord,

inTask);

if (outActivity != null) {

// mLastStartActivityRecord[0] is set in the call to startActivity above.

outActivity[0] = mLastStartActivityRecord[0];

}

// Aborted results are treated as successes externally, but we must track them internally.

return mLastStartActivityResult != START_ABORTED ? mLastStartActivityResult : START_SUCCESS;

}

接着调用startActivity方法

/** DO NOT call this method directly. Use {@link #startActivityLocked} instead. */

private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,

String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,

IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,

IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,

String callingPackage, int realCallingPid, int realCallingUid, int startFlags,

ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,

ActivityRecord[] outActivity, TaskRecord inTask) {

int err = ActivityManager.START_SUCCESS;

// Pull the optional Ephemeral Installer-only bundle out of the options early.

final Bundle verificationBundle

= options != null ? options.popAppVerificationBundle() : null;

ProcessRecord callerApp = null;

if (caller != null) {

// 得到的是代表Launcher进程的callerApp对象,它是ProcessRecord类型的,ProcessRecord用于描述一个应用程序进程

callerApp = mService.getRecordForAppLocked(caller);

if (callerApp != null) {

callingPid = callerApp.pid;

callingUid = callerApp.info.uid;

} else {

Slog.w(TAG, "Unable to find app for caller " + caller

  • " (pid=" + callingPid + ") when starting: "

  • intent.toString());

err = ActivityManager.START_PERMISSION_DENIED;

}

}

ActivityRecord r = new startActivity(mService, callerApp, callingPid, callingUid,

callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(),

resultRecord, resultWho, requestCode, componentSpecified, voiceSession != null,

mSupervisor, options, sourceRecord);

if (outActivity != null) {

outActivity[0] = r;

}

doPendingActivityLaunchesLocked(false);

return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags, true,

options, inTask, outActivity);

}

在启动Activity之前,系统会创建一个startActivity对象,ActivityRecord用于描述一个Activity,用来记录一个Activity的所有信息

private int startActivity(final ActivityRecord r, ActivityRecord sourceRecord,

IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,

int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,

ActivityRecord[] outActivity) {

int result = START_CANCELED;

try {

mService.mWindowManager.deferSurfaceLayout();

result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,

startFlags, doResume, options, inTask, outActivity);

} finally {

}

return result;

}

startActivityUnchecked方法代码比较多,主要处理与栈管理相关的逻辑。

// Note: This method should only be called from {@link startActivity}.

private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,

IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,

int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,

ActivityRecord[] outActivity) {

// If the activity being launched is the same as the one currently at the top, then

// we need to check if it should only be launched once.

final ActivityStack topStack = mSupervisor.mFocusedStack;

final ActivityRecord topFocused = topStack.topActivity();

final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop);

// 启动根Activity时会将Intent的Flag设置为FLAG_ACTIVITY_NEW_TASK

// Should this be considered a new task?

int result = START_SUCCESS;

if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask

&& (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {

newTask = true;

// setTaskFromReuseOrCreateNewTask方法内部会创建一个新的Activity任务栈

result = setTaskFromReuseOrCreateNewTask(

taskToAffiliate, preferredLaunchStackId, topStack);

} else if (mSourceRecord != null) {

result = setTaskFromSourceRecord();

} else if (mInTask != null) {

result = setTaskFromInTask();

} else {

// This not being started from an existing activity, and not part of a new task…

// just put it in the top task, though these days this case should never happen.

setTaskToCurrentTopOrCreateNewTask();

}

if (result != START_SUCCESS) {

return result;

}

if (mDoResume) {

final ActivityRecord topTaskActivity =

mStartActivity.getTask().topRunningActivityLocked();

if (!mTargetStack.isFocusable()

|| (topTaskActivity != null && topTaskActivity.mTaskOverlay

&& mStartActivity != topTaskActivity)) {

} else {

// If the target stack was not previously focusable (previous top running activity

// on that stack was not visible) then any prior calls to move the stack to the

// will not update the focused stack. If starting the new activity now allows the

// task stack to be focusable, then ensure that we now update the focused stack

// accordingly.

if (mTargetStack.isFocusable() && !mSupervisor.isFocusedStack(mTargetStack)) {

mTargetStack.moveToFront(“startActivityUnchecked”);

}

mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,

mOptions);

}

} else {

mTargetStack.addRecentActivityLocked(mStartActivity);

}

mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);

mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(), preferredLaunchStackId,

preferredLaunchDisplayId, mTargetStack.mStackId);

return START_SUCCESS;

}

接下来调用的是ActivityStackSupervisor的resumeFocusedStackTopActivityLocked方法,从这里开始要处理堆栈的事务了

frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java

boolean resumeFocusedStackTopActivityLocked(

ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {

if (!readyToResume()) {

return false;

}

if (targetStack != null && isFocusedStack(targetStack)) {

return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);

}

//获取要启动的Activity所在校的核顶的不是处于停止状态的ActivityRecord

final ActivityRecord r = mFocusedStack.topRunningActivityLocked();

if (r == null || r.state != RESUMED) {

mFocusedStack.resumeTopActivityUncheckedLocked(null, null);

} else if (r.state == RESUMED) {

// Kick off any lingering app transitions form the MoveTaskToFront operation.

mFocusedStack.executeAppTransition(targetOptions);

}

return false;

}

ActivityStackSupervisor相当于ActivityStack的帮助类,需要ActivityStack进行下一步堆栈的处理

frameworks/base/services/core/java/com/android/server/am/ActivityStack.java

boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {

if (mStackSupervisor.inResumeTopActivity) {

// Don’t even start recursing.

return false;

}

boolean result = false;

try {

// Protect against recursion.

mStackSupervisor.inResumeTopActivity = true;

result = resumeTopActivityInnerLocked(prev, options);

} finally {

mStackSupervisor.inResumeTopActivity = false;

}

final ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);

if (next == null || !next.canTurnScreenOn()) {

checkReadyForSleep();

}

return result;

}

resumeTopActivityInnerLocked的内容很多,我们只关心以下代码

private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {

mStackSupervisor.startSpecificActivityLocked(next, true, false);

return true;

}

又回到了ActivityStackSupervisor,调用startSpecificActivityLocked方法

frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java

void startSpecificActivityLocked(ActivityRecord r,

boolean andResume, boolean checkConfig) {

// Is this activity’s application already running?

ProcessRecord app = mService.getProcessRecordLocked(r.processName,

r.info.applicationInfo.uid, true);

r.getStack().setLaunchTime®;

if (app != null && app.thread != null) {

try {

if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0

|| !“android”.equals(r.info.packageName)) {

// Don’t add this if it is a platform component that is marked

// to run in multiple processes, because this is actually

// part of the framework so doesn’t make sense to track as a

// separate apk in the process.

app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode,

mService.mProcessStats);

}

realStartActivityLocked(r, app, andResume, checkConfig);

return;

} catch (RemoteException e) {

Slog.w(TAG, "Exception when starting activity "

  • r.intent.getComponent().flattenToShortString(), e);

}

// If a dead object exception was thrown – fall through to

// restart the application.

}

// 启动应用程序进程

mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,

“activity”, r.intent.getComponent(), false, false, true);

}

接下来就是realStartActivityLocked,到这里堆栈基本处理完毕,准备通知ApplicationThread

final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,

boolean andResume, boolean checkConfig) throws RemoteException {

app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,

System.identityHashCode®, r.info,

// TODO: Have this take the merged configuration instead of separate global

// and override configs.

mergedConfiguration.getGlobalConfiguration(),

mergedConfiguration.getOverrideConfiguration(), r.compat,

r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,

r.persistentState, results, newIntents, !andResume,

mService.isNextTransitionForward(), profilerInfo);

}

这里的app.thread指的是IApplicationThread,它的实现是ActivityThread的内部类ApplicationThread, 其中ApplicationThread继承了IApplicationThread.Stub

ActivityThread 启动Activity 的过程


上一节写到AMS通过各种途径,最终通知到ApplicationThread。而在这一步骤,ApplicationThread通过消息机制告知ActivityThread,

frameworks/base/core/java/android/app/ActivityThread.java#ApplicationThread

@Override

public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,

ActivityInfo info, Configuration curConfig, Configuration overrideConfig,

CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,

int procState, Bundle state, PersistableBundle persistentState,

List pendingResults, List pendingNewIntents,

boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {

updateProcessState(procState, false);

ActivityClientRecord r = new ActivityClientRecord();

r.token = token;

r.ident = ident;

r.intent = intent;

r.referrer = referrer;

r.voiceInteractor = voiceInteractor;

r.activityInfo = info;

r.compatInfo = compatInfo;

r.state = state;

r.persistentState = persistentState;

r.pendingResults = pendingResults;

r.pendingIntents = pendingNewIntents;

r.startsNotResumed = notResumed;

r.isForward = isForward;

r.profilerInfo = profilerInfo;

r.overrideConfig = overrideConfig;

updatePendingConfiguration(curConfig);

sendMessage(H.LAUNCH_ACTIVITY, r);

}

我们看看sendMessage的代码

frameworks/base/core/java/android/app/ActivityThread.java

private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {

if (DEBUG_MESSAGES) Slog.v(

TAG, "SCHEDULE " + what + " " + mH.codeToString(what)

  • ": " + arg1 + " / " + obj);

Message msg = Message.obtain();

msg.what = what;

msg.obj = obj;

msg.arg1 = arg1;

msg.arg2 = arg2;

if (async) {

msg.setAsynchronous(true);

}

mH.sendMessage(msg);

}

它向一个mH对象发送的一个what为H.LAUNCH_ACTIVITY(值为100),obj为ActivityClientRecord的Message,H是ActivityThread内的一个Handler

H收到指令后会调用handleLaunchActivity方法

private class H extends Handler {

public static final int LAUNCH_ACTIVITY = 100;

public static final int RELAUNCH_ACTIVITY = 101;

String codeToString(int code) {

if (DEBUG_MESSAGES) {

switch (code) {

case LAUNCH_ACTIVITY: return “LAUNCH_ACTIVITY”;

}

}

return Integer.toString(code);

}

public void handleMessage(Message msg) {

if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));

switch (msg.what) {

case LAUNCH_ACTIVITY: {

Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, “activityStart”);

final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

r.packageInfo = getPackageInfoNoCheck(

r.activityInfo.applicationInfo, r.compatInfo);

handleLaunchActivity(r, null, “LAUNCH_ACTIVITY”);

Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

} break;

case RELAUNCH_ACTIVITY: {

Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, “activityRestart”);

ActivityClientRecord r = (ActivityClientRecord)msg.obj;

handleRelaunchActivity®;

Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

} break;

}

Object obj = msg.obj;

if (obj instanceof SomeArgs) {

((SomeArgs) obj).recycle();

}

if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));

}

}

这里我们先看下getPackageInfoNoCheck方法,这个方法主要用于获取应用的包信息,然后返回LoadedApk对象

public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,

CompatibilityInfo compatInfo) {

return getPackageInfo(ai, compatInfo, null, false, true, false);

}

private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,

ClassLoader baseLoader, boolean securityViolation, boolean includeCode,

boolean registerPackage) {

final boolean differentUser = (UserHandle.myUserId() != UserHandle.getUserId(aInfo.uid));

synchronized (mResourcesManager) {

WeakReference ref;

if (differentUser) {

// Caching not supported across users

ref = null;

} else if (includeCode) {

ref = mPackages.get(aInfo.packageName);

} else {

ref = mResourcePackages.get(aInfo.packageName);

}

LoadedApk packageInfo = ref != null ? ref.get() : null;

if (packageInfo == null || (packageInfo.mResources != null

&& !packageInfo.mResources.getAssets().isUpToDate())) {

if (localLOGV) Slog.v(TAG, (includeCode ? "Loading code package "
"Loading resource-only package ") + aInfo.packageName
  • " (in " + (mBoundApplication != null

? mBoundApplication.processName : null)

  • “)”);

packageInfo =

new LoadedApk(this, aInfo, compatInfo, baseLoader,

securityViolation, includeCode &&

(aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage);

if (mSystemThread && “android”.equals(aInfo.packageName)) {

packageInfo.installSystemApplicationInfo(aInfo,

getSystemContext().mPackageInfo.getClassLoader());

}

if (differentUser) {

// Caching not supported across users

} else if (includeCode) {

mPackages.put(aInfo.packageName,

new WeakReference(packageInfo));

} else {

mResourcePackages.put(aInfo.packageName,

new WeakReference(packageInfo));

}

}

return packageInfo;

}

}

然后接着看handleLaunchActivity方法

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {

WindowManagerGlobal.initialize();

最后

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

资料⬅专栏获取
n != null

? mBoundApplication.processName : null)

  • “)”);

packageInfo =

new LoadedApk(this, aInfo, compatInfo, baseLoader,

securityViolation, includeCode &&

(aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage);

if (mSystemThread && “android”.equals(aInfo.packageName)) {

packageInfo.installSystemApplicationInfo(aInfo,

getSystemContext().mPackageInfo.getClassLoader());

}

if (differentUser) {

// Caching not supported across users

} else if (includeCode) {

mPackages.put(aInfo.packageName,

new WeakReference(packageInfo));

} else {

mResourcePackages.put(aInfo.packageName,

new WeakReference(packageInfo));

}

}

return packageInfo;

}

}

然后接着看handleLaunchActivity方法

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {

WindowManagerGlobal.initialize();

最后

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。

[外链图片转存中…(img-qv6XnfkS-1718993667533)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

资料⬅专栏获取

相关文章:

Android系统揭秘(一)-Activity启动流程(上)

public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { IApplicationThread whoThread (IApplicationThread) contextThread; … try { … int result …...

使用Java实现哈夫曼编码

前言 哈夫曼编码是一种经典的无损数据压缩算法&#xff0c;它通过赋予出现频率较高的字符较短的编码&#xff0c;出现频率较低的字符较长的编码&#xff0c;从而实现压缩效果。这篇博客将详细讲解如何使用Java实现哈夫曼编码&#xff0c;包括哈夫曼编码的原理、具体实现步骤以…...

IDEA、PyCharm等基于IntelliJ平台的IDE汉化方式

PyCharm 或者 IDEA 等编辑器是比较常用的&#xff0c;默认是英文界面&#xff0c;有些同学用着不方便&#xff0c;想要汉化版本的&#xff0c;但官方没有这个设置项&#xff0c;不过可以通过插件的方式进行设置。 方式1&#xff1a;插件安装 1、打开设置 File->Settings&a…...

visual studio 创建c++项目

目录 环境准备&#xff1a;安装 visual studiovisual studio 创建c项目Tips&#xff1a;新建cpp文件注释与取消注释代码 其他初学者使用Visual Studio开发C和C时常遇到的3个坑 环境准备&#xff1a;安装 visual studio 官网&#xff1a;https://visualstudio.microsoft.com/zh…...

MGV电源维修KUKA机器人电源模块PH2003-4840

MGV电源维修 库卡电源模块维修 机器人电源模块维修 库卡控制器维修 KUKA电源维修 库卡机器人KUKA主机维修 KUKA驱动器模块维修 机械行业维修&#xff1a;西门子系统、法那克系统、沙迪克、FIDIA、天田、阿玛达、友嘉、大宇系统&#xff1b;数控冲床、剪板机、折弯机等品牌数控…...

设置浏览器互不干扰

目录 一、查看浏览器文件路径 二、 其他盘新建文件夹Cache 三、以管理员运行CMD 四、执行命令 一、查看浏览器文件路径 chrome://version/ 二、 其他盘新建文件夹Cache D:\chrome\Cache 三、以管理员运行CMD 四、执行命令 Mklink /d "C:\Users\Lenovo\AppData\Loca…...

kafka操作命令详解

目录 1、集群运维命令 1.1、集群启停命令 1.3、集群迁移命令 1.4、权限管理命令 1.4.1、权限参数介绍 1.4.2、增加权限命令 1.4.3、移出权限命令 1.4.4、查看所有topic权限命令 1.4.5、查看某个topic权限命令 2、生产者命令 2.1、创建topic命令 2.2、删除topic命令 …...

graalvm jdk和openjdk

下载地址:https://github.com/graalvm/graalvm-ce-builds/releases 官网: https://www.graalvm.org...

docker基础使用教程

1.准备工作 例子&#xff1a;工程在docker_test 生成requirements.txt文件命令&#xff1a;&#xff08;使用参考链接2&#xff09; pip list --formatfreeze > requirements.txt 参考链接1&#xff1a; 安装pipreqs可能比较困难 python 项目自动生成环境配置文件require…...

计算机网络 交换机的安全配置

一、理论知识 1.交换机端口安全功能介绍 交换机端口安全功能是针对交换机端口进行安全属性的配置&#xff0c;以控制用户的安全接入。主要包括以下两种配置项&#xff1a; ①限制交换机端口的最大连接数&#xff1a;控制交换机端口连接的主机数量&#xff1b;防止用户进行恶…...

深入解析大语言模型系列:Transformer架构的原理与应用

引言 在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;大语言模型&#xff08;Large Language Models, LLMs&#xff09;近几年取得了突破性的进展&#xff0c;而 Transformer 作为这些模型的核心架构&#xff0c;功不可没。本文将详细介绍 Transformer 的原理、结…...

uni-app地图组件控制

uni.createMapContext(mapId,this) 创建并返回 map 上下文 mapContext 对象。在自定义组件下&#xff0c;第二个参数传入组件实例this&#xff0c;以操作组件内 <map> 组件。 注意&#xff1a;uni.createMapContext(mapId, this) app-nvue 平台 2.2.5 支持 uni.create…...

前端调用api发请求常用的请求头content- type的类型和常用场景

Content-Type 是一个非常重要的HTTP头&#xff0c;它定义了发送给服务器或客户端的数据的MIME类型。这对于服务器和客户端正确解析和处理数据至关重要。下面是一些常见的 Content-Type 值及其用途和区别。 常见的 Content-Type 值 text/plain • 用途: 纯文本&#xff0c;无格…...

数据仓库之SparkSQL

Apache Spark SQL是Spark中的一个组件&#xff0c;专门用于结构化数据处理。它提供了通过SQL和DataFrame API来执行结构化数据查询的功能。以下是对Spark SQL的详细介绍&#xff1a; 核心概念 DataFrame: 定义: DataFrame是一个分布式数据集合&#xff0c;类似于关系型数据库中…...

如何在 MySQL 中导入和导出数据库以及重置 root 密码

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 如何导入和导出数据库 导出 要导出数据库&#xff0c;打开终端&#xff0c;确保你没有登录到 MySQL 中&#xff0c;然后输入以下命令&…...

基于uni-app和图鸟UI的云课堂小程序开发实践

摘要&#xff1a; 随着移动互联网的快速发展&#xff0c;移动学习已成为教育领域的重要趋势。本文介绍了基于uni-app和图鸟UI框架开发的云课堂小程序&#xff0c;该小程序实现了移动教学、移动学习、移动阅读和移动社交的完美结合&#xff0c;为用户提供了一个便捷、高效的学习…...

解决python从TD数据库取50w以上大量数据慢的问题

1.问题背景描述 python项目中的时序数据都存放在TD数据库中&#xff0c;数据是秒级存入的&#xff0c;当查询一周数据时将超过50w数据量&#xff0c;这是一次性获取全量数据到python程序很慢&#xff0c;全流程10秒以上&#xff0c;希望进行优化加速 2.排查 首先&#xff0c…...

游戏心理学Day21

玩家情绪与暴力攻击 情绪 情绪的分类 情绪是一种经常波动的东西&#xff0c;我们既体验过骄傲激动和开心&#xff0c;也体验过羞怯内疚和沮丧。我们的感受高度依赖于情境。研究者区分出至少三种途径来考察作为一种相对固定的人格特征的情绪&#xff0c;即为情感性&#xff0…...

接口测试基础 --- 什么是接口测试及其测试流程?

接口测试是软件测试中的一个重要部分&#xff0c;它主要用于验证和评估不同软件组件之间的通信和交互。接口测试的目标是确保不同的系统、模块或组件能够相互连接并正常工作。 接口测试流程可以分为以下几个步骤&#xff1a; 1.需求分析&#xff1a;首先&#xff0c;需要仔细…...

贪心+动归1

​​​​​​​​​​​​​​跳跃游戏 给你一个非负整数数组 nums &#xff0c;你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标&#xff0c;如果可以&#xff0c;返回 true &#xff1b;否则&#xff0…...

三星S20以上手机中的动态相片及其分解

三星S20以后的相机&#xff0c;相机拍出来的图片&#xff0c;用三星手机自带的“相册”打开之后&#xff0c;还会有“查看动态照片”的选项&#xff0c;点击之后就能查看拍照片时前后2秒左右的视频&#xff01; 不知道这个功能是不是三星独有的。 这样得到的图片非常大。因为…...

一文了解HarmonyOSNEXT发布重点内容

华为在2024年6月21日的开发者大会上正式发布了HarmonyOS NEXT版&#xff0c;这是华为在操作系统领域的一次重大飞跃&#xff0c;标志着华为在构建全场景智能生态方面的卓越成就。HarmonyOS NEXT版不仅带来了全新的系统架构和性能提升&#xff0c;还首次将AI能力融入系统&#x…...

矩阵中严格递增的单元格数

题目链接&#xff1a;leetcode:矩阵中严格递增的单元格数 描述 给你一个下标从 1 开始、大小为 m x n 的整数矩阵 mat&#xff0c;你可以选择任一单元格作为 起始单元格 。 从起始单元格出发&#xff0c;你可以移动到 同一行或同一列 中的任何其他单元格&#xff0c;但前提是目…...

超参数调优-通用深度学习篇(上)

文章目录 深度学习超参数调优网格搜索示例一&#xff1a;网格搜索回归模型超参数示例二&#xff1a;Keras网格搜索 随机搜索贝叶斯搜索 超参数调优框架Optuna深度学习超参数优化框架nvidia nemo大模型超参数优化框架 参数调整理论&#xff1a; 黑盒优化&#xff1a;超参数优化…...

小程序中data-xx是用方式

data-sts"3" 是微信小程序中的一种数据绑定语法&#xff0c;用于在 WXML&#xff08;小程序模板&#xff09;中将自定义的数据绑定到页面元素上。让我详细解释一下&#xff1a; data-xx 的作用&#xff1a; data-xx 允许你在页面元素上自定义属性&#xff0c;以便在事…...

【2024德国工作】外国人在德国找工作是什么体验?

挺难的&#xff0c;德语应该是所有中国人的难点。大部分中国人进德国公司要么是做中国业务相关&#xff0c;要么是做技术领域的工程师。先讲讲人在中国怎么找德国的工作&#xff0c;顺便延申下&#xff0c;德国工作的真实体验&#xff0c;最后聊聊在今年的德国工作签证申请条件…...

Unity中获取数据的方法

Input和GetComponent 一、Input 1、Input类&#xff1a; 用于处理用户输入&#xff08;如键盘、鼠标、触摸等&#xff09;的静态类 2、作用&#xff1a; 允许你检查用户的输入状态。如某个键是否被按下&#xff0c;鼠标的位置&#xff0c;触摸的坐标等 3、实例 (1) 键盘…...

Java的死锁问题

Java中的死锁问题是指两个或多个线程互相持有对方所需的资源&#xff0c;导致它们在等待对方释放资源时永久地阻塞的情况。 死锁产生条件 死锁发生通常需要满足以下四个必要条件&#xff1a; 互斥条件&#xff1a;至少有一个资源是只能被一个线程持有的&#xff0c;如果其他…...

Unity 公用函数整理【二】

1、在规定时间时间内将一个值变化到另一个值&#xff0c;使用Mathf.Lerp实现 private float timer;[Tooltip("当前温度")]private float curTemp;[Tooltip("开始温度")]private float startTemp 20;private float maxTemp 100;/// <summary>/// 升…...

千年古城的味蕾传奇-平凉锅盔

在甘肃平凉这片古老而神秘的土地上&#xff0c;有一种美食历经岁月的洗礼&#xff0c;依然散发着独特的魅力&#xff0c;那便是平凉锅盔。平凉锅盔&#xff0c;那可是甘肃平凉的一张美食名片。它外表金黄&#xff0c;厚实饱满&#xff0c;就像一轮散发着诱人香气的金黄月亮。甘…...

微信小程序视频如何下载

一、工具准备 1、抓包工具Fiddler Download Fiddler Web Debugging Tool for Free by Telerik 2、VLC media player Download official VLC media player for Windows - VideoLAN 3、微信PC端 微信 Windows 版 二、开始抓包 1、打开Fiddler工具&#xff0c;设置修改如下…...

SVN 安装教程

SVN 安装教程 SVN&#xff08;Subversion&#xff09;是一个开源的版本控制系统&#xff0c;广泛用于软件开发和文档管理。本文将详细介绍如何在不同的操作系统上安装SVN&#xff0c;包括Windows、macOS和Linux。 Windows系统上的SVN安装 1. 下载SVN 访问SVN官方网站或Visu…...

HTML静态网页成品作业(HTML+CSS)—— 家乡山西介绍网页(3个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有6个页面。 二、作品演示 三、代…...

【抽代复习笔记】20-群(十四):定理6的补充证明及三道循环置换例题

例1&#xff1a;找出S3中所有不能和(123)交换的元。 解&#xff1a;因为 (123)(1) (1)(123) (123)&#xff0c;(123)(132) (132)(123) (1)&#xff0c;所以(1)、(132)和(123)均可以交换&#xff1b; 而(12)(123) (23)&#xff0c;(123)(12) (13)&#xff0c;故 (12)(12…...

【单片机毕业设计选题24018】-基于STM32和阿里云的农业大棚系统

系统功能: 系统分为手动和自动模式&#xff0c;上电默认为自动模式&#xff0c;自动模式下系统根据采集到的传感器值 自动控制&#xff0c;温度过低后自动开启加热&#xff0c;湿度过高后自动开启通风&#xff0c;光照过低后自动开启补 光&#xff0c;水位过低后自动开启水泵…...

【计算机毕业设计】​206校园顺路代送微信小程序

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…...

9、PHP 实现调整数组顺序使奇数位于偶数前面

题目&#xff1a; 调整数组顺序使奇数位于偶数前面 描述&#xff1a; 输入一个整数数组&#xff0c;实现一个函数来调整该数组中数字的顺序&#xff0c;使得所有的奇数位于数组的前半部分&#xff0c; 所有的偶数位于位于数组的后半部分&#xff0c;并保证奇数和奇数&#xff…...

iOS开发工具-网络封包分析工具Charles

一、Charles简介 Charles 是在 Mac 下常用的网络封包截取工具&#xff0c;在做 移动开发时&#xff0c;我们为了调试与服务器端的网络通讯协议&#xff0c;常常需要截取网络封包来分析。 Charles 通过将自己设置成系统的网络访问代理服务器&#xff0c;使得所有的网络访问请求…...

7、PHP 实现矩形覆盖

题目&#xff1a; 矩形覆盖 描述&#xff1a; 我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。 请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形&#xff0c;总共有多少种方法&#xff1f; <?php function rectCover($number) {$prePreNum 1;$preNum 2;$temp 0;i…...

鸿蒙开发通信与连接:【@ohos.wifiext (WLAN)】

WLAN 说明&#xff1a; 本模块首批接口从API version 8开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 该文档中的接口只供非通用类型产品使用&#xff0c;如路由器等&#xff0c;对于常规类型产品&#xff0c;不应该使用这些接口。 导入模块 …...

Ps:脚本事件管理器

Ps菜单&#xff1a;文件/脚本/脚本事件管理器 Scripts/Script Events Manager 脚本事件管理器 Script Events Manager允许用户将特定的事件&#xff08;如打开、存储或导出文件&#xff09;与 JavaScript 脚本或 Photoshop 动作关联起来&#xff0c;以便在这些事件发生时自动触…...

redis哨兵模式下业务代码连接实现

目录 一&#xff1a;背景 二&#xff1a;实现过程 三&#xff1a;总结 一&#xff1a;背景 在哨兵模式下&#xff0c;真实的redis服务地址由一个固定ip转变为可以变化的ip,这样我们业务代码在连接redis的时候&#xff0c;就需要判断哪个主redis服务地址&#xff0c;哪个是从…...

Java中将文件转换为Base64编码的字节码

在Java中&#xff0c;将文件转换为Base64编码的字节码通常涉及以下步骤&#xff1a; 读取文件内容到字节数组。使用java.util.Base64类对字节数组进行编码。 下面是一个简单的Java示例代码&#xff0c;演示如何实现这个过程&#xff1a; import java.io.File; import java.io…...

【C++高阶】探索STL的瑰宝 map与set:高效数据结构的奥秘与技巧

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ ⏩收录专栏⏪&#xff1a;C “ 登神长阶 ” &#x1f921;往期回顾&#x1f921;&#xff1a;初步了解 二叉搜索树 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀map与set &#x1f4d2;1.…...

cd 命令特殊路径符 mkdir命令

cd 特殊路径符 cd . 表示当前目录&#xff0c;比如 cd ./Desktop表示切换到当前目录下的Desktop目录内&#xff0c;和 cd Desktop效果一致。cd … 表示上一级目录&#xff0c;比如 cd … 即可切换到上一级目录&#xff0c;cd…/…切换到上二级目录。cd ~ 表示 HOME 目录&#…...

Mongodb UPDATE, 使用$position指定向数组中插入新元素的位置

学习mongodb&#xff0c;体会mongodb的每一个使用细节&#xff0c;欢迎阅读威赞的文章。这是威赞发布的第72篇mongodb技术文章&#xff0c;欢迎浏览本专栏威赞发布的其他文章。如果您认为我的文章对您有帮助或者解决您的问题&#xff0c;欢迎在文章下面点个赞&#xff0c;或者关…...

【Kafka】Kafka Broker工作流程、节点服役与退役、副本、文件存储、高效读写数据-08

【Kafka】Kafka Broker工作流程、节点服役与退役、副本、文件存储、高效读写数据 1. Kafka Broker 工作流程1.1 Zookeeper 存储的 Kafka 信息1.2 Kafka Broker总体工作流程1.2.1 Controller介绍 1.3 Broker 重要参数 2. 节点服役与退役3. Kafka副本 1. Kafka Broker 工作流程 …...

如何恢复未格式化分区数据?看这里!

什么是未格式化分区&#xff1f; 未格式化或RAW文件系统的分区无法被Windows操作系统识别和挂载&#xff0c;因此&#xff0c;Windows会提示你进行格式化以创建新的文件系统。注意&#xff0c;不要进行格式化。通常&#xff0c;文件系统变为未格式化或RAW会出现以下常见错误消…...

通过“BOSS”精通比特币,深入认识私钥、账户和钱包

来源:币界原创 作者:636Marx 无论当今数字货币技术如何发展&#xff0c;认识区块链技术幕后的关键机制至关重要。无论您是新手还是经验丰富的数字货币从业者&#xff0c;掌握钱包地址、公钥和私钥的复杂性都有无可替代重要性。进入 BOSS Wallet&#xff0c;这是一款尖端的 Web…...

进程与线程的区别

进程&#xff08;Process&#xff09; 1&#xff1a;进程是操作系统分配资源的基本单位 2&#xff1a;每个进程都有自己独立的虚拟地址空间&#xff0c;虚拟地址空间映射真实物理地址 3&#xff1a;进程之间相互隔离&#xff0c;某一个进程的崩溃不会影响到其它进程 4&…...