面试总结-Android

一、Android基础

binder调用是否会阻塞当前线程?

A:默认跨进程调用Binder 客户端线程会被阻塞,如果客户端不在乎返回结果,调用后想立即返回,可以给方法加上oneway关键字

Android SharedPreference 是否支持多进程读写?

A:不支持的,虽然SharedPreference有跨进程模式,如果项目有多个进程使用同一个Preference,需要使用该模式,但是也已经废弃了,官方建议使用ContentProvider

如何统计 Android 应用的帧率(FPS)?

A:利用Choreographer的postcallback方法接口轮询方式,能够对帧率进行统计

Android ANR是什么?系统如何得知发生了ANR?

A:ANR触发流程,可以比喻为埋炸弹和拆炸弹的过程,
以启动Service为例,Service的onCreate方法调用之前会使用Handler发送延时10s的消息,Service 的onCreate方法执行完,会把这个延时消息移除掉。
假如Service的onCreate方法耗时超过10s,延时消息就会被正常处理,也就是触发ANR,会收集cpu、堆栈等信息,弹ANR Dialog。

Android 面试:卡顿、ANR、死锁,线上如何监控?

Android 组件化之间的组件如何通信

通过建立common组件,各个组件依赖common组件来完成组件化通信,定义好相关的接口;ARouter、Eventbus等提供响应方法

Android子线程如何创建handler
new Thread(new Runnable() {  
    @Override  
    public void run() {  
        Looper.prepare();  
        Handler handler = new Handler();  
        // ...使用handler发送消息  
        Looper.loop();  
    }  
}).start();
介绍Handler postdelay的机制

A:原来在next方法中对链表头部的Message的执行时间进行了判断,如果当前时间小于msg.when,则计算阻塞时间,
然后在循环开始的时候判断如果这个Message有延迟,就调用nativePollOnce(ptr, nextPollTimeoutMillis)进行阻塞。

1、比如postDelay()一个延时10秒钟的Runnable A、消息进队,MessageQueue调用nativePollOnce()阻塞,Looper阻塞;

2、紧接着post()一个Runnable B、消息进队,判断现在A时间还没到、正在阻塞,把B插入消息队列的头部(A的前面),然后调用nativeWake()方法唤醒线程;

3、MessageQueue.next()方法被唤醒后,重新开始读取消息链表,第一个消息B无延时,直接返回给Looper;

4、Looper处理完这个消息再次调用next()方法,MessageQueue继续读取消息链表,第二个消息A还没到时间,计算一下剩余时间(假如还剩9秒)继续调用nativePollOnce()阻塞;
直到阻塞时间到或者下一次有Message进队再次唤醒;

Handler同步消息屏障的作用

A:同步屏障为Handler消息机制增加了一种简单的优先级机制,异步消息的优先级要高于同步消息。Android 应用框架中为了更快的响应UI刷新事件在 ViewRootImpl.scheduleTraversals 中使用了同步屏障,mTraversalRunnable 调用了 performTraversals 执行measure、layout、draw。

为了让mTraversalRunnable尽快被执行,在发消息之前调用MessageQueue.postSyncBarrier设置了同步屏障。

Android 每个线程如何保证只有一个Looper

A:Looper 通过 ThreadLocal 存储在每个线程中: 在 Android 中,Looper 通过 ThreadLocal 存储在每个线程中。Looper.myLooper() 方法实际上是使用 ThreadLocal 来获取当前线程的 Looper。如果当前线程没有关联的 Looper,这个方法会返回 null

Android触摸事件的原理

Android触摸事件机制主要涉及两个方面:触摸事件的类型和事件传递的三个阶段。

触摸事件类型主要有三种:ACTION_DOWN、ACTION_MOVE和ACTION_UP。其中,ACTION_DOWN表示用户按下操作,标志着一次触摸事件的开始;ACTION_MOVE表示用户在屏幕上滑动的过程;ACTION_UP表示用户手指离开屏幕的操作,标志着一次触摸事件的结束。

事件传递的三个阶段包括分发(Dispatch)、拦截(Intercept)和消费(Consume)。在分发阶段,事件由外部的View接收,然后传递给其内层的View,依次传递到更够接收此事件的最小View单元,完成事件捕获过程。在拦截阶段,ViewGroup会通过onInterceptTouchEvent方法来决定是否拦截事件,如果拦截则不再分发给子视图,否则继续传递给子视图。在消费阶段,View会通过onTouchEvent方法来消费事件,包括消费完、消费不完全和消费不了三种情况。

Android触摸事件机制是实现用户与应用程序交互的重要基础,通过处理不同类型的事件和事件传递的三个阶段,应用程序可以响应用户的操作并提供相应的反馈。

如何处理Android滑动冲突?方法有哪些?

在 Android 中,滑动冲突通常发生在包含多个可滑动的 View 或 ViewGroup 的情况下,例如嵌套的 ScrollView、ListView、RecyclerView 等。解决滑动冲突的方法通常包括以下几种:

1. **外部拦截法(父布局拦截事件):**
   - 在父容器的 `onInterceptTouchEvent` 方法中进行事件拦截,根据具体情况判断是否拦截事件。
   - 当父容器确定要处理滑动时,可以在 `onTouchEvent` 方法中进行相应的处理。

2. **内部拦截法(子 View 拦截事件):**
   - 子 View 拦截事件,需要重写子 View 的 `onInterceptTouchEvent` 方法。
   - 子容器在处理事件时,通过 `requestDisallowInterceptTouchEvent(true)` 来通知子 View 不要拦截事件。

3. **使用 NestedScrollView、NestedScrollingParent 和 NestedScrollingChild:**
   - 使用 `NestedScrollView` 作为父容器,它已经实现了 NestedScrollingParent 接口。
   - 在子 View 中使用 `NestedScrollingChild` 接口,以便通知父容器它的滑动情况。

ViewModel是如何在Activity配置发生变化的时候保存数据的

A:生命周期感知: ViewModel 是与生命周期关联的,它能感知相关联的 ActivityFragment 的生命周期变化。当配置发生变化时,例如屏幕旋转导致 Activity 被销毁并重新创建,ViewModel 会存储在 ViewModelStore 中,以便下一个 Activity 实例能够获取到相同的 ViewModel 实例。

单线程池 与 HandlerThread的区别
  1. 执行方式: 单线程池按照提交顺序串行执行任务,而 HandlerThread 通过消息队列和 Looper 实现异步执行任务。

  2. 线程数量: 单线程池中只有一个工作线程,而 HandlerThread 可以创建多个线程。

  3. 使用场景: 单线程池适用于需要保证任务按照指定顺序串行执行的场景,而 HandlerThread 适用于需要在工作线程中处理耗时操作或进行线程间通信的场景。

Serializable 和 Parcelable的区别

`Serializable` 和 `Parcelable` 都是在 Android 中用于实现对象序列化的接口,但它们有一些重要的区别。

1. **性能:**
   - `Parcelable` 性能通常比 `Serializable` 好,因为它是为 Android 设计的,并且在内部实现上更加高效。`Parcelable` 直接将对象的字段写入 Parcel(一种 Android 中的轻量级序列化工具),而不需要像 `Serializable` 那样进行大量的反射操作。

2. **实现方式:**
   - `Serializable` 是 Java 平台的标准序列化接口,它只需实现 `Serializable` 接口并定义一个唯一的序列化标识符(serialVersionUID)即可。Java 虚拟机(JVM)负责处理序列化和反序列化的细节。
   - `Parcelable` 是 Android 提供的接口,需要实现 `Parcelable` 接口,并重写 `writeToParcel` 和 `createFromParcel` 方法。这种方式需要手动编写序列化和反序列化的逻辑,但由于是手动控制,可以更灵活地控制序列化的细节。

3. **跨平台:**
   - `Serializable` 是 Java 平台的标准,因此可用于在不同的 Java 环境中进行对象传递,但可能存在一些兼容性问题。
   - `Parcelable` 是 Android 特有的,不适用于非 Android 环境。如果需要在 Android 和其他平台之间进行对象传递,可能需要使用其他的跨平台序列化方式。

4. **手动实现:**
   - `Parcelable` 需要手动实现相关方法,这样开发者可以更精确地控制对象的序列化和反序列化过程,以提高性能和减小序列化数据的大小。
   - `Serializable` 虽然不需要手动实现方法,但由于它使用反射,可能会导致性能问题,并且无法精确地控制序列化的过程。

综上所述,如果在 Android 环境中进行对象传递,通常推荐使用 `Parcelable`,因为它在性能上更为高效。但如果需要在不同的 Java 环境中进行对象传递,或者希望使用标准的 Java 序列化机制,那么可以选择使用 `Serializable`。

Android View post方法的实现机制

主线程执行: 一旦消息被添加到主线程的消息队列中,主线程的 Looper 就会开始处理消息队列中的消息。当消息包含一个 Runnable 对象时,它会执行 run 方法。因此,通过 View.post 提交的代码块最终会在主线程上执行。

为什么Android只能在主线程更新UI

在 Android 中,UI 框架不是线程安全的,这意味着多个线程并发地修改 UI 可能导致不可预测的结果和应用程序崩溃。为了解决这个问题,Android 引入了一种单线程模型,即主线程(也称为 UI 线程),以确保 UI 操作是串行执行的。

主要原因有以下几点:

1. **View层次结构的访问和更新不是线程安全的:** Android UI 框架的设计是为了简化开发者的工作,并且大多数 UI 操作都需要访问和修改 View 层次结构。如果允许在多个线程中并发地修改 View 层次结构,就会导致竞态条件和不一致性。为了避免这种情况,Android 采用了单线程模型。

2. **性能和同步问题:** 在多线程环境中,需要引入额外的同步机制来确保对 UI 元素的访问是线程安全的。这可能会引入性能开销和复杂性。通过限制 UI 操作在主线程中执行,Android 可以避免这些同步问题。

3. **事件处理机制:** Android 中的触摸事件、键盘事件等都是在主线程中处理的。如果 UI 操作允许在其他线程中执行,可能会导致事件处理与 UI 更新之间的不一致性和难以调试的问题。

为了在主线程中执行后台任务并更新 UI,Android 提供了一些机制,例如使用 `View.post`、`Handler`、`AsyncTask` 等,它们可以帮助你将任务传递到主线程队列中执行。这样,你可以在后台线程中进行耗时操作,但最终的 UI 更新仍然在主线程中完成,确保了线程安全性。

Service 和 IntentService的区别

`Service` 和 `IntentService` 都是 Android 中用于执行后台任务的组件,但它们有一些重要的区别。

1. **执行方式:**
   - **Service:** `Service` 是一个通用的后台执行组件,没有默认的工作线程。你需要在 `Service` 中自行管理线程,确保在后台执行任务而不影响主线程。通常,你需要手动创建线程或使用异步任务(AsyncTask)等方式来处理异步操作。
   - **IntentService:** `IntentService` 是 `Service` 的子类,它默认会创建一个工作线程,并且在工作线程中处理传递给它的 Intent。每次启动 `IntentService` 时,它会将 Intent 放入队列,并在工作线程中逐个处理这些 Intent。

2. **生命周期:**
   - **Service:** `Service` 的生命周期较为灵活,可以通过 `startService()` 和 `bindService()` 启动和绑定。它可以长时间运行,即使调用它的组件(如 Activity)被销毁,`Service` 仍然可以继续运行。
   - **IntentService:** `IntentService` 在处理完所有的 Intent 后会自动停止。这使得 `IntentService` 更适合执行一些单次的、独立的后台任务。

3. **线程管理:**
   - **Service:** 需要手动管理线程,以避免在主线程中执行耗时操作。
   - **IntentService:** 内部已经处理了线程管理,你只需要实现 `onHandleIntent` 方法,该方法在工作线程中被调用。

4. **适用场景:**
   - **Service:** 适用于长时间运行的任务,比如在音乐播放器中播放音乐或在后台下载数据的情况。
   - **IntentService:** 适用于执行单个任务后自动停止的情况,例如在后台处理推送消息或下载文件。

总的来说,`IntentService` 更适合用于简单的、异步的后台任务,因为它自动管理了线程和生命周期,而 `Service` 更为灵活,适用于需要手动管理线程和生命周期的情况。选择使用哪个取决于你的具体需求。

Android 四大组件 哪些是继承context的?为什么这么设计?

在 Android 中,四大组件指的是 Activity、Service、BroadcastReceiver 和 ContentProvider。其中,Activity、Service 和 ContentProvider 是继承自 Context 的,而 BroadcastReceiver 不是。

1. **Activity:** Activity 是 Android 应用程序中的一个核心组件,代表一个用户界面屏幕。Activity 是 Context 的子类,这是因为 Activity 需要访问应用程序的资源(如布局文件、字符串等)、启动其他组件(如启动新的 Activity)以及处理用户交互事件等,而这些操作都需要访问应用程序的上下文信息。

2. **Service:** Service 是 Android 应用程序中的一个后台运行的组件,用于执行长时间运行的任务或处理后台任务。Service 也是 Context 的子类,这是因为 Service 通常需要访问应用程序的资源,并且可能需要在后台执行与用户界面无关的操作。

3. **ContentProvider:** ContentProvider 是 Android 应用程序中用于共享数据的组件,它可以让应用程序之间共享数据。ContentProvider 也是 Context 的子类,这是因为 ContentProvider 需要访问应用程序的数据存储区域(如数据库、文件等)并提供数据访问接口。

4. **BroadcastReceiver:** BroadcastReceiver 是 Android 应用程序中用于接收和处理广播消息的组件。BroadcastReceiver 不是 Context 的子类,这是因为 BroadcastReceiver 主要用于接收系统级别的广播消息,与应用程序的上下文信息关系较小。

继承 Context 的设计使得 Activity、Service 和 ContentProvider 可以方便地访问应用程序的资源和执行相关操作,而不需要传递额外的上下文信息。同时,这也符合面向对象设计的原则,即将相关功能封装在一个类中,提高代码的可维护性和可重用性。

Android 网络连接复用的机制

在 Android 中,网络连接的复用是通过 HTTP/1.1 协议中的 Keep-Alive 机制来实现的。当使用 HTTPURLConnection 或者 HttpClient 进行网络请求时,默认情况下会启用 Keep-Alive 机制,即在请求完成后保持与服务器的 TCP 连接,以便可以在同一个连接上发送多个请求和接收多个响应。

具体来说,当使用 Keep-Alive 时,客户端会在请求头中添加一个 `Connection: Keep-Alive` 的字段,告诉服务器保持连接。服务器在接收到请求后,在响应头中也会添加一个 `Connection: Keep-Alive` 的字段,表示同意保持连接。这样,客户端和服务器之间的 TCP 连接就可以在多次请求和响应之间复用,减少了 TCP 连接的建立和关闭次数,提高了网络请求的效率和性能。

在 Android 中,你可以通过以下方法来禁用 Keep-Alive 机制,以确保每个请求都使用一个新的 TCP 连接:

使用 HttpURLConnection:

HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestProperty("Connection", "close");

需要注意的是,虽然 Keep-Alive 机制可以减少 TCP 连接的建立和关闭次数,提高网络请求的效率,但在某些情况下可能会导致连接超时或连接池耗尽的问题。因此,在设计网络请求时需要根据具体情况进行权衡和选择。

Android Crash治理

参考文章:精选文章|得物App Android Crash治理演进

Android 插件化原理

参考文章:吹爆系列: Android 插件化的今生前世大揭秘

Android binder 原理

在 Android 系统中,Binder 是一种跨进程通信(IPC)机制,用于在不同进程之间传输数据和调用方法。Binder 的实现原理涉及到以下几个关键点:

1. **Binder 驱动:** Binder 机制是基于 Linux 内核的 Binder 驱动实现的。Binder 驱动提供了 Binder 设备节点(/dev/binder)用于进程间通信,并提供了底层的数据传输和处理功能。

2. **Binder 通信流程:**
   - 服务端进程(Server):服务端会创建一个 Binder 对象并将其注册到 Binder 驱动中,然后等待客户端连接。
   - 客户端进程(Client):客户端会获取到服务端的 Binder 对象,并通过 Binder 驱动发送请求到服务端。
   - Binder 驱动:Binder 驱动接收到客户端的请求后,会将请求转发给服务端,并将服务端的响应返回给客户端。

3. **Binder 对象和 BinderProxy:** 在客户端和服务端之间传递的是 Binder 对象的引用,而不是对象本身。客户端获取到的 Binder 对象实际上是一个 BinderProxy 对象,它是 Binder 的代理对象,用于发送请求到服务端。

4. **Binder 的调用过程:**
   - 客户端调用代理对象的方法,并传入参数。
   - 代理对象将方法调用转化为一个请求消息,并通过 Binder 驱动发送到服务端。
   - 服务端接收到请求消息后,调用自己的 Binder 对象的方法,并将结果返回。
   - Binder 驱动将结果返回给客户端的代理对象,代理对象再将结果返回给客户端。

5. **Binder 的安全性:** Binder 机制提供了权限验证和进程隔离机制,确保只有具有相应权限的进程可以访问 Binder 对象,从而保障系统的安全性。

总的来说,Binder 是 Android 系统中实现跨进程通信的重要机制,通过 Binder 机制,不同进程之间可以方便、高效地进行数据传输和方法调用,从而实现各种跨进程通信的功能。

Android APK瘦身有哪些方式?

在 Android 应用开发中,可以通过以下几种方式来减小 APK 大小,即进行 APK 瘦身:

1. **资源优化:** 
   - 使用 WebP 格式替换 PNG、JPEG 等图片,可以减小图片大小;
   - 使用 VectorDrawable 替换大部分 PNG 图片,减小 APK 大小;
   - 删除不必要的资源文件,如未使用的图片、布局文件等。

2. **代码优化:** 
   - 使用 ProGuard、R8 等代码混淆工具来移除未使用的代码和资源,减小 APK 大小;
   - 使用 App Bundle 功能,只打包需要的资源和代码,减小安装包大小;
   - 优化代码结构,减少代码冗余,提高代码复用。

3. **压缩资源和代码:** 
   - 使用 PNG、JPEG 等图片压缩工具对图片进行压缩,减小图片大小;
   - 使用压缩工具对代码进行压缩,减小 APK 大小;
   - 对资源文件进行压缩,减小 APK 大小。

4. **移除不必要的库和功能:** 
   - 移除不必要的第三方库和功能,减小 APK 大小;
   - 使用动态模块加载(Dynamic Feature Module)功能,将不常用的功能打包为独立的模块,按需下载。

5. **资源和代码分包:** 
   - 将资源和代码分包,按需下载,减小安装包大小;
   - 使用 Android App Bundle(aab)格式,系统会根据设备配置和用户需求自动下载和安装相应的资源和代码。

6. **优化资源和代码加载:** 
   - 使用更高效的加载方式,减少资源和代码加载时间;
   - 避免重复加载资源和代码,提高加载效率。

通过以上方式,可以有效地减小 APK 大小,提高应用性能和用户体验。

Android 自定义ViewGroup 支持瀑布流 如何设计

要实现支持瀑布流(Waterfall Flow)的自定义 ViewGroup,你可以按照以下步骤设计:

  1. 确定布局方式: 瀑布流布局是一种不规则的多列布局,每一列的高度可以不同。因此,你需要确定列数和列与列之间的间距,以及行与行之间的间距。

  2. 重写 onMeasure() 方法: 在 onMeasure() 方法中,你需要测量子 View 的大小,并根据列数和间距计算出每列的宽度。同时,根据子 View 的高度确定每一行的高度。

  3. 重写 onLayout() 方法: 在 onLayout() 方法中,你需要确定每个子 View 的位置,按照瀑布流的布局方式来摆放子 View。可以使用一个二维数组来保存每列的高度,然后根据高度最小的列来确定新添加子 View 的位置。

  4. 添加子 View: 在自定义 ViewGroup 中,你需要提供方法来动态添加子 View。当添加子 View 时,需要重新计算布局,并调用 requestLayout() 方法来触发重新布局。

  5. 处理滑动: 如果你希望支持滑动功能,可以在自定义 ViewGroup 中处理触摸事件,并实现滑动逻辑。可以参考 Android 的 ScrollView 或 RecyclerView 的实现方式。

  6. 优化性能: 在实现瀑布流布局时,要注意优化性能,尽量减少不必要的计算和布局操作,避免频繁地请求布局。

Android View绘制流程

Android View 的绘制流程可以简单概括为以下几个步骤:

1. **测量(Measure):** 在测量阶段,系统会调用 View 的 `onMeasure()` 方法来确定 View 的大小。在这个阶段,View 可以通过重写 `onMeasure()` 方法来指定自己的测量规则,例如确定 View 的宽高或者根据内容自适应大小。

2. **布局(Layout):** 在布局阶段,系统会根据测量阶段得到的 View 的大小,来确定 View 的位置。系统会调用 View 的 `onLayout()` 方法来确定 View 的位置。在这个阶段,View 可以通过重写 `onLayout()` 方法来指定自己的位置。

3. **绘制(Draw):** 在绘制阶段,系统会调用 View 的 `onDraw()` 方法来绘制 View 的内容。在这个阶段,View 可以通过重写 `onDraw()` 方法来自定义绘制内容,例如绘制图形、文字等。

4. **绘制缓存(Draw Cache):** 在绘制缓存阶段,系统会将 View 的绘制结果缓存起来,以便下次重绘时可以直接使用缓存结果,从而提高绘制性能。可以通过调用 `setDrawingCacheEnabled(true)` 来开启绘制缓存。

5. **重绘(Invalidate and Redraw):** 当 View 需要重绘时,可以调用 `invalidate()` 方法来请求重绘。系统会在下一个绘制周期中重新调用 View 的 `onDraw()` 方法来实现重绘。

6. **视图树的绘制(Traversal):** 在绘制过程中,系统会遍历整个视图树(View Hierarchy),从根视图开始递归绘制每个子视图。在遍历过程中,系统会根据绘制顺序(从后向前)来绘制视图,保证上层视图覆盖下层视图。

视图树中的 View 绘制顺序是从顶层到底层,即从父 View 到子 View,从后向前绘制,保证上层 View 覆盖下层 View

以上就是 Android View 的绘制流程

RecyclerView 缓存机制

参考:RecyclerView缓存机制

Android Message是如何分发到对应的Handler的?

在 Android 中,Message 的 target 变量是在发送消息时由 Handler 赋值的。当你调用 Handler 的 sendMessage 或者 post 等方法发送消息时,Handler 会创建一个 Message 对象,并将这个 Message 的 target 属性设置为自己,即当前的 Handler。

target 的作用是用来标识消息应该被发送到哪个 Handler。当消息被放入消息队列中时,Looper 会从消息中取出 target,并将消息分发给 target 对应的 Handler 处理。这样,每个 Message 都知道自己应该被哪个 Handler 处理,从而实现了消息的分发功能。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值