面试题 - Andorid - 四大组件相关

131 阅读28分钟

让我逐个为您详细解答这些Android面试题:

1. Activity 与 Fragment 通信方式

graph LR
    A[Activity] --> |1.接口回调| B[Fragment]
    A --> |2.Bundle传递| B
    B --> |3.ViewModel共享| A
    A --> |4.EventBus| B

1.1主要通信方式:

  1. 接口回调:就像餐厅服务员(Fragment)把客人点单传给厨师(Activity)
// 定义接口
public interface OnFragmentInteractionListener {
    void onFragmentInteraction(String data);
}

// Fragment中
((OnFragmentInteractionListener)getActivity()).onFragmentInteraction("数据");

优点:解耦合,符合面向接口编程原则 缺点:需要处理生命周期,避免内存泄漏

  1. Bundle传递:像快递包裹一样打包数据
Bundle bundle = new Bundle();
bundle.putString("key", "value");
fragment.setArguments(bundle);
  1. ViewModel共享:像家庭共享的储物柜
SharedViewModel viewModel = new ViewModelProvider(getActivity()).get(SharedViewModel.class);

特点:

  • 生命周期感知
  • 数据持久化(配置更改)
  • 内存泄漏防护
  1. EventBus:像小区的公告栏,谁都可以发布和接收消息

  2. Fragment Result API(Jetpack新特性)

// Fragment A
parentFragmentManager.setFragmentResult("requestKey", bundleOf("key" to value))

// Fragment B
parentFragmentManager.setFragmentResultListener("requestKey", viewLifecycleOwner) { key, bundle ->
    // Handle result
}

优势:生命周期安全,官方推荐方案

1.2 进阶通信方式

  1. Flow + StateFlow/SharedFlow
class SharedViewModel : ViewModel() {
    private val _uiState = MutableStateFlow<UiState>(UiState.Initial)
    val uiState: StateFlow<UiState> = _uiState.asStateFlow()
    
    fun updateState(newState: UiState) {
        viewModelScope.launch {
            _uiState.emit(newState)
        }
    }
}

优势:

  • 冷流特性,支持背压
  • 协程集成
  • 线程安全
  1. Hilt依赖注入
@HiltViewModel
class SharedViewModel @Inject constructor(
    private val repository: Repository
) : ViewModel() {
    // ViewModel implementation
}

2. LaunchMode特点和应用场景

2.1 主要模式

想象一个办公楼的不同开门政策:

  1. standard(标准模式)
  • 特点:每次都新建实例
  • 场景:普通页面
  • 比喻:每个访客都获得新的访客卡
  1. singleTop(栈顶复用)
  • 特点:栈顶有就复用,没有就新建
  • 场景:通知栏点击
  • 比喻:电梯已在顶层就不用重新呼叫
  1. singleTask(栈内复用)
  • 特点:栈内已有就复用并清除上面的
  • 场景:主页面
  • 比喻:老板回公司,上面的会议自动取消
  1. singleInstance(独享栈)
  • 特点:单独任务栈
  • 场景:特殊界面(如闹钟提醒)
  • 比喻:总裁专用电梯

2.2 任务栈管理机制

// 示例:自定义任务栈
<activity
    android:name=".MainActivity"
    android:launchMode="singleTask"
    android:taskAffinity="com.example.custom.task"/>
  1. standard模式原理
  • 每次创建新实例都会调用ActivityThread.performLaunchActivity()
  • 实例与任务栈一一对应
  1. singleTop实现机制
@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    // 栈顶复用时调用
    setIntent(intent); // 更新Intent
}
  1. singleTask内部处理
  • ActivityManagerService负责任务栈管理
  • 通过taskAffinity确定目标任务栈
  • 清理目标Activity上方实例(clearTop机制)
  1. singleInstance特殊处理
  • 独立任务栈实现跨应用共享
  • 系统级别的Activity常用此模式

2.3 实际应用场景分析

  1. 电商场景
首页(singleTask) -> 商品列表(standard) -> 商品详情(standard) -> 支付页面(singleTop)
  1. 社交场景
主页(singleTask) -> 聊天列表(standard) -> 聊天界面(singleTop) -> 用户信息(standard)

3. BroadcastReceiver vs LocalBroadcastReceiver

graph LR
    A[BroadcastReceiver] --> B[全局广播]
    C[LocalBroadcastReceiver] --> D[应用内广播]

比喻:

  • BroadcastReceiver:像城市广播,全城都能听到
  • LocalBroadcastReceiver:像家庭对讲机,只在家里通信

区别:

  1. 作用范围:全局 vs 应用内
  2. 安全性:LocalBroadcastReceiver更安全
  3. 性能:Local更高效

3.1 实现原理对比

  1. 全局广播(BroadcastReceiver)
// 基于Binder机制
public final void sendBroadcast(Intent intent) {
    ActivityManagerNative.getDefault().broadcastIntent(
        mMainThread.getApplicationThread(), intent,
        mUserHandle.getIdentifier());
}
  1. 本地广播(LocalBroadcastManager)
// 基于Handler机制
public class LocalBroadcastManager {
    private final Handler mHandler;
    private final Map<BroadcastReceiver, ArrayList<IntentFilter>> mReceivers
            = new HashMap<>();
            
    public boolean sendBroadcast(Intent intent) {
        synchronized (mReceivers) {
            // 处理广播分发
        }
    }
}

3.2 性能对比

  1. 内存占用
  • LocalBroadcastManager: ~0.2MB
  • 全局广播: 根据注册数量增长
  1. 执行效率
  • Local: 无需跨进程,约提升30%效率
  • 全局: 需要Binder调用

4. Context了解

想象Context是一个万能管家:

graph TD
    A[Context] --> B[Application Context]
    A --> C[Activity Context]
    A --> D[Service Context]

主要功能:

  1. 访问资源
  2. 启动Activity/Service
  3. 获取系统服务
  4. 获取数据库/文件路径

4.1 Context继承体系

graph TD
    A[Context抽象类] --> B[ContextWrapper]
    B --> C[Application]
    B --> D[Service]
    B --> E[ContextThemeWrapper]
    E --> F[Activity]

4.2 内存泄漏防护

public class MyManager {
    private Context context;
    
    public MyManager(Context context) {
        // 使用Application Context防止内存泄漏
        this.context = context.getApplicationContext();
    }
}

5. IntentFilter

就像快递分拣规则:

匹配机制:

  1. action匹配(必须完全匹配)
  2. category匹配(可以没有,但有必须匹配)
  3. data匹配(URI和数据类型)

使用场景:

  1. 隐式启动组件
  2. 接收广播
  3. 处理特定URL

5.1 匹配机制源码分析

public final class IntentFilter {
    private final ArrayList<String> mActions;
    private final ArrayList<String> mCategories;
    private final ArrayList<AuthorityEntry> mDataAuthorities;
    private final ArrayList<PatternMatcher> mDataPaths;
    private final ArrayList<String> mDataSchemes;
    private final ArrayList<String> mDataTypes;
}

匹配优先级:

graph TD
    A[Action匹配] --> B[Category匹配]
    B --> C[Data匹配]
    C --> D[Priority优先级]

5.2 高级应用场景

  1. Deep Links处理
<intent-filter android:autoVerify="true">
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data
        android:host="example.com"
        android:pathPattern="/.*"
        android:scheme="https" />
</intent-filter>
  1. 自定义URL Scheme
class DeepLinkHandler {
    fun handleDeepLink(context: Context, uri: Uri) {
        when {
            uri.host == "products" -> handleProducts(uri.lastPathSegment)
            uri.host == "orders" -> handleOrders(uri.getQueryParameter("id"))
        }
    }
}

6. startService vs bindService

graph TB
    A[Service启动方式] --> B[startService]
    A --> C[bindService]
    B --> D[独立运行]
    C --> E[绑定生命周期]

比喻:

  • startService:像后台音乐播放,独立运行
  • bindService:像蓝牙连接,断开就停止

6.1 生命周期管理

graph TD
    A[startService] --> B[onCreate]
    B --> C[onStartCommand]
    C --> D[运行中]
    D --> E[onDestroy]
    
    F[bindService] --> G[onCreate]
    G --> H[onBind]
    H --> I[onUnbind]
    I --> J[onDestroy]

6.2 进程间通信实现

1. AIDL方式
// IMyService.aidl
interface IMyService {
    void performAction(in ParcelableData data);
    List<String> getResults();
}

// Service实现
private final IMyService.Stub binder = new IMyService.Stub() {
    @Override
    public void performAction(ParcelableData data) {
        // 跨进程调用实现
    }
};
2. Messenger方式
public class MessengerService extends Service {
    private final Messenger messenger = new Messenger(new Handler(msg -> {
        switch (msg.what) {
            case MSG_TYPE_ONE:
                // 处理消息
                break;
        }
        return true;
    }));
}

7. Service保活

主要方法:

  1. 提高进程优先级(前台服务)
  2. 相互唤醒机制
  3. JobScheduler定时启动
  4. 用户可感知(通知栏)

让我详细介绍 Service 的基本使用和保活机制:

1. 基础 Service 的创建和使用

public class MyService extends Service {
    private static final String TAG = "MyService";
    
    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "Service onCreate");
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "Service onStartCommand");
        // 返回粘性 Service
        return START_STICKY;
    }
    
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

启动服务:

// 启动服务
Intent intent = new Intent(this, MyService.class);
startService(intent);

// 停止服务
stopService(intent);

2. 前台 Service

public class ForegroundService extends Service {
    private static final int NOTIFICATION_ID = 1;
    
    @Override
    public void onCreate() {
        super.onCreate();
        startForeground(NOTIFICATION_ID, createNotification());
    }
    
    private Notification createNotification() {
        // 创建通知渠道(Android 8.0及以上必需)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(
                "foreground_service",
                "前台服务",
                NotificationManager.IMPORTANCE_LOW
            );
            NotificationManager manager = getSystemService(NotificationManager.class);
            manager.createNotificationChannel(channel);
        }
        
        return new NotificationCompat.Builder(this, "foreground_service")
            .setContentTitle("服务运行中")
            .setContentText("点击查看详情")
            .setSmallIcon(R.drawable.ic_notification)
            .build();
    }
}

3. 双进程保活

// 主进程服务
public class MainService extends Service {
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // 启动守护进程服务
        startService(new Intent(this, GuardService.class));
        return START_STICKY;
    }
}

// 守护进程服务
public class GuardService extends Service {
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // 检查主进程服务是否存活
        if (!isServiceRunning(MainService.class)) {
            startService(new Intent(this, MainService.class));
        }
        return START_STICKY;
    }
}

4. AIDL 进程间通信

// IServiceConnection.aidl
interface IServiceConnection {
    void onServiceConnected();
    void onServiceDisconnected();
}
public class RemoteService extends Service {
    private final IServiceConnection.Stub binder = new IServiceConnection.Stub() {
        @Override
        public void onServiceConnected() {
            // 服务连接
        }
        
        @Override
        public void onServiceDisconnected() {
            // 服务断开
        }
    };
    
    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }
}

5. JobScheduler 保活

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class MyJobService extends JobService {
    @Override
    public boolean onStartJob(JobParameters params) {
        // 检查服务是否运行
        if (!isServiceRunning(MainService.class)) {
            startService(new Intent(this, MainService.class));
        }
        return false;
    }
    
    @Override
    public boolean onStopJob(JobParameters params) {
        return false;
    }
}

注册 Job:

public class ServiceManager {
    public static void scheduleJob(Context context) {
        JobScheduler scheduler = (JobScheduler) 
            context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
            
        JobInfo.Builder builder = new JobInfo.Builder(
            1,
            new ComponentName(context, MyJobService.class)
        );
        
        builder.setPeriodic(15 * 60 * 1000); // 15分钟
        builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
        builder.setPersisted(true);
        
        scheduler.schedule(builder.build());
    }
}

6. WorkManager 实现

public class ServiceWorker extends Worker {
    public ServiceWorker(@NonNull Context context, 
                        @NonNull WorkerParameters params) {
        super(context, params);
    }
    
    @Override
    public Result doWork() {
        // 检查并启动服务
        if (!isServiceRunning(MainService.class)) {
            getApplicationContext().startService(
                new Intent(getApplicationContext(), MainService.class)
            );
        }
        return Result.success();
    }
}

注册 Worker:

public class WorkManagerUtil {
    public static void startServiceWorker(Context context) {
        PeriodicWorkRequest workRequest = 
            new PeriodicWorkRequest.Builder(
                ServiceWorker.class, 
                15, TimeUnit.MINUTES
            )
            .setConstraints(new Constraints.Builder()
                .setRequiredNetworkType(NetworkType.CONNECTED)
                .build()
            )
            .build();
            
        WorkManager.getInstance(context)
            .enqueueUniquePeriodicWork(
                "ServiceWork",
                ExistingPeriodicWorkPolicy.REPLACE,
                workRequest
            );
    }
}

7. 广播接收保活

public class BootReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
            // 系统启动完成后启动服务
            context.startService(new Intent(context, MainService.class));
        }
    }
}

综合保活策略:

public class ServiceKeepAlive {
    public static void keepAlive(Context context) {
        // 1. 启动前台服务
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            context.startForegroundService(
                new Intent(context, ForegroundService.class)
            );
        } else {
            context.startService(
                new Intent(context, ForegroundService.class)
            );
        }
        
        // 2. 启动双进程守护
        context.startService(new Intent(context, GuardService.class));
        
        // 3. 注册 JobScheduler
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            ServiceManager.scheduleJob(context);
        }
        
        // 4. 注册 WorkManager
        WorkManagerUtil.startServiceWorker(context);
    }
}

注意事项:

  1. Android 8.0 后台限制
  2. 电池优化限制
  3. 各厂商系统差异
  4. 进程保活耗电问题
  5. 用户体验影响

建议:

  1. 必要时才使用保活
  2. 合理使用前台服务
  3. 遵守系统规范
  4. 考虑用户体验
  5. 做好兼容性适配

8. ContentProvider数据共享

像图书馆管理系统:

  1. 统一的数据访问接口
  2. 权限控制
  3. CRUD操作封装

8.1 跨进程数据共享原理

public class CustomProvider extends ContentProvider {
    private static final UriMatcher sUriMatcher;
    private static final SQLiteDatabase db;
    
    @Override
    public Cursor query(...) {
        SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
        // 实现查询逻辑
    }
}

8.2 数据变化通知机制

class DataRepository @Inject constructor(
    private val contentResolver: ContentResolver
) {
    private val observer = object : ContentObserver(Handler(Looper.getMainLooper())) {
        override fun onChange(selfChange: Boolean) {
            // 处理数据变化
        }
    }
    
    init {
        contentResolver.registerContentObserver(
            Uri.parse("content://authority/path"),
            true,
            observer
        )
    }
}

9. 横竖屏切换生命周期

onPause → onSaveInstanceState → onStop → onDestroy → onCreate → onStart → onRestoreInstanceState → onResume

9.1 配置变更处理

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        if (savedInstanceState != null) {
            // 恢复状态
            viewModel.restoreState(savedInstanceState)
        }
    }
    
    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        viewModel.saveState(outState)
    }
}

9.2 性能优化方案

<activity
    android:name=".MainActivity"
    android:configChanges="orientation|screenSize|keyboardHidden"
    android:screenOrientation="user" />

让我详细解释这几个配置的含义和使用场景:

  1. android:configChanges
android:configChanges="orientation|screenSize|keyboardHidden"

这个配置告诉系统在这些变化发生时不要重建 Activity,而是回调 onConfigurationChanged

public class MainActivity extends Activity {
    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        
        // 处理屏幕旋转
        if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
            // 横屏处理
            handleLandscape();
        } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
            // 竖屏处理
            handlePortrait();
        }
    }
    
    private void handleLandscape() {
        // 比如调整视频播放器大小
        VideoView videoView = findViewById(R.id.video_view);
        videoView.setLayoutParams(new LayoutParams(MATCH_PARENT, MATCH_PARENT));
    }
}

各个值的含义:

  • orientation: 屏幕方向变化
  • screenSize: 屏幕大小变化
  • keyboardHidden: 键盘显示状态变化
  1. android:screenOrientation
android:screenOrientation="user"

这个属性控制 Activity 的屏幕方向,常见值:

<!-- 固定方向 -->
android:screenOrientation="portrait"     <!-- 固定竖屏 -->
android:screenOrientation="landscape"    <!-- 固定横屏 -->

<!-- 自动方向 -->
android:screenOrientation="user"         <!-- 跟随用户设置 -->
android:screenOrientation="sensor"       <!-- 跟随重力感应 -->
android:screenOrientation="unspecified"  <!-- 未指定,系统决定 -->

实际应用示例:

  1. 视频播放器
<activity
    android:name=".VideoPlayerActivity"
    android:configChanges="orientation|screenSize"
    android:screenOrientation="sensor" />
public class VideoPlayerActivity extends Activity {
    private VideoView videoView;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_video);
        
        videoView = findViewById(R.id.video_view);
    }
    
    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        
        // 屏幕旋转时不重建,而是直接调整布局
        if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
            // 横屏:视频全屏
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
            videoView.setLayoutParams(new LayoutParams(MATCH_PARENT, MATCH_PARENT));
        } else {
            // 竖屏:恢复正常
            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
            videoView.setLayoutParams(originalParams);
        }
    }
}
  1. 游戏界面
<activity
    android:name=".GameActivity"
    android:configChanges="orientation|screenSize|keyboardHidden"
    android:screenOrientation="landscape" />
  1. 表单界面
<activity
    android:name=".FormActivity"
    android:configChanges="keyboardHidden"
    android:screenOrientation="portrait" />

使用建议:

  1. 何时使用 configChanges
// 需要自己处理配置变化时:
- 视频播放器需要无缝切换横竖屏
- 游戏不希望因旋转重启
- 有复杂状态需要保持
  1. 何时使用 screenOrientation
// 需要固定方向时:
- 游戏可能只支持横屏
- 表单录入固定竖屏
- 视频播放需要支持旋转
  1. 最佳实践
// 1. 一般应用界面
<activity
    android:name=".NormalActivity"
    android:screenOrientation="user" />  // 跟随系统设置

// 2. 视频播放界面
<activity
    android:name=".VideoActivity"
    android:configChanges="orientation|screenSize"
    android:screenOrientation="sensor" />  // 支持旋转

// 3. 游戏界面
<activity
    android:name=".GameActivity"
    android:configChanges="orientation|screenSize|keyboardHidden"
    android:screenOrientation="landscape" />  // 固定横屏

注意事项:

  1. 不要滥用 configChanges
  2. 正确处理配置变化
  3. 合理选择屏幕方向
  4. 考虑用户体验
  5. 测试各种场景

9.3 activity生命周期

想象一下 Activity 就像一个演员在舞台上的表演过程:

🎭 生命周期主要阶段解析

  1. onCreate() - 演员准备上台
  • 演员在后台化妆、准备道具
  • Activity 初始化、加载布局
  • 只执行一次
  1. onStart() - 演员走到台前
  • 演员进入观众可见范围
  • Activity 变得可见,但还未到前台
  • 还不能与用户交互
  1. onResume() - 演员开始表演
  • 演员成为舞台焦点,开始表演
  • Activity 位于前台,可与用户交互
  • 用户可以操作界面
  1. onPause() - 演员暂时退到后台
  • 另一个演员需要短暂出场
  • Activity 部分可见但失去焦点
  • 应保存重要数据
  1. onStop() - 演员完全退场
  • 演员退到幕后,观众看不见
  • Activity 完全不可见
  • 可释放不必要的资源
  1. onDestroy() - 演出结束
  • 演员卸妆离开剧场
  • Activity 被销毁
  • 释放所有资源

📊 生命周期流程图

graph TD
    A[启动 Activity] --> B[onCreate]
    B --> C[onStart]
    C --> D[onResume]
    D --> E[Activity 运行中]
    E --> F[onPause]
    F --> G[onStop]
    G --> H[onDestroy]
    G --> I[onRestart]
    I --> C
    F --> D

🔑 关键要点总结

  1. 完整生命周期:onCreate() → onDestroy()
  2. 可见生命周期:onStart() → onStop()
  3. 前台生命周期:onResume() → onPause()

📝 常见场景示例

  1. 打开新Activity
  • 当前Activity:onPause() → onStop()
  • 新Activity:onCreate() → onStart() → onResume()
  1. 按下返回键
  • 当前Activity:onPause() → onStop() → onDestroy()
  1. 按下Home键
  • 当前Activity:onPause() → onStop()
  1. 重新回到应用
  • onRestart() → onStart() → onResume()

💡 实用建议

  1. onCreate() 中进行初始化操作
  2. onPause() 中保存关键数据
  3. onDestroy() 中释放资源
  4. 避免在onPause() 中进行耗时操作

这样的生命周期机制确保了 Android 应用能够有序地管理资源和状态,提供流畅的用户体验。理解生命周期对于开发稳定的 Android 应用至关重要。

9.4 再次总结个流程图

让我用流程图来展示 Activity 的生命周期:

graph TD
    A[Activity 启动] --> B[onCreate]
    B --> C[onStart]
    C --> D[onResume]
    D --> E{用户交互}
    
    E -->|其他Activity覆盖| F[onPause]
    F -->|完全不可见| G[onStop]
    
    E -->|按Home键| F
    E -->|锁屏| F
    
    G -->|重新可见| H[onRestart]
    H --> C
    
    G -->|内存不足| I[onDestroy]
    G -->|返回键| I
    
    F -->|重新获得焦点| D
    
    E -->|配置改变| J[配置变化]
    J -->|未设置configChanges| K[销毁重建流程]
    K --> F
    K --> G
    K --> I
    K --> B
    
    J -->|设置configChanges| L[onConfigurationChanged]
    L --> E
    
    style E fill:#f9f,stroke:#333,stroke-width:4px

特殊情况的补充流程:

  1. Activity 切换时的完整流程:
sequenceDiagram
    participant A as Activity A
    participant B as Activity B
    
    A->>A: onPause
    A->>B: onCreate
    A->>B: onStart
    A->>B: onResume
    A->>A: onStop
  1. 对话框显示时:
graph TD
    A[Activity可见] --> B[onPause]
    B -->|对话框关闭| C[onResume]
    C --> A
  1. 内存回收与恢复:
graph TD
    A[正常运行] --> B[onPause]
    B --> C[onStop]
    C --> D[onSaveInstanceState]
    D --> E[onDestroy]
    
    F[重新打开] --> G[onCreate with savedInstanceState]
    G --> H[onStart]
    H --> I[onResume]
  1. 生命周期状态图:
stateDiagram-v2
    [*] --> Created: onCreate
    Created --> Started: onStart
    Started --> Resumed: onResume
    Resumed --> Paused: onPause
    Paused --> Resumed: onResume
    Paused --> Stopped: onStop
    Stopped --> Started: onRestart
    Stopped --> [*]: onDestroy

这些流程图展示了 Activity 生命周期的主要流转过程,希望这样更直观!

10. onNewIntent调用时机

onNewIntent 方法在 singleTopsingleTask 模式下会被调用,具体来说:

  1. singleTop 模式:
// 当新的Intent到达时,如果该Activity在栈顶,会调用onNewIntent
public class SearchActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedState) {
        super.onCreate(savedState);
        // 第一次创建时调用
        doSearch(getIntent().getStringExtra("query"));
    }
    
    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        // 已在栈顶时,新搜索请求到达
        doSearch(intent.getStringExtra("query"));
    }
}

Task Stack:
[SearchActivity]  ← 新Intent到达,调用onNewIntent
[MainActivity]
  1. singleTask 模式:
// 当新的Intent到达时,如果该Activity已经存在于栈中,会调用onNewIntent
public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedState) {
        super.onCreate(savedState);
        // 第一次创建时调用
    }
    
    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        // Activity已存在,新Intent到达时调用
        String action = intent.getAction();
        if ("SHOW_CART".equals(action)) {
            showCartPage();
        }
    }
}

Task Stack:
[ActivityB]       ← 这些Activity会被清除
[ActivityA]       ← 这些Activity会被清除
[MainActivity]    ← 已存在,调用onNewIntent
  1. standard 模式:
// 永远不会调用onNewIntent,因为总是创建新实例
Task Stack:
[ActivityA-新实例]  ← 创建新实例,调用onCreate
[ActivityA-旧实例]
[MainActivity]
  1. singleInstance 模式:
// 类似singleTask,但在独立的任务栈中
public class CallActivity extends Activity {
    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        // 新来电到达时调用
        String phoneNumber = intent.getStringExtra("phone");
        updateCallUI(phoneNumber);
    }
}

Task Stack 1:          Task Stack 2:
[MainActivity]         [CallActivity] ← 新Intent到达,调用onNewIntent
[ActivityA]

实际使用示例:

  1. 搜索场景:
public class SearchActivity extends Activity {
    @Override
    protected void onCreate(Bundle state) {
        super.onCreate(state);
        setContentView(R.layout.activity_search);
        handleSearchIntent(getIntent());
    }
    
    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        // 必须调用setIntent,否则getIntent还是旧的
        setIntent(intent);
        handleSearchIntent(intent);
    }
    
    private void handleSearchIntent(Intent intent) {
        String query = intent.getStringExtra("query");
        if (query != null) {
            performSearch(query);
        }
    }
}
  1. 主界面场景:
public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle state) {
        super.onCreate(state);
        setContentView(R.layout.activity_main);
        processIntent(getIntent());
    }
    
    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        setIntent(intent);
        processIntent(intent);
    }
    
    private void processIntent(Intent intent) {
        String action = intent.getAction();
        if ("OPEN_NOTIFICATION".equals(action)) {
            openNotification(intent.getExtras());
        }
    }
}
  1. 通知处理:
public class NotificationActivity extends Activity {
    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        // 更新当前Intent
        setIntent(intent);
        
        // 处理通知数据
        Bundle extras = intent.getExtras();
        if (extras != null) {
            String notificationId = 
                extras.getString("notification_id");
            updateUI(notificationId);
        }
    }
}

注意事项:

  1. 更新Intent:
@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    // 重要!更新当前Intent
    setIntent(intent);
    // 处理新Intent
}
  1. 生命周期:
// singleTop模式下:
onPause() → onNewIntent() → onResume()

// singleTask模式下:
[清除上面的Activity] → onNewIntent() → onResume()
  1. 数据处理:
@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    setIntent(intent);
    
    // 建议提取公共方法处理Intent
    processIntent(intent);
}

private void processIntent(Intent intent) {
    // 统一处理Intent数据
}

这样,你就能更好地理解和使用 onNewIntent 方法了!

10.1 调用时机和原理

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    // 1. SingleTop模式下栈顶复用
    // 2. SingleTask模式下已存在实例
    // 3. SingleInstance模式下唯一实例
    processNewIntent(intent);
}

10.2 最佳实践

让我用一个生动的外卖配送的例子来解释 onNewIntent 的作用:

想象你是一家外卖店铺(Activity),正在处理订单(Intent):

public class OrderActivity extends Activity {
    
    // 相当于店铺开门营业
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_order);
        
        // 处理第一个订单
        handleOrder(getIntent());
    }
    
    // 新订单来了!
    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        setIntent(intent); // 更新当前的订单
        handleOrder(intent); // 处理新订单
    }
    
    private void handleOrder(Intent intent) {
        String orderId = intent.getStringExtra("order_id");
        // 处理订单逻辑
    }
}

生活场景类比:

  1. 普通模式 (standard)
// 每次都新开一家店
Intent intent = new Intent(this, OrderActivity.class);
intent.putExtra("order_id", "12345");
startActivity(intent);

就像是:每接到一个订单就开一家新店 - 显然不合理!

  1. 单例模式 (singleTop/singleTask/singleInstance)
// Manifest 配置
<activity
    android:name=".OrderActivity"
    android:launchMode="singleTop"/>

就像是:

  • 已经有一家店在营业了
  • 新订单来了不会再开新店
  • 而是通过 onNewIntent 通知现有的店铺

实际应用场景:

  1. 消息通知处理
public class NotificationActivity extends Activity {
    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        
        // 比如收到微信消息,不需要开多个聊天窗口
        String chatId = intent.getStringExtra("chat_id");
        updateChat(chatId);
    }
}
  1. 搜索界面
public class SearchActivity extends Activity {
    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        
        // 新的搜索关键词来了,更新当前搜索结果
        String keyword = intent.getStringExtra("keyword");
        performSearch(keyword);
    }
}
  1. 扫码结果处理
public class ScanActivity extends Activity {
    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        
        // 连续扫描多个码,在同一个界面更新显示
        String scanResult = intent.getStringExtra("scan_result");
        showScanResult(scanResult);
    }
}

生动理解:

  1. 你在用微信时点击通知栏:

    • 如果微信没打开:创建新的微信界面(onCreate)
    • 如果微信已经打开:更新现有界面(onNewIntent)
  2. 就像餐厅服务:

    • 餐厅关门时来客人 → 开门迎接(onCreate)
    • 餐厅营业时来客人 → 直接接待(onNewIntent)
    • 不会因为新客人就再开一家分店!

主要作用总结:

  1. 避免重复创建 Activity
  2. 复用已存在的 Activity
  3. 更新现有 Activity 的状态
  4. 节省系统资源
  5. 提供更好的用户体验

使用场景:

  • 通知栏点击处理
  • 搜索界面
  • 扫码页面
  • 聊天窗口
  • 任何需要单例的交互界面

记住:就像一家店不需要为每个顾客都开新分店,Activity 也不需要为每个新 Intent 都创建新实例!

10.3 Activity栈的管理

让我用生活中的例子来解释这几种启动模式:

  1. standard(标准模式):
// 想象成坐电梯
每个人(Activity)进来都占一个位置
即使是同一个人,也会占用新的位置

Task Stack:
[小明]
[小红]
[小明]  // 同一个人可以重复进来
[小张]
  1. singleTop(栈顶复用):
// 想象成排队买奶茶
如果你已经在队伍最前面了,就不用重新排队
但如果你在中间,还是得重新排

Task Stack:
[小明]  ← 已经在顶部,复用这个实例
[小红]
[小明]  ← 在中间,会创建新实例
[小张]
  1. singleTask(栈内复用):
// 想象成公司CEO
整个公司(Task)只能有一个CEO
如果CEO要来,上面的人都得离开

开始:
[员工A]
[员工B]
[CEO]   ← 已经存在
[员工C]

来了新CEO:
[员工A]
[CEO]   ← 上面的员工B被清除
[员工C]
  1. singleInstance(独享任务栈):
// 想象成总统专车
总统有专门的车队,不和其他车辆混在一起

Task Stack 1 (普通任务栈):    Task Stack 2 (专用任务栈):
[普通Activity]                [SingleInstance Activity]
[普通Activity]                
[普通Activity]                

具体代码示例:

  1. singleTop:
// AndroidManifest.xml
<activity
    android:name=".SingleTopActivity"
    android:launchMode="singleTop"/>

// 使用场景:通知栏点击
public class NotificationActivity extends Activity {
    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        // 处理新的Intent
        String data = intent.getStringExtra("data");
    }
}
  1. singleTask:
// AndroidManifest.xml
<activity
    android:name=".MainActivity"
    android:launchMode="singleTask"/>

// 使用场景:应用主界面
public class MainActivity extends Activity {
    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        // 回到主界面时的处理
    }
}
  1. singleInstance:
// AndroidManifest.xml
<activity
    android:name=".CallActivity"
    android:launchMode="singleInstance"/>

// 使用场景:来电界面
public class CallActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 处理来电
    }
}

常见使用场景:

  1. singleTop:
// 适合接收频繁更新的界面
- 通知栏点击界面
- 搜索结果界面
- 消息提醒界面
  1. singleTask:
// 适合作为应用程序入口
- 主界面
- 购物车界面
- 个人中心界面
  1. singleInstance:
// 适合需要独立的界面
- 来电界面
- 闹钟提醒界面
- 应用锁界面

生活类比总结:

  1. standard
像公交车,来一个人就占一个新位置
  1. singleTop
像排队买奶茶,如果你已经在队伍最前面就不用重新排
  1. singleTask
像公司CEO,整个公司只能有一个,新CEO来了上面的人都得走
  1. singleInstance
像总统专车,独享一个车队,不和其他车混在一起

11. Intent数据限制

限制:约1MB

解决方案:

  1. 使用Bundle
  2. 文件共享
  3. 数据库
  4. ContentProvider

11.1 Binder事务缓冲区限制

  • 同步调用:约1MB
  • 异步调用:约0.5MB

11.2 大数据传输方案

我来介绍几种 Intent 传输大数据的解决方案:

  1. 使用 Bundle + Parcelable 适用于中等大小数据(建议 < 1MB):
public class DataModel implements Parcelable {
    // 使用 @JvmField 优化序列化性能
    @JvmField
    public final byte[] data;
    
    public DataModel(byte[] data) {
        this.data = data;
    }
    
    // Parcelable 实现
    protected DataModel(Parcel in) {
        data = new byte[in.readInt()];
        in.readByteArray(data);
    }
    
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(data.length);
        dest.writeByteArray(data);
    }
    
    // ... 其他 Parcelable 方法 ...
}
  1. 使用文件共享 适用于大文件传输:
public class FileTransferUtil {
    public static Uri shareFile(Context context, File file) {
        Uri contentUri = FileProvider.getUriForFile(context,
            "com.example.fileprovider",
            file);
            
        // 授予临时读取权限
        context.grantUriPermission(
            targetPackage,
            contentUri,
            Intent.FLAG_GRANT_READ_URI_PERMISSION
        );
        
        return contentUri;
    }
}

// 使用示例
Intent intent = new Intent(this, TargetActivity.class);
intent.setData(shareFile(this, largeFile));
startActivity(intent);
  1. 使用 ContentProvider 适用于结构化大数据:
public class LargeDataProvider extends ContentProvider {
    private static final String AUTHORITY = "com.example.provider";
    public static final Uri CONTENT_URI = 
        Uri.parse("content://" + AUTHORITY + "/data");
        
    // 发送方
    public static void sendLargeData(Context context, byte[] data) {
        ContentValues values = new ContentValues();
        values.put("data", data);
        context.getContentResolver().insert(CONTENT_URI, values);
    }
    
    // 接收方
    public static byte[] receiveLargeData(Context context) {
        Cursor cursor = context.getContentResolver().query(
            CONTENT_URI, null, null, null, null);
        // ... 读取数据 ...
    }
}
  1. 使用内存缓存 适用于同进程间传递:
public class DataCache {
    private static final DataCache INSTANCE = new DataCache();
    private final Map<String, WeakReference<Object>> cache = new ConcurrentHashMap<>();
    
    public static String cacheData(Object data) {
        String key = UUID.randomUUID().toString();
        INSTANCE.cache.put(key, new WeakReference<>(data));
        return key;
    }
    
    public static Object retrieveData(String key) {
        WeakReference<Object> ref = INSTANCE.cache.remove(key);
        return ref != null ? ref.get() : null;
    }
}

// 使用示例
// 发送方
String key = DataCache.cacheData(largeData);
Intent intent = new Intent(this, TargetActivity.class);
intent.putExtra("data_key", key);

// 接收方
String key = getIntent().getStringExtra("data_key");
Object data = DataCache.retrieveData(key);
  1. 使用 AIDL 适用于跨进程大数据传输:
interface IDataTransfer {
    void transferData(in byte[] data);
}
public class DataService extends Service {
    private final IDataTransfer.Stub binder = new IDataTransfer.Stub() {
        @Override
        public void transferData(byte[] data) {
            // 处理数据
        }
    };
}

最佳实践建议:

  1. 选择合适的传输方式
public class DataTransferUtil {
    public static void transferData(Context context, Object data) {
        if (data instanceof byte[] && ((byte[]) data).length < 1024 * 1024) {
            // 小数据用 Intent
            transferViaIntent(context, (byte[]) data);
        } else if (data instanceof File) {
            // 文件用 FileProvider
            transferViaFileProvider(context, (File) data);
        } else {
            // 其他大数据用 ContentProvider
            transferViaContentProvider(context, data);
        }
    }
}
  1. 内存优化
// 分片传输大数据
public class ChunkedTransfer {
    private static final int CHUNK_SIZE = 1024 * 1024; // 1MB
    
    public static void transferLargeData(byte[] data) {
        int chunks = (data.length + CHUNK_SIZE - 1) / CHUNK_SIZE;
        for (int i = 0; i < chunks; i++) {
            int start = i * CHUNK_SIZE;
            int length = Math.min(CHUNK_SIZE, data.length - start);
            byte[] chunk = Arrays.copyOfRange(data, start, start + length);
            // 传输单个分片
            transferChunk(chunk, i, chunks);
        }
    }
}
  1. 异步处理
public class AsyncTransfer {
    public static void transferDataAsync(final byte[] data) {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        executor.execute(() -> {
            // 在后台线程处理数据传输
            transferData(data);
        });
    }
}

选择建议:

  • < 1MB:使用 Intent + Bundle
  • 文件:使用 FileProvider
  • 结构化数据:使用 ContentProvider
  • 跨进程:使用 AIDL
  • 临时数据:使用内存缓存

注意事项:

  1. 考虑内存限制
  2. 处理传输异常
  3. 注意性能影响
  4. 合理选择传输方式
  5. 必要时使用分片传输

11.3Bundle的详细使用

Bundle 主要用于在 Android 组件之间传递数据。让我来详细解释它的使用场景和方法:

  1. Activity 间传递数据:
// 发送数据
Intent intent = new Intent(this, SecondActivity.class);
Bundle bundle = new Bundle();
bundle.putString("name", "张三");
bundle.putInt("age", 25);
bundle.putParcelable("user", userObject);
intent.putExtras(bundle);  // 或直接 intent.putExtra()
startActivity(intent);

// 接收数据
public class SecondActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        Bundle bundle = getIntent().getExtras();
        if (bundle != null) {
            String name = bundle.getString("name");
            int age = bundle.getInt("age");
            User user = bundle.getParcelable("user");
        }
    }
}
  1. 保存和恢复状态:
public class MainActivity extends Activity {
    private String userName;
    private int score;
    
    // 保存状态
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putString("userName", userName);
        outState.putInt("score", score);
    }
    
    // 恢复状态
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState != null) {
            userName = savedInstanceState.getString("userName");
            score = savedInstanceState.getInt("score");
        }
    }
}
  1. Fragment 传递参数:
// 创建带参数的Fragment
public static UserFragment newInstance(String userId, boolean isAdmin) {
    UserFragment fragment = new UserFragment();
    Bundle args = new Bundle();
    args.putString("userId", userId);
    args.putBoolean("isAdmin", isAdmin);
    fragment.setArguments(args);
    return fragment;
}

// 在Fragment中获取参数
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Bundle args = getArguments();
    if (args != null) {
        String userId = args.getString("userId");
        boolean isAdmin = args.getBoolean("isAdmin");
    }
}
  1. Bundle 支持的数据类型:
bundle.putBoolean("key", true);
bundle.putByte("key", byte);
bundle.putChar("key", 'a');
bundle.putShort("key", short);
bundle.putInt("key", 1);
bundle.putLong("key", long);
bundle.putFloat("key", float);
bundle.putDouble("key", double);
bundle.putString("key", "string");
bundle.putCharSequence("key", charSequence);
bundle.putParcelable("key", parcelable);
bundle.putSerializable("key", serializable);
bundle.putIntegerArrayList("key", integerArrayList);
bundle.putStringArrayList("key", stringArrayList);
bundle.putParcelableArrayList("key", parcelableArrayList);
  1. 自定义对象传递:
// 1. Parcelable方式(推荐)
public class User implements Parcelable {
    private String name;
    private int age;
    
    protected User(Parcel in) {
        name = in.readString();
        age = in.readInt();
    }
    
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeInt(age);
    }
    
    public static final Creator<User> CREATOR = new Creator<User>() {
        @Override
        public User createFromParcel(Parcel in) {
            return new User(in);
        }
        
        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };
    
    @Override
    public int describeContents() {
        return 0;
    }
}

// 2. Serializable方式
public class Book implements Serializable {
    private String title;
    private String author;
}
  1. 常见使用场景:

a. 启动Activity时传递数据:

// 启动Activity并传递多个数据
private void startDetailActivity() {
    Intent intent = new Intent(this, DetailActivity.class);
    Bundle bundle = new Bundle();
    bundle.putString("title", "商品详情");
    bundle.putParcelable("product", product);
    bundle.putStringArrayList("images", imageUrls);
    intent.putExtras(bundle);
    startActivity(intent);
}

b. Fragment间通信:

// 从一个Fragment发送数据到另一个Fragment
Bundle bundle = new Bundle();
bundle.putString("message", "Hello from Fragment A");
Fragment fragmentB = new FragmentB();
fragmentB.setArguments(bundle);

c. 保存复杂状态:

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    
    // 保存列表状态
    outState.putParcelableArrayList("items", itemsList);
    
    // 保存滚动位置
    outState.putInt("scrollPosition", 
        recyclerView.getLayoutManager()
                   .findFirstVisibleItemPosition());
                   
    // 保存用户输入
    outState.putString("searchQuery", 
        searchView.getQuery().toString());
}
  1. 注意事项:

a. 大小限制:

// Bundle有大小限制,不要传递太大的数据
// 大数据建议使用其他方式:数据库、文件等

b. 类型安全:

// 使用类型安全的方法获取数据
String name = bundle.getString("name", "默认值");
int age = bundle.getInt("age", 0);

c. 空值检查:

Bundle bundle = getIntent().getExtras();
if (bundle != null && bundle.containsKey("key")) {
    // 使用数据
}

d. 键值管理:

// 建议使用常量管理键值
public static final String EXTRA_USER_ID = "user_id";
public static final String EXTRA_USER_NAME = "user_name";

// 使用时
bundle.putString(EXTRA_USER_ID, userId);

12. ContentProvider/Resolver/Observer关系

graph LR
    A[ContentProvider] --> |提供数据| B[ContentResolver]
    B --> |访问数据| C[应用]
    D[ContentObserver] --> |监听变化| A

12.1 架构设计

graph LR
    A[ContentProvider] -->|提供数据| B[ContentResolver]
    B -->|查询/更新| C[应用程序]
    D[ContentObserver] -->|监听变化| A
    A -->|通知变化| D

12.2 数据同步机制

我来用一个生活中的例子来解释 ContentProvider 的数据同步机制。

想象一个共享的通讯录应用场景:

public class ContactProvider extends ContentProvider {
    // 数据变化的 URI
    public static final Uri CONTENT_URI = Uri.parse("content://com.example.contacts/contacts");
    
    @Override
    public void onChange(boolean selfChange) {
        // 通知观察者数据发生变化
        getContext().getContentResolver().notifyChange(CONTENT_URI, null);
    }
    
    // 插入联系人时
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        // ... 插入数据的代码 ...
        
        // 数据变化后通知
        onChange(false);
        return uri;
    }
}

就像是:

  1. 你有一个共享的通讯录(ContentProvider)
  2. 小明和小红都在使用这个通讯录(不同的应用)
  3. 小明添加了一个新联系人(数据更新)
  4. 系统会自动通知小红的应用(数据同步)

要监听这些变化,小红的应用需要:

public class ContactActivity extends Activity {
    private ContentObserver observer = new ContentObserver(new Handler()) {
        @Override
        public void onChange(boolean selfChange) {
            // 数据发生变化时更新界面
            refreshContactList();
        }
    };
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        // 注册观察者
        getContentResolver().registerContentObserver(
            ContactProvider.CONTENT_URI, 
            true, 
            observer
        );
    }
}

这就像是:

  1. 小红告诉系统:"请帮我留意通讯录的变化"(注册观察者)
  2. 当小明修改通讯录时,系统会自动通知小红(触发 onChange)
  3. 小红的应用就能及时更新显示最新的联系人列表

这个机制的优点是:

  • 自动化:不需要手动检查更新
  • 实时性:数据变化后立即通知
  • 解耦合:数据提供方和使用方互不干扰

这就是为什么 ContentProvider 特别适合在不同应用间共享和同步数据。

12.3 ContentProvider的线程处理

我来总结 ContentProvider 中需要注意的线程相关问题:

  1. 主线程调用问题
public class MyContentProvider extends ContentProvider {
    @Override
    public Cursor query(...) {
        // 警告:查询操作可能很耗时
        // 不要直接在这里进行耗时操作
        return database.query(...);  // 错误示范
    }
}

// 正确的使用方式
public class MainActivity extends Activity {
    private void loadData() {
        // 在异步线程中查询
        new AsyncTask<Void, Void, Cursor>() {
            @Override
            protected Cursor doInBackground(Void... params) {
                return getContentResolver().query(...);
            }
        }.execute();
    }
}
  1. 数据库操作的线程安全
public class MyContentProvider extends ContentProvider {
    private SQLiteDatabase mDb;
    
    // 使用 SQLiteOpenHelper 确保数据库操作的线程安全
    private static class DatabaseHelper extends SQLiteOpenHelper {
        // ... 数据库相关代码 ...
    }
    
    @Override
    public boolean onCreate() {
        mDb = new DatabaseHelper(getContext()).getWritableDatabase();
        return true;
    }
}
  1. 批量操作的原子性
public class MyContentProvider extends ContentProvider {
    @Override
    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) {
        SQLiteDatabase db = mDb;
        db.beginTransaction();
        try {
            ContentProviderResult[] results = super.applyBatch(operations);
            db.setTransactionSuccessful();
            return results;
        } finally {
            db.endTransaction();
        }
    }
}
  1. 异步加载的最佳实践
public class MainActivity extends Activity {
    private static final int LOADER_ID = 1;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        // 使用 LoaderManager 进行异步加载
        LoaderManager.getInstance(this).initLoader(LOADER_ID, null, new LoaderManager.LoaderCallbacks<Cursor>() {
            @Override
            public Loader<Cursor> onCreateLoader(int id, Bundle args) {
                return new CursorLoader(MainActivity.this,
                    Uri.parse("content://your.provider"),
                    null, null, null, null);
            }
            
            @Override
            public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
                // 在主线程处理数据
            }
            
            @Override
            public void onLoaderReset(Loader<Cursor> loader) {
                // 清理资源
            }
        });
    }
}

主要注意事项:

  1. ANR 风险

    • ContentProvider 的 CRUD 操作都可能在主线程被调用
    • 耗时操作必须放在工作线程中执行
    • 建议使用 LoaderManager 或 协程等进行异步操作
  2. 并发访问

    • ContentProvider 可能被多个线程同时访问
    • 使用 SQLiteOpenHelper 来保证数据库操作的线程安全
    • 对共享资源的访问需要同步处理
  3. 事务处理

    • 批量操作要使用事务来保证原子性
    • 正确处理事务的开始和结束
    • 注意异常情况下的回滚处理
  4. 生命周期感知

    • 使用 LoaderManager 可以感知 Activity 生命周期
    • 避免在 Activity 销毁后继续进行数据操作
    • 及时清理不需要的观察者和回调
  5. 性能优化

    • 合理使用批量操作(bulkInsert)
    • 避免频繁的数据库操作
    • 适当使用缓存机制

记住这些要点可以帮助开发出更稳定、高效的 ContentProvider 实现。

12.4 ContentProvider对共享资源的访问需要同步处理 如何最优方案

我来介绍 ContentProvider 中共享资源访问的几种最优同步处理方案:

  1. 使用 ReadWriteLock 适用于读多写少的场景:
public class DataProvider extends ContentProvider {
    private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    private final Lock readLock = rwLock.readLock();
    private final Lock writeLock = rwLock.writeLock();
    
    @Override
    public Cursor query(...) {
        readLock.lock();
        try {
            // 读取操作
            return database.query(...);
        } finally {
            readLock.unlock();
        }
    }
    
    @Override
    public Uri insert(...) {
        writeLock.lock();
        try {
            // 写入操作
            return database.insert(...);
        } finally {
            writeLock.unlock();
        }
    }
}
  1. 使用 ConcurrentHashMap 作为缓存 适用于需要内存缓存的场景:
public class CachedProvider extends ContentProvider {
    private final ConcurrentHashMap<String, Object> mCache = new ConcurrentHashMap<>();
    
    private Object getFromCache(String key) {
        // 线程安全的读取
        return mCache.get(key);
    }
    
    private void updateCache(String key, Object value) {
        // 线程安全的更新
        mCache.put(key, value);
    }
}
  1. 使用 synchronized 块优化 针对细粒度的同步需求:
public class OptimizedProvider extends ContentProvider {
    private final Object mLock = new Object();
    private volatile Map<String, Object> mData;
    
    // 读取操作使用 volatile
    public Object getData(String key) {
        return mData.get(key);
    }
    
    // 写入操作使用同步块
    public void updateData(String key, Object value) {
        synchronized(mLock) {
            Map<String, Object> newData = new HashMap<>(mData);
            newData.put(key, value);
            mData = newData;
        }
    }
}
  1. 使用 AtomicReference 实现无锁同步 适用于简单的原子操作:
public class AtomicProvider extends ContentProvider {
    private final AtomicReference<Data> atomicData = new AtomicReference<>();
    
    public void updateData(Data newData) {
        while (true) {
            Data current = atomicData.get();
            if (atomicData.compareAndSet(current, newData)) {
                break;
            }
        }
    }
}
  1. 使用 ThreadLocal 处理线程局部变量 适用于每个线程需要独立副本的场景:
public class ThreadLocalProvider extends ContentProvider {
    private final ThreadLocal<DatabaseHelper> localDatabase = 
        new ThreadLocal<DatabaseHelper>() {
            @Override
            protected DatabaseHelper initialValue() {
                return new DatabaseHelper(getContext());
            }
        };
    
    public void performDatabaseOperation() {
        // 每个线程获取自己的数据库连接
        DatabaseHelper db = localDatabase.get();
        // 执行操作
    }
}

最佳实践建议:

  1. 选择合适的同步策略
  • 读多写少:使用 ReadWriteLock
  • 缓存场景:使用 ConcurrentHashMap
  • 简单原子操作:使用 AtomicReference
  • 线程独立数据:使用 ThreadLocal
  1. 优化锁的粒度
// 不好的做法 - 锁粒度太大
synchronized(lock) {
    // 很多操作
}

// 好的做法 - 只锁定必要的部分
// 预处理
synchronized(lock) {
    // 关键操作
}
// 后续处理
  1. 避免死锁
// 始终按照固定顺序获取多个锁
private final Object lockA = new Object();
private final Object lockB = new Object();

public void operation() {
    synchronized(lockA) {
        synchronized(lockB) {
            // 操作
        }
    }
}
  1. 使用 volatile 配合锁
public class OptimizedCache {
    private volatile boolean initialized = false;
    private final Object lock = new Object();
    
    public void initializeIfNeeded() {
        if (!initialized) {  // 快速检查
            synchronized(lock) {
                if (!initialized) {  // 双重检查
                    // 初始化操作
                    initialized = true;
                }
            }
        }
    }
}

这些方案各有优势,选择时需要考虑:

  • 访问模式(读写比例)
  • 性能要求
  • 代码复杂度
  • 数据一致性要求

根据具体场景选择最合适的方案,并且要注意避免过度设计。

13. Activity加载流程

graph TD
    A[点击图标] --> B[Launcher请求AMS]
    B --> C[AMS创建进程]
    C --> D[ActivityThread]
    D --> E[创建Activity]
    E --> F[生命周期回调]

主要步骤:

  1. 启动请求
  2. AMS处理
  3. 进程创建
  4. Activity创建
  5. 生命周期管理

13.1 详细流程

graph TD
    A[Launcher调用startActivity] --> B[AMS处理请求]
    B --> C[创建进程]
    C --> D[创建ActivityThread]
    D --> E[创建Application]
    E --> F[创建Activity]
    F --> G[调用生命周期方法]

13.2 源码分析要点

// ActivityThread.java
public final class ActivityThread {
    private Activity performLaunchActivity(ActivityClientRecord r) {
        // 1. 创建Context
        ContextImpl appContext = createBaseContextForActivity(r);
        
        // 2. 创建Activity实例
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
        }
        
        // 3. 关联Context
        activity.attach(appContext, ...);
        
        // 4. 调用onCreate
        activity.onCreate(r.state);
    }
}

这些深入的解答涵盖了:

  1. 源码级别的实现原理
  2. 性能优化考虑
  3. 最佳实践示例
  4. 架构设计思想
  5. 进程间通信机制
  6. 内存管理策略

14.FileProvider的使用

可以访问你指定的任何应用私有目录中的文件。让我详细解释下:

  1. FileProvider 的配置:
<!-- AndroidManifest.xml -->
<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="com.example.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

<!-- res/xml/file_paths.xml -->
<paths>
    <!-- 缓存目录 -->
    <cache-path name="cache" path="/" />
    <!-- 内部存储目录 -->
    <files-path name="files" path="/" />
    <!-- 外部缓存目录 -->
    <external-cache-path name="external_cache" path="/" />
    <!-- 外部文件目录 -->
    <external-files-path name="external_files" path="/" />
</paths>
  1. 使用不同目录的示例:
public class FileTransferUtil {
    // 使用内部缓存目录
    public static Uri shareCacheFile(Context context, String fileName) {
        File cacheFile = new File(context.getCacheDir(), fileName);
        return getFileUri(context, cacheFile);
    }
    
    // 使用内部文件目录
    public static Uri shareInternalFile(Context context, String fileName) {
        File internalFile = new File(context.getFilesDir(), fileName);
        return getFileUri(context, internalFile);
    }
    
    // 使用外部缓存目录
    public static Uri shareExternalCacheFile(Context context, String fileName) {
        File externalCacheFile = new File(context.getExternalCacheDir(), fileName);
        return getFileUri(context, externalCacheFile);
    }
    
    // 使用外部文件目录
    public static Uri shareExternalFile(Context context, String fileName) {
        File externalFile = new File(
            context.getExternalFilesDir(null), 
            fileName
        );
        return getFileUri(context, externalFile);
    }
    
    private static Uri getFileUri(Context context, File file) {
        return FileProvider.getUriForFile(
            context,
            "com.example.fileprovider",
            file
        );
    }
}
  1. 完整的文件共享示例:
public class FileTransferUtil {
    public static Uri shareFile(Context context, File sourceFile, String fileName) {
        // 1. 确定目标目录
        File targetDir = context.getCacheDir(); // 或其他目录
        File targetFile = new File(targetDir, fileName);
        
        // 2. 复制文件(如果需要)
        try {
            copyFile(sourceFile, targetFile);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
        
        // 3. 获取Content URI
        Uri contentUri = FileProvider.getUriForFile(
            context,
            "com.example.fileprovider",
            targetFile
        );
        
        // 4. 授予权限
        context.grantUriPermission(
            targetPackage,
            contentUri,
            Intent.FLAG_GRANT_READ_URI_PERMISSION
        );
        
        return contentUri;
    }
    
    private static void copyFile(File source, File target) throws IOException {
        try (InputStream in = new FileInputStream(source);
             OutputStream out = new FileOutputStream(target)) {
            byte[] buffer = new byte[8192];
            int read;
            while ((read = in.read(buffer)) != -1) {
                out.write(buffer, 0, read);
            }
        }
    }
}
  1. 使用建议:

根据文件用途选择合适的目录:

// 临时文件:使用缓存目录
File cacheFile = new File(context.getCacheDir(), "temp.jpg");

// 需要长期保存的文件:使用文件目录
File permanentFile = new File(context.getFilesDir(), "data.txt");

// 大文件:考虑使用外部存储
File largeFile = new File(
    context.getExternalFilesDir(null), 
    "large_video.mp4"
);
  1. 文件清理:
public class FileCleanupUtil {
    // 清理缓存文件
    public static void cleanupCacheFiles(Context context) {
        File cacheDir = context.getCacheDir();
        deleteRecursive(cacheDir);
    }
    
    // 清理过期文件
    public static void cleanupOldFiles(Context context, long maxAge) {
        File cacheDir = context.getCacheDir();
        long now = System.currentTimeMillis();
        
        File[] files = cacheDir.listFiles();
        if (files != null) {
            for (File file : files) {
                if (now - file.lastModified() > maxAge) {
                    file.delete();
                }
            }
        }
    }
}
  1. 注意事项:
// 1. 检查文件是否存在
if (!file.exists()) {
    throw new FileNotFoundException("File not found");
}

// 2. 检查文件大小
if (file.length() > MAX_FILE_SIZE) {
    throw new IOException("File too large");
}

// 3. 处理权限
if (ContextCompat.checkSelfPermission(context, 
    Manifest.permission.WRITE_EXTERNAL_STORAGE) 
    != PackageManager.PERMISSION_GRANTED) {
    // 请求权限
}

// 4. 异步处理大文件
executor.execute(() -> {
    Uri uri = shareFile(context, largeFile);
    handler.post(() -> {
        // 更新UI
    });
});

这样,你可以根据具体需求选择合适的存储位置和处理方式。