Android第十三次面试总结基础

发布于:2025-06-08 ⋅ 阅读:(16) ⋅ 点赞:(0)

Activity生命周期和四大启动模式详解

一、Activity 生命周期

Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机:

  1. onCreate()​

    • 调用时机​:Activity 首次创建时调用。
    • 作用​:初始化布局(setContentView)、绑定数据、创建后台线程等。
    • 注意​:在此方法中应避免耗时操作。
  2. onStart()​

    • 调用时机​:Activity 可见但未获得焦点(例如被对话框覆盖)。
    • 作用​:恢复UI更新或资源加载。
  3. onResume()​

    • 调用时机​:Activity 进入前台并可与用户交互。
    • 作用​:启动动画、传感器监听、高频率更新UI等。
    • 关键点​:此时 Activity 位于栈顶。
  4. onPause()​

    • 调用时机​:Activity 失去焦点(如弹出对话框或跳转到其他 Activity)。
    • 作用​:保存临时数据、释放资源(如摄像头)。
    • 注意​:需快速执行,否则会影响新 Activity 的启动。
  5. onStop()​

    • 调用时机​:Activity 完全不可见(被其他 Activity 覆盖或退出)。
    • 作用​:停止动画、释放非必要资源。
  6. onDestroy()​

    • 调用时机​:Activity 被销毁(用户主动退出或系统回收资源)。
    • 作用​:清理内存、注销广播等。
  7. onRestart()​

    • 调用时机​:Activity 从停止状态重新回到前台(如按返回键返回)。
    • 流程​:onRestart()onStart()onResume()

场景应用

场景 1:打开新页面

流程​:

  1. 原 Activity 执行 onPause()(失去焦点)
  2. 新 Activity 依次执行:
    • onCreate()(初始化)
    • onStart()(可见)
    • onResume()(可交互)
  3. 原 Activity 执行 onStop()(完全不可见)

典型场景​:

  • 从主页跳转到详情页
  • 列表页打开新的商品页

场景 2:返回上一个页面

流程​:

  1. 当前 Activity 执行 onPause()
  2. 上一个 Activity 依次执行:
    • onRestart()(重新激活)
    • onStart()(可见)
    • onResume()(可交互)
  3. 当前 Activity 执行:
    • onStop()
    • onDestroy()(被销毁)

典型场景​:

  • 提交订单后返回购物车
  • 查看图片详情后返回相册

场景 3:屏幕旋转

流程​:

  1. onPause() → onSaveInstanceState()(保存数据)
  2. onStop() → onDestroy()(销毁实例)
  3. 重新创建:
    • onCreate()(携带保存的数据)
    • onStart()
    • onRestoreInstanceState()(恢复数据)
    • onResume()

开发重点​:

  • 在 onSaveInstanceState() 保存编辑框内容/滚动位置
  • 避免在旋转时中断网络请求

场景 4:被弹窗/来电中断
场景 生命周期变化 恢复顺序
普通对话框 onPause() onResume()
全屏弹窗/来电 onPause() → onStop() onRestart() → onResume()

关键区别​:

  • 是否完全遮挡决定是否触发 onStop()


二、四大启动模式(Launch Mode)

通过 AndroidManifest.xml 或 Intent 标志(如 FLAG_ACTIVITY_NEW_TASK)指定,控制 Activity 实例与任务栈(Task)的关系。

  1. standard(默认模式)​

    • 行为​:每次启动创建新实例,无论是否已存在。
    • 示例​:A → B (standard) → B (standard),栈内为 A-B-B。
    • 注意​:可能导致栈中重复实例。
  2. singleTop(栈顶复用)​

    • 行为​:若目标 Activity 位于栈顶,直接复用(调用 onNewIntent());否则创建新实例。
    • 用例​:避免重复通知(如点击通知栏打开同一页面)。
    • 示例​:栈 A-B,启动 B (singleTop) 会复用;启动 C (singleTop) 则新建。
  3. singleTask(栈内单例)​

    • 行为​:在栈中只保留一个实例。若存在则复用(清除其上所有 Activity,调用 onNewIntent());否则新建。
    • 任务栈​:默认在声明时通过 taskAffinity 指定独立栈。
    • 用例​:应用主页(如微信主界面,保证唯一性)。
    • 示例​:栈 A-B-C,启动 B (singleTask) 会清除 C,栈变为 A-B。
  4. singleInstance(全局单例)​

    • 行为​:独占一个任务栈,且栈内仅此一个 Activity。
    • 用例​:独立应用调用(如系统拨号界面)。
    • 示例​:栈1: A-B,启动 C (singleInstance) 会新建栈2: C。返回时先回到栈1。

onNewIntent() 是 Activity 被复用时的数据刷新入口,用于处理同一个 Activity 实例被再次启动时的新意图(Intent),而无需重建页面。

核心作用​(3个关键点):

  1. 复用已有页面

    • 避免重复创建相同 Activity(节省内存)
    • 保持当前页面状态(如滚动位置、输入内容)
  2. 更新数据

    protected void onNewIntent(Intent intent) {
        setIntent(intent); // 必须更新!否则getIntent()拿旧数据
        refreshUI(intent); // 根据新数据刷新界面
    }
  3. 特定场景触发

    • singleTop​:当 Activity 位于栈顶时
    • singleTask/singleInstance​:当 Activity 已存在于栈中时

典型场景​:

  • 点击通知栏多次打开同一聊天页(直接刷新消息)
  • 从支付结果页返回后再次支付(复用页面更新订单)
  • 全局搜索框重复搜索(保留搜索历史记录)

记住一个原则​:
只要看到 singleTop/singleTask,就要考虑是否需要重写 onNewIntent() 处理数据刷新!


启动模式在后台运行时的关键行为解析

一、后台启动 Activity 的通用规则
  1. 基本行为​:

    • 当应用在后台时启动新 Activity,系统会先将应用带回前台
    • 新 Activity 会被添加到当前任务栈中
    • 用户按返回键时会回到上一个 Activity
  2. 核心影响​:

    • 应用进程优先级提升到前台进程
    • 可能触发系统回收机制(低内存时后台进程优先被杀)

二、四大启动模式在后台的表现

1. ​standard(默认模式)​

后台启动行为​:

  • 每次启动都会创建新实例
  • 新实例直接入栈,位于原栈顶之上
  • 内存影响​:可能导致栈内多个相同实例,增加内存压力

典型场景​:

// 后台服务收到新消息时启动
Intent intent = new Intent(context, ChatActivity.class);
intent.putExtra("msg_id", newMsgId);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

结果​:

  • 栈内可能出现:Main → Chat(1) → Chat(2) → Chat(3)
  • 内存不足时可能被整体回收

2. ​singleTop(栈顶复用)​

后台启动行为​:

  • 若目标 Activity ​已在栈顶​:触发 onNewIntent() 但不创建新实例
  • 不在栈顶​:创建新实例入栈
  • 省内存优势​:避免栈顶重复创建

典型场景​:

// 后台收到多条通知时
protected void onNewIntent(Intent intent) {
    // 更新当前聊天页面数据
    showNewMessage(intent.getStringExtra("new_msg"));
}

实际效果​:

  • 用户正在聊天页:刷新当前页面
  • 用户在其它页:新建聊天页

3. ​singleTask(栈内单例)​

后台启动行为​:

  1. 查找是否存在目标 Activity 的任务栈
  2. 若存在:
    • 清除该实例之上的所有 Activity
    • 触发 onNewIntent()
    • 整个任务栈移到前台
  3. 若不存在:创建新任务栈

内存管理优势​:

  • 自动清理栈内多余 Activity
  • 保持单实例节省内存

典型场景​:

// 支付完成后跳转回主页
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);

后台效果​:

  • 原栈:Login → Product → Checkout
  • 启动后:MainActivity(清除所有支付流程页面)

4. ​singleInstance(全局单例)​

后台启动行为​:

  • 独占任务栈直接带到前台
  • 若已存在:直接显示现有实例(触发 onNewIntent()
  • 跨应用特性​:独立于应用主任务栈

特殊内存管理​:

  • 独立进程空间(通过 android:process 属性)
  • 系统回收时可能单独保留

典型场景​:

// 从后台服务启动相机
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

后台效果​:

  • 相机应用独立运行
  • 拍照完成后返回原应用

三、后台启动的注意事项

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. 最佳实践
  1. 后台启动 singleTask 主页​:

    // 清理所有历史栈
    Intent intent = new Intent(context, MainActivity.class);
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | 
                   Intent.FLAG_ACTIVITY_CLEAR_TASK);
  2. 避免后台 standard 启动​:

    • 易导致 OOM(内存溢出)
    • 返回栈混乱
  3. 跨进程通信​:

    // 启动独立进程的Activity
    <activity android:process=":camera_process"/>

四、各模式后台表现对比表

启动模式 后台启动特点 内存效率 适用场景
standard 持续堆叠新实例 需多实例的普通页面
singleTop 栈顶复用省资源 通知/消息更新
singleTask 清理栈内多余页面 应用主页/核心入口
singleInstance 独立进程不受主应用影响 最高 相机/电话等系统级功能
模式 实例数量 栈位置 典型场景
standard 多个 当前栈 普通页面
singleTop 栈顶唯一 当前栈 防重复启动(通知栏)
singleTask 栈内唯一 可指定新栈 应用主界面
singleInstance 全局唯一 独占新栈 独立功能(如相机)

微信小游戏双图标背后的启动模式解析

场景重现:

  1. 点击微信图标启动微信主界面

  2. 在微信内点击进入小游戏

  3. 后台出现两个独立图标​:

    • 微信主应用图标

    • 小游戏独立图标

核心启动模式:singleInstance

实现原理​:

<!-- 小游戏Activity声明示例 -->
<activity
    android:name=".GameActivity"
    android:launchMode="singleInstance"
    android:taskAffinity="com.tencent.game"
    android:process=":game_process" />

三大关键机制​:

  1. 独立进程

    android:process=":game_process"
    • 小游戏运行在独立的 com.tencent.mm:game_process 进程

    • 与微信主进程 com.tencent.mm 完全隔离

    • 效果​:系统显示两个独立进程图标

  2. 独立任务栈

    android:taskAffinity="com.tencent.game"
    android:launchMode="singleInstance"
    • 创建专属任务栈(与微信主栈隔离)

    • 效果​:

      • 最近任务显示两个独立任务项

      • 游戏退出时直接回到手机桌面,不经过微信

  3. 跨进程通信

    // 微信启动游戏的关键代码
    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 允许多实例

完整启动流程:

  1. 用户点击小游戏入口

  2. 微信创建子进程​:

    • 通过 startActivity() 跨进程启动

    • 指定 singleInstance + NEW_TASK 标志

  3. 系统创建资源隔离区​:

    • 独立内存空间(分配专属内存)

    • 独立渲染线程(避免微信主线程卡顿)

    • 独立任务栈(系统记录为独立应用)

  4. 游戏结束后​:

    • 游戏进程销毁

    • 微信主进程不受影响

    • 返回路径​:游戏 → 桌面 (不返回微信)

技术优势:

  1. 性能隔离​:

    • 游戏占500MB内存不影响微信聊天

    • 游戏崩溃不会导致微信闪退

    # 进程内存占用示例
    com.tencent.mm: 300MB   # 微信主进程
    com.tencent.mm:game: 500MB # 游戏进程
  2. 独立生命周期​:

    操作

    微信主进程

    游戏进程

    进入游戏

    onPause()

    onCreate()

    游戏切后台

    -

    onPause->onStop

    关闭游戏

    onResume()

    onDestroy()

    游戏崩溃

    无影响

    自动重启

  3. 用户体验优化​:

    • 游戏可独立操作(微信后台保持运行)

    • 小窗口模式双向互动(微信浮窗+游戏)

对比其他启动模式:

启动模式

是否分进程

是否独立图标

适用场景

standard

普通页面跳转

singleTask

微信钱包

singleTop

公众号文章

singleInstance

​**✅**​

小游戏/视频通话

典型应用场景:

  1. 微信/QQ内置小游戏

  2. 直播平台连麦功能

  3. 银行App的安全键盘

  4. AR扫描模块

ContentProvider

一、本质:​数据网关 + 跨进程通信中介

ContentProvider 的核心是 ​数据访问的抽象层,其本质包含双重角色:

  1. 数据网关 (Data Gateway)​

    • 提供统一的数据访问接口(CRUD)
    • 隐藏底层存储实现(SQLite/文件/网络)
    • 通过 URI 实现数据路由
  2. 跨进程通信中介 (IPC Broker)​

    • 基于 Binder 机制实现进程间通信
    • 将数据请求转发到目标进程
    • 封装跨进程数据传输细节

二、底层运作流程(源码级解析)

注册阶段:系统级路由表构建
// 系统启动时注册 Provider
ActivityThread.handleBindApplication() {
    // 1. 反射创建 Provider 实例
    ContentProvider provider = clazz.newInstance();
    
    // 2. 绑定 Context 并初始化
    provider.attachInfo(context, providerInfo);
    
    // 3. 加入全局路由表 (ActivityManagerService)
    ActivityManagerService.publishContentProviders() {
        mProviderMap.put(authority, provider); // 存入路由表
    }
}
  • 关键数据结构​:mProviderMap(系统服务中的全局哈希表)
  • ​:Authority(如 com.example.provider
  • ​:ContentProvider 实例的 Binder 代理对象

Service 启动方式与实战场景详解

一、两种核心启动方式
1. startService() 启动方式

本质特点​:

  • 启动后服务与组件完全解耦
  • 生命周期独立运行
  • 必须显式停止​(调用 stopSelf() 或 stopService())

完整生命周期流程​:

  1. 首次启动:onCreate() → onStartCommand()
  2. 后续启动:直接触发 onStartCommand()
  3. 停止服务: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;
    }
}
2. bindService() 启动方式

本质特点​:

  • 建立组件与服务的绑定关系
  • 通过 IBinder 接口实现双向通信
  • 绑定解绑自动管理生命周期

完整生命周期流程​:

  1. 首次绑定:onCreate() → onBind()
  2. 通信期间:通过 Binder 接口交互
  3. 所有绑定解除:onUnbind() → onDestroy()

场景适用​:

  • 实时交互场景​:如音乐控制(播放/暂停/进度)
  • 功能模块解耦​:如支付模块服务
  • 跨进程通信(IPC)​​:不同应用间的数据交换

实战代码​:

// 绑定音乐服务
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);

Service使用场景

音乐/视频播放服务

核心实现方案​:

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) {
        // 构建通知内容(包含播放控制按钮)
        // ...
    }
}

关键技术与注意事项​:

  1. 音频焦点管理​:
AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
int result = audioManager.requestAudioFocus(
    focusChangeListener, 
    AudioManager.STREAM_MUSIC, 
    AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
  1. 耳机事件处理​:
// 注册耳机拔出广播
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(); // 耳机拔出时暂停播放
        }
    }
};
  1. 通知栏控制​:
  • 通过 MediaStyle 通知样式添加播放控制按钮
  • 更新播放进度(Android 12+支持直接显示播放进度条)
  1. 播放状态持久化​:
  • 在 onDestroy() 中保存当前播放位置
  • 在 onCreate() 中恢复最后播放位置

网站公告

今日签到

点亮在社区的每一天
去签到