首页 > 其他分享 >深入理解Activity启动流程和AMS框架(二)

深入理解Activity启动流程和AMS框架(二)

时间:2024-07-29 20:29:15浏览次数:16  
标签:流程 Activity ActivityRecord activity new AMS TaskRecord

链接https://mp.weixin.qq.com/s?__biz=MzIwNjQ1NzQxNA==&mid=2247484150&idx=1&sn=4007a9cff85df88941e73869b89b1ed4&chksm=97201ddfa05794c9e33c8d0d9e83f407922d24282dc9bd8d67c72debf9713abb1b0586f00bc9&scene=21#wechat_redirect

续 深入理解Activity启动流程和AMS框架(一)

由此我们可以得到ActivityThread框架图:

图片

通常ApplicationThread中的接口方法都是以“schedule”开头,而ActivityThread中对应的处理方法则以“handle”开头。

6、Activity的创建并回调onCreate()

ActivityThread中的成员变量mActivities中保存了所有ActivityClientRecord对象,该类中重要的成员变量如下:

static final class ActivityClientRecord {
    IBinder token;                // Activity对象的全局唯一标示
    Intent intent;                // 启动Activity的Intent
    Activity activity;            // Activity对象
    Window window;                // Activity的窗口对象
    boolean paused;               // 是否在暂停状态的标志
    boolean stopped;              // 是否在停止状态的标志
    ActivityInfo activityInfo;    // Activity的信息
    LoadedApk packageInfo;        // 包的信息
}

token的类型是IBinder,在ActivityManagerService中会为每个应用程序中的Activity对象建立了一个对应的ActivityRecord对象,ActivityRecord会创建一个token对象来作为Activity的标识。这个token是个Binder对象,但是它不是为提供Binder服务而创建的,这里只是利用Binder对象的 系统全局唯一性 来作为标识符。 每一个Activity都包含了一个Window对象,Window对象关联着应用框架的一大块内容。

从上面的框架图我们可以得知:启动一个Activity,首先会经过AMS和ActivityThread之间又长又臭的调用和回调,最终才会调到ActivityThread的handleLaunchActivity()方法。下面我们看一下接下来的流程。

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
      ......
      // 执行Activity创建和onCreate流程
      Activity a = performLaunchActivity(r, customIntent);

      if (a != null) {
          r.createdConfig = new Configuration(mConfiguration);
          Bundle oldState = r.state;
          // 执行Activity的onResume流程
          handleResumeActivity(r.token, false, r.isForward,
                  !r.activity.mFinished && !r.startsNotResumed);
          ......
      } else {
          // If there was an error, for any reason, tell the activity
          // manager to stop us.
          try {
              ActivityManagerNative.getDefault()
                  .finishActivity(r.token, Activity.RESULT_CANCELED, null, false);
          } catch (RemoteException ex) {
              // Ignore
          }
      }
  }

handleLaunchActivity()再调用performLaunchActivity()方法来创建Activity并执行onCreate流程,返回后再执行Activity的onResume流程。继续深入,不要怂。

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
     // 首先,从ActivityClientRecord中获取待启动的Activity的组件信息            
     ActivityInfo aInfo = r.activityInfo;
     if (r.packageInfo == null) {
         // 获取Activity的packageInfo 
         r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                 Context.CONTEXT_INCLUDE_CODE);
     }

     ComponentName component = r.intent.getComponent();
     if (component == null) {
         // 获取Activity的ComponentName 
         component = r.intent.resolveActivity(
             mInitialApplication.getPackageManager());
         r.intent.setComponent(component);
     }

     if (r.activityInfo.targetActivity != null) {
         component = new ComponentName(r.activityInfo.packageName,
                 r.activityInfo.targetActivity);
     }

     Activity activity = null;
     try {
         // 拿到加载Activity的ClassLoader, 创建Activity对象 
         java.lang.ClassLoader cl = r.packageInfo.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);
         }
     }

     try {
         // 拿到当前进程中的Application对象
         Application app = r.packageInfo.makeApplication(false, mInstrumentation);

         if (activity != null) {
             // 创建Activity的ApplicationContext
             Context appContext = createBaseContextForActivity(r, activity);
             CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
             Configuration config = new Configuration(mCompatConfiguration);
             // 调用Activity的attach方法在其内部保存一些ActivityClientRecord内的属性
             // 来完成一些重要的数据初始化
             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);
             ......
             // 通过mInstrumentation调用Activity的onCreate方法
             if (r.isPersistable()) {  
                 mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
             } else {
                 mInstrumentation.callActivityOnCreate(activity, r.state);
             }
             ...
             // 将ActivityClientRecord中的activity属性设置为当前activity
             r.activity = activity;
             ......
         }
         r.paused = true;

         // 以ActivityClientRecord中的token为key,mActivities中添加待启动的activity
         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;
 }

从上面代码我们发现,ActivityThread最后调用了mInstrumentation变量的newActivity()方法来创建Activity对象,并回调了callActivityOnCreate()方法,走完Activity的onCreate流程。

public class Instrumentation {
    public void callActivityOnCreate(Activity activity, Bundle icicle) {
        prePerformCreate(activity);
        activity.performCreate(icicle); // 回调Activity.onCreate()
        postPerformCreate(activity);
    }

    public void callActivityOnCreate(Activity activity, Bundle icicle,
            PersistableBundle persistentState) {
        prePerformCreate(activity);
        activity.performCreate(icicle, persistentState); // 回调Activity.onCreate()
        postPerformCreate(activity);
    }
}

public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks2,
        Window.OnWindowDismissedCallback {

    final void performCreate(Bundle icicle) {
        onCreate(icicle);
        mActivityTransitionState.readState(icicle);
        performCreateCommon();
    }

    final void performCreate(Bundle icicle, PersistableBundle persistentState) {
        onCreate(icicle, persistentState);
        mActivityTransitionState.readState(icicle);
        performCreateCommon();
    }
}

这样Activity在应用端进程ActivityThread的启动流程和生命周期回调流程都简单分析完了。

Instrumentation是什么?和ActivityThread是什么关系?

Instrumentation可以理解为应用进程的管家,ActivityThread要执行创建或暂停某个Activity等操作时,都需要通过Instrumentation来进行具体的实现(回调Activity的生命周期)。每一个应用程序只有一个Instrumentation对象,每个Activity内都有一个对该对象的引用。 另外,当我们在Activity中调用startActivity()的 时候,实际上调用的还是Instrumentation的相关的方法mInstrumentation.execStartActivity()。

public ActivityResult execStartActivity(
        Context who, IBinder contextThread, IBinder token, String target,
        Intent intent, int requestCode, Bundle options) {
    IApplicationThread whoThread = (IApplicationThread) contextThread;
    if (mActivityMonitors != null) {
        synchronized (mSync) {
            final int N = mActivityMonitors.size();
            for (int i=0; i<N; i++) {
                final ActivityMonitor am = mActivityMonitors.get(i);
                if (am.match(who, null, intent)) {
                    am.mHits++;
                    if (am.isBlocking()) {
                        return requestCode >= 0 ? am.getResult() : null;
                    }
                    break;
                }
            }
        }
    }
    try {
        intent.migrateExtraStreamToClipData();
        intent.prepareToLeaveProcess();
        // 通知AMS执行startActivity的流程
        int result = ActivityManagerNative.getDefault()
            .startActivity(whoThread, who.getBasePackageName(), intent,
                    intent.resolveTypeIfNeeded(who.getContentResolver()),
                    token, target, requestCode, 0, null, options);
        checkStartActivityResult(result, intent);
    } catch (RemoteException e) {
        throw new RuntimeException("Failure from system", e);
    }
    return null;
}

ActivityManagerNative.getDefault返回的就是ActivityManagerService的远程接口,即ActivityManagerProxy。 除了启动Activity之外,还有很多方法,有兴趣的可以深入了解一下:

图片

通俗的理解,ActivityThread负责创建这个“家庭”,并负责对外打交道,比如接收AMS的通知等。Instrumentation负责管理这个家庭,安排每个Activity的日常流程等工作。

四、小结

AMS通过Binder进行IPC通讯,通知应用进程ActivityThread启动指定Activity

  • 调用ApplicationThread.scheduleLaunchActivity()。

  • 经过Handler消息传动,调用ActivityThread.handleLaunchActivity()。

  • 调用ActivityThread.performLaunchActivity()完成Activity的加载,并最终调用Activity生命周期的onCreate()方法

  • performLaunchActivity返回,继续调用ActivityThread.handleResumeActivity(),该方法内部又调用ActivityThread.performResumeActivity(),其内部仅仅调用了目标Activity的onResume()方法。到此Activity启动完成。

  • Activity的其他生命周期方法onRestart等,可以根据onCreate流程为类推,举一反三。

五、ActivitymanagerService(AMS)框架

ActivitymanagerService是Android Framework的一个核心系统服务,它管理着Android系统中的4大组件:Activity、Service、ContentProvider和BroadcastReceiver。前面已经介绍了,Android希望模糊进程的作用,取而代之以组件的概念,AMS正是这一理念的实现。AMS除了管理这4大组件之外,同时也管理和调度所有用户进程。

1、ActivityManager、ActivityManagerService、ActivityManagerNative、ActivityManagerProxy的关系

AMS是一个Binder服务,但是AMS的Binder框架代码不是通过AIDL自动生成的。AMS从ActivityManagerNative类派生,这个类加上IActivityManager、ActivityManagerProxy和ActivityManager共同实现了AMS Binder框架。它们的关系如下图:

图片

  • ActivityManagerNative类的代码虽然也很庞大,但只是实现Binder的框架,我们可以不用关心这个类的实现细节。

  • ActivityManagerProxy类是作为ActivityManagerNative的嵌入类来实现的,它是代理对象,起接口作用。

  • ActivityManager通过调用ActivityManagerNative的getDefault()方法来得到ActivityManagerProxy对象的引用。

理解了AMS的Binder框架类的作用,后面分析AMS服务的时候,可以不用关系这些类,只需要集中精力在AMS类的本身上。

2、AMS的初始化

AMS运行在SystemServer进程中,对象的创建是在SystemServer类初始化时完成的。

public final class SystemServer {
    ......
    private ActivityManagerService mActivityManagerService;
    private void startBootstrapServices() {
        ...
        mActivityManagerService = mSystemServiceManager.startService(
                ActivityManagerService.Lifecycle.class).getService();  // 创建AMS服务
        mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
        mActivityManagerService.setInstaller(installer);
        ...
    }
    ......
}

从代码中可以看到,这里调用了mSystemServiceManager.startService()方法,这个方法将根据传入的参数(类的class)来创建类的实例对象,并注册到ServiceManager中。 接下来我们看一下AMS的构造方法做了些什么:

public ActivityManagerService(Context systemContext) {
    mContext = systemContext;
    mFactoryTest = FactoryTest.getMode();
    // 获取运行在SystemServer中的ActivityThread对象
    mSystemThread = ActivityThread.currentActivityThread();
    // 创建用于处理消息的线程和Handler对象
    mHandlerThread = new ServiceThread(TAG,
            android.os.Process.THREAD_PRIORITY_FOREGROUND, false /*allowIo*/);
    mHandlerThread.start();
    mHandler = new MainHandler(mHandlerThread.getLooper());
    // 创建用于UI操作相关的Handler对象
    mUiHandler = new UiHandler();

    // 创建管理广播的数据结构
    mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
            "foreground", BROADCAST_FG_TIMEOUT, false);
    mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
            "background", BROADCAST_BG_TIMEOUT, true);
    mBroadcastQueues[0] = mFgBroadcastQueue;
    mBroadcastQueues[1] = mBgBroadcastQueue;

    mServices = new ActiveServices(this);  // 创建管理组件Service的对象
    mProviderMap = new ProviderMap(this);  // 创建管理组件Provider的对象

    // 获取系统的data和system目录
    File dataDir = Environment.getDataDirectory();
    File systemDir = new File(dataDir, "system");
    systemDir.mkdirs();

    // 创建BatteryStatsService服务
    mBatteryStatsService = new BatteryStatsService(systemDir, mHandler);
    mBatteryStatsService.getActiveStatistics().readLocked();
    mBatteryStatsService.scheduleWriteToDisk();
    mOnBattery = DEBUG_POWER ? true
            : mBatteryStatsService.getActiveStatistics().getIsOnBattery();
    mBatteryStatsService.getActiveStatistics().setCallback(this);
    // 创建ProcessStatsService服务
    mProcessStats = new ProcessStatsService(this, new File(systemDir, "procstats"));
    // 创建AppOpsService应用权限管理服务
    mAppOpsService = new AppOpsService(new File(systemDir, "appops.xml"), mHandler);
    // 打开文件urigrants.xml
    mGrantFile = new AtomicFile(new File(systemDir, "urigrants.xml"));

    // 设置0号用户作为第一个用户
    mStartedUsers.put(UserHandle.USER_OWNER, new UserState(UserHandle.OWNER, true));
    mUserLru.add(UserHandle.USER_OWNER);
    updateStartedUserArrayLocked();
    // 获取OpenglES的版本
    GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
        ConfigurationInfo.GL_ES_VERSION_UNDEFINED);

    mTrackingAssociations = "1".equals(SystemProperties.get("debug.track-associations"));
    // 设置mConfiguration配置项为系统缺省值
    mConfiguration.setToDefaults();
    mConfiguration.setLocale(Locale.getDefault());

    mConfigurationSeq = mConfiguration.seq = 1;
    mProcessCpuTracker.init();

    mCompatModePackages = new CompatModePackages(this, systemDir, mHandler);
    // 创建Intent “防火墙”
    mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler);
    // 创建最近任务栏的栈管理对象
    mRecentTasks = new RecentTasks(this);
    // 创建Activity栈的管理对象mStackSupervisor 
    mStackSupervisor = new ActivityStackSupervisor(this, mRecentTasks); 
    mTaskPersister = new TaskPersister(systemDir, mStackSupervisor, mRecentTasks);
    // 创建统计CPU使用情况的线程
    mProcessCpuThread = new Thread("CpuTracker") {
        ......
    }

    // 把服务加到watchdog的监控中
    Watchdog.getInstance().addMonitor(this);
    Watchdog.getInstance().addThread(mHandler);
}

AMS构造方法的主要作用是创建了4大组件Activity、Service、BroadcastReceiver和ContentProvider的管理对象以及一些内部对象,逻辑比较简单。

3、如何启动进程

AMS中启动一个进程调用的是addAppLocked()方法。

final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated,
            String abiOverride) {
      ProcessRecord app;
      // isolated为true表示要启动一个新进程
      if (!isolated) { // 在已经启动的进程列表中查找
          app = getProcessRecordLocked(info.processName, info.uid, true);
      } else {
          app = null;
      }

      if (app == null) {
          // 创建一个ProcessRecord对象
          app = newProcessRecordLocked(info, null, isolated, 0);
          updateLruProcessLocked(app, false, null);
          updateOomAdjLocked();
      }
      ......
      if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
          mPersistentStartingProcesses.add(app);
          // 启动进程
          startProcessLocked(app, "added application", app.processName, abiOverride,
                  null /* entryPoint */, null /* entryPointArgs */);
      }

      return app;
  }

addAppLocked()方法会根据参数isolated来决定是否启动一个新进程,如果为true,即使系统中可能已经有一个同名的进程存在,也会再创建一个新进程。getProcessRecordLocked()方法用来在当前运行的进程列表中查找进程。newProcessRecordLocked()方法用来创建一个ProcessRecord的数据结构。updateLruProcessLocked方法用来更新运行中的进程的状态,updateOomAdjLocked用来更新进程的优先级,这两个方法是Process管理的核心。篇幅有限,这里不再展开分析咯。

4、ActivityRecord、TaskRecord、ActivityStack的关系

(1)、ActivityRecord

final class ActivityRecord {
    // AMS的引用  
    final ActivityManagerService service; // owner  
    // token用来和WMS交互  
    final IApplicationToken.Stub appToken; // window manager token  
    final ActivityInfo info; // all about me  
    final ApplicationInfo appInfo; // information about activity's app  
    final ComponentName realActivity;  // the intent component, or target of an alias.
    ...  

    //Activity资源信息  
    CharSequence nonLocalizedLabel;  // the label information from the package mgr.  
    int labelRes;           // the label information from the package mgr.  
    int icon;               // resource identifier of activity's icon.  
    int logo;               // resource identifier of activity's logo.  
    int theme;              // resource identifier of activity's theme.  
    int realTheme;          // actual theme resource we will use, never 0.  
    int windowFlags;        // custom window flags for preview window.  

    // ActivityRecord所在的TaskRecord  
    TaskRecord task;        // the task this is in.  
    ...  
    // ActivityRecord所在进程  
    ProcessRecord app;      // if non-null, hosting application  
    ActivityState state;    // current state we are in  
    ...  
}

ActivityRecord就是Activity在AMS中的代表,ActivityRecord和应用中的Activity一一对应,并且包含了Activity的所有信息。(就好比Activity在ActivityThread中对应ActivityClientRecord一样) 每一个ActivityRecord都会有一个Activity与之对应,一个Activity可能会有多个ActivityRecord,因为Activity可以被多次实例化,取决于其Launchmode。 其中task对象标识了其所归属的TaskRecord,这样ActivityRecord和TaskRecord就联系起来了。

(2)TaskRecord

final class TaskRecord {
    // TaskRecord的唯一标识  
    final int taskId;       // Unique identifier for this task.  
    .....  
    // This represents the last resolved activity values for this task  
    // NOTE: This value needs to be persisted with each task  
    TaskDescription lastTaskDescription = new TaskDescription();  

    // TaskRecord里所有的ActivityRecord信息  
    /** List of all activities in the task arranged in history order */  
    final ArrayList<ActivityRecord> mActivities;  

    // TaskRecord所在的stack  
    ActivityStack stack;  
    ......
}

TaskRecord中我列出来三个成员变量:

  • taskId:唯一标示的id号

  • mActivities:当前栈中所有的ActivityRecord

  • stack:ActivityStack的引用,为了回调ActivityStack的方法

从TaskRecord 的数据结构我们可以看到,mActivities保存了一组ActivityRecord信息,同时每一个ActivityRecord都有其所在TaskRecord的引用。TaskRecord都有一个唯一标示taskId,还包含了ActivityStack的引用对象。 因此,TaskRecord可以理解为一个mActivities组成的栈,也可以理解为一个由一系列Activity组成的活动。

(3)、 ActivityStack

final class ActivityStack {
    /** 
     * The back history of all previous (and possibly still 
     * running) activities.  It contains #TaskRecord objects. 
     */  
    private final ArrayList<TaskRecord> mTaskHistory = new ArrayList<>();  

    ...  

    final int mStackId;  
    ...  
    /** Run all ActivityStacks through this */  
    final ActivityStackSupervisor mStackSupervisor; 
}
  • mStackId:唯一标示的id号

  • mTaskHistory:这个记录的是一组TaskRecord的栈信息

  • mStackSupervisor:ActivityStack的管理类,也是为了方便管理Task,被AMS调用等

从名字上看,ActivityStack更像是一个栈,实际上它就是一个Manager的角色。 ActivityStack负责管理协调ActivityRecord和TaskRecord,通过mStackId来唯一标识,持有ActivityStackSupervisor的引用。

(4)、关系图

每一个ActivityRecord都会有一个Activity与之对应。一系列相关的ActivityRecord组成了一个TaskRecord,TaskRecord是存在于ActivityStack中,ActivityStackSupervisor是用来管理这些ActivityStack的。 下面是一个简单的关系图

图片

Task 是Activity的集合。Android把用户一次相关操作用使用的Activity按照先后顺序保存在Task中,这样当用户按back键的时候就能按照相反的顺序依次返回退出。Task像一个栈,以先进后出的方式管理着Activity。系统运行时内存中会存在多个Task,当我们查看最近任务栏时,会弹出一个列表让你选择,这个列表就是系统中存在的Task集合,选择一个Task会将它所包含的Activity作为一个整体带到前台。Task中的Activity的顺序通常是不能改变的,只能压栈和出栈。

AMS中使用ActivityStack 类来管理Task,它管理的Task都存放在成员变量mTaskHistory中。mTaskHistory也是一个列表,存储的是TaskRecord对象。TaskRecord对象代表一个Task,他的成员变量mActivities也是一个列表,存储了属于该Task中的所有ActivityRecord对象。

标签:流程,Activity,ActivityRecord,activity,new,AMS,TaskRecord
From: https://www.cnblogs.com/zhanyaowang/p/18331014

相关文章

  • Android 10.0 Launcher 启动流程
    在前面SystemUI启动流程中说到,在SystemServer中会去启动各种系统服务,这里的launcher也是启动的其中一个服务ActivityManagerService去启动的。在android10之前,系统四大组件的启动都是在ActivityManagerService中,在android10中,单独抽出了一个ActivityTaskManagerService,主要......
  • (六)Redis 消息队列 List、Streams
    Redis适合做消息队列吗?有什么解决方案?首先要明白消息队列的消息存取需求和工作流程。1、消息队列我们一般把消息队列中发送消息的组件称为生产者,把接收消息的组件称为消费者,下图是一个通用的消息队列的架构模型:消息队列在存取消息时,必须要满足三个需求,分别是消息保序、处理重......
  • 发布npm包流程
    npm发布包流程背景:假设你写了一些js方法想发布参考:https://www.youtube.com/watch?v=xnfdm-s8adI、1.初始化环境mkdirmethodjscdmethodjsnpminit-ynpmi-Dtsup@5typescript@4#node<14时的兼容版本,tsup用于编译打包ts代码#确保用的是npm地址npmconfig......
  • SpringMVC请求执行流程
    SpringMVC是一个基于Spring框架的MVC(Model-View-Controller)框架,它简化了Web应用程序的开发。在SpringMVC中,请求的执行流程是一个精心设计的过程,涉及多个核心组件的协同工作。以下是对SpringMVC请求执行流程的详细解析,内容不少于2000字。一、SpringMVC请求执行流程概述Spri......
  • 流程控制语句结构
    流程控制语句结构目录流程控制语句结构1.顺序结构定义与特点示例2.分支结构2.1.if-else语句2.2.switch语句3.循环结构1.for循环2.while循环3.do-while循环使用场景和注意事项4.跳转语句5.绘制程序流程图基本步骤常用符号示例工具1.顺序结构定义与特点定义:顺序结构是指程......
  • 计算机毕业设计实现流程,看这篇就够了(1.2w字超详细流程)
    计算机专业毕业设计实现流程 目录如何完成毕业设计选题提交毕业设计任务书完成系统分析和系统设计完成项目编码工作撰写论文项目答辩 1.完成毕业设计选题选题的类型计算机毕业相关的设计最近几年类型比较多的题目为:XX管理系统、XX网站建设、XX小程序设计与实现、XX公......
  • 【版本控制系统Git】Git的基本操作与工作流程
    版本控制系统GitGit的基本操作与工作流程目录引言Git概述Git的基本操作安装Git配置Git创建仓库常用命令Git的工作流程克隆仓库分支管理提交更改合并分支解决冲突最佳实践结论引言在现代软件开发中,版本控制系统(VCS)是不可或缺的工具。它能够帮助开发团队管理代码的......
  • 干货!全网最权威最全面介绍网络短剧上线资质以及备案流程
    今天,小编给大家详细梳理汇总一个全网最权威最全面的介绍网络微短剧上线所需要的资质,以及短剧备案流程到底是怎么样的?100%内行人实战总结的干货,希望对广大短剧从业者有所帮助。一、从事网络微短剧需要哪些资质?1、广播电视节目制作经营许可证(必备)根据《广播电视节目制作经营管......
  • Spring 前后端数据交互到数据库流程
            前端通过HTTP请求(如AJAX、Fetch或Axios等)向后端发送数据请求。后端Spring框架接收到请求后,由控制器(Controller)负责解析请求,并根据请求类型(如GET、POST等)和路径调用相应的服务(Service)。服务层处理业务逻辑,可能涉及调用数据访问层(DAO)来操作数据库。数据访问层使......
  • SpringMVC源码解析(二):请求执行流程
    SpringMVC源码系列文章SpringMVC源码解析(一):web容器启动流程SpringMVC源码解析(二):请求执行流程目录前言DispatcherServlet入口一、获取HandlerExcutionChain(包括Handler)1、获取Handler1.1、通过request获取查找路径1.2、通过查找路径获取HandlerMethod2、获取执......