Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解
一、Activity 生命周期
Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机:
-
onCreate()
- 调用时机:Activity 首次创建时调用。
- 作用:初始化布局(
setContentView
)、绑定数据、创建后台线程等。 - 注意:在此方法中应避免耗时操作。
-
onStart()
- 调用时机:Activity 可见但未获得焦点(例如被对话框覆盖)。
- 作用:恢复UI更新或资源加载。
-
onResume()
- 调用时机:Activity 进入前台并可与用户交互。
- 作用:启动动画、传感器监听、高频率更新UI等。
- 关键点:此时 Activity 位于栈顶。
-
onPause()
- 调用时机:Activity 失去焦点(如弹出对话框或跳转到其他 Activity)。
- 作用:保存临时数据、释放资源(如摄像头)。
- 注意:需快速执行,否则会影响新 Activity 的启动。
-
onStop()
- 调用时机:Activity 完全不可见(被其他 Activity 覆盖或退出)。
- 作用:停止动画、释放非必要资源。
-
onDestroy()
- 调用时机:Activity 被销毁(用户主动退出或系统回收资源)。
- 作用:清理内存、注销广播等。
-
onRestart()
- 调用时机:Activity 从停止状态重新回到前台(如按返回键返回)。
- 流程:
onRestart()
→onStart()
→onResume()
。
场景应用
场景 1:打开新页面
流程:
- 原 Activity 执行
onPause()
(失去焦点) - 新 Activity 依次执行:
onCreate()
(初始化)onStart()
(可见)onResume()
(可交互)
- 原 Activity 执行
onStop()
(完全不可见)
典型场景:
- 从主页跳转到详情页
- 列表页打开新的商品页
场景 2:返回上一个页面
流程:
- 当前 Activity 执行
onPause()
- 上一个 Activity 依次执行:
onRestart()
(重新激活)onStart()
(可见)onResume()
(可交互)
- 当前 Activity 执行:
onStop()
onDestroy()
(被销毁)
典型场景:
- 提交订单后返回购物车
- 查看图片详情后返回相册
场景 3:屏幕旋转
流程:
onPause()
→onSaveInstanceState()
(保存数据)onStop()
→onDestroy()
(销毁实例)- 重新创建:
onCreate()
(携带保存的数据)onStart()
onRestoreInstanceState()
(恢复数据)onResume()
开发重点:
- 在
onSaveInstanceState()
保存编辑框内容/滚动位置 - 避免在旋转时中断网络请求
场景 4:被弹窗/来电中断
场景 | 生命周期变化 | 恢复顺序 |
---|---|---|
普通对话框 | onPause() | onResume() |
全屏弹窗/来电 | onPause() → onStop() | onRestart() → onResume() |
关键区别:
- 是否完全遮挡决定是否触发
onStop()
二、四大启动模式(Launch Mode)
Android 的四大启动模式核心基于任务栈管理机制实现。系统通过 ActivityManagerService(AMS)维护一个全局的任务栈结构,每个任务栈由 TaskRecord 对象表示,遵循后进先出的堆栈原则。当启动 Activity 时,AMS 会根据不同启动模式的预设规则,动态调整任务栈的组成。
standard 模式是最基础的方式。它简单地无条件创建新 Activity 实例,并将其压入当前任务栈顶部。这种模式不涉及任何复用判断,因此可能在栈中积累多个相同 Activity 实例,对内存管理构成压力。系统在处理时直接调用 ActivityStarter 的强制压栈方法,整个过程没有任何条件检测。
适用场景:
- 常规页面跳转(如商品详情页、新闻内容页)
- 多实例并行需求(如浏览器标签页、文档编辑页)
典型案例: - 电商应用浏览商品:
主页 → 商品A页 → 商品B页
,栈内存在多个不同商品页实例 - 文档应用编辑文件:同时打开
文档1
、文档2
,返回时可逐个切换
singleTop 模式的核心在于栈顶检查机制。启动时,AMS 会立即判断目标 Activity 是否恰好位于当前栈顶。如果匹配成功,则直接触发该实例的 onNewIntent() 进行数据刷新,完全跳过创建流程;若不在栈顶,则回退到 standard 模式创建新实例。这种设计特别适合通知栏点击等高频重复启动的场景。
适用场景:
- 避免重复通知(通知栏多次点击跳转同一页)
- 高频操作入口(如刷新按钮、重复提交页面)
典型案例: - 微信消息页面:当停留在
聊天页A
时收到新消息,点击通知直接刷新当前页面 - 支付倒计时页:用户多次点击“重新支付”按钮,复用当前页面更新倒计时
特殊场景: - 扫二维码页面:已打开扫码页时再次触发扫码,直接复用而非新建
singleTask 模式采用任务栈重构策略。系统会优先搜索与目标 Activity 的 taskAffinity 匹配的任务栈,检测是否存在已有实例。如果找到,AMS 会先清除该实例之上的所有 Activity(触发它们的 onDestroy),将目标实例置于栈顶并调用 onNewIntent();若未找到,则新建独立任务栈。这种清除机制有效保障了栈内实例唯一性,但处理成本较高。
适用场景:
- 应用主页(如微信主界面、设置首页)
- 登录/授权中转站(确保返回时跳过登录页)
典型案例: - 支付流程结束:
商品页 → 收银台 → 支付成功页
,点击“返回首页”清空整个支付栈,直达主页 - 社交应用登录:若已登录过,再次打开APP直接进入
主页面
而非登录页
singleInstance 模式具有全局隔离特性。该模式强制目标 Activity 独占整个任务栈,且栈内不允许其他 Activity 存在。启动时,AMS 先全局搜索专属栈:若存在则直接提至前台复用;若不存在则创建带唯一 ID 的新栈。这种模式实现了跨应用级的隔离,在系统低内存回收时可能被独立保留,常用于相机、拨号等系统组件。
适用场景:
- 系统级共享组件(相机、通讯录选择器)
- 独立功能模块(如浮窗通话界面)
典型案例: - 系统相机调用:应用中启动相机拍照,返回时直接回到原应用
- 第三方分享面板:微信分享时调起系统分享栈,完成后自动销毁
特殊注意: - 银行应用安全键盘:通过独立进程防止密码输入被截屏
场景需求 | 推荐模式 |
---|---|
常规页面跳转(允许多实例) | standard |
避免栈顶重复(如通知点击) | singleTop |
应用入口/核心页(需清栈) | singleTask |
独立系统组件(跨进程调用) | singleInstance |
通过 AndroidManifest.xml
或 Intent 标志(如 FLAG_ACTIVITY_NEW_TASK
)指定,控制 Activity 实例与任务栈(Task)的关系。
onNewIntent()
是 Activity 被复用时的数据刷新入口,用于处理同一个 Activity 实例被再次启动时的新意图(Intent),而无需重建页面。
核心作用(3个关键点):
-
复用已有页面
- 避免重复创建相同 Activity(节省内存)
- 保持当前页面状态(如滚动位置、输入内容)
-
更新数据
protected void onNewIntent(Intent intent) {setIntent(intent); // 必须更新!否则getIntent()拿旧数据refreshUI(intent); // 根据新数据刷新界面 }
-
特定场景触发
- singleTop:当 Activity 位于栈顶时
- singleTask/singleInstance:当 Activity 已存在于栈中时
典型场景:
- 点击通知栏多次打开同一聊天页(直接刷新消息)
- 从支付结果页返回后再次支付(复用页面更新订单)
- 全局搜索框重复搜索(保留搜索历史记录)
记住一个原则:
只要看到 singleTop
/singleTask
,就要考虑是否需要重写 onNewIntent()
处理数据刷新!
启动模式在后台运行时的关键行为解析
一、后台启动 Activity 的通用规则
-
基本行为:
- 当应用在后台时启动新 Activity,系统会先将应用带回前台
- 新 Activity 会被添加到当前任务栈中
- 用户按返回键时会回到上一个 Activity
-
核心影响:
- 应用进程优先级提升到前台进程
- 可能触发系统回收机制(低内存时后台进程优先被杀)
二、后台启动的注意事项
1. Android 8.0+ 限制
- 后台启动限制:应用在后台时无法随意启动 Activity
- 解决方案:
// 必须使用全屏通知 NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID).setContentIntent(pendingIntent) // 用户点击才启动.setFullScreenIntent(pendingIntent, true); // 紧急通知
2. 内存回收策略
启动模式 | 回收优先级 | 恢复难度 |
---|---|---|
standard | 高 | 难(多实例) |
singleTop | 中 | 中等 |
singleTask | 低 | 易(单实例) |
singleInstance | 最低 | 最易 |
3. 最佳实践
-
后台启动 singleTask 主页:
// 清理所有历史栈 Intent intent = new Intent(context, MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
-
避免后台 standard 启动:
- 易导致 OOM(内存溢出)
- 返回栈混乱
-
跨进程通信:
// 启动独立进程的Activity <activity android:process=":camera_process"/>
三、各模式后台表现对比表
启动模式 | 后台启动特点 | 内存效率 | 适用场景 |
---|---|---|---|
standard | 持续堆叠新实例 | 低 | 需多实例的普通页面 |
singleTop | 栈顶复用省资源 | 中 | 通知/消息更新 |
singleTask | 清理栈内多余页面 | 高 | 应用主页/核心入口 |
singleInstance | 独立进程不受主应用影响 | 最高 | 相机/电话等系统级功能 |
模式 | 实例数量 | 栈位置 | 典型场景 |
---|---|---|---|
standard | 多个 | 当前栈 | 普通页面 |
singleTop | 栈顶唯一 | 当前栈 | 防重复启动(通知栏) |
singleTask | 栈内唯一 | 可指定新栈 | 应用主界面 |
singleInstance | 全局唯一 | 独占新栈 | 独立功能(如相机) |
微信小游戏双图标背后的启动模式解析
场景重现:
-
点击微信图标启动微信主界面
-
在微信内点击进入小游戏
-
后台出现两个独立图标:
-
微信主应用图标
-
小游戏独立图标
-
核心启动模式:singleInstance
实现原理:
<!-- 小游戏Activity声明示例 -->
<activityandroid:name=".GameActivity"android:launchMode="singleInstance"android:taskAffinity="com.tencent.game"android:process=":game_process" />
三大关键机制:
-
独立进程
android:process=":game_process"
-
小游戏运行在独立的
com.tencent.mm:game_process
进程 -
与微信主进程
com.tencent.mm
完全隔离 -
效果:系统显示两个独立进程图标
-
-
独立任务栈
android:taskAffinity="com.tencent.game" android:launchMode="singleInstance"
-
创建专属任务栈(与微信主栈隔离)
-
效果:
-
最近任务显示两个独立任务项
-
游戏退出时直接回到手机桌面,不经过微信
-
-
-
跨进程通信
// 微信启动游戏的关键代码 Intent intent = new Intent(); intent.setComponent(new ComponentName("com.tencent.mm", "com.tencent.mm.plugin.game.GameActivity")); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); startActivity(intent);
-
使用
FLAG_ACTIVITY_MULTIPLE_TASK
允许多实例
-
完整启动流程:
-
用户点击小游戏入口
-
微信创建子进程:
-
通过
startActivity()
跨进程启动 -
指定
singleInstance
+NEW_TASK
标志
-
-
系统创建资源隔离区:
-
独立内存空间(分配专属内存)
-
独立渲染线程(避免微信主线程卡顿)
-
独立任务栈(系统记录为独立应用)
-
-
游戏结束后:
-
游戏进程销毁
-
微信主进程不受影响
-
返回路径:游戏 → 桌面 (不返回微信)
-
技术优势:
-
性能隔离:
-
游戏占500MB内存不影响微信聊天
-
游戏崩溃不会导致微信闪退
# 进程内存占用示例 com.tencent.mm: 300MB # 微信主进程 com.tencent.mm:game: 500MB # 游戏进程
-
-
独立生命周期:
操作
微信主进程
游戏进程
进入游戏
onPause()
onCreate()
游戏切后台
-
onPause->onStop
关闭游戏
onResume()
onDestroy()
游戏崩溃
无影响
自动重启
-
用户体验优化:
-
游戏可独立操作(微信后台保持运行)
-
小窗口模式双向互动(微信浮窗+游戏)
-
对比其他启动模式:
启动模式 | 是否分进程 | 是否独立图标 | 适用场景 |
---|---|---|---|
standard | ❌ | ❌ | 普通页面跳转 |
singleTask | ❌ | ❌ | 微信钱包 |
singleTop | ❌ | ❌ | 公众号文章 |
singleInstance | ✅ | **✅** | 小游戏/视频通话 |
典型应用场景:
-
微信/QQ内置小游戏
-
直播平台连麦功能
-
银行App的安全键盘
-
AR扫描模块
ContentProvider应用场景
ContentProvider 是 Android 实现应用间安全数据共享(基于 URI 和 Binder)的核心组件,同时通过空实现 Provider 的 onCreate()
机制实现库的自动初始化。
ContentProvider 的主要应用场景
a) 应用间共享数据(最常见目的)
* **系统功能:** Android系统自身提供了大量ContentProvider让应用访问系统数据:* `ContactsContract.Contacts` (通讯录联系人)* `MediaStore.Images.Media` (相册图片)* `MediaStore.Audio.Media` (音频文件)* `CalendarContract.Events` (日历事件)* `CallLog.Calls` (通话记录)* `Settings.System` (系统设置)
* **第三方应用:** 当你的应用需要向其他应用**提供数据**时(如提供用户配置、自定义数据库内容),你需要实现自己的ContentProvider,并定义相应的authority和URI结构。
b) 同一个应用内的结构化数据访问
* 即使只在应用内部使用,ContentProvider也提供了一种**标准化**的方式来访问应用本身的数据库或结构化文件。这有利于统一数据访问逻辑、解耦UI和数据层(符合MVC/MVP/MVVM架构思想)、并便于以后扩展成跨应用共享(仅需添加权限和可能的URI结构调整)。
c) 跨进程回调/协调
* 一些复杂的跨应用协作可以通过ContentProvider的`query()`、`insert()`等操作来触发。例如,应用A通过调用应用B的ContentProvider插入一条特定数据,可以作为一种信号通知应用B执行某项操作(不过Intent广播和Service是更直接的IPC方式)。
d) 应用初始化 - Auto-initialization Providers
* **这是非常关键且容易被问到的点!** 你在开发中可能会遇到一些第三方库(或Android Jetpack库),需要在应用启动(`Application.onCreate()`之前)就立即执行初始化。**ContentProvider 是实现这种“自动初始化”的核心机制。**
* **原理:** 系统会在应用进程启动时,**早于**创建 `Application` 实例并调用其 `onCreate()` 方法之前,先**实例化**并**初始化**(调用 `onCreate()` 方法)清单文件中所有在`<application>`标签里声明的ContentProvider。
* **库的做法:** 一个需要预初始化的库可以在其 SDK 中包含一个**不暴露任何实际数据 URI 的“空” ContentProvider**。1. 这个 Provider 在清单文件中声明。2. 它的唯一目的是在其 `onCreate()` 方法中执行库所需的初始化代码(如初始化单例、设置全局配置、数据库设置等)。3. 由于其 `onCreate()` 调用时机非常早(在 `Application.onCreate()` 之前),库就能确保在应用的任何代码运行前完成初始化。
* **常见例子:*** **WorkManager (>= v2.1):** 使用 `WorkManagerInitializer` Provider 来自动初始化。* **Firebase:** `FirebaseApp`的初始化通常通过Provider(如`FirebaseInitProvider`)完成。* **Google Play Services SDKs:** 某些服务可能使用此机制。* **Mockito / Robolectric 等测试框架:** 有时用于注入测试依赖。
* **优势:** 开发者只需引入库依赖并配置清单文件(通常是自动合并的),无需在 `Application.onCreate()` 中显式调用 `initialize()` 方法,减少了初始化配置遗漏的可能性。
* **注意:** 过度使用会略微增加启动时间,因为系统需要加载和初始化这些 Provider。应仅用于必要的库初始化。
代码示例:
利用 ContentProvider 实现应用初始化
自定义初始化 Provider 实现 (空内容提供者)
public class AppInitProvider extends ContentProvider {@Overridepublic boolean onCreate() {// 系统在 Application.onCreate() 前调用initializeApp();return true; // 初始化成功}private void initializeApp() {// 1. 初始化单例配置AppConfig.initialize(getContext());// 2. 设置全局异常处理Thread.setDefaultUncaughtExceptionHandler(new AppCrashHandler());// 3. 初始化网络请求库OkHttpClient client = new OkHttpClient.Builder().connectTimeout(10, TimeUnit.SECONDS).build();RetrofitManager.init(client);// 4. 初始化数据库框架Room.databaseBuilder(getContext(), AppDatabase.class, "app-db").fallbackToDestructiveMigration().build();// 5. 三方库初始化 (示例)FirebaseApp.initializeApp(getContext());}// 空方法 - 此 Provider 不提供实际数据@Nullable @Override public Cursor query(...) { return null; }@Nullable @Override public Uri insert(...) { return null; }@Override public int delete(...) { return 0; }@Override public int update(...) { return 0; }@Nullable @Override public String getType(Uri uri) { return null; }
}
AndroidManifest.xml 配置
<application><!-- 系统将优先初始化此 Provider --><providerandroid:name=".init.AppInitProvider"android:authorities="${applicationId}.appinit" <!-- 唯一标识符 -->android:exported="false" <!-- 不对外暴露 -->android:initOrder="100" <!-- 多个 Provider 时指定初始化顺序 -->tools:ignore="ExportedContentProvider"/> <!-- 常规 Application 类 --><activityandroid:name=".MainActivity".../>
</application>
Application 类中的后续初始化
public class MyApplication extends Application {@Overridepublic void onCreate() {super.onCreate();// 此时 AppInitProvider 已完成初始化// 1. 初始化必须依赖前序操作的组件initializePushNotifications();// 2. 验证初始化状态if(!AppConfig.isInitialized()) {throw new RuntimeException("初始化失败! 请检查AppInitProvider");}}private void initializePushNotifications() {// 依赖 AppInitProvider 中初始化的网络库PushService.setHttpClient(RetrofitManager.getClient());}
}
使用此模式后,开发者只需添加依赖库,零代码初始化三方库 - 特别适合SDK开发者。如 Firebase 通过此机制实现完全自动初始化,无需在 Application 中调用 FirebaseApp.initializeApp()
。
Service 启动方式与实战场景详解
Service是Android执行后台长时操作(如音乐播放、网络下载)或跨进程通信(通过AIDL)的核心组件,其生命周期由系统管理,分为启动状态(startService()
)和绑定状态(bindService()
),8.0+系统需结合前台服务或JobScheduler规避后台限制。
一、两种核心启动方式
1. startService() 启动方式
本质特点:
- 启动后服务与组件完全解耦
- 生命周期独立运行
- 必须显式停止(调用 stopSelf() 或 stopService())
完整生命周期流程:
- 首次启动:
onCreate()
→onStartCommand()
- 后续启动:直接触发
onStartCommand()
- 停止服务:
onDestroy()
场景适用:
- 后台长期任务:如音乐播放、定位追踪
- 无交互任务:如日志上传、数据同步
- 跨应用操作:如推送消息处理
实战代码:
// 启动下载服务
Intent downloadIntent = new Intent(this, DownloadService.class);
downloadIntent.putExtra("file_url", "https://example.com/file.zip");
startService(downloadIntent);// 服务内部停止
public class DownloadService extends Service {@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {new Thread(() -> {downloadFile(intent.getStringExtra("file_url"));stopSelf(); // 下载完成后自动停止}).start();return START_NOT_STICKY;}
}
2. bindService() 启动方式
本质特点:
- 建立组件与服务的绑定关系
- 通过 IBinder 接口实现双向通信
- 绑定解绑自动管理生命周期
完整生命周期流程:
- 首次绑定:
onCreate()
→onBind()
- 通信期间:通过 Binder 接口交互
- 所有绑定解除:
onUnbind()
→onDestroy()
场景适用:
- 实时交互场景:如音乐控制(播放/暂停/进度)
- 功能模块解耦:如支付模块服务
- 跨进程通信(IPC):不同应用间的数据交换
实战代码:
// 绑定音乐服务
ServiceConnection conn = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder binder) {MusicService.MusicBinder musicBinder = (MusicService.MusicBinder) binder;musicBinder.play(); // 直接调用服务方法}
};
bindService(new Intent(this, MusicService.class), conn, BIND_AUTO_CREATE);// 解绑服务
unbindService(conn);
Service使用场景
音乐/视频播放服务
核心实现方案:
public class MediaPlaybackService extends Service implements MediaPlayer.OnPreparedListener, MediaPlayer.OnCompletionListener {private MediaPlayer mediaPlayer;private MediaSession mediaSession;private static final int NOTIFICATION_ID = 101;@Overridepublic void onCreate() {// 初始化媒体播放器mediaPlayer = new MediaPlayer();mediaPlayer.setAudioAttributes(new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build());// 创建媒体会话(支持锁屏控制)mediaSession = new MediaSession(this, "MediaPlaybackService");mediaSession.setCallback(new MediaSessionCallback());mediaSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS | MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);// 设置通知通道(Android 8.0+必须)createNotificationChannel();}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {String action = intent.getStringExtra("action");if("PLAY".equals(action)) {String mediaUrl = intent.getStringExtra("media_url");playMedia(mediaUrl);} else if("PAUSE".equals(action)) {pausePlayback();}return START_NOT_STICKY;}private void playMedia(String mediaUrl) {try {// 停止当前播放if (mediaPlayer.isPlaying()) {mediaPlayer.stop();}mediaPlayer.reset();mediaPlayer.setDataSource(mediaUrl);mediaPlayer.prepareAsync(); // 异步准备播放mediaPlayer.setOnPreparedListener(this);// 转换为前台服务Notification notification = buildMediaNotification("正在播放");startForeground(NOTIFICATION_ID, notification);} catch (IOException e) {Log.e("MediaService", "播放初始化失败", e);}}@Overridepublic void onPrepared(MediaPlayer mp) {mediaPlayer.start();mediaSession.setActive(true);// 更新通知显示播放状态Notification notification = buildMediaNotification("正在播放");NotificationManager nm = getSystemService(NotificationManager.class);nm.notify(NOTIFICATION_ID, notification);}// 构建媒体通知(含播放控制按钮)private Notification buildMediaNotification(String status) {// 构建通知内容(包含播放控制按钮)// ...}
}
关键技术与注意事项:
- 音频焦点管理:
AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
int result = audioManager.requestAudioFocus(focusChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
- 耳机事件处理:
// 注册耳机拔出广播
IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
registerReceiver(noisyReceiver, intentFilter);BroadcastReceiver noisyReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {if(AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {pausePlayback(); // 耳机拔出时暂停播放}}
};
- 通知栏控制:
- 通过 MediaStyle 通知样式添加播放控制按钮
- 更新播放进度(Android 12+支持直接显示播放进度条)
- 播放状态持久化:
- 在 onDestroy() 中保存当前播放位置
- 在 onCreate() 中恢复最后播放位置
相关文章:
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...

华硕a豆14 Air香氛版,美学与科技的馨香融合
在快节奏的现代生活中,我们渴望一个能激发创想、愉悦感官的工作与生活伙伴,它不仅是冰冷的科技工具,更能触动我们内心深处的细腻情感。正是在这样的期许下,华硕a豆14 Air香氛版翩然而至,它以一种前所未有的方式&#x…...

云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...

以光量子为例,详解量子获取方式
光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学(silicon photonics)的光波导(optical waveguide)芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中,光既是波又是粒子。光子本…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码
目录 一、👨🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML 六、🥇 如何让学习不再盲目 七、🎁更多干货 一、👨…...
python报错No module named ‘tensorflow.keras‘
是由于不同版本的tensorflow下的keras所在的路径不同,结合所安装的tensorflow的目录结构修改from语句即可。 原语句: from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后: from tensorflow.python.keras.lay…...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...
【Java学习笔记】BigInteger 和 BigDecimal 类
BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点:传参类型必须是类对象 一、BigInteger 1. 作用:适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...

九天毕昇深度学习平台 | 如何安装库?
pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子: 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...
服务器--宝塔命令
一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行! sudo su - 1. CentOS 系统: yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...
使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度
文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...

HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...

2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!
简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...
Xen Server服务器释放磁盘空间
disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...
LeetCode - 199. 二叉树的右视图
题目 199. 二叉树的右视图 - 力扣(LeetCode) 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...
CSS设置元素的宽度根据其内容自动调整
width: fit-content 是 CSS 中的一个属性值,用于设置元素的宽度根据其内容自动调整,确保宽度刚好容纳内容而不会超出。 效果对比 默认情况(width: auto): 块级元素(如 <div>)会占满父容器…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...
管理学院权限管理系统开发总结
文章目录 🎓 管理学院权限管理系统开发总结 - 现代化Web应用实践之路📝 项目概述🏗️ 技术架构设计后端技术栈前端技术栈 💡 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 🗄️ 数据库设…...

用机器学习破解新能源领域的“弃风”难题
音乐发烧友深有体会,玩音乐的本质就是玩电网。火电声音偏暖,水电偏冷,风电偏空旷。至于太阳能发的电,则略显朦胧和单薄。 不知你是否有感觉,近两年家里的音响声音越来越冷,听起来越来越单薄? —…...

C++使用 new 来创建动态数组
问题: 不能使用变量定义数组大小 原因: 这是因为数组在内存中是连续存储的,编译器需要在编译阶段就确定数组的大小,以便正确地分配内存空间。如果允许使用变量来定义数组的大小,那么编译器就无法在编译时确定数组的大…...

中医有效性探讨
文章目录 西医是如何发展到以生物化学为药理基础的现代医学?传统医学奠基期(远古 - 17 世纪)近代医学转型期(17 世纪 - 19 世纪末)现代医学成熟期(20世纪至今) 中医的源远流长和一脉相承远古至…...