Android 互联网大厂,高频重点面试题集分享(二)

一、说说 ContentProvider 的初始化顺序?

在Android应用启动过程中,ContentProvider的初始化顺序是由系统控制的,而开发者对其影响有限。

通常,ContentProvider是在应用进程启动时,由Android框架在ApplicationonCreate方法之前初始化的,主要原因是ContentProvider可能会在应用的其他组件创建之前需要被访问(比如系统为了满足跨进程通信或数据存取的需要)。

1.1 Provider 的启动顺序

系统预加载:

系统级ContentProvider,如:ContactsMedia相关的提供者,会在设备启动或用户登录时由系统初始化。

应用内部的 ContentProvider

  • 声明顺序:AndroidManifest.xml中声明的ContentProvider其初始化顺序遵循该文件中的声明顺序。Android会按照Manifest中次序初始化ContentProvider
  • 优先级原则: 不同应用间没有明确的提供者加载顺序,但同一个应用内部是遵循Manifest文件中的书写顺序。

1.2 影响启动顺序的因素

  • Manifest 优先级: 虽然按Manifest中的顺序初始化,但无法保证在所有不同设备和操作系统版本下都能严格遵循这一定义。
  • 初始化依赖: 若某个ContentProvider依赖于另一个提供者,显式地控制初始化顺序则会变得更复杂。
  • 多进程情况: 如果不同的ContentProvider在不同的进程中运行,进程的启动时间可能对它们的初始化顺序有影响,这和进程管理的复杂有关。

1.3 如何自定义ContentProvider并影响启动顺序

虽然开发者不能完全控制启动顺序,但可以通过以下几种做法稍微管理ContentProvider的加载顺序:

  • 慎重设计依赖: 避免在ContentProvider中依赖其他尚未初始化的提供者。
  • 手动依赖管理: 在可能的情况下,将依赖逻辑放在另一个启动后加载的组件中如ActivityService中,以延迟加载具体功能。
  • 避免复杂的初始化逻辑:ContentProvideronCreate方法中尽量避免复杂的逻辑,保持轻量级,这样即便初始化顺序无法预知也不会造成大的性能或逻辑问题。
  • 使用静态代码块: 可选择在ContentProvider外部使用静态代码块或独立的初始化方法来提升必要资源的准备。

二、如何优化Android应用的启动时间?

2.1 冷启动优化

冷启动是指应用从完全关闭状态启动的过程。优化冷启动时间的关键在于减少启动时加载的资源量和初始化操作的耗时。

  • 可通过将非关键组件的初始化操作延迟到应用启动后执行,可以减少冷启动时间。

示例代码:

Kotlin 复制代码
class MyApplication:Application(){  
    override fun onCreate(){  
        super.onCreate()  
        // 延迟初始化非关键组件  
        Handler().postDelayed({  
            initNonCriticalComponents()  
        },1000)  
    }  
  
    private fun initNonCriticalComponents(){  
        // 初始化非关键组件  
    }  
}

2.2 热启动优化

热启动是指应用从后台恢复的过程。优化热启动时间的关键在于合理管理应用的生命周期,避免不必要的资源重新加载。

  • 可通过缓存数据,避免在热启动时重新加载数据,可以显著提升热启动速度。

示例代码:

Kotlin 复制代码
class MainActivity:AppCompatActivity(){  
    private lateinit var cachedData:List<String>  
  
    override fun onCreate(savedInstanceState:Bundle?){  
        super.onCreate(savedInstanceState)  
        if(savedInstanceState !=null){  
            // 从缓存中恢复数据  
            cachedData = 
            savedInstanceState.getStringArrayList("cachedData") as List<String>  
        }else{  
            // 加载新数据  
            cachedData =loadData()  
        }  
    }  
  
    private fun loadData():List<String>{  
        // 加载数据  
        returnlistOf("Data1","Data2","Data3")  
    }  
  
    override fun onSaveInstanceState(outState:Bundle){  
        super.onSaveInstanceState(outState)  
        outState.putStringArrayList("cachedData",ArrayList(cachedData))  
    }  
}

2.3 使用启动框架管理启动任务

使用启动框架可以将核心业务提前加载完成,同时将任务细粒度化。例如,为了使首页更快地展示,可以将首页的数据请求和UI渲染相剥离。

  • 可通过任务分发器管理启动任务,可以优化启动流程,减少启动耗时。

示例代码:

Kotlin 复制代码
class MyApplication:Application(){  
    override fun onCreate(){  
        super.onCreate()  
        // 初始化启动任务分发器  
        TaskDispatcher.init(this)  
        val dispatcher:TaskDispatcher=TaskDispatcher.createInstance()  
        // 添加任务并启动  
        dispatcher.addTask(InitSumHelperTask(this))  
                .addTask(InitMmkvTask())  
                .addTask(InitAppManagerTask())  
                .addTask(InitArouterTask())  
                .start()  
        // 等待需要等待的任务执行完成  
        dispatcher.await()  
    }  
}

2.4 延迟加载非关键任务

将非关键任务延迟加载可以减少启动时的初始化工作,提升启动速度。

  • 可通过延迟加载非关键任务,可以避免在启动时执行不必要的操作,提升启动速度。

示例代码:

Kotlin 复制代码
homeAdAdapter.onFirstFrameTimeCall = {  
    AppExecutors.mainThread.executeDelay(Runnable {  
        // 任务延迟3s执行  
        initToastTask()  
    }, 3000)  
}

2.5 使用启动引导页

在应用启动时显示一个启动引导页,同时异步加载应用资源,可以提升用户感知的启动速度。

  • 可通过设置启动引导页,可以掩盖启动时的空白时间,提升用户体验。

示例代码:

xml 复制代码
<style name="SplashTheme" parent="Theme.AppCompat.NoActionBar">  
    <item name="android:windowBackground">@drawable/splash_background</item>  
</style>

2.6 优化onCreate方法

避免在ActivityonCreate方法中执行耗时操作,使用HandlerThread协程来异步处理耗时任务。

  • 可通过将耗时操作移至后台线程执行,可以避免阻塞主线程,提升启动速度。

示例代码:

Kotlin 复制代码
class HomeActivity:AppCompatActivity(){  
    override fun onCreate(savedInstanceState:Bundle?){  
        super.onCreate(savedInstanceState)  
        setContentView(R.layout.activity_home)  
  
        // 异步加载数据  
        val handlerThread =HandlerThread("DataHandlerThread")  
        handlerThread.start()  
        val handler =Handler(handlerThread.looper)  
        handler.post {  
            // 执行耗时操作  
            val data =loadData()  
            // 回到主线程更新UI  
            runOnUiThread {  
                textView.text = data  
            }  
        }  
    }  
  
    private fun loadData():String{  
        // 模拟耗时操作  
        Thread.sleep(2000)  
        return"加载完成"  
    }  
}

2.7 性能监控与测试工具

  • Profiler: 用于监控应用的CPU、内存和网络使用情况,帮助开发者识别性能瓶颈。
  • TraceView: 用于分析应用的执行流程,找出耗时操作。
  • Systrace: 用于系统级的性能分析,帮助开发者优化系统启动过程。

三、性能优化篇:Framework层调优实战

3.1 如何定位Native层内存泄漏?

阿里方案:

  • 使用libmemunreachable检测不可达内存
  • AddressSanitizer实时监控(性能损耗<2%)
adb 复制代码
检测命令   
adb shell am dumpheap -n <pid> /data/local/tmp/heap.txt

3.2 View绘制优化方案

字节方案:

  • 使用RenderThread异步绘制(Android 12+)
  • 硬件加速策略动态切换

示例代码:

java 复制代码
public class CustomView extends View {  
  private final ThreadedRenderer mRenderer;  
   
  void draw(Canvas canvas) {  
    if (mUseHardwareAccel) {  
      mRenderer.draw(canvas); // 硬件加速路径   
    } else {  
      super.draw(canvas); // 软件绘制   
    }  
  }  
}

3.3 如何将冷启动时间从4秒压缩到800ms?

可优化点如下:

  • ContentProvider治理:合并初始化逻辑,平均每个CP可节省80ms
  • 类加载优化:使用App Bundles动态交付非必要模块
  • IO异步化:如将SharedPreferences替换为MMKV,提高读写性能
  • 布局预加载:使用ViewStub + Merge 标签减少层级嵌套
  • 启动阶段监控:集成Firebase Performance Monitoring实现自动化埋点

四、组件启动篇:Activity启动全链路

4.1 Activity启动的跨进程调用

标准答案:

  • 冷启动:至少4次跨进程调用(含Zygote进程fork)
  • 热启动:2次跨进程调用(AMS -> ApplicationThread

流程拆解示例:

log 复制代码
  Client->>AMS: startActivity()  
  AMS->>Zygote: fork进程   
  Zygote->>AMS: 返回新进程PID   
  AMS->>ApplicationThread: scheduleLaunchActivity()  
  ApplicationThread->>ActivityThread: handleLaunchActivity()

4.2 为什么会有"白屏问题"?

底层原理:

  • WindowManagerServiceattach()阶段同步创建窗口
  • 主题背景绘制早于onCreate()执行

优化方案:

  • 使用SplashScreen API(API 31+)
  • 异步加载布局(ViewStub方案)

示例代码:

java 复制代码
// 异步加载核心代码   
public void onCreate(Bundle savedState) {  
  super.onCreate(savedState);  
  ViewStub stub = findViewById(R.id.async_stub);  
  stub.setInflateListener((stub, inflated) -> {  
    // 主线程空闲时执行   
  });  
}

更多分享

  1. Android 互联网大厂,高频重点面试题集分享(一)
  2. Android 常规基础面试题分享
  3. Android Kotlin协程相关面试题分享
  4. Android 架构以及优化相关面试题分享
相关推荐
网安Ruler1 小时前
代码审计-PHP专题&原生开发&SQL注入&1day分析构造&正则搜索&语句执行监控&功能定位
android
paid槮3 小时前
MySql基础:数据类型
android·mysql·adb
用户2018792831674 小时前
AMS和app通信的小秘密
android
用户2018792831674 小时前
ThreadPoolExecutor之市场雇工的故事
android
诺诺Okami4 小时前
Android Framework-Launcher-InvariantDeviceProfile
android
Antonio9155 小时前
【音视频】Android NDK 与.so库适配
android·音视频
sun00770014 小时前
android ndk编译valgrind
android
AI视觉网奇15 小时前
android studio 断点无效
android·ide·android studio
jiaxi的天空15 小时前
android studio gradle 访问不了
android·ide·android studio
No Silver Bullet16 小时前
android组包时会把从maven私服获取的包下载到本地吗
android