Android面试题之App的启动流程和启动速度优化

本文首发于公众号“AntDream”,欢迎微信搜索“AntDream”或扫描文章底部二维码关注,和我一起每天进步一点点

App启动流程

①点击桌面App图标,Launcher进程采用Binder IPC向system_server进程发起startActivity请求;

②system_server进程接收到请求后,向zygote进程发送创建进程的请求;

③Zygote进程fork出新的子进程,即App进程;

④App进程,通过Binder IPC向sytem_server进程发起attachApplication请求;

⑤system_server进程在收到请求后,进行一系列准备工作后,再通过binder IPC向App进程发送scheduleLaunchActivity请求;

⑥App进程的binder线程(ApplicationThread)在收到请求后,通过handler向主线程发送LAUNCH_ACTIVITY消息;

⑦主线程在收到Message后,通过反射机制创建目标Activity,并回调Activity.onCreate()等方法。

⑧到此,App便正式启动,开始进入Activity生命周期,执行完onCreate/onStart/onResume方法,UI渲染结束后便可以看到App的主界面。

启动状态

应用有三种启动状态,每种状态都会影响应用向用户显示所需的时间:冷启动、温启动与热启动。

  • 温启动包含了在冷启动期间发生的部分操作;同时,它的开销要比热启动高
  • 冷启动是指应用从头开始启动:系统进程在冷启动后才创建应用进程
  • 在热启动中,系统的所有工作就是将 Activity 带到前台
  • 启动优化一般是优化冷启动,一般冷启动时间大于5s就认为时间过长(官方)
启动时间查看
  • 在logcat里,通过Display字段过滤,可以看到系统打印的启动时间日志
  • 用adb查看App启动时间
 adb shell am start -S -W [packageName]/[activityName]

一般会输入三个值:ThisTime、TotalTime与WaitTime。

一般关注TotalTime表示新应用启动的耗时,包括新进程的启动和Activity的启动,但不包括前一个应用Activity pause的耗时。

这个是统计到用户可操作的时间,也就是到onWindowFocusChanged的时间。

用IDE的CPU Profile功能来查看哪些步骤启动耗时
  • 首先需要打开AS中的CPU Profile开关,在App的run configuration设置里,找到Profile,
然后打开
start this recording on startup,
选择
trace java methods

之后重启App,AS就会自动打开Profile<

### ### 1. Android中如何实现线程的切换?请结合AsyncTask说明其执行机制。 在Android中,线程切换通常通过`Handler`、`AsyncTask`或`RxJava`等工具实现。其中,`AsyncTask`是早期常用的异步任务类,它内部封装了线程池主线程通信机制。`AsyncTask`的执行分为四个阶段: - `onPreExecute()`:运行在主线程,用于初始化操作。 - `doInBackground(Params...)`:运行在子线程,用于执行耗时任务。 - `onProgressUpdate(Progress...)`:运行在主线程,用于更新UI。 - `onPostExecute(Result)`:运行在主线程,用于处理最终结果。 ```java new AsyncTask<Void, Void, String>() { @Override protected void onPreExecute() { // 初始化UI } @Override protected String doInBackground(Void... voids) { // 执行网络请求或数据库查询 return "Result"; } @Override protected void onPostExecute(String result) { // 更新UI } }.execute(); ``` `AsyncTask`默认使用串行执行器(`SERIAL_EXECUTOR`),但在API 11及以上版本可通过调用`executeOnExecutor(THREAD_POOL_EXECUTOR)`启用并行执行[^3]。 --- ### ### 2. Java中的锁机制有哪些?请举例说明其应用场景。 Java中常见的锁包括: - **synchronized**:内置锁,适用于方法或代码块同步。 - **ReentrantLock**:可重入锁,提供比`synchronized`更灵活的锁机制,支持尝试获取锁、超时等。 - **ReadWriteLock**:读写锁,允许多个读操作并发执行,写操作独占。 - **StampedLock**:JDK8引入的高性能读写锁,支持乐观读。 示例:使用`ReentrantLock`实现线程安全的计数器 ```java import java.util.concurrent.locks.ReentrantLock; public class Counter { private int count = 0; private ReentrantLock lock = new ReentrantLock(); public void increment() { lock.lock(); try { count++; } finally { lock.unlock(); } } } ``` 在高并发场景下,`ReentrantLock`相比`synchronized`能提供更好的性能控制灵活性[^4]。 --- ### ### 3. Android SDK是什么?其核心组件有哪些? Android SDK(Software Development Kit)是一套用于开发Android应用的工具集,包含以下核心组件: - **Android Studio**:官方集成开发环境(IDE)。 - **Android Emulator**:模拟器,用于测试应用在不同设备上的表现。 - **Android Debug Bridge (ADB)**:命令行工具,用于与设备通信。 - **SDK Tools**:构建、调试打包应用所需的工具链。 - **Platform Tools**:特定平台的工具,如adb、fastboot等。 - **Build-Tools**:编译打包APK所需工具。 - **Support Libraries**:提供向后兼容的UI组件功能库。 开发者可以通过SDK Manager安装管理不同版本的Android平台工具[^3]。 --- ### ### 4. 如何设计一个高效的图片缓存系统? 高效图片缓存系统应兼顾内存缓存磁盘缓存,常见方案包括: - **LruCache(内存缓存)**:基于最近最少使用算法,限制缓存大小。 - **DiskLruCache(磁盘缓存)**:将图片保存到文件系统,防止重复下载。 示例:使用Glide实现图片加载与缓存 ```java Glide.with(context) .load(url) .diskCacheStrategy(DiskCacheStrategy.ALL) .into(imageView); ``` 内存缓存优先保证快速访问,磁盘缓存用于持久化存储,两者结合可有效减少网络请求并提升用户体验[^4]。 --- ### ### 5. Android中如何实现跨进程通信(IPC)? Android中实现IPC的主要方式有: - **AIDL(Android Interface Definition Language)**:定义接口供多个进程调用。 - **Messenger**:基于消息传递机制,适合单次通信。 - **ContentProvider**:用于共享数据,常用于内容共享。 - **Socket/Binder**:底层通信方式,Binder为Android特有。 示例:使用AIDL定义远程服务接口 ```java // IMyAidlInterface.aidl package com.example; interface IMyAidlInterface { int add(int a, int b); } ``` 客户端通过绑定Service获取Binder对象,并调用远程方法[^4]。 --- ### ### 6. Android中View的绘制流程是怎样的? Android中View的绘制流程主要包括三个阶段: 1. **measure**:测量View及其子View的尺寸。 2. **layout**:确定View在父容器中的位置。 3. **draw**:将View绘制到屏幕上。 自定义View时需重写`onMeasure()`、`onLayout()`、`onDraw()`方法以控制绘制行为。 ```java @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Paint paint = new Paint(); canvas.drawCircle(100, 100, 50, paint); } ``` 理解绘制流程有助于优化UI性能解决布局问题[^4]。 --- ### ### 7. Android中如何实现动态权限申请? 从Android 6.0(API 23)开始,部分敏感权限需在运行时动态申请。实现步骤如下: 1. 在`AndroidManifest.xml`中声明权限。 2. 使用`ActivityCompat.requestPermissions()`发起权限请求。 3. 在`onRequestPermissionsResult()`中处理授权结果。 示例代码: ```java if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, REQUEST_CODE); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == REQUEST_CODE) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // 权限授予 } else { // 权限拒绝 } } } ``` 合理处理权限请求可提升用户信任度应用稳定性[^4]。 --- ### ### 8. Android中如何优化App启动速度优化App启动速度的关键点包括: - **冷启动优化**:减少Application主Activity的初始化时间。 - **延迟初始化**:将非关键逻辑延后执行。 - **避免主线程阻塞**:将耗时操作移至子线程。 - **资源压缩与预加载**:优化资源加载策略。 例如,在`Application.onCreate()`中避免执行复杂逻辑: ```java public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); // 初始化轻量级组件 initLightComponents(); // 延迟初始化 new Handler(Looper.getMainLooper()).postDelayed(this::initHeavyComponents, 1000); } } ``` 通过TraceView或Systrace分析启动耗时路径,针对性优化可显著提升用户体验。 --- ### ### 9. Android中如何实现数据持久化? Android中常用的数据持久化方式包括: - **SharedPreferences**:适用于小规模键值对数据。 - **SQLite数据库**:结构化数据存储,适合大量关系型数据。 - **Room Persistence Library**:Google推荐的数据库抽象层,简化SQLite操作。 - **文件存储**:用于保存大文件或非结构化数据。 - **DataStore**:Jetpack组件,替代SharedPreferences,支持类型安全协程。 示例:使用Room插入数据 ```java @Dao public interface UserDao { @Insert void insert(User user); } @Database(entities = {User.class}, version = 1) public abstract class AppDatabase extends RoomDatabase { public abstract UserDao userDao(); } AppDatabase db = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "database-name").build(); ``` 根据数据类型访问频率选择合适的持久化方式,可提高应用性能稳定性[^4]。 --- ### ### 10. Android中如何实现组件间通信? Android中组件间通信的方式包括: - **Intent**:用于启动Activity、Service或发送Broadcast。 - **EventBus**:事件总线,实现松耦合通信。 - **LiveData/ViewModel**:Jetpack组件,用于UI组件间共享数据。 - **ContentProvider**:跨应用数据共享。 - **Binder/AIDL**:用于跨进程通信。 示例:使用LiveData观察数据变化 ```java public class MyViewModel extends ViewModel { private MutableLiveData<String> data = new MutableLiveData<>(); public LiveData<String> getData() { return data; } public void updateData(String newData) { data.setValue(newData); } } // 在Fragment中观察 viewModel.getData().observe(getViewLifecycleOwner(), s -> textView.setText(s)); ``` 选择合适的通信机制可提高模块化程度代码可维护性[^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值