【Android】Activity的生命周期

Activity的生命周期

1.返回栈

其实Android是使用任务(task)来管理Activity的,一个任务就是一组存放在栈里的Activity的集合,这个栈也被称作返回栈(back stack)。栈是一种后进先出的数据结构,在默认情况下,每当我们启动了一个新的Activity,它就会在返回栈中入栈,并处于栈顶的位置。而每当我们按下Back键或调用finish()方法去销毁一个Activity时,处于栈顶的Activity就会出栈,前一个入栈的Activity就会重新处于栈顶的位置。系统总是会显示处于栈顶的Activity给用户。

在这里插入图片描述

2.Activity状态

每个Activity在其生命周期中最多可能会有4种状态。

Android Activity 的生命周期是其运行过程中所经历的一系列状态。每个 Activity 在其生命周期中可能会经历以下几种状态:

  1. 活跃状态(运行状态)(Active or Running):
    1. 这是 Activity 的正常运行状态。它处于用户界面的最顶层,并且能够接收用户的输入。
    2. 系统最不愿意回收的就是处于运行状态的Activity,因为这会带来非常差的用户体验。
  2. 暂停状态(Paused):
    1. 当一个新的 Activity 部分遮挡了当前 Activity,或者当前 Activity 调用了 sleep 方法时,它就会进入暂停状态。在这种状态下,Activity 仍然可见,但是无法接收用户输入。
    2. 当一个Activity不再处于栈顶位置,但仍然可见时,Activity就进入了暂停状态。你可能会觉得,既然Activity已经不在栈顶了,怎么会可见呢?这是因为并不是每一个Activity都会占满整个屏幕,比如对话框形式的Activity只会占用屏幕中间的部分区域。处于暂停状态的Activity仍然是完全存活着的,系统也不愿意回收这种Activity(因为它还是可见的,回收可见的东西都会在用户体验方面有不好的影响),只有在内存极低的情况下,系统才会去考虑回收这种Activity。
  3. 停止状态(Stopped):
    1. Activity 被另一个 Activity 完全遮挡时,它就会进入停止状态。在这种状态下,Activity 不再可见,但是它的状态和成员信息仍然被保留。
    2. 系统仍然会为这种Activity保存相应的状态和成员变量,但是这并不是完全可靠的,当其他地方需要内存时,处于停止状态的Activity有可能会被系统回收。
  4. 非可见状态(销毁状态)(Invisible):
    1. 当系统需要为 Activity 显示一个透明的活动,如对话框,或者 Activity 调用了 finish() 方法后,它就会进入非可见状态。在这种状态下,Activity 对用户完全不可见,系统可能会随时将其销毁
    2. 一个Activity从返回栈中移除后就变成了销毁状态。系统最倾向于回收处于这种状态的Activity,以保证手机的内存充足。

3.Activity的生存期

在这里插入图片描述

  1. onCreate(Bundle savedInstanceState):
    1. 这是 Activity 生命周期中的第一个回调方法,用于进行 Activity 的初始化工作。当 Activity 第一次创建时被调用。
    2. 您可以在这里执行一次性的初始化操作,如设置布局和恢复保存的状态。
  2. onStart():
    1. Activity 从停止状态变为可见状态时调用。
    2. 此时 Activity 对用户可见,但还没有进入前台。
  3. onResume():
    1. Activity 进入前台并开始与用户交互时调用。这是 Activity 运行时的活跃状态,可以执行UI更新和用户交互。
  4. onPause():
    1. Activity 部分被另一个 Activity 遮挡,即将失去焦点时调用。这是一个保存当前 Activity 状态的好时机,例如保存用户输入或应用状态。
    2. 我们通常会在这个方法中将一些消耗CPU的资源释放掉,以及保存一些关键数据,但这个方法的执行速度一定要快,不然会影响到新的栈顶Activity的使用。
  5. onStop():
    1. Activity 不再可见时调用。这可能是因为另一个 Activity 完全覆盖了它,或者 Activity 调用了 finish() 方法。在这个方法中,您应该释放不需要的资源。
  6. onRestart():
    1. Activity 从停止状态恢复到可见状态(运行状态)时调用。这通常发生在 Activity 被另一个 Activity 短暂覆盖后。
  7. onDestroy():
    1. Activity 被销毁时调用。这是一个释放资源和取消注册广播接收器、停止服务等操作的好时机。
  8. onSaveInstanceState(Bundle outState):
    1. 这个方法在 Activity 被系统销毁之前调用,但 Activity 被销毁并不是由于用户操作,如屏幕旋转或配置更改。您可以在这里保存 Activity 的状态,这些状态将在 Activity 重建时通过 onCreate(Bundle savedInstanceState) 方法传递。

在这里插入图片描述

  1. 创建(Creation):
    1. 这个阶段由 onCreate(Bundle savedInstanceState) 方法开始,表示 Activity 正在被创建。如果 Activity 由于配置更改(如屏幕旋转)被系统销毁并重新创建,savedInstanceState 参数将携带之前的状态信息。这个阶段通常用于初始化 Activity,设置布局和恢复状态。
  2. 运行(Active):
    1. 这个阶段包括 onStart()onResume() 两个回调方法。onStart() 表示 Activity 变得可见,但用户可能还不能与之交互。紧接着,onResume() 表示 Activity 已经准备好与用户交互,它现在处于前台并且是活跃状态。
  3. 暂停(Pause):
    1. 这个阶段由 onPause()onStop() 两个回调方法组成。onPause() 表示 Activity 即将失去焦点,但用户不应该在这个时刻保存 Activity 的持久状态,因为这个方法可能会被频繁调用。紧接着,onStop() 表示 Activity 已经不可见,您可以在这个时刻释放不需要的资源。

onRestart() 方法是特殊的,因为它既不与创建阶段对应,也不与运行或暂停阶段对应。它在 Activity 从停止状态变为可见状态时被调用,通常发生在 Activity 被另一个 Activity 短暂覆盖后,或者当 Activity 被系统销毁并需要重新启动时。

在这里插入图片描述

4.Activity被回收了怎么办

前面我们说过,当一个Activity进入了停止状态,是有可能被系统回收的。那么想象以下场景:应用中有一个Activity A,用户在Activity A的基础上启动了Activity B,Activity A就进入了停止状态,这个时候由于系统内存不足,将Activity A回收掉了,然后用户按下Back键返回Activity A,会出现什么情况呢?其实还是会正常显示Activity A的,只不过这时并不会执行onRestart()方法,而是会执行Activity A的onCreate()方法,因为Activity A在这种情况下会被重新创建一次。

这样看上去好像一切正常,可是别忽略了一个重要问题:Activity A中是可能存在临时数据和状态的。打个比方,MainActivity中如果有一个文本输入框,现在你输入了一段文字,然后启动NormalActivity,这时MainActivity由于系统内存不足被回收掉,过了一会你又点击了Back键回到MainActivity,你会发现刚刚输入的文字都没了,因为MainActivity被重新创建了。

如果我们的应用出现了这种情况,是会比较影响用户体验的,所以得想想办法解决这个问题。其实,Activity中还提供了一个onSaveInstanceState()回调方法,这个方法可以保证在Activity被回收之前一定会被调用,因此我们可以通过这个方法来解决问题。

onSaveInstanceState()方法会携带一个Bundle类型的参数,Bundle提供了一系列的方法用于保存数据,比如可以使用putString()方法保存字符串,使用putInt()方法保存整型数据,以此类推。每个保存方法需要传入两个参数,第一个参数是键,用于后面从Bundle中取值,第二个参数是真正要保存的内容。

@Override
public void onSaveInstanceState(Bundle outState) {super.onSaveInstanceState(outState);String tempData = "Something you just typed";outState.putString("data_key", tempData);
}

数据是已经保存下来了,那么我们应该在哪里进行恢复呢?

细心的你也许早就发现,我们一直使用的onCreate()方法其实也有一个Bundle类型的参数。这个参数在一般情况下都是null,但是如果在Activity被系统回收之前,你通过onSaveInstanceState()方法保存数据,这个参数就会带有之前保存的全部数据,我们只需要再通过相应的取值方法将数据取出即可。

修改MainActivity的onCreate()方法,如下所示:

@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);Log.d(tag, "onCreate");setContentView(R.layout.activity_main);if (savedInstanceState != null) {String tempData = savedInstanceState.getString("data_key");Log.d(tag, "tempData is " + tempData);}
}

使用Bundle保存和取出数据是不是有些似曾相识呢?没错!我们在使用Intent传递数据时也用的类似的方法。这里提醒一点,Intent还可以结合Bundle一起用于传递数据。首先我们可以把需要传递的数据都保存在Bundle对象中,然后再将Bundle对象存放在Intent里。到了目标Activity之后,先从Intent中取出Bundle,再从Bundle中一一取出数据。

8.Activity的启动模式

在实际项目中我们应该根据特定的需求为每个Activity指定恰当的启动模式。

启动模式一共有4种,分别是standard、singleTop、singleTask和singleInstance,可以在AndroidManifest.xml中通过给标签指定android:launchMode属性来选择启动模式。下面我们来逐个进行学习。

standard

standard是Activity默认的启动模式,在不进行显式指定的情况下,所有Activity都会自动使用这种启动模式。到目前为止,我们写过的所有Activity都是使用的standard模式。

经过上一节的学习,你已经知道了Android是使用返回栈来管理Activity的,在standard模式下,每当启动一个新的Activity,它就会在返回栈中入栈,并处于栈顶的位置。对于使用standard模式的Activity,系统不会在乎这个Activity是否已经在返回栈中存在,每次启动都会创建一个该Activity的新实例。我们现在通过实践来体会一下standard模式,这次还是在ActivityTest项目的基础上修改。

首先关闭ActivityLifeCycleTest项目,打开ActivityTest项目。修改FirstActivity中onCreate()方法的代码,如下所示:

@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);Log.d("FirstActivity", this.toString());setContentView(R.layout.first_layout);Button button1 = findViewById(R.id.button1); // 确保你的按钮ID是button1button1.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent(FirstActivity.this, FirstActivity.class);startActivity(intent);}});
}

singleTop

可能在有些情况下,你会觉得standard模式不太合理。Activity明明已经在栈顶了,为什么再次启动的时候还要创建一个新的Activity实例呢?别着急,这只是系统默认的一种启动模式而已,你完全可以根据自己的需要进行修改,比如使用singleTop模式。当Activity的启动模式指定为singleTop,在启动Activity时如果发现返回栈的栈顶已经是该Activity,则认为可以直接使用它,不会再创建新的Activity实例。

我们还是通过实践来体会一下,修改AndroidManifest.xml中FirstActivity的启动模式,如下所示:

    <application ...><activity android:name=".FirstActivity"android:launchMode="singleTop"android:label="This is FirstActivity"><intent-filter><action android:name="android.intent.action.MAIN"/><category android:name="android.intent.category.LAUNCHER"/></intent-filter></activity><!-- 其他活动和组件 --></application>

但是之后不管你点击多少次按钮都不会再有新的打印信息出现,因为目前FirstActivity已经处于返回栈的栈顶,每当想要再启动一个FirstActivity时,都会直接使用栈顶的Activity,因此FirstActivity只会有一个实例,仅按一次Back可以退出程序。不过当FirstActivity并未处于栈顶位置时,再启动FirstActivity还是会创建新的实例的。

下面我们来实验一下,修改FirstActivity中onCreate()方法的代码

这次我们点击按钮后启动的是SecondActivity。然后修改SecondActivity中onCreate()方法的代码

我们在SecondActivity中添加了一行打印日志,并且在按钮点击事件里加入了启动FirstActivity的代码。现在重新运行程序,在FirstActivity界面点击按钮进入SecondActivity,然后在SecondActivity界面点击按钮,又会重新进入FirstActivity。

可以看到系统创建了两个不同的FirstActivity实例,这是由于在SecondActivity中再次启动FirstActivity时,栈顶Activity已经变成了SecondActivity,因此会创建一个新的FirstActivity实例。现在按下Back键会返回到SecondActivity,再次按下Back键又会回到FirstActivity,再按一次Back键才会退出程序。

singleTask

使用singleTop模式可以很好地解决重复创建栈顶Activity的问题,但是正如你在上一节所看到的,如果该Activity并没有处于栈顶的位置,还是可能会创建多个Activity实例的。那么有没有什么办法可以让某个Activity在整个应用程序的上下文中只存在一个实例呢?这就要借助singleTask模式来实现了。当Activity的启动模式指定为singleTask,每次启动该Activity时,系统首先会在返回栈中检查是否存在该Activity的实例,如果发现已经存在则直接使用该实例,并把在这个Activity之上的所有其他Activity统统出栈,如果没有发现就会创建一个新的Activity实例。

我们还是通过代码来更加直观地理解一下。修改AndroidManifest.xml中FirstActivity的启动模式:

<activity android:name=".FirstActivity"android:launchMode="singleTask"android:label="This is FirstActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter>
</activity>

然后在FirstActivity中添加onRestart()方法,并打印日志:Log.d(“FirstActivity”, “onRestart”)

最后在SecondActivity中添加onDestroy()方法,并打印日志:Log.d(“SecondActivity”, “onDestroy”)

其实从打印信息中就可以明显看出,在SecondActivity中启动FirstActivity时,会发现返回栈中已经存在一个FirstActivity的实例,并且是在SecondActivity的下面,于是SecondActivity会从返回栈中出栈,而FirstActivity重新成为了栈顶Activity,因此FirstActivity的onRestart()方法和SecondActivity的onDestroy()方法会得到执行。现在返回栈中只剩下一个FirstActivity的实例了,按一下Back键就可以退出程序。

singleInstance

singleInstance模式应该算是4种启动模式中最特殊也最复杂的一个了,你也需要多花点工夫来理解这个模式。不同于以上3种启动模式,指定为singleInstance模式的Activity会启用一个新的返回栈来管理这个Activity(其实如果singleTask模式指定了不同的taskAffinity,也会启动一个新的返回栈)。

那么这样做有什么意义呢?想象以下场景,假设我们的程序中有一个Activity是允许其他程序调用的,如果想实现其他程序和我们的程序可以共享这个Activity的实例,应该如何实现呢?使用前面3种启动模式肯定是做不到的,因为每个应用程序都会有自己的返回栈,同一个Activity在不同的返回栈中入栈时必然创建了新的实例。而使用singleInstance模式就可以解决这个问题,在这种模式下,会有一个单独的返回栈来管理这个Activity,不管是哪个应用程序来访问这个Activity,都共用同一个返回栈,也就解决了共享Activity实例的问题。

SecondActivity的Task id不同于FirstActivity和ThirdActivity,这说明SecondActivity确实是存放在一个单独的返回栈里的,而且这个栈中只有SecondActivity这一个Activity。

然后我们按下Back键进行返回,你会发现ThirdActivity竟然直接返回到了FirstActivity,再按下Back键又会返回到SecondActivity,再按下Back键才会退出程序,这是为什么呢?

其实原理很简单,由于FirstActivity和ThirdActivity是存放在同一个返回栈里的,当在ThirdActivity的界面按下Back键时,ThirdActivity会从返回栈中出栈,那么FirstActivity就成为了栈顶Activity显示在界面上,因此也就出现了从ThirdActivity直接返回到FirstActivity的情况。然后在FirstActivity界面再次按下Back键,这时当前的返回栈已经空了,于是就显示了另一个返回栈的栈顶Activity,即SecondActivity。最后再次按下Back键,这时所有返回栈都已经空了,也就自然退出了程序。

Activity的最佳实践

知晓当前是在哪一个Activity

这个技巧将教会你如何根据程序当前的界面就能判断出这是哪一个Activity。可能你会觉得挺纳闷的,我自己写的代码怎么会不知道这是哪一个Activity呢?然而现实情况是,在你进入一家公司之后,更有可能的是接手一份别人写的代码,因为你刚进公司就正好有一个新项目启动的概率并不高。阅读别人的代码时有一个很头疼的问题,就是当你需要在某个界面上修改一些非常简单的东西时,却半天找不到这个界面对应的Activity是哪一个。学会了本节的技巧之后,这对你来说就再也不是难题了。

我们还是在ActivityTest项目的基础上修改,首先需要新建一个BaseActivity类。右击com.example.activitytest包→New→Kotlin File/Class,在弹出的窗口中输入BaseActivity,创建类型选择Class

注意,这里的BaseActivity和普通Activity的创建方式并不一样,因为我们不需要让BaseActivity在AndroidManifest.xml中注册,所以选择创建一个普通的类就可以了。然后让BaseActivity继承自AppCompatActivity,并重写onCreate()方法

public class BaseActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);Log.d("BaseActivity", this.getClass().getSimpleName());}
}

我们在onCreate()方法中加了一行日志,用于打印当前实例的类名。我们先是获取了当前实例的Class对象,然后再调用simpleName获取当前实例的类名。

接下来我们需要让BaseActivity成为ActivityTest项目中所有Activity的父类

修改FirstActivity、SecondActivity和ThirdActivity的继承结构,让它们不再继承自AppCompatActivity,而是继承自BaseActivity。而由于BaseActivity又是继承自AppCompatActivity的,所以项目中所有Activity的现有功能并不受影响,它们仍然继承了Activity中的所有特性。现在重新运行程序,然后通过点击按钮分别进入FirstActivity、SecondActivity和ThirdActivity的界面,这时观察Logcat中的打印信息

现在每当我们进入一个Activity的界面,该Activity的类名就会被打印出来,这样我们就可以时刻知晓当前界面对应的是哪一个Activity了。

随时随地退出程序

如果目前你手机的界面还停留在ThirdActivity,你会发现当前想退出程序是非常不方便的,需要连按3次Back键才行。按Home键只是把程序挂起,并没有退出程序。如果我们的程序需要注销或者退出的功能该怎么办呢?看来要有一个随时随地都能退出程序的方案才行。其实解决思路也很简单,只需要用一个专门的集合对所有的Activity进行管理就可以了。

下面我们就来实现一下。新建一个单例类ActivityCollector作为Activity的集合,代码如下所示

public class ActivityCollector {private static ActivityCollector instance;private ArrayList<Activity> activities;private ActivityCollector() {activities = new ArrayList<>();}public static synchronized ActivityCollector getInstance() {if (instance == null) {instance = new ActivityCollector();}return instance;}public void addActivity(Activity activity) {activities.add(activity);}public void removeActivity(Activity activity) {activities.remove(activity);}public void finishAll() {for (Activity activity : activities) {if (!activity.isFinishing()) {activity.finish();}}activities.clear();}
}

这里使用了单例类,是因为全局只需要一个Activity集合。

在集合中,我们通过一个ArrayList来暂存Activity,然后提供了一个addActivity()方法,用于向ArrayList中添加Activity;

提供了一个removeActivity()方法,用于从ArrayList中移除Activity;

最后提供了一个finishAll()方法,用于将ArrayList中存储的Activity全部销毁。

注意在销毁Activity之前,我们需要先调用activity.isFinishing来判断Activity是否正在销毁中,因为Activity还可能通过按下Back键等方式被销毁,如果该Activity没有正在销毁中,我们再去调用它的finish()方法来销毁它。

修改BaseActivity中的代码

public class ActivityCollector {private static ActivityCollector instance;private List<Activity> activities = new ArrayList<>();private ActivityCollector() {}public static synchronized ActivityCollector getInstance() {if (instance == null) {instance = new ActivityCollector();}return instance;}public void addActivity(Activity activity) {activities.add(activity);}public void removeActivity(Activity activity) {activities.remove(activity);}public void finishAll() {for (Activity activity : activities) {if (!activity.isFinishing()) {activity.finish();}}activities.clear();}
}

在BaseActivity的onCreate()方法中调用了ActivityCollector的addActivity()方法,表明将当前正在创建的Activity添加到集合里。

然后在BaseActivity中重写onDestroy()方法,并调用了ActivityCollector的removeActivity()方法,表明从集合里移除一个马上要销毁的Activity。

从此以后,不管你想在什么地方退出程序,只需要调用ActivityCollector.finishAll()方法就可以

android.os.Process.killProcess(android.os.Process.myPid())

killProcess()方法用于杀掉一个进程,它接收一个进程id参数,我们可以通过myPid()方法来获得当前程序的进程id。需要注意的是,killProcess()方法只能用于杀掉当前程序的进程,不能用于杀掉其他程序。

启动Activity的最佳写法

启动Activity的方法相信你已经非常熟悉了,

首先通过Intent构建出当前的“意图”,

然后调用startActivity()或startActivityForResult()方法将Activity启动起来,

如果有数据需要在Activity之间传递,也可以借助Intent来完成。

在真正的项目开发中经常会出现对接的问题。比如SecondActivity并不是由你开发的,但现在你负责开发的部分需要启动SecondActivity,而你却不清楚启动SecondActivity需要传递哪些数据。

这时无非就有两个办法:一个是你自己去阅读SecondActivity中的代码,另一个是询问负责编写SecondActivity的同事。你会不会觉得很麻烦呢?其实只需要换一种写法,就可以轻松解决上面的窘境。修

改SecondActivity中的代码,如下所示:

public class SecondActivity extends BaseActivity {public static void actionStart(Context context, String data1, String data2) {Intent intent = new Intent(context, SecondActivity.class);intent.putExtra("param1", data1); // 修复了方括号为正常字符intent.putExtra("param2", data2);context.startActivity(intent);}
}

我们在SecondActivity 中添加了一个actionstart()方法,在这个方法中完成了Intent的构建,

另外所有SecondActivity中需要的数据都是通过actionStart()方法的参数传递过来的,然后把它们存储到Intent中,最后调用 startActivity()方法启动 SecondActivity。

现在只需要一行代码就可以启动SecondActivity

button1.setOnClickListener { SecondActivity.actionStart(this, "data1", "data2") }context.startActivity(intent);}
}

我们在SecondActivity 中添加了一个actionstart()方法,在这个方法中完成了Intent的构建,

另外所有SecondActivity中需要的数据都是通过actionStart()方法的参数传递过来的,然后把它们存储到Intent中,最后调用 startActivity()方法启动 SecondActivity。

现在只需要一行代码就可以启动SecondActivity

button1.setOnClickListener { SecondActivity.actionStart(this, "data1", "data2") }

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

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

相关文章

自动驾驶-预测概览

通过生成一条路径来预测一个物体的行为&#xff0c;在每一个时间段内&#xff0c;为每一辆汽车重新计算预测他们新生成的路径&#xff0c;这些预测路径为规划阶段做出决策提供了必要信息 预测路径有实时性的要求&#xff0c;预测模块能够学习新的行为。我们可以使用多源的数据…

【Unity实战100例】Unity声音可视化多种显示效果

目录 一、技术背景 二、界面搭建 三、 实现 UIAudioVisualizer 基类 四、实现 AudioSampler 类 五、实现 IAudioSample 接口 六、实现MusicAudioVisualizer 七、实现 MicrophoneAudioManager 类 八、实现 MicrophoneAudioVisualizer 类 九、源码下载 Unity声音可视化四…

系统架构设计师教程 第3章 信息系统基础知识-3.6 办公自动化系统(OAS)-解读

系统架构设计师教程 第3章 信息系统基础知识-3.6 办公自动化系统&#xff08;OAS&#xff09; 3.6.1 办公自动化系统的概念3.6.1.1 办公活动3.6.1.1 办公自动化的概念 3.6.2 办公自动化系统的功能3.6.2.1 事务处理3.6.2.1.1 单机系统3.6.2.1.2 多机系统 3.6.2.2 信息管理3.6.2.…

YOLOV8/V7/V5的PCB缺陷检测:可视化界面+GUI+目标计数+视频目标检测与跟踪

在本文中&#xff0c;我将介绍如何使用PyQt5创建一个YOLOv8V7/V5目标检测的可视化界面&#xff0c;可以根据需求选择YOLOv8V7/V5的权重。 该可视化界面的功能丰富&#xff0c;包含内容&#xff1a; 1.GUI目标计数视频目标检测与跟踪 2.完整的OLO数据格式制作流程以及代码 3…

逆向案例二十三——请求头参数加密,某区块链交易逆向

网址&#xff1a;aHR0cHM6Ly93d3cub2tsaW5rLmNvbS96aC1oYW5zL2J0Yy90eC1saXN0L3BhZ2UvNAo 抓包分析&#xff0c;发现请求头有X-Apikey参数加密&#xff0c;其他表单和返回内容没有加密。 直接搜索关键字&#xff0c;X-Apikey&#xff0c;找到疑似加密位置&#xff0c;注意这里…

Spring Boot 中使用 Resilience4j 实现弹性微服务的简单了解

1. 引言 在微服务架构中&#xff0c;服务的弹性是非常重要的。Resilience4j 是一个轻量级的容错库&#xff0c;专为函数式编程设计&#xff0c;提供了断路器、重试、舱壁、限流器和限时器等功能。 这里不做过多演示&#xff0c;只是查看一下官方案例并换成maven构建相关展示&…

系统架构设计师教程(清华第二版) 第3章 信息系统基础知识-3.3 管理信息系统(MIS)-解读

系统架构设计师教程 第3章 信息系统基础知识-3.3 管理信息系统(MIS) 3.3.1 管理信息系统的概念3.3.1.1 部件组成3.3.1.2 结构分类3.3.1.2.1 开环结构3.3.1.2.2 闭环结构3.3.1.3 金字塔结构3.3.2 管理信息系统的功能3.3.3 管理信息系统的组成3.3.3.1 销售市场子系统3.3.3.2…

《系统架构设计师教程(第2版)》第12章-信息系统架构设计理论与实践-02-信息系统架构

文章目录 1. 概述1.1 信息系统架构&#xff08;ISA&#xff09;1.2 架构风格 2. 信息系统架构分类2.1 信息系统物理结构2.1.1 集中式结构2.1.2 分布式结构 2.2 信息系统的逻辑结构1&#xff09;横向综合2&#xff09;纵向综合3&#xff09;纵横综合 3. 信息系统架构的一般原理4…

Android14之调试广播实例(二百二十五)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

windows实现自动化按键

1.选择目标窗口 获取窗口句柄 void KeyPresser::selectWindow() {SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, NULL, WinEventProc, 0, 0, WINEVENT_OUTOFCONTEXT);selectedWindowLabel->setText("请点击目标窗口..."); }void CALLBACK …

word的进阶

Word的基本操作 这里主要用到的软件是WPS软件。 一、创建一个文档 第一种&#xff1a;快捷键&#xff1a;ctrln第二种&#xff1a;通过界面鼠标点击 二、设置文档背景 更换过的背景如下&#xff1a; 三、章节、目录导航的设置 四、插入目录页 五、对历史文档进行管理 六、…

收银系统源码-千呼新零售收银视频介绍

千呼新零售2.0系统是零售行业连锁店一体化收银系统&#xff0c;包括线下收银线上商城连锁店管理ERP管理商品管理供应商管理会员营销等功能为一体&#xff0c;线上线下数据全部打通。 适用于商超、便利店、水果、生鲜、母婴、服装、零食、百货、宠物等连锁店使用。 详细介绍请…

基于 asp.net家庭财务管理系统设计与实现

博主介绍&#xff1a;专注于Java .net php phython 小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设&#xff0c;从业十五余年开发设计教学工作 ☆☆☆ 精彩专栏推荐订阅☆☆☆☆☆不然下次找不到哟 我的博客空间发布了1000毕设题目 方便大家学习使用感兴趣的可以先…

XLua原理(一)

项目中活动都是用xlua开发的&#xff0c;项目周更热修也是用xlua的hotfix特性来做的。现研究底层原理&#xff0c;对于项目性能有个更好的把控。 本文认为看到该文章的人已具备使用xlua开发的能力&#xff0c;只研究介绍下xlua的底层实现原理。 一.lua和c#交互原理 概括&…

Github报错:Kex_exchange_identification: Connection closed by remote host

文章目录 1. 背景介绍2. 排查和解决方案 1. 背景介绍 Github提交或者拉取代码时&#xff0c;报错如下&#xff1a; Kex_exchange_identification: Connection closed by remote host fatal: Could not read from remote repository.Please make sure you have the correct ac…

【Qt】常用控件 Q widget的enabled属性,geometry属性

Qt是一个实现图形化程序的程序。为了便于我们开发&#xff0c;Qt为我们提供了许多“控件”。我们需要熟悉并掌握这些控件的使用。 一.什么是控件 控件是构成⼀个图形化界⾯的基本要素. 示例一&#xff1a; 像上述⽰例一中的,按钮,列表视图,树形视图,单⾏输⼊框,多⾏输⼊框,滚动…

OPC UA边缘计算耦合器BL205工业通信的最佳解决方案

OPC UA耦合器BL205是钡铼技术基于下一代工业互联网技术推出的分布式、可插拔、结构紧凑、可编程的IO系统&#xff0c;可直接接入SCADA、MES、MOM、ERP等IT系统&#xff0c;无缝链接OT与IT层&#xff0c;是工业互联网、工业4.0、智能制造、数字化转型解决方案中IO系统最佳方案。…

硅谷裸机云多IP服务器怎么样?

硅谷裸机云多IP服务器是一种在硅谷地区提供的、具有多个IP地址的裸机云服务器。这种服务器结合了裸机服务器的高性能和云服务器的灵活性&#xff0c;同时提供了多个IP地址&#xff0c;为用户的各种需求提供了支持。以下是关于硅谷裸机云多IP服务器的一些详细信息&#xff0c;ra…

智能硬件——0-1开发流程

文章目录 流程图1. 市场分析具体分析 2. 团队组建2. 团队组建早期团队配置建议配置一&#xff1a;基础型团队 (4人)配置二&#xff1a;扩展型团队 (6人)配置三&#xff1a;全面型团队 (7人) 3. 产品需求分析4. ID设计&#xff08;Industrial Design, 工业设计&#xff09;5. 结…

智慧监狱整体解决方案

智慧监狱整体解决方案摘要&#xff1a; 对智慧监狱的理解 智慧监狱通过集成监控图像资源、报警信息、安防信息和业务信息&#xff0c;实现资源共享和信息互通。构建三级警戒架构&#xff0c;实现分监区、监狱和局级监狱管理局的应急指挥和管理。 发展历程 从2006年至2021年&am…