乐闻世界logo
搜索文章和话题

面试题手册

Android中Binder机制的原理是什么,为什么使用Binder?

Binder是Android系统中实现进程间通信(IPC)的核心机制,也是Android系统的特色之一。为什么Android选择Binder| 通信方式 | 优点 | 缺点 || ---------- | ------------ | ---------- || 管道 | 简单 | 单向通信,效率低 || Socket | 通用 | 开销大,速度慢 || 共享内存 | 速度快 | 同步复杂,安全性差 || Binder | 高效、安全、易用 | 学习曲线陡峭 |Binder的核心优势高效性只需一次内存拷贝(传统IPC需要两次)基于C/S架构,通信效率高安全性内核层验证进程身份(UID/PID)支持建立私有通道易用性封装了复杂的底层实现提供AIDL等高级接口Binder的工作原理1. 核心组件Binder驱动:位于内核空间,管理Binder通信ServiceManager:管理所有系统服务的注册和查询Client/Server:通信的双方2. 内存映射机制发送方进程 Binder驱动 接收方进程 | | | | 数据 | 内存映射(MMAP) | | -------------> | -----------------> | | 用户空间 | 内核空间 | 用户空间发送方数据拷贝到内核空间接收方通过内存映射直接访问,无需再次拷贝3. 通信流程1. Server注册服务 → ServiceManager2. Client查询服务 → ServiceManager3. Client获取Server的Binder代理4. Client通过Binder代理调用Server方法5. Binder驱动完成数据传递Binder在Android中的应用ActivityManagerService (AMS)管理Activity生命周期进程调度WindowManagerService (WMS)窗口管理屏幕显示PackageManagerService (PMS)应用包管理权限管理AIDL(Android Interface Definition Language)// IRemoteService.aidlinterface IRemoteService { int add(int a, int b); String getMessage();}AIDL编译后自动生成:Stub:服务端实现Proxy:客户端代理面试要点Binder是Android特有的IPC机制理解一次拷贝的内存映射原理掌握AIDL的基本使用方法Binder线程池默认16个线程注意Binder通信的数据大小限制(1MB左右)
阅读 0·3月7日 19:50

Android中Handler机制的工作原理是什么?

Handler是Android中实现线程间通信的核心机制,用于在工作线程和主线程(UI线程)之间传递消息。Handler机制的核心组件1. Handler(处理器)作用:发送和处理消息特点:绑定到创建它的线程的Looper负责发送Message到MessageQueue处理Looper分发的消息2. Looper(循环器)作用:消息循环,不断从MessageQueue取出消息特点:每个线程只能有一个Looper主线程默认已创建Looper子线程需要手动调用Looper.prepare()和Looper.loop()3. MessageQueue(消息队列)作用:存储消息的队列特点:先进先出(FIFO)按时间排序(when字段)底层使用单链表实现4. Message(消息)作用:携带数据和信息的载体属性:what:消息标识arg1/arg2:整型参数obj:对象参数callback:Runnable回调Handler工作流程发送消息流程:Handler.sendMessage() → MessageQueue.enqueueMessage() → 消息入队处理消息流程:Looper.loop() → MessageQueue.next() → Handler.dispatchMessage() → Handler.handleMessage()代码示例// 主线程HandlerHandler mainHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { // 处理消息,更新UI }};// 子线程Handlernew Thread(() -> { Looper.prepare(); Handler threadHandler = new Handler() { @Override public void handleMessage(Message msg) { // 处理消息 } }; Looper.loop();}).start();Handler内存泄漏问题原因:非静态内部类持有外部Activity引用延迟消息导致Activity无法被回收解决方案:使用静态内部类 + WeakReference在Activity销毁时移除所有消息使用Handler的removeCallbacksAndMessages(null)面试要点主线程Looper在ActivityThread.main()中创建Handler.post()和sendMessage()的区别Message.obtain()复用消息对象,避免内存分配IdleHandler在消息队列空闲时执行
阅读 0·3月7日 12:13

Android中Jetpack组件有哪些,它们的作用是什么?

Jetpack是Google推出的一套Android开发组件库,旨在帮助开发者遵循最佳实践、减少样板代码,并编写可在各种Android版本和设备上一致运行的代码。Jetpack组件分类1. 架构组件(Architecture Components)ViewModelclass MyViewModel : ViewModel() { private val _data = MutableLiveData<String>() val data: LiveData<String> = _data fun loadData() { // 配置变更时数据不会丢失 _data.value = "Loaded" }}作用:管理UI相关的数据,生命周期感知特点:配置变更(如旋转屏幕)时数据保留LiveDataviewModel.data.observe(this) { value -> // 自动处理生命周期,避免内存泄漏 textView.text = value}作用:可观察的数据持有者,生命周期感知特点:自动清理,避免内存泄漏Room@Entitydata class User(@PrimaryKey val id: Int, val name: String)@Daointerface UserDao { @Query("SELECT * FROM user") fun getAll(): LiveData<List<User>> @Insert fun insert(user: User)}作用:SQLite的抽象层,简化数据库操作特点:编译时SQL检查,支持LiveData返回DataBinding<TextView android:text="@{viewModel.userName}" />作用:将布局中的UI组件与数据源绑定特点:减少findViewById,自动更新UINavigation// 管理Fragment跳转和返回栈findNavController().navigate(R.id.action_detail)作用:管理应用内导航特点:可视化导航图,支持Deep LinkWorkManagerval workRequest = OneTimeWorkRequestBuilder<MyWorker>().build()WorkManager.getInstance(context).enqueue(workRequest)作用:管理可延迟的后台任务特点:保证任务执行,支持约束条件2. UI组件Fragment作用:模块化的UI组件特点:与Activity解耦,复用性强RecyclerView作用:高效显示大量数据列表特点:ViewHolder模式,四级缓存ViewPager2作用:页面滑动切换特点:基于RecyclerView,支持垂直滑动3. 基础组件AppCompat作用:向后兼容支持特点:使用新特性同时兼容旧版本Android KTX// Kotlin扩展函数sharedPreferences.edit { putString("key", "value")}作用:Kotlin友好的API扩展特点:简化代码,更符合Kotlin习惯Multidex作用:突破65536方法数限制特点:自动分包处理4. 行为组件DownloadManager作用:管理长时间下载任务特点:系统级服务,断点续传Media & Media3作用:音频视频播放特点:统一的媒体播放APINotifications作用:创建和管理通知特点:兼容各版本通知特性Permissions// 简化权限请求requestPermissionLauncher.launch(Manifest.permission.CAMERA)作用:简化运行时权限处理Jetpack优势| 优势 | 说明 || ---------- | ---------------- || 向后兼容 | 组件支持Android 5.0+ || 生命周期感知 | 自动管理生命周期,避免内存泄漏 || 减少样板代码 | 简化常见开发任务 || 一致性 | 统一的API设计 || 测试友好 | 组件可独立测试 |MVVM架构实践View (Activity/Fragment) ↓ 观察ViewModel ↓ 调用Repository ↓ 获取数据Data Source (Room/Network)面试要点理解ViewModel的生命周期范围掌握LiveData的转换操作(map、switchMap)了解Room的数据库迁移理解DataBinding的双向绑定掌握Navigation的Deep Link使用
阅读 0·3月7日 12:13

Android中View的绘制流程是怎样的?

View的绘制流程是Android UI系统的核心,理解绘制机制对于自定义View和性能优化至关重要。View绘制的三个阶段View的绘制流程遵循Measure → Layout → Draw三个阶段:View绘制流程:measure() → onMeasure() → 测量View宽高 ↓layout() → onLayout() → 确定View位置 ↓draw() → onDraw() → 绘制View内容第一阶段:Measure(测量)测量目的确定View的宽度和高度(measuredWidth/measuredHeight)。MeasureSpecMeasureSpec是父View对子View的尺寸要求,由模式和尺寸组成:| 模式 | 值 | 含义 || ----------- | ---------- | ----------------------- || EXACTLY | 0x40000000 | 精确值,如match_parent或具体数值 || AT_MOST | 0x80000000 | 最大值,如wrap_content || UNSPECIFIED | 0x00000000 | 无限制,如ScrollView中的子View |测量流程@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); // 根据模式计算实际尺寸 int width = calculateWidth(widthMode, widthSize); int height = calculateHeight(heightMeasureSpec); setMeasuredDimension(width, height);}第二阶段:Layout(布局)布局目的确定View在父容器中的位置(left, top, right, bottom)。布局流程@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) { // 遍历子View,确定每个子View的位置 for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); child.layout(childLeft, childTop, childRight, childBottom); }}第三阶段:Draw(绘制)绘制步骤draw()方法包含6个步骤:public void draw(Canvas canvas) { // 1. 绘制背景 drawBackground(canvas); // 2. 保存画布状态 saveCount = canvas.getSaveCount(); // 3. 绘制内容(子类实现) onDraw(canvas); // 4. 绘制子View dispatchDraw(canvas); // 5. 绘制装饰(如滚动条) onDrawForeground(canvas); // 6. 恢复画布状态 canvas.restoreToCount(saveCount);}ViewGroup的绘制特点ViewGroup继承自View,但增加了子View管理:测量阶段:遍历测量所有子View布局阶段:确定子View的位置绘制阶段:dispatchDraw()绘制所有子View绘制优化技巧1. 减少重绘// 使用invalidate(Rect)局部重绘invalidate(0, 0, 100, 100);// 使用ViewStub延迟加载<ViewStub android:id="@+id/stub" android:layout="@layout/view" />2. 避免过度绘制移除不必要的背景使用clipRect减少绘制区域使用GPU Overdraw调试工具检测3. 使用硬件加速<application android:hardwareAccelerated="true">自定义View要点public class CustomView extends View { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); // 处理wrap_content if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.AT_MOST) { setMeasuredDimension(defaultWidth, defaultHeight); } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 绘制自定义内容 canvas.drawCircle(cx, cy, radius, paint); }}面试要点理解MeasureSpec的三种模式掌握requestLayout()和invalidate()的区别了解绘制流程的触发时机掌握自定义View的基本步骤理解硬件加速和软件绘制的区别
阅读 0·3月7日 12:12

Android中内存泄漏的常见场景有哪些,如何检测和避免?

内存泄漏是指程序中已分配的内存由于某种原因未释放或无法释放,导致可用内存逐渐减少,最终可能引发OOM(Out Of Memory)崩溃。常见内存泄漏场景1. 静态变量持有Activity/Context引用public class Singleton { private static Singleton instance; private Context context; private Singleton(Context context) { this.context = context; // 泄漏!持有Activity引用 } // 解决方案:使用ApplicationContext private Singleton(Context context) { this.context = context.getApplicationContext(); }}2. 非静态内部类/匿名内部类public class MainActivity extends Activity { private Handler handler = new Handler() { // 非静态内部类 @Override public void handleMessage(Message msg) { // 持有外部Activity引用 } };}// 解决方案:使用静态内部类 + WeakReferenceprivate static class MyHandler extends Handler { private WeakReference<Activity> weakRef; MyHandler(Activity activity) { weakRef = new WeakReference<>(activity); }}3. 未取消注册的监听器和广播// 内存泄漏示例registerReceiver(receiver, filter);// 忘记在onDestroy中unregisterReceiver// 解决方案@Overrideprotected void onDestroy() { super.onDestroy(); unregisterReceiver(receiver);}4. 资源未关闭数据库Cursor未关闭IO流未关闭Bitmap未回收5. 集合中的对象引用static List<Activity> activityList = new ArrayList<>();// 添加Activity但不移除activityList.add(activity);内存泄漏检测工具1. Android Studio Memory Profiler实时监控内存分配堆转储(Heap Dump)分析查看对象引用链2. LeakCanarydependencies { debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.x'}自动检测内存泄漏生成泄漏引用链通知提示泄漏信息3. MAT(Memory Analyzer Tool)分析hprof文件查找支配树(Dominator Tree)识别泄漏嫌疑对象内存泄漏避免策略1. 使用ApplicationContext// 当不需要Activity特性时Intent intent = new Intent(getApplicationContext(), Target.class);2. 及时释放资源@Overrideprotected void onDestroy() { super.onDestroy(); // 移除Handler消息和回调 handler.removeCallbacksAndMessages(null); // 取消网络请求 call.cancel(); // 注销监听器 sensorManager.unregisterListener(listener);}3. 使用WeakReferenceprivate static class MyAsyncTask extends AsyncTask<Void, Void, Void> { private WeakReference<Activity> weakActivity; MyAsyncTask(Activity activity) { weakActivity = new WeakReference<>(activity); } @Override protected Void doInBackground(Void... voids) { // 后台任务 return null; } @Override protected void onPostExecute(Void result) { Activity activity = weakActivity.get(); if (activity != null && !activity.isFinishing()) { // 更新UI } }}4. 使用Lifecycle组件class MyObserver implements DefaultLifecycleObserver { @Override public void onDestroy(LifecycleOwner owner) { // 自动清理 }}面试要点理解Java内存模型和GC机制掌握引用类型:强引用、软引用、弱引用、虚引用熟悉常见泄漏场景和解决方案能够使用工具分析和定位泄漏了解Android内存优化最佳实践
阅读 0·3月7日 12:12

Android中如何进行性能优化,有哪些常用工具?

性能优化是Android开发的核心技能,涉及内存、UI、网络、电量等多个维度。1. 内存优化内存泄漏检测LeakCanary:自动检测内存泄漏Android Profiler:实时监控内存分配MAT:分析hprof文件,查找支配树内存优化策略// 1. 使用SparseArray替代HashMap<Integer, Object>val sparseArray = SparseArray<String>()// 2. 图片内存优化val options = BitmapFactory.Options().apply { inSampleSize = 2 // 缩放采样 inBitmap = reusableBitmap // Bitmap复用}// 3. 使用LRU缓存val cache = LruCache<String, Bitmap>(maxMemory / 8)大图优化使用inSampleSize压缩使用WebP格式使用图片加载库(Glide、Picasso、Coil)2. UI渲染优化布局优化<!-- 1. 减少布局层级 --><merge> <!-- 减少一层ViewGroup --><include> <!-- 复用布局 --><ViewStub> <!-- 延迟加载 -->避免过度绘制// 1. 移除不必要的背景window.setBackgroundDrawable(null)// 2. 使用clipRect减少绘制区域canvas.clipRect(left, top, right, bottom)列表优化// RecyclerView优化recyclerView.setHasFixedSize(true)recyclerView.setItemViewCacheSize(20)recyclerView.setRecycledViewPool(pool)3. 网络优化请求优化// 1. 使用连接池okHttpClient.connectionPool(ConnectionPool(5, 5, TimeUnit.MINUTES))// 2. 启用Gzip压缩okHttpClient.addInterceptor(GzipInterceptor())// 3. 合理设置超时okHttpClient.connectTimeout(10, TimeUnit.SECONDS)数据缓存// 1. 使用Cache-Control@Headers("Cache-Control: max-age=3600")// 2. 本地缓存策略val cache = Cache(cacheDir, 10 * 1024 * 1024) // 10MB4. 电量优化Doze模式和App Standby理解系统省电机制使用高优先级FCM消息批量处理后台任务后台任务优化// 使用WorkManager替代后台Serviceval constraints = Constraints.Builder() .setRequiredNetworkType(NetworkType.CONNECTED) .setRequiresBatteryNotLow(true) .build()val workRequest = PeriodicWorkRequestBuilder<SyncWorker>(1, TimeUnit.HOURS) .setConstraints(constraints) .build()定位优化// 使用平衡精度模式locationRequest.priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY// 及时移除定位更新fusedLocationClient.removeLocationUpdates(callback)5. 包体积优化资源优化android { // 1. 移除无用资源 lintOptions { checkReleaseBuilds false } // 2. 资源压缩 shrinkResources true minifyEnabled true // 3. 只保留特定语言 resConfigs "zh", "en"}代码优化// 1. 使用ProGuard/R8混淆proguardFiles getDefaultProguardFile('proguard-android-optimize.txt')// 2. 动态功能模块android { dynamicFeatures = [':feature1', ':feature2']}6. 启动优化异步初始化延迟加载非必要组件使用SplashScreen API7. 常用性能分析工具| 工具 | 用途 | 使用场景 || --------------------- | ----------- | --------- || Android Profiler | CPU/内存/网络监控 | 实时监控应用性能 || Systrace | 系统级性能分析 | 分析帧率、启动时间 || Layout Inspector | 布局层级分析 | 优化布局嵌套 || GPU Overdraw | 过度绘制检测 | 优化绘制性能 || LeakCanary | 内存泄漏检测 | 开发阶段检测泄漏 || StrictMode | 违规检测 | 检测主线程IO等 || Battery Historian | 电量分析 | 分析电量消耗 |8. Systrace使用示例# 抓取tracepython systrace.py -a com.example -o trace.html sched gfx view# 分析重点# 1. 查看帧率(Frame)是否掉帧# 2. 查看UI线程是否阻塞# 3. 查看GC频率9. 性能优化检查清单[ ] 使用Release模式测试性能[ ] 在低端设备上验证[ ] 监控线上性能数据[ ] 定期进行性能回归测试[ ] 建立性能基准指标面试要点掌握内存优化的常见方法理解UI渲染原理和优化手段熟悉各种性能分析工具的使用了解电量优化的最佳实践掌握APK瘦身的技术方案
阅读 0·3月7日 12:11

Android中热修复技术的原理是什么,有哪些主流方案?

热修复(HotFix)是一种在不重新发布应用的情况下,动态修复线上Bug的技术方案。热修复的核心原理1. 类加载机制Android使用PathClassLoader和DexClassLoader加载类:PathClassLoader:加载已安装APK的dex文件DexClassLoader:加载任意路径的dex文件2. 热修复的基本思路原理:让类加载器优先加载修复后的类,覆盖有问题的类实现方式:1. 将修复代码打包成dex文件2. 通过反射插入到dexElements数组前面3. 类加载时优先找到修复类主流热修复方案对比| 方案 | 原理 | 优点 | 缺点 | 代表 || --------------- | --------------- | --------- | --------- | ------------- || 底层替换 | 替换ArtMethod结构体 | 即时生效,无需重启 | 兼容性差,稳定性低 | AndFix、Sophix || 类加载 | 修改dexElements数组 | 稳定性高,兼容性好 | 需要重启生效 | Tinker、QZone || Instant Run | 自定义ClassLoader | 开发调试方便 | 仅适用于开发 | Google官方 |详细方案分析1. Tinker(微信)原理:1. 生成新旧APK的差分包(patch.dex)2. 下载patch.dex到本地3. 合并patch.dex和原APK的dex4. 重启后通过修改dexElements加载新dex特点:- 支持类、资源、so库替换- 需要重启应用生效- 差分包小,下载快2. Sophix(阿里云)原理:1. 底层替换方案:替换ArtMethod的入口2. 类加载方案:作为兜底方案特点:- 即时生效,无需重启- 支持方法级修复- 收费方案,稳定性好3. Robust(美团)原理:1. 编译期在每个方法插入逻辑2. 运行时通过路由跳转到修复类特点:- 即时生效- 包体积增加少- 需要提前插入代码类加载方案实现细节Dex插桩核心代码public class HotFix { public static void patch(Context context, File patchDexFile) { try { // 获取PathClassLoader ClassLoader classLoader = context.getClassLoader(); // 获取pathList字段 Object pathList = getField(classLoader, "pathList"); // 获取dexElements字段 Object[] dexElements = (Object[]) getField(pathList, "dexElements"); // 创建新的dexElements(包含patch.dex) Object[] newElements = makeDexElements(patchDexFile); // 合并数组:patch.dex在前 Object[] combined = (Object[]) Array.newInstance( dexElements.getClass().getComponentType(), newElements.length + dexElements.length ); System.arraycopy(newElements, 0, combined, 0, newElements.length); System.arraycopy(dexElements, 0, combined, newElements.length, dexElements.length); // 替换dexElements setField(pathList, "dexElements", combined); } catch (Exception e) { e.printStackTrace(); } }}热修复的限制1. 无法修复的情况AndroidManifest.xml的修改新增四大组件资源ID变化导致的资源引用错误部分ROM的兼容性限制2. 安全风险代码注入风险需要校验patch签名传输过程需要加密面试要点理解类加载机制和双亲委托模型掌握dexElements插桩原理了解各方案的优缺点和适用场景理解热修复的局限性和安全风险熟悉Tinker、Sophix等主流方案
阅读 0·3月7日 12:11

Android中Activity的生命周期有哪些方法,分别在什么情况下触发?

Android Activity生命周期详解Activity生命周期是Android面试的核心考点,理解生命周期对开发稳定的应用至关重要。完整生命周期方法1. onCreate()触发时机:Activity首次创建时作用:初始化UI、恢复状态、设置布局注意:此时Activity尚未可见2. onStart()触发时机:Activity即将可见时作用:注册广播、启动动画等轻量级初始化注意:Activity还未获得焦点3. onResume()触发时机:Activity获得焦点,可以与用户交互作用:启动相机、定位等需要焦点的操作注意:此时Activity位于前台4. onPause()触发时机:Activity失去焦点,但仍部分可见作用:保存关键数据、停止动画、释放资源注意:执行要快,否则会阻塞下一个Activity显示5. onStop()触发时机:Activity完全不可见作用:释放资源、注销广播、停止定位注意:可能被系统回收6. onDestroy()触发时机:Activity被销毁前作用:最终清理工作注意:区分正常销毁和配置变更导致的重建7. onRestart()触发时机:Activity从停止状态重新启动作用:恢复停止前的状态典型场景生命周期流程| 场景 | 生命周期流程 ||------|-------------|| 首次启动 | onCreate → onStart → onResume || 跳转到其他Activity | onPause → onStop || 返回原Activity | onRestart → onStart → onResume || 按Home键 | onPause → onStop || 屏幕旋转 | onPause → onStop → onDestroy → onCreate → onStart → onResume || 系统回收 | 直接调用onDestroy,无回调 |面试要点在onCreate和onRestoreInstanceState中恢复数据onPause中不要执行耗时操作使用ViewModel避免配置变更导致的数据丢失
阅读 0·3月6日 23:09

Android中ANR是什么,如何定位和解决ANR问题?

Android ANR详解ANR(Application Not Responding)是Android系统中当应用程序在特定时间内无法响应用户输入或广播时的错误状态。ANR的触发条件| 类型 | 触发条件 | 时间限制 ||------|---------|---------|| 输入事件ANR | 按键或触摸事件未响应 | 5秒 || BroadcastReceiver ANR | onReceive()执行超时 | 前台10秒,后台60秒 || Service ANR | Service生命周期方法执行超时 | 20秒 || ContentProvider ANR | ContentProvider操作超时 | 10秒 |ANR产生的原因1. 主线程耗时操作网络请求在主线程执行大量数据库操作复杂的计算任务加载大文件或图片2. 死锁主线程等待子线程释放锁子线程又等待主线程释放锁3. 内存不足系统资源紧张频繁GC导致卡顿4. Binder通信超时跨进程调用超时服务端进程无响应ANR的定位方法1. 查看Logcat日志E/ActivityManager: ANR in com.example.appE/ActivityManager: Reason: Input dispatching timed out2. 分析traces.txt文件/data/anr/traces.txt包含:所有线程的堆栈信息ANR发生的时间点阻塞的代码位置3. 使用Android Studio ProfilerCPU Profiler查看主线程状态识别耗时方法4. StrictMode检测StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() .detectDiskReads() .detectDiskWrites() .detectNetwork() .penaltyLog() .build());ANR的解决方案1. 异步处理耗时操作// 使用AsyncTask(已废弃,建议使用其他方式)// 使用线程池ExecutorService executor = Executors.newFixedThreadPool(4);executor.execute(() -> { // 耗时操作});// 使用Kotlin协程lifecycleScope.launch(Dispatchers.IO) { // 耗时操作}2. 使用HandlerThreadHandlerThread handlerThread = new HandlerThread("background");handlerThread.start();Handler backgroundHandler = new Handler(handlerThread.getLooper());3. 优化数据库操作使用事务批量操作建立合适的索引避免主线程查询大量数据4. 避免死锁统一锁的获取顺序使用tryLock()替代lock()减少锁的持有时间面试要点ANR是系统保护机制,不是崩溃traces.txt是定位ANR的关键主线程不能执行耗时操作使用Systrace分析性能问题关注卡顿监控和线上ANR收集
阅读 0·3月6日 23:09