首页 > 其他分享 >android App启动流程三-Activity启动流程

android App启动流程三-Activity启动流程

时间:2024-03-19 23:00:02浏览次数:28  
标签:transaction mTransactionHandler 启动 int 流程 state activity android final

上一篇我们介绍了从App的进程创建到Application启动执行,今天我们继续深入学习一下,Activity的启动流程。

realStartActivityLocked

我们接着上一篇,从ActivityTaskManagerService.attachApplication函数看起,最终发现会执行到ActivityTaskSupervisor.realStartActivityLocked方法去真正的准备启动Activity,还记得我们上一篇中如果APP进程已经创建,也会执行到这个方法么?

 boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
            boolean andResume, boolean checkConfig) throws RemoteException {

        ...

        try {
            ...

            try {
               ...

                // 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),
                        System.identityHashCode(r), 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, proc.getReportedProcState(),
                        r.icicle, r.persistentState, results, newIntents,
                        dc.isNextTransitionForward(), proc.createProfilerInfoIfNeeded(),
                                r.assistToken));

                // Set desired final state.
                final ActivityLifecycleItem lifecycleItem;
                //传入参数为true
                if (andResume) {
                    lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward());
                } else {
                    lifecycleItem = PauseActivityItem.obtain();
                }
                clientTransaction.setLifecycleStateRequest(lifecycleItem);

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

               ...

            } catch (RemoteException e) {
               ...
            }
        } finally {
            endDeferResume();
        }
 
        ...

        return true;
    }

上述代码andResume值从前面逻辑可知,一直为true,因此会执行if判断,lifecycleItem = ResumeActivityItem,继续执行到ClientLifecycleManager.scheduleTransaction:

void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
        final IApplicationThread client = transaction.getClient();
        transaction.schedule();
        if (!(client instanceof Binder)) {
            // If client is not an instance of Binder - it's a remote call and at this point it is
            // safe to recycle the object. All objects used for local calls will be recycled after
            // the transaction is executed on client in ActivityThread.
            transaction.recycle();
        }

ClientTransaction.schedule:

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

这里的这个mClient其实就是ActivityThread:

@Override
public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
    ActivityThread.this.scheduleTransaction(transaction);
}

通过以上代码,最终执行到ClientTransactionHandler.scheduleTransaction:

void scheduleTransaction(ClientTransaction transaction) {
    transaction.preExecute(this);
    sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
}

在这段代码中,发送了一个handler消息给ActivityThread:

case EXECUTE_TRANSACTION:
    final ClientTransaction transaction = (ClientTransaction) msg.obj;
    mTransactionExecutor.execute(transaction);
    if (isSystem()) {
        // Client transactions inside system process are recycled on the client side
        // instead of ClientLifecycleManager to avoid being cleared before this
        // message is handled.
        transaction.recycle();
    }
    // TODO(lifecycler): Recycle locally scheduled transactions.
    break;
public void execute(ClientTransaction transaction) { 
    ...... 
    executeCallbacks(transaction);  //code1 
 
    executeLifecycleState(transaction);  //code2 
    mPendingActions.clear(); 
    if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "End resolving transaction");
}

executeCallbacks

public void executeCallbacks(ClientTransaction transaction) {
    final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
    if (callbacks == null || callbacks.isEmpty()) {
        // No callbacks to execute, return early.
        return;
    }
    if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Resolving callbacks in transaction");

    final IBinder token = transaction.getActivityToken();
    ActivityClientRecord r = mTransactionHandler.getActivityClient(token);

    // In case when post-execution state of the last callback matches the final state requested
    // for the activity in this transaction, we won't do the last transition here and do it when
    // moving to final state instead (because it may contain additional parameters from server).
    final ActivityLifecycleItem finalStateRequest = transaction.getLifecycleStateRequest();
    final int finalState = finalStateRequest != null ? finalStateRequest.getTargetState()
            : UNDEFINED;
    // Index of the last callback that requests some post-execution state.
    final int lastCallbackRequestingState = lastCallbackRequestingState(transaction);

    final int size = callbacks.size();
    for (int i = 0; i < size; ++i) {
        final ClientTransactionItem item = callbacks.get(i);
        if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Resolving callback: " + item);
        final int postExecutionState = item.getPostExecutionState();
        final int closestPreExecutionState = mHelper.getClosestPreExecutionState(r,
                item.getPostExecutionState());
        if (closestPreExecutionState != UNDEFINED) {
            cycleToPath(r, closestPreExecutionState, transaction);
        }

        item.execute(mTransactionHandler, token, mPendingActions);
        item.postExecute(mTransactionHandler, token, mPendingActions);
        if (r == null) {
            // Launch activity request will create an activity record.
            r = mTransactionHandler.getActivityClient(token);
        }

        if (postExecutionState != UNDEFINED && r != null) {
            // Skip the very last transition and perform it by explicit state request instead.
            final boolean shouldExcludeLastTransition =
                    i == lastCallbackRequestingState && finalState == postExecutionState;
            cycleToPath(r, postExecutionState, shouldExcludeLastTransition, transaction);
        }
    }
}

其中item.execute会触发ClientTransactionItem.execute,其实就是执行LaunchActivityItem.execute:

public void execute(ClientTransactionHandler client, IBinder token,
                    PendingTransactionActions pendingActions) {
    Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
    ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
            mOverrideConfig, mReferrer, mVoiceInteractor, mState, mPersistentState,
            mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo,
            client, mAssistToken, mShareableActivityToken, mLaunchedFromBubble,
            mTaskFragmentToken);
    client.handleLaunchActivity(r, pendingActions, mDeviceId, null /* customIntent */);
    Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}

继续执行ClientTransactionHandler.handleLaunchActivity,其实就是执行ActivityThread.handleLaunchActivity:

public Activity handleLaunchActivity(ActivityClientRecord r, 
    PendingTransactionActions pendingActions, Intent customIntent) { 
    ...... 
    WindowManagerGlobal.initialize(); //code1 
    final Activity a = performLaunchActivity(r, customIntent);  //code2 
    ...... 
} 


private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { 
    ......

    ContextImpl appContext = createBaseContextForActivity(r);
    Activity activity = null;
    try {
        java.lang.ClassLoader cl = appContext.getClassLoader();
        //创建一个activity 
        activity = mInstrumentation.newActivity(  //code 3 
                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);
        }
    }
    try {
        //LoadedApk 构建 makeApplication  
        Application app = r.packageInfo.makeApplication(false, mInstrumentation); 
        ......
        if (activity != null) { 
            ......
            // Activity resources must be initialized with the same loaders as the 
            // application context. 
            appContext.getResources().addLoaders(
                    app.getResources().getLoaders().toArray(new ResourcesLoader[0]));
            appContext.setOuterContext(activity);
            //code4 
            //会在这个方法中创建Activity的PhoneWindow,并绑定对应的WindowManager。 
            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 (customIntent != null) {
                activity.mIntent = customIntent;
            }
            r.lastNonConfigurationInstances = null;
            checkAndBlockForNetworkAccess();
            activity.mStartedActivity = false;
            int theme = r.activityInfo.getThemeResource();
            if (theme != 0) {
                activity.setTheme(theme);
            }

            activity.mCalled = false;

            //code5 
            // 设置 mLifecycleState 为 ON_CREATE 
            if (r.isPersistable()) {
                mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
            } else {
                mInstrumentation.callActivityOnCreate(activity, r.state);

            }
            if (!activity.mCalled) {
                throw new SuperNotCalledException(
                        "Activity " + r.intent.getComponent().toShortString() +
                                " did not call through to super.onCreate()");
            }
            r.activity = activity;
            mLastReportedWindowingMode.put(activity.getActivityToken(),
                    config.windowConfiguration.getWindowingMode());
        }
        // 设置 mLifecycleState 为 ON_CREATE 
        r.setState(ON_CREATE);
        // updatePendingActivityConfiguration() reads from mActivities to update 
        // ActivityClientRecord which runs in a different thread. Protect modifications to 
        // mActivities to avoid race. 
        synchronized (mResourcesManager) {
            mActivities.put(r.token, r);
        }
    } catch (SuperNotCalledException e) {
        throw e;
    } catch (Exception e) {
        if (!mInstrumentation.onException(activity, e)) {
            throw new RuntimeException(
                    "Unable to start activity " + component
                            + ": " + e.toString(), e);
        }
    }
    return activity;
} 

上面代码code1处是启动windowManagerGlobal,由于windowManagerGlobal是单例模式,所以,一般认为 windowManagerGlobal的初始化就是在此时,其实就是为activity准备WindowManagerService。在code2处,将执 行performLaunchActivity,大家仔细阅读performLaunchActivity的函数会不难发现 code3处创建了一个activity实 例对象。然后执行到code4处,此处执行了activity的attach 生命周期,在这个生命周期里面,构建了activity的唯一 的phoneWindow对象,并且并绑定对应的WindowManager方便后期使用。然后代码会执行到code5,在code5里面就是调用Activity的onCreate生命周期的过程,同时mLifecycleState被设置为ON_CREATE。

这个地方就是执行了Activity.onCreate()生命周期。我们继续往下看:

executeLifecycleState

我们继续分析一下executeLifecycleState:

private void executeLifecycleState(ClientTransaction transaction) {
    //获取ActivityLifecycleItem,我们知道这里获取的是我们之前添加的ResumeActivityItem 
    //其值是由ActivityTaskSupervisor中的realStartActivityLocked函数 
    final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest();
    if (lifecycleItem == null) {
        // No lifecycle request, return early. 
        return;
    }

    final IBinder token = transaction.getActivityToken();
    final ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
    if (DEBUG_RESOLVER) {
        Slog.d(TAG, tId(transaction) + "Resolving lifecycle state: "
                + lifecycleItem + " for activity: "
                + getShortActivityName(token, mTransactionHandler));
    }

    if (r == null) {
        // Ignore requests for non-existent client records for now. 
        return;
    }

    // Cycle to the state right before the final requested state. 
    //code1 关键点,ResumeActivityItem的getTargetState 是 ON_RESUME 
    cycleToPath(r, lifecycleItem.getTargetState(), true /* excludeLastState */,
            transaction);

    // Execute the final transition with proper parameters. 
    //code2 执行 ResumeActivityItem 的 execute 
    lifecycleItem.execute(mTransactionHandler, token, mPendingActions);
    lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions);
}

我们继续执行code1处的方法:

private void cycleToPath(ActivityClientRecord r, int finish, boolean excludeLastState,
                         ClientTransaction transaction) {
    final int start = r.getLifecycleState();
    if (DEBUG_RESOLVER) {
        Slog.d(TAG, tId(transaction) + "Cycle activity: "
                + getShortActivityName(r.token, mTransactionHandler)
                + " from: " + getStateName(start) + " to: " + getStateName(finish)
                + " excludeLastState: " + excludeLastState);
    }
    //这里的 start = r.getLifecycleState() 是 ON_CREATE,finish 是 ON_RESUME 
    //通过 mHelper 调用 getLifecyclePath 返回的 path 是 ON_START和ON_RESUME的数组,下面会有解析 
    //这里是 Activity 执行 onStart 函数的关键所在
    final IntArray path = mHelper.getLifecyclePath(start, finish, excludeLastState);
    performLifecycleSequence(r, path, transaction);
}

知道每个参数的意义后,我们先看一下ON_CREATE和ON_RESUME的值是多少,方便后续计算

public @interface LifecycleState{}
public static final int UNDEFINED = -1;
public static final int PRE_ON_CREATE = 0;
public static final int ON_CREATE = 1;
public static final int ON_START = 2;
public static final int ON_RESUME = 3;
public static final int ON_PAUSE = 4;
public static final int ON_STOP = 5;
public static final int ON_DESTROY = 6;
public static final int ON_RESTART = 7;

根据上面代码可知,ON_CREATE = 1,ON_RESUME = 3,我们执行getLifecyclePath:

public IntArray getLifecyclePath(int start, int finish, boolean excludeLastState) {
    if (start == UNDEFINED || finish == UNDEFINED) {
        throw new IllegalArgumentException("Can't resolve lifecycle path for undefined state");
    }
    if (start == ON_RESTART || finish == ON_RESTART) {
        throw new IllegalArgumentException(
                "Can't start or finish in intermittent RESTART state");
    }
    if (finish == PRE_ON_CREATE && start != finish) {
        throw new IllegalArgumentException("Can only start in pre-onCreate state");
    }

    mLifecycleSequence.clear();
    if (finish >= start) {
        if (start == ON_START && finish == ON_STOP) {
            // A case when we from start to stop state soon, we don't need to go
            // through the resumed, paused state.
            mLifecycleSequence.add(ON_STOP);
        } else {
            //此时finish = 3,start = 1,会执行到这里,然后把2和3,也就是ON_START和ON_RESUME添加到mLifecycleSequence并返回出去
            for (int i = start + 1; i <= finish; i++) {
                mLifecycleSequence.add(i);
            }
        }
    } else { // finish < start, can't just cycle down
        if (start == ON_PAUSE && finish == ON_RESUME) {
            // Special case when we can just directly go to resumed state.
            mLifecycleSequence.add(ON_RESUME);
        } else if (start <= ON_STOP && finish >= ON_START) {
            // Restart and go to required state.

            // Go to stopped state first.
            for (int i = start + 1; i <= ON_STOP; i++) {
                mLifecycleSequence.add(i);
            }
            // Restart
            mLifecycleSequence.add(ON_RESTART);
            // Go to required state
            for (int i = ON_START; i <= finish; i++) {
                mLifecycleSequence.add(i);
            }
        } else {
            // Relaunch and go to required state

            // Go to destroyed state first.
            for (int i = start + 1; i <= ON_DESTROY; i++) {
                mLifecycleSequence.add(i);
            }
            // Go to required state
            for (int i = ON_CREATE; i <= finish; i++) {
                mLifecycleSequence.add(i);
            }
        }
    }

    // Remove last transition in case we want to perform it with some specific params.
    if (excludeLastState && mLifecycleSequence.size() != 0) {
        mLifecycleSequence.remove(mLifecycleSequence.size() - 1);
    }

    return mLifecycleSequence;
}

上面finish = 3 ,start =1,计算得知,执行该函数后的path值为2和3的数组,对照生命周期值为ON_START和ON_RESUME,继续执行 performLifecycleSequence:

private void performLifecycleSequence(ActivityClientRecord r, IntArray path,
                                      ClientTransaction transaction) {
    final int size = path.size();
    for (int i = 0, state; i < size; i++) {
        state = path.get(i);
        if (DEBUG_RESOLVER) {
            Slog.d(TAG, tId(transaction) + "Transitioning activity: "
                    + getShortActivityName(r.token, mTransactionHandler)
                    + " to state: " + getStateName(state));
        }
        switch (state) {
            case ON_CREATE:
                mTransactionHandler.handleLaunchActivity(r, mPendingActions,
                        Context.DEVICE_ID_INVALID, null /* customIntent */);
                break;
            case ON_START:
                mTransactionHandler.handleStartActivity(r, mPendingActions,
                        null /* activityOptions */);
                break;
            case ON_RESUME:
                mTransactionHandler.handleResumeActivity(r, false /* finalStateRequest */,
                        r.isForward, false /* shouldSendCompatFakeFocus */,
                        "LIFECYCLER_RESUME_ACTIVITY");
                break;
            case ON_PAUSE:
                mTransactionHandler.handlePauseActivity(r, false /* finished */,
                        false /* userLeaving */, 0 /* configChanges */,
                        false /* autoEnteringPip */, mPendingActions,
                        "LIFECYCLER_PAUSE_ACTIVITY");
                break;
            case ON_STOP:
                mTransactionHandler.handleStopActivity(r, 0 /* configChanges */,
                        mPendingActions, false /* finalStateRequest */,
                        "LIFECYCLER_STOP_ACTIVITY");
                break;
            case ON_DESTROY:
                mTransactionHandler.handleDestroyActivity(r, false /* finishing */,
                        0 /* configChanges */, false /* getNonConfigInstance */,
                        "performLifecycleSequence. cycling to:" + path.get(size - 1));
                break;
            case ON_RESTART:
                mTransactionHandler.performRestartActivity(r, false /* start */);
                break;
            default:
                throw new IllegalArgumentException("Unexpected lifecycle state: " + state);
        }
    }
}

上面代码会紧接着执行ON_START的生命周期:

case ON_START:
    mTransactionHandler.handleStartActivity(r, mPendingActions,
                        null /* activityOptions */);
    break;

上面的具体逻辑同学们可以自行查看源码,和handleLaunchActivity大同小异,最终会把state设置成ON_START,继续执行for循环i = ON_RESUME,

case ON_RESUME:
    mTransactionHandler.handleResumeActivity(r, false /* finalStateRequest */,
                        r.isForward, false /* shouldSendCompatFakeFocus */,
                        "LIFECYCLER_RESUME_ACTIVITY");
    break;

调用ActivityThread.handleResumeActivity:

public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
                                 boolean isForward, boolean shouldSendCompatFakeFocus, String reason) {
    
    unscheduleGcIdler();
    mSomeActivitiesChanged = true;

    //code 1
    if (!performResumeActivity(r, finalStateRequest, reason)) {
        return;
    }
    if (mActivitiesToBeDestroyed.containsKey(r.token)) {
        return;
    }
    
    ...

    //code 2
    Looper.myQueue().addIdleHandler(new Idler());
}

上述code1执行后,会把state状态设置成ON_RESUME,大家可以自行查看源码。

code2处,根据之前的handler的知识,大家知道,这个是一个空闲时执行的handler。其实就是在一个Activity A跳转到Activity B后,会执行此Idler。其实也就会通过AMS执行A的onStop方法。

类说明

ClientTransactionItem 对象,一个回调消息,client 端可以调用执行,实现了 BaseClientRequest 接口,在接口 里面定义了3个方法:preExecute,execute,poseExecute。

LaunchActivityItem 继承了 ClientTransactionItem。

ClientLifecycleManager 生命周期管理类,它能够组合多个客户端生命周期变换的请求或回调事务,并将它们作为单个事务执行。

ClientTransaction 是一个容器,持有一系列可以发送给 client 的消息(比如声明周期的状态),包括有 mActivityCallbacks 列表和一个目标状态 mLifecycleStateRequest。

TransactionExecutor 用来执行 ClientTransaction,以正确的顺序管理事务执行 execute(),定义在 ActivityThread 应用端。

ClientTransactionHandler是一个抽象类,定义了一系列生命周期处理Activity生命周期的接口,由ActivityThread 实现。

ActivityLifecycleItem 继承自 ClientTransctionItem,主要的子类有 ResumeActivityItem、PauseActivityItem、 StopActivityItem、DestoryActivityItem。

ActivityThread 它管理应用程序进程中主线程中执行的调度和执行活动、广播以及活动管理器请求的其他操作。

流程图

最后附上流程图:

标签:transaction,mTransactionHandler,启动,int,流程,state,activity,android,final
From: https://blog.csdn.net/anhuilee/article/details/136857796

相关文章

  • AOSP平台编写Android-ebpf程序(tracepoint)的一些map定义和使用问题,导致map和prog无法
     前言本片文章并不主要讲解在AOSP平台ebpf程序的整个编写流程,只是一些的map的定义使用问题,如有需要可查看,aosp平台的整个下载流程,以及简单的程序的编译和如何push到手机运行,这位up是我在ebpf领域探索的领路人,本站ID:LiujiaHuan13,如果有需要up本人后面会考虑写一篇aosp程序书写......
  • 直播预约丨《袋鼠云大数据实操指南》No.1:从理论到实践,离线开发全流程解析
    近年来,新质生产力、数据要素及数据资产入表等新兴概念犹如一股强劲的浪潮,持续冲击并革新着企业数字化转型的观念视野,昭示着一个以数据为核心驱动力的新时代正稳步启幕。面对这些引领经济转型的新兴概念,为了更好地服务于客户并提供切实可行的实践指导,自3月20日起,袋鼠云将推出全新......
  • git在单分支(自己分支)上的操作流程
    文章目录一、git命令整体操作流程(了解)二、idea中git操作流程(常用-图文)1、add2、commit,提交代码3、pull拉取最新代码4、push推送代码到远程仓库5、最后就可以在远程仓库中看你提交的代码了。平时在idea中,在自己的git分支上的操作还是比较频繁的,但是很多刚开始操作......
  • 鸿鹄电子招投标系统源码实现与立项流程:基于Spring Boot、Mybatis、Redis和Layui的企业
    随着企业的快速发展,招采管理逐渐成为企业运营中的重要环节。为了满足公司对内部招采管理提升的要求,建立一个公平、公开、公正的采购环境至关重要。在这个背景下,我们开发了一款电子招标采购软件,以最大限度地控制采购成本,提高招投标工作的公开性和透明性,并确保符合国家电子招投标......
  • STM32_LVGL移植流程及注意事项
    STM32——LVGL移植流程及注意事项下载源码(lvgl8.2):点击git下载.源码精简lvgl-8.2​|build:使用Cmake工具编译的相关文件​|demos:lvgl官方的测试demos​|docs:lvgl文档​......
  • 基于jmeter的性能全流程测试
    01、做性能测试的步骤1、服务器性能监控首先要在对应服务器上面安装性能监控工具,比如linux系统下的服务器,可以选择nmon或者其他的监控工具,然后在jmeter模拟场景跑脚本的时候,同时启动监控工具,这样就可以获得jmeter的聚合报告和服务器的性能报告,然后分析这两份报告,得到性能测试的......
  • 五、jsPlumb实现流程图配置--连线
    一、线条创建在第一篇文章讲到过线条一共有四种类型Bezier、Straight、Flowchart、StateMachine,以及每种类型的样子,接下来就演示如何创建线条。创建一条连线有两种方式:通过代码创建;用户使用鼠标拖拽进行创建。1.通过代码创建使用jsPlumb提供的connectAPI可以创建连线。......
  • STC89C52启动!!!(四)中断
    单片机中断的定义在单片机中,中断是一种机制,用于在特定事件发生时打断当前正在执行的程序流,转而执行预定义的中断服务程序。这种机制可以使单片机在处理实时事件或紧急任务时能够及时响应,提高系统的实时性和可靠性。当单片机设置了中断并且中断条件发生时,单片机会立即停止当......
  • egg 启动报错
    版本nodev18.12.0egg^3.17.5 背景先前执行yarnstart可以正常启动,今日修改代码再次启动,发现报错,如下图(比较长没有截完) 排查与解决1.默认node版本16.18.0,vue3项目要更高的版本,因此导致项目起不来还比较频繁,于是先忽视报错,无脑切node版本到18.12.0,未果用......
  • Android 圆形进度条ProgressBar实现固定进度
    原文:Android圆形进度条ProgressBar实现固定进度-Stars-One的杂货小窝之前遇到一个问题,发现Android里的圆形进度条无法固定一个进度,记录一下解决方法探究假设我们在xml中这样写:<?xmlversion="1.0"encoding="utf-8"?><FrameLayoutxmlns:android="http://schemas.and......