2024跳槽必备——Android从初阶到架构师面试复习大纲(含解题思路)

  • startService

  • bindService

4、Service生命周期
  1. 手动调用startService()启动服务,自动调用内部方法:onCreate()、onStartCommand(),如果一个Service被startService()多次启动,那么onCreate()也只会调用一次。

  2. 手动调用stopService()关闭服务,自动调用内部方法:onDestory(),如果一个Service被启动且被绑定,如果在没有解绑的前提下使用stopService()关闭服务是无法停止服务的。

  3. 手动调用bindService()后,自动调用内部方法:onCreate()、onBind()。

  4. 手动调用unbindService()后,自动调用内部方法:onUnbind()、onDestory()。

  5. startService()和stopService()只能开启和关闭Service,无法操作Service,调用者退出后Service仍然存在;bindService()和unbindService()可以操作Service,调用者退出后,Service随着调用者销毁。

Broadcast Receiver面试题


1、Broadcast Receiver是什么

Broadcast是四大组件之一,是一种广泛运用在应用程序之间传输信息的机制,通过发送Intent来传送我们的数据

2、Broadcast Receiver的使用场景
  • 同一App具有多个进程的不同组件之间的消息通信

  • 不同App之间的组件之间的消息通信

3、Broadcast Receiver的种类
  • 普通广播

  • 有序广播

  • 本地广播

  • Sticky广播

4、Broadcast Receiver的实现
  • 静态注册:注册后一直运行,尽管Activity、进程、App被杀死还是可以接收到广播

  • 动态注册:跟随Activity的生命周期

5、Broadcast Receiver实现机制
  • 自定义广播类继承BroadcastReceiver,复写onReceiver()

  • 通过Binder机制向AMS进行注册广播

  • 广播发送者通过Binder机制向AMS发送广播

  • AMS查找符合相应条件的广播发送到BroadcastReceiver相应的循环队列中

  • 消息队列执行拿到广播,回调BroadcastReceiver的onReceiver()

6、LocalBroadcastManager特点
  • 本地广播只能在自身App内传播,不必担心泄漏隐私数据

  • 本地广播不允许其他App对你的App发送该广播,不必担心安全漏洞被利用

  • 本地广播比全局广播更高效

  • 以上三点都是源于其内部是用Handler实现的

WebView面试题


1、WebView安全漏洞
  • API16之前存在远程代码执行安全漏洞,该漏洞源于程序没有正确限制使用WebView.addJavascriptInterface方法,远程攻击者可通过使用Java反射机制利用该漏洞执行任意Java对象的方法
2、WebView销毁步骤
  • WebView在其他容器上时(如:LinearLayout),当销毁Activity时,需要在onDestroy()中先移除容器上的WebView,然后再将WebView.destroy(),这样就不会导致内存泄漏
3、WebView的jsbridge
  • 客户端和服务端之间可以通过Javascript来互相调用各自的方法
4、WebViewClient的onPageFinished
  • WebViewClient的onPageFinished在每次完成页面的时候调用,但是遇到未加载完成的页面跳转其他页面时,就会一直调用,使用WebChromeClient.onProgressChanged可以替代
5、WebView后台耗电
  • 在WebView加载页面的时候,会自动开启线程去加载,如果不很好的关闭这些线程,就会导致电量消耗加大,可以采用暴力的方法,直接在onDestroy方法中System.exit(0)结束当前正在运行中的java虚拟机
6、WebView硬件加速

Android3.0引入硬件加速,默认会开启,WebView在硬件加速的情况下滑动更加平滑,性能更加好,但是会出现白块或者页面闪烁的副作用,建议WebView暂时关闭硬件加速

7、WebView内存泄漏

由于WebView是依附于Activity的,Activity的生命周期和WebView启动的线程的生命周期是不一致的,这会导致WebView一直持有对这个Activity的引用而无法释放,解决方案如下

  • 独立进程,简单暴力,不过可能涉及到进程间通信(推荐)

  • 动态添加WebView,对传入WebView中使用的Context使用弱引用

Binder面试题


1、Linux内核的基本知识
  • 进程隔离/虚拟地址空间:进程间是不可以共享数据的,相当于被隔离,每个进程被分配到不同的虚拟地址中

  • 系统调用:Linux内核对应用有访问权限,用户只能在应用层通过系统调用,调用内核的某些程序

  • binder驱动:它负责各个用户的进程,通过binder通信内核来进行交互的模块

2、为什么使用Binder
  • 性能上,相比传统的Socket更加高效

  • 安全性高,支持协议双方互相校验

3、Binder通信原理
  1. Service端通过Binder驱动在ServiceManager的查找表中注册Object对象的add方法

  2. Client端通过Binder驱动在ServiceManager的查找表中找到Object对象的add方法,并返回proxy对象的add方法,add方法是个空实现,proxy对象也不是真正的Object对象,是通过Binder驱动封装好的代理类的add方法

  3. 当Client端调用add方法时,Client端会调用proxy对象的add方法,通过Binder驱动去请求ServiceManager来找到Service端真正对象,然后调用Service端的add方法

4、AIDL
  1. 客户端通过aidl文件的Stub.asInterface()方法,拿到Proxy代理类

  2. 通过调用Proxy代理类的方法,将参数进行封包后,调用底层的transact()方法

  3. transact()方法会回调onTransact()方法,进行参数的解封

  4. 在onTransact()方法中调用服务端对应的方法,并将结果返回

5、BpBinder和BBinder

BpBinder(客户端)对象和BBinder(服务端)对象,它们都从IBinder类中派生而来,BpBinder(客户端)对象是BBinder(服务端)对象的代理对象。

  • client端:BpBinder.transact()来发送事务请求

  • server端:BBinder.onTransact()会接收到相应事务

6.Binder有什么优势

性能方面

· 共享内存 0次数据拷贝

· Binder 1次数据拷贝

· Socket/管道/消息队列 2次数据拷贝

稳定性方面

· Binder:基于C/S架构,客户端(Client)有什么需求就丢给服务端(Server)去完成,架构清晰、职责明确又相互独立,自然稳定性更好

· 共享内存:虽然无需拷贝,但是控制复杂,难以使用

· 从稳定性的角度讲,Binder机制是优于内存共享的。

安全性方面

· 传统的IPC没有任何安全措施,安全依赖上层协议来确保。

· 传统的IPC方法无法获得对方可靠的进程用户ID/进程UI(UID/PID),从而无法鉴别对方身份。

· 传统的IPC只能由用户在数据包中填入UID/PID,容易被恶意程序利用。

· 传统的IPC访问接入点是开放的,无法阻止恶意程序通过猜测接收方地址获得连接。

· Binder既支持实名Binder,又支持匿名Binder,安全性高。

7.Binder是如何做到一次拷贝的

主要是因为Linux是使用的虚拟内存寻址方式,它有如下特性:

· 用户空间的虚拟内存地址是映射到物理内存中的

· 对虚拟内存的读写实际上是对物理内存的读写,这个过程就是内存映射

· 这个内存映射过程是通过系统调用mmap()来实现的 Binder借助了内存映射的方法,在内核空间和接收方用户空间的数据缓存区之间做了一层内存映射,就相当于直接拷贝到了接收方用户空间的数据缓存区,从而减少了一次数据拷贝

8.MMAP的内存映射原理了解吗

MMAP内存映射的实现过程,总的来说可以分为三个阶段:

(一)进程启动映射过程,并在虚拟地址空间中为映射创建虚拟映射区域

\1. 进程在用户空间调用库函数mmap,原型:void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);

\2. 在当前进程的虚拟地址空间中,寻找一段空闲的满足要求的连续的虚拟地址

\3. 为此虚拟区分配一个vm_area_struct结构,接着对这个结构的各个域进行了初始化

\4. 将新建的虚拟区结构(vm_area_struct)插入进程的虚拟地址区域链表或树中

(二)调用内核空间的系统调用函数mmap(不同于用户空间函数),实现文件物理地址和进程虚拟地址的一一映射关系

\1. 为映射分配了新的虚拟地址区域后,通过待映射的文件指针,在文件描述符表中找到对应的文件描述符,通过文件描述符,链接到内核“已打开文件集”中该文件的文件结构体(struct file),每个文件结构体维护着和这个已打开文件相关各项信息。

\2. 通过该文件的文件结构体,链接到file_operations模块,调用内核函数mmap,其原型为:int mmap(struct file *filp, struct vm_area_struct *vma),不同于用户空间库函数。

\3. 内核mmap函数通过虚拟文件系统inode模块定位到文件磁盘物理地址。

\4. 通过remap_pfn_range函数建立页表,即实现了文件地址和虚拟地址区域的映射关系。此时,这片虚拟地址并没有任何数据关联到主存中。

(三)进程发起对这片映射空间的访问,引发缺页异常,实现文件内容到物理内存(主存)的拷贝 注:前两个阶段仅在于创建虚拟区间并完成地址映射,但是并没有将任何文件数据的拷贝至主存。真正的文件读取是当进程发起读或写操作时。

进程的读或写操作访问虚拟地址空间这一段映射地址,通过查询页表,发现这一段地址并不在物理页面上。因为目前只建立了地址映射,真正的硬盘数据还没有拷贝到内存中,因此引发缺页异常。

\1. 缺页异常进行一系列判断,确定无非法操作后,内核发起请求调页过程。

\2. 调页过程先在交换缓存空间(swap cache)中寻找需要访问的内存页,如果没有则调用nopage函数把所缺的页从磁盘装入到主存中。

\3. 之后进程即可对这片主存进行读或者写的操作,如果写操作改变了其内容,一定时间后系统会自动回写脏页面到对应磁盘地址,也即完成了写入到文件的过程。

注:修改过的脏页面并不会立即更新回文件中,而是有一段时间的延迟,可以调用msync()来强制同步, 这样所写的内容就能立即保存到文件里了。

9.Binder机制是如何跨进程的

1.Binder驱动

· 在内核空间创建一块接收缓存区,

· 实现地址映射:将内核缓存区、接收进程用户空间映射到同一接收缓存区

2.发送进程通过系统调用(copy_from_user)将数据发送到内核缓存区。由于内核缓存区和接收进程用户空间存在映射关系,故相当于也发送了接收进程的用户空间,实现了跨进程通信。

10.说说四大组件的通信机制

1.activity (1)一个Activity通常就是一个单独的屏幕(窗口)。 (2)Activity之间通过Intent进行通信。 (3)android应用中每一个Activity都必须要在AndroidManifest.xml配置文件中声明,否则系统将不识别也不执行该Activity。

2.service (1)service用于在后台完成用户指定的操作。service分为两种:

· started(启动):当应用程序组件(如activity)调用startService()方法启动服务时,服务处于started状态。

· bound(绑定):当应用程序组件调用bindService()方法绑定到服务时,服务处于bound状态。

(2)startService()与bindService()区别:

· started service(启动服务)是由其他组件调用startService()方法启动的,这导致服务的onStartCommand()方法被调用。当服务是started状态时,其生命周期与启动它的组件无关,并且可以在后台无限期运行,即使启动服务的组件已经被销毁。因此,服务需要在完成任务后调用stopSelf()方法停止,或者由其他组件调用stopService()方法停止。

· 使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止,大有“不求同时生,必须同时死”的特点。

(3)开发人员需要在应用程序配置文件中声明全部的service,使用标签。 (4)Service通常位于后台运行,它一般不需要与用户交互,因此Service组件没有图形用户界面。Service组件需要继承Service基类。Service组件通常用于为其他组件提供后台服务或监控其他组件的运行状态。

3.content provider (1)android平台提供了Content Provider使一个应用程序的指定数据集提供给其他应用程序。其他应用可以通过ContentResolver类从该内容提供者中获取或存入数据。 (2)只有需要在多个应用程序间共享数据是才需要内容提供者。例如,通讯录数据被多个应用程序使用,且必须存储在一个内容提供者中。它的好处是统一数据访问方式。 (3)ContentProvider实现数据共享。ContentProvider用于保存和获取数据,并使其对所有应用程序可见。这是不同应用程序间共享数据的唯一方式,因为android没有提供所有应用共同访问的公共存储区。 (4)开发人员不会直接使用ContentProvider类的对象,大多数是通过ContentResolver对象实现对ContentProvider的操作。 (5)ContentProvider使用URI来唯一标识其数据集,这里的URI以content://作为前缀,表示该数据由ContentProvider来管理。

4.broadcast receiver (1)你的应用可以使用它对外部事件进行过滤,只对感兴趣的外部事件(如当电话呼入时,或者数据网络可用时)进行接收并做出响应。广播接收器没有用户界面。然而,它们可以启动一个activity或serice来响应它们收到的信息,或者用NotificationManager来通知用户。通知可以用很多种方式来吸引用户的注意力,例如闪动背灯、震动、播放声音等。一般来说是在状态栏上放一个持久的图标,用户可以打开它并获取消息。 (2)广播接收者的注册有两种方法,分别是程序动态注册和AndroidManifest文件中进行静态注册。 (3)动态注册广播接收器特点是当用来注册的Activity关掉后,广播也就失效了。静态注册无需担忧广播接收器是否被关闭,只要设备是开启状态,广播接收器也是打开着的。也就是说哪怕app本身未启动,该app订阅的广播在触发时也会对它起作用。

11.为什么Intent不能传递大数据

Intent携带信息的大小其实是受Binder限制。数据以Parcel对象的形式存放在Binder传递缓存中。如果数据或返回值比传递buffer大,则此次传递调用失败并抛出TransactionTooLargeException异常。 Binder传递缓存有一个限定大小,通常是1Mb。但同一个进程中所有的传输共享缓存空间。多个地方在进行传输时,即时它们各自传输的数据不超出大小限制,TransactionTooLargeException异常也可能会被抛出。在使用Intent传递数据时,1Mb并不是安全上限。因为Binder中可能正在处理其它的传输工作。不同的机型和系统版本,这个上限值也可能会不同。

Handler面试题


1、Handler是什么

Handler通过发送和处理Message和Runnable对象来关联相对应线程的MessageQueue

2、Handler使用方法
  • post(runnable)

  • sendMessage(message)

3、Handler工作原理
4、Handler引起的内存泄漏
  • 原因:非静态内部类持有外部类的匿名引用,导致Activity无法释放

  • 解决:

  • Handler内部持有外部Activity的弱引用

  • Handler改为静态内部类

  • Handler.removeCallback()

5、一个线程有几个 Looper?几个 Handler?

一个Thread只能有一个Looper,一个MessageQueen,可以有多个Handler 以一个线程为基准,他们的数量级关系是: Thread(1) : Looper(1) : MessageQueue(1) : Handler(N)

6、Handler 内存泄漏原因? 以及最佳解决方案?

泄露原因: Handler 允许我们发送延时消息,如果在延时期间用户关闭了 Activity,那么该 Activity 会泄露。 这个泄露是因为 Message 会持有 Handler,而又因为 Java 的特性,内部类会持有外部类,使得 Activity 会被 Handler 持有,这样最终就导致 Activity 泄露。

解决方案

\1. 最直接的思路就是避免使用非静态内部类。使用Handler的时候,放在一个新建的文件中来继承Handler或者使用静态的内部类来替代。静态内部类不会隐含的持有外部类的引用,因此这个activity也就不会出现内存泄漏问题。

\2. 如果你需要在Handler内部调用外部Activity的方法,你可以让这个Handler持有这个Activity的弱引用,这样便不会出现内存泄漏的问题了。

\3. 另外,对于匿名类Runnable,我们同样可以设置成静态的,因为静态内部类不会持有外部类的引用。

\4. 注意:如果使用Handler发送循环消息,最好是在Activity的OnDestroy方法中调用**mLeakHandler.removeCallbacksAndMessages(null);**移除消息。(这不是解决内存泄漏的方法)

7、为何主线程可以new Handler?如果想要在子线程中new Handler 要做些什么准备?

每一个handler必须要对应一个looper,主线程会自动创建Looper对象,不需要我们手动创建,所以主线程可以直接创建handler。 在new handler的时候没有传入指定的looper就会默认绑定当前创建handler的线程的looper,如果没有looper就报错。

因为在主线程中,Activity内部包含一个Looper对象,它会自动管理Looper,处理子线程中发送过来的消息。而对于子线程而言,没有任何对象帮助我们维护Looper对象,所以需要我们自己手动维护。 所以要在子线程开启Handler要先创建Looper,并开启Looper循环

如果在子线程中创建了一个Handler,那么就必须做三个操作:

\1. prepare();

\2. loop();

\3. quit();

8、子线程中维护的Looper,消息队列无消息的时候的处理方案是什么?有什么用?

在Handler机制里面有一个Looper,在Looper机制里面有一个函数,叫做quitSafely()和quit()函数,这两个函数是调用的MessageQueue的quit()。

所以说,这个时候Looper就结束了(跳出了死循环),则达成了第二个作用:释放线程

9、既然可以存在多个 Handler 往 MessageQueue 中添加数据(发消息时各个 Handler 可能处于不同线程),那它内部是如何确保线程安全的?

这里主要关注 MessageQueue 的消息存取即可,看源码内部的话,在往消息队列里面存储消息时,会拿当前的 MessageQueue 对象作为锁对象,这样通过加锁就可以确保操作的原子性和可见性了。

消息的读取也是同理,也会拿当前的 MessageQueue 对象作为锁对象,来保证多线程读写的一个安全性。

10、我们使用 Message 时应该如何创建它?

创建的它的方式有两种: 一种是直接 new 一个 Message 对象, 另一种是通过调用 Message.obtain() 的方式去复用一个已经被回收的 Message, 当然日常使用者是推荐使用后者来拿到一个 Message,因为不断的去创建新对象的话,可能会导致垃圾回收区域中新生代被占满,从而触发 GC。

Message 中的 sPool 就是用来存放被回收的 Message,当我们调用 obtain 后,会先查看是否有可复用的对象,如果真的没有才会去创建一个新的 Message 对象。

补充:主要的 Message 回收时机是: 1.在 MQ 中 remove Message 后; 2.单次 loop 结束后; 3.我们主动调用 Message 的 recycle 方法后

11.Looper死循环为什么不会导致应用卡死?

Launch桌面的图标第一次启动Activity时,会最终走到ActivityThread的main方法,在main方法里面创建Looper和MessageQueue处理主线程的消息,然后Looper.loop()方法进入死循环,我们的Activity的生命周期都是通过Handler机制处理的,包括 onCreate、onResume等方法,下面是loop方法循环。

主线程的主要方法就是消息循环,一旦退出消息循环,那么你的应用也就退出了,Looer.loop()方法可能会引起主线程的阻塞,但只要它的消息循环没有被阻塞,能一直处理事件就不会产生ANR异常。

造成ANR的不是主线程阻塞,而是主线程的Looper消息处理过程发生了任务阻塞,无法响应手势操作,不能及时刷新UI。

阻塞与程序无响应没有必然关系,虽然主线程在没有消息可处理的时候是阻塞的,但是只要保证有消息的时候能够立刻处理,程序是不会无响应的。

总结:应用卡死压根与这个Looper没有关系,应用在没有消息需要处理的时候,它是在睡眠,释放线程;卡死是ANR,而Looper是睡眠。

AsyncTask面试题


1、AsyncTask是什么

它本质上就是一个封装了线程池和Handler的异步框架

2、AsyncTask使用方法
  • 三个参数

  • Params:表示后台任务执行时的参数类型,该参数会传给AysncTask的doInBackground()方法

  • Progress:表示后台任务的执行进度的参数类型,该参数会作为onProgressUpdate()方法的参数

  • Result:表示后台任务的返回结果的参数类型,该参数会作为onPostExecute()方法的参数

  • 五个方法

  • onPreExecute():异步任务开启之前回调,在主线程中执行

  • doInBackground():执行异步任务,在线程池中执行

  • onProgressUpdate():当doInBackground中调用publishProgress时回调,在主线程中执行

  • onPostExecute():在异步任务执行之后回调,在主线程中执行

  • onCancelled():在异步任务被取消时回调

3、AsyncTask工作原理
4、AsyncTask引起的内存泄漏
  • 原因:非静态内部类持有外部类的匿名引用,导致Activity无法释放

  • 解决:

  • AsyncTask内部持有外部Activity的弱引用

  • AsyncTask改为静态内部类

  • AsyncTask.cancel()

5、AsyncTask生命周期

在Activity销毁之前,取消AsyncTask的运行,以此来保证程序的稳定

6、AsyncTask结果丢失
  • 12
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值