Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机:
onCreate()
setContentView
)、绑定数据、创建后台线程等。onStart()
onResume()
onPause()
onStop()
onDestroy()
onRestart()
onRestart()
→ onStart()
→ onResume()
。流程:
onPause()
(失去焦点)onCreate()
(初始化)onStart()
(可见)onResume()
(可交互)onStop()
(完全不可见)典型场景:
流程:
onPause()
onRestart()
(重新激活)onStart()
(可见)onResume()
(可交互)onStop()
onDestroy()
(被销毁)典型场景:
流程:
onPause()
→ onSaveInstanceState()
(保存数据)onStop()
→ onDestroy()
(销毁实例)onCreate()
(携带保存的数据)onStart()
onRestoreInstanceState()
(恢复数据)onResume()
开发重点:
onSaveInstanceState()
保存编辑框内容/滚动位置场景 | 生命周期变化 | 恢复顺序 |
---|---|---|
普通对话框 | onPause() |
onResume() |
全屏弹窗/来电 | onPause() → onStop() |
onRestart() → onResume() |
关键区别:
onStop()
通过 AndroidManifest.xml
或 Intent 标志(如 FLAG_ACTIVITY_NEW_TASK
)指定,控制 Activity 实例与任务栈(Task)的关系。
standard(默认模式)
singleTop(栈顶复用)
onNewIntent()
);否则创建新实例。singleTask(栈内单例)
onNewIntent()
);否则新建。taskAffinity
指定独立栈。singleInstance(全局单例)
onNewIntent()
是 Activity 被复用时的数据刷新入口,用于处理同一个 Activity 实例被再次启动时的新意图(Intent),而无需重建页面。
核心作用(3个关键点):
复用已有页面
更新数据
protected void onNewIntent(Intent intent) {
setIntent(intent); // 必须更新!否则getIntent()拿旧数据
refreshUI(intent); // 根据新数据刷新界面
}
特定场景触发
典型场景:
记住一个原则:
只要看到 singleTop
/singleTask
,就要考虑是否需要重写 onNewIntent()
处理数据刷新!
基本行为:
核心影响:
后台启动行为:
典型场景:
// 后台服务收到新消息时启动
Intent intent = new Intent(context, ChatActivity.class);
intent.putExtra("msg_id", newMsgId);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
结果:
后台启动行为:
onNewIntent()
但不创建新实例典型场景:
// 后台收到多条通知时
protected void onNewIntent(Intent intent) {
// 更新当前聊天页面数据
showNewMessage(intent.getStringExtra("new_msg"));
}
实际效果:
后台启动行为:
onNewIntent()
内存管理优势:
典型场景:
// 支付完成后跳转回主页
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
后台效果:
后台启动行为:
onNewIntent()
)特殊内存管理:
典型场景:
// 从后台服务启动相机
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
后台效果:
// 必须使用全屏通知
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentIntent(pendingIntent) // 用户点击才启动
.setFullScreenIntent(pendingIntent, true); // 紧急通知
启动模式 | 回收优先级 | 恢复难度 |
---|---|---|
standard | 高 | 难(多实例) |
singleTop | 中 | 中等 |
singleTask | 低 | 易(单实例) |
singleInstance | 最低 | 最易 |
后台启动 singleTask 主页:
// 清理所有历史栈
Intent intent = new Intent(context, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_CLEAR_TASK);
避免后台 standard 启动:
跨进程通信:
// 启动独立进程的Activity
启动模式 | 后台启动特点 | 内存效率 | 适用场景 |
---|---|---|---|
standard | 持续堆叠新实例 | 低 | 需多实例的普通页面 |
singleTop | 栈顶复用省资源 | 中 | 通知/消息更新 |
singleTask | 清理栈内多余页面 | 高 | 应用主页/核心入口 |
singleInstance | 独立进程不受主应用影响 | 最高 | 相机/电话等系统级功能 |
模式 | 实例数量 | 栈位置 | 典型场景 |
---|---|---|---|
standard | 多个 | 当前栈 | 普通页面 |
singleTop | 栈顶唯一 | 当前栈 | 防重复启动(通知栏) |
singleTask | 栈内唯一 | 可指定新栈 | 应用主界面 |
singleInstance | 全局唯一 | 独占新栈 | 独立功能(如相机) |
场景重现:
点击微信图标启动微信主界面
在微信内点击进入小游戏
后台出现两个独立图标:
微信主应用图标
小游戏独立图标
核心启动模式:singleInstance
实现原理:
三大关键机制:
独立进程
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 的核心是 数据访问的抽象层,其本质包含双重角色:
数据网关 (Data Gateway)
跨进程通信中介 (IPC Broker)
// 系统启动时注册 Provider
ActivityThread.handleBindApplication() {
// 1. 反射创建 Provider 实例
ContentProvider provider = clazz.newInstance();
// 2. 绑定 Context 并初始化
provider.attachInfo(context, providerInfo);
// 3. 加入全局路由表 (ActivityManagerService)
ActivityManagerService.publishContentProviders() {
mProviderMap.put(authority, provider); // 存入路由表
}
}
mProviderMap
(系统服务中的全局哈希表)com.example.provider
)本质特点:
完整生命周期流程:
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 {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(() -> {
downloadFile(intent.getStringExtra("file_url"));
stopSelf(); // 下载完成后自动停止
}).start();
return START_NOT_STICKY;
}
}
本质特点:
完整生命周期流程:
onCreate()
→ onBind()
onUnbind()
→ onDestroy()
场景适用:
实战代码:
// 绑定音乐服务
ServiceConnection conn = new ServiceConnection() {
@Override
public 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);
核心实现方案:
public class MediaPlaybackService extends Service
implements MediaPlayer.OnPreparedListener, MediaPlayer.OnCompletionListener {
private MediaPlayer mediaPlayer;
private MediaSession mediaSession;
private static final int NOTIFICATION_ID = 101;
@Override
public 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();
}
@Override
public 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);
}
}
@Override
public 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() {
@Override
public void onReceive(Context context, Intent intent) {
if(AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {
pausePlayback(); // 耳机拔出时暂停播放
}
}
};