让我逐个为您详细解答这些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主要通信方式:
- 接口回调:就像餐厅服务员(Fragment)把客人点单传给厨师(Activity)
// 定义接口
public interface OnFragmentInteractionListener {
void onFragmentInteraction(String data);
}
// Fragment中
((OnFragmentInteractionListener)getActivity()).onFragmentInteraction("数据");
优点:解耦合,符合面向接口编程原则 缺点:需要处理生命周期,避免内存泄漏
- Bundle传递:像快递包裹一样打包数据
Bundle bundle = new Bundle();
bundle.putString("key", "value");
fragment.setArguments(bundle);
- ViewModel共享:像家庭共享的储物柜
SharedViewModel viewModel = new ViewModelProvider(getActivity()).get(SharedViewModel.class);
特点:
- 生命周期感知
- 数据持久化(配置更改)
- 内存泄漏防护
-
EventBus:像小区的公告栏,谁都可以发布和接收消息
-
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 进阶通信方式
- 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)
}
}
}
优势:
- 冷流特性,支持背压
- 协程集成
- 线程安全
- Hilt依赖注入
@HiltViewModel
class SharedViewModel @Inject constructor(
private val repository: Repository
) : ViewModel() {
// ViewModel implementation
}
2. LaunchMode特点和应用场景
2.1 主要模式
想象一个办公楼的不同开门政策:
- standard(标准模式)
- 特点:每次都新建实例
- 场景:普通页面
- 比喻:每个访客都获得新的访客卡
- singleTop(栈顶复用)
- 特点:栈顶有就复用,没有就新建
- 场景:通知栏点击
- 比喻:电梯已在顶层就不用重新呼叫
- singleTask(栈内复用)
- 特点:栈内已有就复用并清除上面的
- 场景:主页面
- 比喻:老板回公司,上面的会议自动取消
- singleInstance(独享栈)
- 特点:单独任务栈
- 场景:特殊界面(如闹钟提醒)
- 比喻:总裁专用电梯
2.2 任务栈管理机制
// 示例:自定义任务栈
<activity
android:name=".MainActivity"
android:launchMode="singleTask"
android:taskAffinity="com.example.custom.task"/>
- standard模式原理
- 每次创建新实例都会调用
ActivityThread.performLaunchActivity() - 实例与任务栈一一对应
- singleTop实现机制
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// 栈顶复用时调用
setIntent(intent); // 更新Intent
}
- singleTask内部处理
- ActivityManagerService负责任务栈管理
- 通过taskAffinity确定目标任务栈
- 清理目标Activity上方实例(clearTop机制)
- singleInstance特殊处理
- 独立任务栈实现跨应用共享
- 系统级别的Activity常用此模式
2.3 实际应用场景分析
- 电商场景
首页(singleTask) -> 商品列表(standard) -> 商品详情(standard) -> 支付页面(singleTop)
- 社交场景
主页(singleTask) -> 聊天列表(standard) -> 聊天界面(singleTop) -> 用户信息(standard)
3. BroadcastReceiver vs LocalBroadcastReceiver
graph LR
A[BroadcastReceiver] --> B[全局广播]
C[LocalBroadcastReceiver] --> D[应用内广播]
比喻:
- BroadcastReceiver:像城市广播,全城都能听到
- LocalBroadcastReceiver:像家庭对讲机,只在家里通信
区别:
- 作用范围:全局 vs 应用内
- 安全性:LocalBroadcastReceiver更安全
- 性能:Local更高效
3.1 实现原理对比
- 全局广播(BroadcastReceiver)
// 基于Binder机制
public final void sendBroadcast(Intent intent) {
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent,
mUserHandle.getIdentifier());
}
- 本地广播(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 性能对比
- 内存占用
- LocalBroadcastManager: ~0.2MB
- 全局广播: 根据注册数量增长
- 执行效率
- Local: 无需跨进程,约提升30%效率
- 全局: 需要Binder调用
4. Context了解
想象Context是一个万能管家:
graph TD
A[Context] --> B[Application Context]
A --> C[Activity Context]
A --> D[Service Context]
主要功能:
- 访问资源
- 启动Activity/Service
- 获取系统服务
- 获取数据库/文件路径
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
就像快递分拣规则:
匹配机制:
- action匹配(必须完全匹配)
- category匹配(可以没有,但有必须匹配)
- data匹配(URI和数据类型)
使用场景:
- 隐式启动组件
- 接收广播
- 处理特定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 高级应用场景
- 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>
- 自定义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保活
主要方法:
- 提高进程优先级(前台服务)
- 相互唤醒机制
- JobScheduler定时启动
- 用户可感知(通知栏)
让我详细介绍 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);
}
}
注意事项:
- Android 8.0 后台限制
- 电池优化限制
- 各厂商系统差异
- 进程保活耗电问题
- 用户体验影响
建议:
- 必要时才使用保活
- 合理使用前台服务
- 遵守系统规范
- 考虑用户体验
- 做好兼容性适配
8. ContentProvider数据共享
像图书馆管理系统:
- 统一的数据访问接口
- 权限控制
- 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" />
让我详细解释这几个配置的含义和使用场景:
- 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: 键盘显示状态变化
- android:screenOrientation
android:screenOrientation="user"
这个属性控制 Activity 的屏幕方向,常见值:
<!-- 固定方向 -->
android:screenOrientation="portrait" <!-- 固定竖屏 -->
android:screenOrientation="landscape" <!-- 固定横屏 -->
<!-- 自动方向 -->
android:screenOrientation="user" <!-- 跟随用户设置 -->
android:screenOrientation="sensor" <!-- 跟随重力感应 -->
android:screenOrientation="unspecified" <!-- 未指定,系统决定 -->
实际应用示例:
- 视频播放器
<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);
}
}
}
- 游戏界面
<activity
android:name=".GameActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
android:screenOrientation="landscape" />
- 表单界面
<activity
android:name=".FormActivity"
android:configChanges="keyboardHidden"
android:screenOrientation="portrait" />
使用建议:
- 何时使用 configChanges
// 需要自己处理配置变化时:
- 视频播放器需要无缝切换横竖屏
- 游戏不希望因旋转重启
- 有复杂状态需要保持
- 何时使用 screenOrientation
// 需要固定方向时:
- 游戏可能只支持横屏
- 表单录入固定竖屏
- 视频播放需要支持旋转
- 最佳实践
// 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" /> // 固定横屏
注意事项:
- 不要滥用 configChanges
- 正确处理配置变化
- 合理选择屏幕方向
- 考虑用户体验
- 测试各种场景
9.3 activity生命周期
想象一下 Activity 就像一个演员在舞台上的表演过程:
🎭 生命周期主要阶段解析
- onCreate() - 演员准备上台
- 演员在后台化妆、准备道具
- Activity 初始化、加载布局
- 只执行一次
- onStart() - 演员走到台前
- 演员进入观众可见范围
- Activity 变得可见,但还未到前台
- 还不能与用户交互
- onResume() - 演员开始表演
- 演员成为舞台焦点,开始表演
- Activity 位于前台,可与用户交互
- 用户可以操作界面
- onPause() - 演员暂时退到后台
- 另一个演员需要短暂出场
- Activity 部分可见但失去焦点
- 应保存重要数据
- onStop() - 演员完全退场
- 演员退到幕后,观众看不见
- Activity 完全不可见
- 可释放不必要的资源
- 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
🔑 关键要点总结:
- 完整生命周期:onCreate() → onDestroy()
- 可见生命周期:onStart() → onStop()
- 前台生命周期:onResume() → onPause()
📝 常见场景示例:
- 打开新Activity:
- 当前Activity:onPause() → onStop()
- 新Activity:onCreate() → onStart() → onResume()
- 按下返回键:
- 当前Activity:onPause() → onStop() → onDestroy()
- 按下Home键:
- 当前Activity:onPause() → onStop()
- 重新回到应用:
- onRestart() → onStart() → onResume()
💡 实用建议:
- onCreate() 中进行初始化操作
- onPause() 中保存关键数据
- onDestroy() 中释放资源
- 避免在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
特殊情况的补充流程:
- 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
- 对话框显示时:
graph TD
A[Activity可见] --> B[onPause]
B -->|对话框关闭| C[onResume]
C --> A
- 内存回收与恢复:
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]
- 生命周期状态图:
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 方法在 singleTop 和 singleTask 模式下会被调用,具体来说:
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]
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
standard模式:
// 永远不会调用onNewIntent,因为总是创建新实例
Task Stack:
[ActivityA-新实例] ← 创建新实例,调用onCreate
[ActivityA-旧实例]
[MainActivity]
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]
实际使用示例:
- 搜索场景:
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);
}
}
}
- 主界面场景:
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());
}
}
}
- 通知处理:
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);
}
}
}
注意事项:
- 更新Intent:
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// 重要!更新当前Intent
setIntent(intent);
// 处理新Intent
}
- 生命周期:
// singleTop模式下:
onPause() → onNewIntent() → onResume()
// singleTask模式下:
[清除上面的Activity] → onNewIntent() → onResume()
- 数据处理:
@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");
// 处理订单逻辑
}
}
生活场景类比:
- 普通模式 (standard)
// 每次都新开一家店
Intent intent = new Intent(this, OrderActivity.class);
intent.putExtra("order_id", "12345");
startActivity(intent);
就像是:每接到一个订单就开一家新店 - 显然不合理!
- 单例模式 (singleTop/singleTask/singleInstance)
// Manifest 配置
<activity
android:name=".OrderActivity"
android:launchMode="singleTop"/>
就像是:
- 已经有一家店在营业了
- 新订单来了不会再开新店
- 而是通过
onNewIntent通知现有的店铺
实际应用场景:
- 消息通知处理
public class NotificationActivity extends Activity {
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// 比如收到微信消息,不需要开多个聊天窗口
String chatId = intent.getStringExtra("chat_id");
updateChat(chatId);
}
}
- 搜索界面
public class SearchActivity extends Activity {
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// 新的搜索关键词来了,更新当前搜索结果
String keyword = intent.getStringExtra("keyword");
performSearch(keyword);
}
}
- 扫码结果处理
public class ScanActivity extends Activity {
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// 连续扫描多个码,在同一个界面更新显示
String scanResult = intent.getStringExtra("scan_result");
showScanResult(scanResult);
}
}
生动理解:
-
你在用微信时点击通知栏:
- 如果微信没打开:创建新的微信界面(onCreate)
- 如果微信已经打开:更新现有界面(onNewIntent)
-
就像餐厅服务:
- 餐厅关门时来客人 → 开门迎接(onCreate)
- 餐厅营业时来客人 → 直接接待(onNewIntent)
- 不会因为新客人就再开一家分店!
主要作用总结:
- 避免重复创建 Activity
- 复用已存在的 Activity
- 更新现有 Activity 的状态
- 节省系统资源
- 提供更好的用户体验
使用场景:
- 通知栏点击处理
- 搜索界面
- 扫码页面
- 聊天窗口
- 任何需要单例的交互界面
记住:就像一家店不需要为每个顾客都开新分店,Activity 也不需要为每个新 Intent 都创建新实例!
10.3 Activity栈的管理
让我用生活中的例子来解释这几种启动模式:
standard(标准模式):
// 想象成坐电梯
每个人(Activity)进来都占一个位置
即使是同一个人,也会占用新的位置
Task Stack:
[小明]
[小红]
[小明] // 同一个人可以重复进来
[小张]
singleTop(栈顶复用):
// 想象成排队买奶茶
如果你已经在队伍最前面了,就不用重新排队
但如果你在中间,还是得重新排
Task Stack:
[小明] ← 已经在顶部,复用这个实例
[小红]
[小明] ← 在中间,会创建新实例
[小张]
singleTask(栈内复用):
// 想象成公司CEO
整个公司(Task)只能有一个CEO
如果CEO要来,上面的人都得离开
开始:
[员工A]
[员工B]
[CEO] ← 已经存在
[员工C]
来了新CEO:
[员工A]
[CEO] ← 上面的员工B被清除
[员工C]
singleInstance(独享任务栈):
// 想象成总统专车
总统有专门的车队,不和其他车辆混在一起
Task Stack 1 (普通任务栈): Task Stack 2 (专用任务栈):
[普通Activity] [SingleInstance Activity]
[普通Activity]
[普通Activity]
具体代码示例:
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");
}
}
singleTask:
// AndroidManifest.xml
<activity
android:name=".MainActivity"
android:launchMode="singleTask"/>
// 使用场景:应用主界面
public class MainActivity extends Activity {
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// 回到主界面时的处理
}
}
singleInstance:
// AndroidManifest.xml
<activity
android:name=".CallActivity"
android:launchMode="singleInstance"/>
// 使用场景:来电界面
public class CallActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 处理来电
}
}
常见使用场景:
singleTop:
// 适合接收频繁更新的界面
- 通知栏点击界面
- 搜索结果界面
- 消息提醒界面
singleTask:
// 适合作为应用程序入口
- 主界面
- 购物车界面
- 个人中心界面
singleInstance:
// 适合需要独立的界面
- 来电界面
- 闹钟提醒界面
- 应用锁界面
生活类比总结:
standard:
像公交车,来一个人就占一个新位置
singleTop:
像排队买奶茶,如果你已经在队伍最前面就不用重新排
singleTask:
像公司CEO,整个公司只能有一个,新CEO来了上面的人都得走
singleInstance:
像总统专车,独享一个车队,不和其他车混在一起
11. Intent数据限制
限制:约1MB
解决方案:
- 使用Bundle
- 文件共享
- 数据库
- ContentProvider
11.1 Binder事务缓冲区限制
- 同步调用:约1MB
- 异步调用:约0.5MB
11.2 大数据传输方案
我来介绍几种 Intent 传输大数据的解决方案:
- 使用 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 方法 ...
}
- 使用文件共享 适用于大文件传输:
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);
- 使用 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);
// ... 读取数据 ...
}
}
- 使用内存缓存 适用于同进程间传递:
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);
- 使用 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) {
// 处理数据
}
};
}
最佳实践建议:
- 选择合适的传输方式
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);
}
}
}
- 内存优化
// 分片传输大数据
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);
}
}
}
- 异步处理
public class AsyncTransfer {
public static void transferDataAsync(final byte[] data) {
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(() -> {
// 在后台线程处理数据传输
transferData(data);
});
}
}
选择建议:
- < 1MB:使用 Intent + Bundle
- 文件:使用 FileProvider
- 结构化数据:使用 ContentProvider
- 跨进程:使用 AIDL
- 临时数据:使用内存缓存
注意事项:
- 考虑内存限制
- 处理传输异常
- 注意性能影响
- 合理选择传输方式
- 必要时使用分片传输
11.3Bundle的详细使用
Bundle 主要用于在 Android 组件之间传递数据。让我来详细解释它的使用场景和方法:
- 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");
}
}
}
- 保存和恢复状态:
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");
}
}
}
- 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");
}
}
- 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. 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;
}
- 常见使用场景:
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());
}
- 注意事项:
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;
}
}
就像是:
- 你有一个共享的通讯录(ContentProvider)
- 小明和小红都在使用这个通讯录(不同的应用)
- 小明添加了一个新联系人(数据更新)
- 系统会自动通知小红的应用(数据同步)
要监听这些变化,小红的应用需要:
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
);
}
}
这就像是:
- 小红告诉系统:"请帮我留意通讯录的变化"(注册观察者)
- 当小明修改通讯录时,系统会自动通知小红(触发 onChange)
- 小红的应用就能及时更新显示最新的联系人列表
这个机制的优点是:
- 自动化:不需要手动检查更新
- 实时性:数据变化后立即通知
- 解耦合:数据提供方和使用方互不干扰
这就是为什么 ContentProvider 特别适合在不同应用间共享和同步数据。
12.3 ContentProvider的线程处理
我来总结 ContentProvider 中需要注意的线程相关问题:
- 主线程调用问题
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();
}
}
- 数据库操作的线程安全
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;
}
}
- 批量操作的原子性
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();
}
}
}
- 异步加载的最佳实践
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) {
// 清理资源
}
});
}
}
主要注意事项:
-
ANR 风险
- ContentProvider 的 CRUD 操作都可能在主线程被调用
- 耗时操作必须放在工作线程中执行
- 建议使用 LoaderManager 或 协程等进行异步操作
-
并发访问
- ContentProvider 可能被多个线程同时访问
- 使用 SQLiteOpenHelper 来保证数据库操作的线程安全
- 对共享资源的访问需要同步处理
-
事务处理
- 批量操作要使用事务来保证原子性
- 正确处理事务的开始和结束
- 注意异常情况下的回滚处理
-
生命周期感知
- 使用 LoaderManager 可以感知 Activity 生命周期
- 避免在 Activity 销毁后继续进行数据操作
- 及时清理不需要的观察者和回调
-
性能优化
- 合理使用批量操作(bulkInsert)
- 避免频繁的数据库操作
- 适当使用缓存机制
记住这些要点可以帮助开发出更稳定、高效的 ContentProvider 实现。
12.4 ContentProvider对共享资源的访问需要同步处理 如何最优方案
我来介绍 ContentProvider 中共享资源访问的几种最优同步处理方案:
- 使用 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();
}
}
}
- 使用 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);
}
}
- 使用 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;
}
}
}
- 使用 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;
}
}
}
}
- 使用 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();
// 执行操作
}
}
最佳实践建议:
- 选择合适的同步策略
- 读多写少:使用 ReadWriteLock
- 缓存场景:使用 ConcurrentHashMap
- 简单原子操作:使用 AtomicReference
- 线程独立数据:使用 ThreadLocal
- 优化锁的粒度
// 不好的做法 - 锁粒度太大
synchronized(lock) {
// 很多操作
}
// 好的做法 - 只锁定必要的部分
// 预处理
synchronized(lock) {
// 关键操作
}
// 后续处理
- 避免死锁
// 始终按照固定顺序获取多个锁
private final Object lockA = new Object();
private final Object lockB = new Object();
public void operation() {
synchronized(lockA) {
synchronized(lockB) {
// 操作
}
}
}
- 使用 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[生命周期回调]
主要步骤:
- 启动请求
- AMS处理
- 进程创建
- Activity创建
- 生命周期管理
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);
}
}
这些深入的解答涵盖了:
- 源码级别的实现原理
- 性能优化考虑
- 最佳实践示例
- 架构设计思想
- 进程间通信机制
- 内存管理策略
14.FileProvider的使用
可以访问你指定的任何应用私有目录中的文件。让我详细解释下:
- 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>
- 使用不同目录的示例:
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
);
}
}
- 完整的文件共享示例:
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);
}
}
}
}
- 使用建议:
根据文件用途选择合适的目录:
// 临时文件:使用缓存目录
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"
);
- 文件清理:
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. 检查文件是否存在
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
});
});
这样,你可以根据具体需求选择合适的存储位置和处理方式。