Android Framework之Activity启动流程

news/2024/11/18 3:26:54/文章来源:https://www.cnblogs.com/zhanyaowang/p/18293205

原文地址 https://juejin.cn/post/7212432799848579133

启动Activty触发点为Activity中的startActivity。

Activity startActivity ->

Instrumentation --> execStartActivity

 
try {
    intent.migrateExtraStreamToClipData(who);
    intent.prepareToLeaveProcess(who);
    //
    int result = android.app.ActivityTaskManager.getService().startActivity(whoThread,
            who.getBasePackageName(), who.getAttributionTag(), 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);
}
 
/** @hide */
public static IActivityTaskManager getService() {
    //通过aidl接口获取ActivityTaskManagerService操作对象
    return IActivityTaskManagerSingleton.get();
}

通过aidl接口得到ActivityTaskManagerService的对象(简称ATMS),activity启动原来是ams管理的,在android10之后增加了ATMS,将原来AMS中的部分代码移动了此类中。

image.png

Instrumentation中调用了execStartActivity之后,最终调用了ATMS中的startActivityAsUser()方法。

 
@Override
public final int startActivity(IApplicationThread caller, String callingPackage,
        String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,
        String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,
        Bundle bOptions) {
    return startActivityAsUser(caller, callingPackage, callingFeatureId, intent, resolvedType,
            resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions,
            UserHandle.getCallingUserId());
}
 
private int startActivityAsUser(IApplicationThread caller, String callingPackage,
        @Nullable String callingFeatureId, Intent intent, String resolvedType,
        IBinder resultTo, String resultWho, int requestCode, int startFlags,
        ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) {
    assertPackageMatchesCallingUid(callingPackage);
    enforceNotIsolatedCaller("startActivityAsUser");

    userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser,
            Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");

    // TODO: Switch to user app stacks here.
    //通过ActivityStartController最终调用到了ActivityStarter中的execute方法。
    return getActivityStartController().obtainStarter(intent, "startActivityAsUser")
            .setCaller(caller)
            .setCallingPackage(callingPackage)
            .setCallingFeatureId(callingFeatureId)
            .setResolvedType(resolvedType)
            .setResultTo(resultTo)
            .setResultWho(resultWho)
            .setRequestCode(requestCode)
            .setStartFlags(startFlags)
            .setProfilerInfo(profilerInfo)
            .setActivityOptions(bOptions)
            .setUserId(userId)
            .execute();
}

通过ActivityStartController最终调用到了ActivityStarter中的execute方法。

execute方法方法调用:startActivityInner---> deliverToCurrentTopIfNeeded

RootWindowContainer-->resumeFocusedStacksTopActivities

ActivityStack-->resumeTopActivityUncheckedLocked-->resumeTopActivityInnerLocked

在resumeTopActivityInnerLocked末尾处调用了ActivityStackSupervisor中的startSpecificActivity方法。

image.png

ActivityStackSupervisor--->startSpecificActivity 代码如下:

 
void startSpecificActivity(ActivityRecord r, boolean andResume, boolean checkConfig) {
    // Is this activity's application already running?
    final WindowProcessController wpc =
            mService.getProcessController(r.processName, r.info.applicationInfo.uid);

    boolean knownToBeDead = false;
    if (wpc != null && wpc.hasThread()) {
        try {
            realStartActivityLocked(r, wpc, 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.
        knownToBeDead = true;
    }

    r.notifyUnknownVisibilityLaunchedForKeyguardTransition();

    final boolean isTop = andResume && r.isTopRunningActivity();
    mService.startProcessAsync(r, knownToBeDead, isTop, isTop ? "top-activity" : "activity");
}

此处,如果有app进程,则直接调用realStartActivityLocked方法。

如果没有进程,则去启动进程,我们进入mService.startProcessAsync() 代码,进行跟踪。

ActivityTaskManagerService-->startProcessAsync

image.png

方法中调用了这个方法:ActivityManagerInternal::startProcess,这个接口直接回调到了AMS中的startProcess。

ActivityManagerService-->startProcess--> startProcessLocked

ProcessList-->startProcessLocked-->startProcessLocked

image.png

final String entryPoint = "android.app.ActivityThread"; 这个值很关键

startProcessLocked方法继续往下。

startProcessLocked-->startProcess-->startProcess()-->createAppZygoteForProcessIfNeeded

image.png

image.png

创建AppZygote对象,保存到zygote集合中,并返回此对象。

创建appzygote对象之后,直接调用start方法。

image.png

此时来到了ZygoteProcess类中的start方法。

ZygoteProcess-->start()-->startViaZygote()

image.png

start方法调用后会根据传入的processClass参数进行创建新的进程然后调用该进程的main方法,在start函数reture之后创建出来的进程不受影响,继续运行。

进入到startViaZygote方法之内。

image.png

调用zygoteSendArgsAndGetResult,注意入参,会检查是否需要建立socket链接进行通信。

进入openZygoteSocketIfNeeded方法,最终调用connect方法。

image.png

 
/**
 * @hide for internal use only
 */
public static final int SOCKET_BUFFER_SIZE = 256;

先是创建一个LocalSocket,然后connect链接,并设置了传输buffer字节限制,SOCKET_BUFFER_SIZE=256.

socket链接之后,我们回到zygoteSendArgsAndGetResult,进入该方法中。

image.png

进入到 attemptUsapSendArgsAndGetResult 方法中,写入消息:

image.png

前面传入的 final String entryPoint = "android.app.ActivityThread" 值就是processClass,被socket发送了出去。

Zygote查看接收方法,如果调用进程的main方法的。

zygote初始化的时候会开启一个无限循环,一直读取发送过来的值。

ZygoteInit -->main()

ZygoteServer --> runSelectLoop()

image.png

进入到processOneCommand方法中,继续调用 handleChildProc 方法,最后调用Zygote中的 childZygoteInit 方法。

ZygoteServer --> runSelectLoop()

ZygoteConnection --> processOneCommand()--> handleChildProc()

processOneCommand的方法如下:

image.png

readArgumentList就是读取localSocket发送过来的值。(android.app.ActivityThread 这个processClass字符参数就被发送到了这里)

ZygoteInit --> childZygoteInit()

 
/**
 * The main function called when starting a child zygote process. This is used as an alternative
 * to zygoteInit(), which skips calling into initialization routines that start the Binder
 * threadpool.
 */
static final Runnable childZygoteInit(
        int targetSdkVersion, String[] argv, ClassLoader classLoader) {
    RuntimeInit.Arguments args = new RuntimeInit.Arguments(argv);
    return RuntimeInit.findStaticMain(args.startClass, args.startArgs, classLoader);
}

RuntimeInit-->findStaticMain() 通过反射调用class的main方法。

image.png

传递进来的className是ActivityThread,所以此处反射的就是ActivityThread的main方法,我们直接进入其中看下代码。

ActivityThread.main()

代码如下:

 
public static void main(String[] args) {
    
    ……………………
    
    Looper.prepareMainLooper();

    // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
    // It will be in the format "seq=114"
    long startSeq = 0;
    if (args != null) {
        for (int i = args.length - 1; i >= 0; --i) {
            if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                startSeq = Long.parseLong(
                        args[i].substring(PROC_START_SEQ_IDENT.length()));
            }
        }
    }
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);

    …………………

    // End of event ActivityThreadMain.
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}

ActivityThread --> main()-->attach(false)

image.png

注意此处的attach传入的参数是false,不是true,我们不要看到下面的 createAppContext中创建context逻辑里面去了。

IActivityManager --> attachApplication(); 此类是aidl接口,让ActivityThread和AMS直接通信。

ActivityManageService--> attachApplication() 这个方法很长,大约四百多行,我们直接搜索参数thread使用的地方,找到了thread.bindApplication();

ActivityThread-->bindApplication() -->sendMessage(H.BIND_APPLICATION, data)-->handleBindApplication()

image.png

图片中红色箭头处是创建applicatio的流程:

LoadedApk-->makeApplication()

Instumentation--> newApplication()

Application --> attach()-->attachBaseContext()

ContextWrapper -->attachBaseContext()

此刻的Application创建好之后,并没有发现执行application的onCreate方法,所以我们需要回到ActtivityThread中的当前代码节点继续往下看。

 
try {
    //创建application
    app = data.info.makeApplication(data.restrictedBackupMode, null);

    if (!data.restrictedBackupMode) {
        if (!ArrayUtils.isEmpty(data.providers)) {
             //初始化contentProvider
            installContentProviders(app, data.providers);
        }
    }

    try {
        mInstrumentation.onCreate(data.instrumentationArgs);
    }
    catch (Exception e) {
        throw new RuntimeException(
            "Exception thrown in onCreate() of "
            + data.instrumentationName + ": " + e.toString(), e);
    }
    try {
        //回调application的onCreate方法
        mInstrumentation.callApplicationOnCreate(app);
    } catch (Exception e) {
        if (!mInstrumentation.onException(app, e)) {
            throw new RuntimeException(
              "Unable to create application " + app.getClass().getName()
              + ": " + e.toString(), e);
        }
    }
} finally {
    // If the app targets < O-MR1, or doesn't change the thread policy
    // during startup, clobber the policy to maintain behavior of b/36951662
    if (data.appInfo.targetSdkVersion < Build.VERSION_CODES.O_MR1
            || StrictMode.getThreadPolicy().equals(writesAllowedPolicy)) {
        StrictMode.setThreadPolicy(savedPolicy);
    }
}

我们进入到callApplicationOnCreate方法中,代码如下:

 
/**
 * Perform calling of the application's {@link Application#onCreate}
 * method.  The default implementation simply calls through to that method.
 *
 * <p>Note: This method will be called immediately after {@link #onCreate(Bundle)}.
 * Often instrumentation tests start their test thread in onCreate(); you
 * need to be careful of races between these.  (Well between it and
 * everything else, but let's start here.)
 *
 * @param app The application being created.
 */
public void callApplicationOnCreate(Application app) {
    app.onCreate();
}

该方法直接执行了Application的onCreate方法,此时我们的程序已经算启动了。

前面流程先回顾一下

activity通过startActivity时,最终会调用到ActivityStackSupervisor--->startSpecificActivity() ,进程如果不存在,则去启动,如果进程存在,则会执行realStartActivityLocked()方法。

image.png

而在该方法中,我们刚刚是看的进程未启动的流程,所以这个地方它是如何又回调的这个地方来的呢?我们回到ActivityManagerService中的thread.bindApplication()代码节点继续往下走。

image.png

图片中红框一 调用了ATMS中的attachApplication() 方法。

红框二处是绑定服务的流程,先略过。

进入到ATMS中的attachApplication方法。

ATMS-->attachApplication();

RootWindowContainer --> attachApplication() -- startActivityForAttachedApplicationIfNeeded();

image.png

进入到startActivityForAttachedApplicationIfNeede方法中。

 
private boolean startActivityForAttachedApplicationIfNeeded(ActivityRecord r,
        WindowProcessController app, ActivityRecord top) {
    if (r.finishing || !r.okToShowLocked() || !r.visibleIgnoringKeyguard || r.app != null
            || app.mUid != r.info.applicationInfo.uid || !app.mName.equals(r.processName)) {
        return false;
    }

    try {
        //此处回调了ActivityStackSupervisor中的realStartActivityLocked方法。
        if (mStackSupervisor.realStartActivityLocked(r, app,
                top == r && r.isFocusable() /*andResume*/, true /*checkConfig*/)) {
            mTmpBoolean = true;
        }
    } catch (RemoteException e) {
        Slog.w(TAG, "Exception in new application when starting activity "
                + top.intent.getComponent().flattenToShortString(), e);
        mTmpRemoteException = e;
        return true;
    }
    return false;
}

此处回调了ActivityStackSupervisor中的realStartActivityLocked方法。

ActivityStackSupervisor --> realStartActivityLocked();

这个地方就和前面形成了闭环,即启动activity时,先检查进程是否存在,如果存在,直接启动activity;如果不存在,去创建进程,进程创建成功后,再次启动activity。

我们进入到realStartActivityLocked 看看activity是怎么启动的,它的生命周期又是如何回调的。

 
// Create activity launch transaction.
final ClientTransaction clientTransaction = ClientTransaction.obtain(
        proc.getThread(), r.appToken);

final DisplayContent dc = r.getDisplay().mDisplayContent;
clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),


// Schedule transaction.
mService.getLifecycleManager().scheduleTransaction(clientTransaction);

该方法中调用了ATMS中的getLifecycleManager方法,并最终调用了ClientLifecycleManager.scheduleTransaction()方法,并将ClientTransaction对象当做参数传递了进去。

注意:此处创建了ClientTransaction对象,并addCallBack(). LaunchActivityItem,这个LaunchActivityItem是ClientTransactionItem的子类,后面执行execut方法时会被回调到。

 
public void schedule() throws RemoteException {
    mClient.scheduleTransaction(this);
}

ClientLifecycleManager --> scheduleTransaction()

IApplicationThread --> scheduleTransaction()

ApplicationThread(ActivityThrad的内部类) --> scheduleTransaction()

ClientTransactionHandler(ActivitThread的父类) --> scheduleTransaction()

 

/** Prepare and schedule transaction for execution. */
void scheduleTransaction(ClientTransaction transaction) {
    transaction.preExecute(this);
    sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
}

ActivityThread的handler中查看该消息。

image.png

TransactionExecutor -- > execute() --> executeCallbacks()

ClientTransactionItem --> execute() 调用其父类的execute方法。

LaunchActivityItem --> execute() .

ClientTransactionHandler --> handleLaunchActivity() 它只有一个子类,就是ActivityThread。

ActivityThread --> handleLaunchActivity() --> performLaunchActivity();

performLaunchActivity代码精简如下:

 
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    
    //反射创建activity类。
    ContextImpl appContext = createBaseContextForActivity(r);
    Activity activity = null;
    try {
        java.lang.ClassLoader cl = appContext.getClassLoader();
        activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);
        StrictMode.incrementExpectedActivityCount(activity.getClass());
        r.intent.setExtrasClassLoader(cl);
        r.intent.prepareToEnterProcess();
        if (r.state != null) {
            r.state.setClassLoader(cl);
        }
    } catch (Exception e) {
        if (!mInstrumentation.onException(activity, e)) {
            throw new RuntimeException(
                "Unable to instantiate activity " + component
                + ": " + e.toString(), e);
        }
    }
    

    activity.attach(appContext, this, getInstrumentation(), r.token,
            r.ident, app, r.intent, r.activityInfo, title, r.parent,
            r.embeddedID, r.lastNonConfigurationInstances, config,
            r.referrer, r.voiceInteractor, window, r.configCallback,
            r.assistToken);


    if (activity != null) {      
        activity.mCalled = false;
        //执行Activity的onCreate方法。
        if (r.isPersistable()) {
            mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
        } else {
            mInstrumentation.callActivityOnCreate(activity, r.state);
        }

    }

    return activity;
}

其中先是反射创建了activity类,然后attach,最后通过Instumentation回调了Activity的onCreate方法。

Intrumentation --> callActivityOnCreate(); Activity --> onCreate();

 
mWindow = new PhoneWindow(this, window, activityConfigCallback);

attach方法就是赋值变量、然后给Activity创建一个视图窗口。

Activity.onCreate()被触发

在触发了Activity的onCreate的方法之后,整个Activity就算启动了,而Activity的启动流程也算是完毕。

Activity的其他生命周期,如start、resume、restart等方法回调是在TransactionExecutor --execute()--> executeLifecycleState().这个和前面的executeCallbacks()触发流程基本差不多,这里就不继续分析了。

avtivity启动后都是在onResume界面才能看到画面,所以我们单独看下onResume的触发流程。

TransactionExecutor-->executeLifecycleState()

ActivityLifecycleItem(父类的方法) --> execute

ResumeActivityItem --> handleResumeActivity()

ActivityThread --> handleResumeActivity();

 
if (r.activity.mVisibleFromClient) {
    r.activity.makeVisible();
}

Activity中的makeVisible方法如下:

 
void makeVisible() {
    if (!mWindowAdded) {
        ViewManager wm = getWindowManager();
        wm.addView(mDecor, getWindow().getAttributes());
        mWindowAdded = true;
    }
    mDecor.setVisibility(View.VISIBLE);
}

mDecor.setVisibility(View.VISIBLE);

mDecor.setVisibility(View.VISIBLE) 显示界面。

Activity启动全流程汇总

Activity --> startActivity()

Instrumentation ---> execStartActivity

ActivityTaskManagerService --> startActivityAsUser()

ActivityStarter--execute()--> startActivityInner--》deliverToCurrentTopIfNeeded()

RootWindowContainer--》resumeFocusedStacksTopActivities

ActivityStack-->resumeTopActivityUncheckedLocked-->resumeTopActivityInnerLocked

ActivityStackSupervisor--》startSpecificActivity()

startSpecificActivity方法内有两种情况:

 
1、存在进程,直接做创建activity并启动等流程:ActivityStackSupervisor--》 realStartActivityLocked

2、不存在进程,创建进程,然后再次回调  realStartActivityLocked()。

先看进程没有的流程。

先从zygote发送进程创建的流程开始。

ActivityTaskManagerService-->startProcessAsync

ActivityManagerService-->startProcess--> startProcessLocked()

ProcessList-->startProcessLocked-->startProcessLocked()-->startProcess-->startProcess()-->createAppZygoteForProcessIfNeeded()去创建zygote对象

 
//在startProcesslocked会判断是否启动了app进程,如果启动了,直接返回,没有则去通过Zygote发送消息的方式启动ActivitThread进程,

ZygoteProcess-->start()-->startViaZygote()--》zygoteSendArgsAndGetResult()

 
先建立localSocket链接,然后发送消息,去创建ActivitThread,并调用main方法。

另外此处发送的消息是什么要搞清楚?是processClass字段,值是 android.app.ActivityThread 

发送消息成功后,我们再看Zygote的接收流程:

从zygote初始化走起。

ZygoteInit -->main()

ZygoteServer --> runSelectLoop() :

 
runSelectLoop开启无限循环,一直读取localSocket的值。

ZygoteConnection --> processOneCommand()--> handleChildProc()

 
    try {
        //一直读取消息,在前面发送“com.app.AcitityThread”消息之后,这里会受到此消息。
        args = Zygote.readArgumentList(mSocketReader);
    } catch (IOException ex) {
        throw new IllegalStateException("IOException on command socket", ex);
    }

ZygoteInit --> childZygoteInit()

RuntimeInit-->findStaticMain() 通过反射调用class的main方法。

这里就是Zygote的发送消息、接收消息并创建ActivityThread且执行其main()方法的流程。


查看ActivityThread被创建之后的流程。

ActivityThread.main()

ActivityThread --> main()-->attach(false)

 
    attach传入的是false。

IActivityManager --> attachApplication();

 
    IActivityManager此类是aidl接口,让ActivityThread和AMS直接通信

ActivityManageService--> attachApplication()

ActivityThread-->bindApplication() -->sendMessage(H.BIND_APPLICATION, data)-->handleBindApplication()

 
    //handleBindApplication执行流程如下:

LoadedApk-->makeApplication()

Instumentation--> newApplication()

Application --> attach()-->attachBaseContext()

ContextWrapper -->attachBaseContext()

application和context都创建完毕之后,继续执行ActivityThread 之后的代码。

ActivityThread-->callApplicationOnCreate()

 
public void callApplicationOnCreate(Application app) {
	app.onCreate();
}		

此处执行了Application的onCreate方法,我们的程序已经启动

进程启动并执行onCreate方法之后,我们回到ATMS继续执行之后的方法。

ATMS-->attachApplication();

RootWindowContainer --> attachApplication() -- startActivityForAttachedApplicationIfNeeded();

ActivityStackSupervisor --> realStartActivityLocked();

 
   调用realStartActivityLocke()d和刚开始点击activity时的流程形成了闭环,即启动进程之后,再去执行启动Activity的流程。

我们现在可以查看realStartActivityLocked()方法之后的流程。

ClientLifecycleManager --> scheduleTransaction()

IApplicationThread --> scheduleTransaction()

ApplicationThread(ActivityThrad的内部类) --> scheduleTransaction()

ClientTransactionHandler(ActivitThread的父类) --> scheduleTransaction()

TransactionExecutor -- > execute() --> executeCallbacks()

ClientTransactionItem --> execute()

LaunchActivityItem --> execute()

ClientTransactionHandler --> handleLaunchActivity()

 
    ClientTransactionHandler它只有一个子类,就是ActivityThread。

ActivityThread --> handleLaunchActivity() --> performLaunchActivity();

 
    performLaunchActivity此处反射创建了activity类,然后attach,最后通过Instumentation回调了Activity的onCreate方法。

Activity.onCreate()被触发

至此Actiivty的启动流程已经完毕。

最后我们再看一下activity显示的流程。

avtivity启动后都是在onResume界面才能看到画面,所以我们单独看下onResume的触发流程。

TransactionExecutor-->executeLifecycleState()

ActivityLifecycleItem(父类的方法) --> execute

ResumeActivityItem --> handleResumeActivity()

ActivityThread --> handleResumeActivity();

 
if (r.activity.mVisibleFromClient) {
	r.activity.makeVisible();
}

void makeVisible() {
	if (!mWindowAdded) {
		ViewManager wm = getWindowManager();
		wm.addView(mDecor, getWindow().getAttributes());
		mWindowAdded = true;
	}
	mDecor.setVisibility(View.VISIBLE);
}

	
mDecor.setVisibility(View.VISIBLE);

mDecor.setVisibility(View.VISIBLE);这个就是显出view的方法,VISIBLE之后,我们的界面也能看到了。

启动时序总图

image.png

总结

Activity启动流程通过startActivity触发,会去检查进程是否启动,如果没有启动进程,会通过Zygote进程通过localSocket发送消息,消息的最关键信息是进程名字:android.app.ActivityThread。

因为zygote开启了无限循环检测,所以能够立马收到消息,并反射创建我们的ActivityThread类,并执行main方法。

ActivityThrad创建之后,会通过AMS中的attachApplication方法,创建Application,最后回到ActivityThread将application绑定context,最后执行Application的onCreate方法,app进程启动。

application启动之后再去启动activity。

启动activity最终会调用ActivityThread中的handleLaunchActivity方法,此方法调用之后会触发activity的onCreate方法,activity启动。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/741545.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

3大主流分布式事务框架详解(图文总结)

1 简要介绍 随着微服务架构的不断发展,分布式系统逐渐普及到后端领域的每一个角落。 在分布式系统中,跨多个服务的数据一致性一直是一个重大挑战,为解决这一挑战,分布式事务应运而生。 作者在之前的文章《五种分布式事务解决方案》和《4大主流分布式算法介绍》中,详细介绍…

数据跨境传输法规日趋完善,企业如何规避合规风险?

随着全球化的发展,跨境数据传输变得日益频繁。在数字化时代,数据安全是企业运营的关键。数据跨境传输由于涉及不同国家和地区,其安全合规性面临着更大的风险和挑战。2022年,国家网信办发布了《数据出境安全评估办法》(以下简称《办法》),并于同年9月1日开始实施。《办法…

asp.net mvc中数据传递的几种方式

2.第二步,要想session正常使用,在启动文件中配置如下 3.第三步,接受数据

保密U盘仍然存在数据安全危机?该怎么用才能规避?

保密U盘以前主要用于国家涉密单位或部门,但随着人们对于信息安全的重视越来越高,在民用企事业单位以及个人用户方面也应用得日益广泛。使用保密U盘在安全性上比普通U盘具有优势,但却仍然存在安全危机,具体为: 病毒和木马程序的风险: 保密U盘在使用过程中,极易被植入“摆…

离散傅里叶变换(DFT)和快速傅里叶变换(FFT)

离散傅里叶变换(DFT)和快速傅里叶变换(FFT)是信号处理和数字信号处理中的基本工具。它们用于将时间域的信号转换为频率域的表示,帮助分析信号的频谱成分。 1. 离散傅里叶变换(DFT) 1.1 DFT的基本概念 DFT是将离散时间信号转换为频域表示的工具。对于长度为 N 的离散信号…

Simple WPF: WPF实现一个MINIO等S3兼容对象存储上传文件的小工具

之前在阿里云ECS 99元/年的活动实例上搭建了一个测试用的MINIO服务,以前都是直接当基础设施来使用的,这次准备自己学一下S3兼容API相关的对象存储开发,因此有了这个小工具。目前仅包含上传功能,后续计划开发一个类似图床的对象存储应用。最新内容优先发布于个人博客:小虎技…

前缀和简析

前缀和 前置知识:$\sum_{i = 1}^{r}{a_i} = a_l + a_{l + 1} + \dots + a_{r - 1} + a_r$ 考虑以下数组:$i$ $1$ $2$ $3$$a_I$ $3$ $5$ $7$如果我们要查询 $\sum_{i = 1}^{2}{a_i}$,很显然可以得到 $\sum_{i = 1}^{2}{a_i} = 3 + 5 = 8$。如果我们要查询 $\sum_{i = l}^{r}{…

推挽输出和开漏输出

一、推挽输出 1.1推挽输出的概念 推挽(push-pull)输出是由两个MOS或者三极管组成,两个管子始终保持一个导通,另一个截止的状态。当输入高电平时,叫做推;上管Q1导通,下管Q2关闭;电流走向VCC→Q1→Vout。当输入低电平时,叫做挽;上管Q1关闭,下管Q2导通;电流走向Vout→…

数据血缘系列(3)—— 数据血缘可视化之美

大家好,我是独孤风。在当今数据驱动的商业环境中,数据治理成为企业成功的关键因素之一,而数据血缘正是数据治理成功的一个关键。 本文我们详细探讨下数据血缘可视化是什么,该如何实现。并顺便对比一下Apache Atlas 、Datahub、Openmetadata、Marquez、SQLLineage、Amundsen…

进度报告3

(1)1.学习Scanner来用键盘输入数据并写出实例代码并运行成功2.分支与循环结构,与c语言差不多3.Random来生成随机数用Random的案例:猜数字游戏4.数组(2)继续往后学习

防火防盗防CDN流量盗刷

没想到自己的小破站也逃不掉被攻击的命,分分钟就给我刷欠费了。本来不想写这篇文章的,但看到好多大佬(小林coding、 JavaGuide)近期cdn都被盗刷了。 还是来提醒下大家,防火防盗防cdn流量盗刷 事故时间:2024年7月5日晚8点左右 事故现场:好不容易到了周五,想着第二天就周…

记内存条硬件损坏蓝屏的 dump 文件分析

本文记录我的电脑的内存条的硬件损坏了,导致用着用着就蓝屏,我通过启动和故障恢复配置自动蓝屏打 dump 的功能,在蓝屏时创建了 dump 文件。通过分析 dump 文件大概猜测是内存的问题以下是我用 windbg 分析的两次蓝屏 第一次是用着 VS 的过程蓝屏了,我发现好多次都是使用 VS…