Android面试总结

1.Service总结

定义
service 是能在后台执行长时间操作而不提供用户界面的组件,属于四大组件之一(ps:运行于主线程,因此不能进行耗时操作,除非独立开启子线程)

生命周期
❤ 生命周期简介

  • onCreate() : 服务第一次创建时被调用,只调用一次;
  • onStartCommond() : 服务启动时被调用,可调用多次;
  • onBind() : 服务被绑定时调用,只调用一次;
  • onUnbind() : 服务被解绑时调用;
  • onDestory() : 服务停止时被调用

❤ 使用 startService() 方式启动的生命周期:onCreate() - onStartCommond() - onDestory()
❤ 使用 onBind() 方式启动的生命周期:onCreate() - onBind() - onUnbind() - onDestory()

启动方式
两种启动方式:Context.startService(),Context.bindService()

❤ Context.startService()

  • 特点 :调用该方法时若服务没有创建,则执行 onCreate() - onStartCommond() 方法,若已创建则只回调 onStartCommond() 并可重复调用,服务一旦创建就会一直运行,不依附于 Act 等,除非外部调用 stopService() 或服务自身调用 stopSelf() , 服务停止并回调 onDestory() 方法,停止方法只会回调一次
  • 应用场景 : 一般用于不需要与调用方进行通信的操作

❤ Context.bindService()

  • 特点 :提供了一个客户端-服务器接口,允许服务与组件进行交互,发送请求,获取结果,可绑定多个组件,当所有组件都解绑时服务停止运行,或者当启动服务当Context销毁时服务也会停止运行
  • 应用场景 :一般用于需要获取服务运行情况的场景,需要交互

❤ 两种启动方式结合使用

  • 特点 :如果调用了startService() ,由于此时已经完成了 onCreate() - onStartCommond() ,所以当调用 onBind() 方法时只会回调 onBind(),如果想停止服务,必须调用 onUnbind(), 和 stopService() 或 stopSelf()

Activity 与 Service 的交互
❤ 只在绑定服务和解绑服务时传递数据

  1. 创建 Service 时,创建内部类继承 Binder,添加相应的方法,可接受需要的参数,并声明该类,在重写的 onBind() 方法中 return 该属性值 ;
  2. 创建类继承 ServiceConnection 并重写 onServiceConnected() 和 onServiceDisconnected() ;
  3. 在调用bindService() 方法时传入以上参数,则在绑定和解绑服务时会回调重写的方法,并调用相关逻辑 ;

❤ 在服务执行过程中进行交互
可采用两种方式:

  • 接口回调
  1. 在创建的 Service 中新建接口,并声明相应的 set 方法,在需要的位置进行接口的方法调用;
  2. 在启用 Service 的类中实现相应的接口,重写必要的方法,进行数据传递
  • 采用广播的方式进行数据传递

Service 是否能进行耗时操作
Service 是执行在主线程中,不能进行耗时操作,除非手动开启一个子线程

如何保证 Service 不被杀死

  • 在 onStartCommand() 方法中设置 flags 值为 START_STICKY,使 Service 在被杀死后尝试再次启动
  • 提升 Service 的优先级,比如说创建一个前台服务(构建好Notification,调用 startForeground())
  • 在依附的界面销毁的时候发送广播,在广播接收中启动该 Service

2.BroadcastReceiver 广播

类型

  • 普通广播
    异步执行的广播,所有的接收者基本在同一时间收到广播,不能控制广播接收的顺序
  • 有序广播
    同步执行的广播,在广播发出后优先级高的广播可先接收到广播,并可决定是否阻断广播
  • 本地广播
    发出的广播只能在应用内部进行传递,并且广播接收器也只能接收本地应用发出的广播
  • 系统广播
    涉及手机的基本操作都会发送广播,如网络状态变化,开机,拍照,电池电量低等
  • 粘性广播
    在Android 5.0已失效

广播的注册方式,异同
广播分为静态注册和动态注册

  • 静态注册:在Manifest 里进行注册,即使程序未启动也能接受广播,不受任何组件的影响,即使程序杀死也能接受到广播,一般用于需要实时监测广播的情况,如消息推送接收
  • 动态注册:在代码中进行注册,只有在程序启动后才能接收到广播,建议在 onResume() 中注册广播,在 onPause() 中取消广播,避免引起内存泄漏,一般该广播用于需要在特定情况下才监听的广播

3.Activity 启动模式

  • standard 标准模式:每启动一个 Activity 就创建一个实例,默认到启动模式;

  • singleTop 栈顶复用模式:如果新启动到 Activity 在栈顶则不创建并回掉onNewInstance() 方法,否则创建 Activity;

  • singleTask 栈内复用模式:如果新的 Activity 在栈中存在,则不创建,并且移除该 Activity 以上的 Activity 将该 Activity 至于栈顶,此时回调 onNewInstance()方法,如果在栈中不存在该 Activity,则创建该 Activity;

    eg: 如果Act的跳转顺序为 A-B-C-D-A-B,其中只有 B 的启动模式为 singleTask ,其他均为standard,则此时栈内存在的 Act 只有AB

  • singleInstance 单实例启动模式:使用该启动模式的Act单独位于一个栈中,且该任务栈中有唯一一个实例

    eg:如果有 Act 的跳转顺序为 A-B-C,其中只有B的启动模式为singleInstance,其他均为 standard ,则此时 A,C位于一个栈,B单独位于一个栈,此时按回退键,Act 的顺序为 C-A-B

4.Java四种引用类型:强引用,软引用,弱引用,虚引用

强引用

  • 概念:只要还有强引用指向一个对象,垃圾回收期就不会回收它,程序宁可跑出 OutOfMemory() 异常也不会回收该引用变量,如果想强中断该对象的引用,可手动将变量赋值为 null ,则该变量会在合适的时机被回收
  • 使用方式

eg: Object a = new Object();
只有当 a 被释放的时候 a 才能被回收

  • 应用场景:描述必须的对象,我们经常使用到的引用

软引用

  • 概念:只要当程序内存不足时才会被回收
  • 使用方式

eg:

  • SoftReference sr = new SoftReference(new String("abc"));
    ps:上边代码表示只有软引用,当被回收后 sr.get() 返回 null;
  • String a = new String("abc"); -- 强引用
    SoftReference sr = new SoftRefrence(a); --软引用
    ps:若内存不足时,sr 会被回收,此时 sr.get() 返回 null , a为强可达对象,只剩强引用
    若手动 a = null;此时 sr为软可达对象,只剩软引用持有,在 sr 被回收前仍可通过软引用获取相应数据
  • 应用场景:描述有用但非必须的对象,常用于对内存敏高度要求比较高的缓存,如网页或者图片缓存;当内存足够时我们可以通过软引用来取值,而不是从繁忙的真实数据中拿值,会提升运行速度

弱引用

  • 概念:只要触发了垃圾回收机制,就会被回收,无论内存够不够用
  • 使用方式

eg: WeakReference wr = new WeakReference(new String("abc"));
引用方法可参考上边软引用

  • 应用场景:也用来描述非必须对象,一般用在回调函数中防止内存泄漏

虚引用

  • 概念:与对象的生命周期无关,随时都可能被回收,与软引用和弱引用不同的是必须与引用队列 (ReferenceQueue) 一同使用,在回收时程序发现其有虚引用,会在回收对象内存前,将虚引用加到与之关联的队列里再进行回收
  • 使用方式

eg: ReferenceQueue rq = new ReferenceQueue();
String a = new String("abc");
PhantomReference pr = new PhantomReference(a,rq);

  • 应用场景:多用于对对象的回收监听

5.App 的启动过程

涉及到的类

  • Zygote 进程 : 是由 Linux 创建出的一个进程,负责其他进程的创建和启动,Zygote 在启动时会创建 SystemService 进程,该进程里面运行着很多 Android 系统的 Service ,如 ActivityManagerService ,Zygote 运行稳定后当需要启动一个App 时, ActivityMangerService 就会通过 Socket 向 Zygote 发送一个指令,Zygote 就会为相应的 App 创建进程
  • Launcher : 我们的手机桌面可以理解为一个 App ,Launcher 可以理解为一个 Activity ,桌面上的应用为相应的 icon ,可响应点击等事件,Zygote 创建之后会启动各种SystemService,SystemService会启动各种ManagerService,包括 ActivityManagerService,ActivityManagerService 会启动Home 应用程序 Launcher
  • ActivityManagerService(AMS): 管理四大组件,统一调度各应用进程,属于 Binder 的实现类,开发者通过 ActivityManager 实现与 AMS 的交互
  • ActivityManager : 由于 AMS 作为守护进程运行在底层程序中,如果程序员直接调用 AMS 会涉及安全等问题,故提供 ActivityManager 作为开发者与 AMS 交互的入口
  • Binder : Android 跨进程通信(IPC) 的一种方式;
  • ActivityThread : 应用的入口类,并不是 Thread,通过调用 main 方法开启消息循环队列;其所在的线程成为主线程,ActivityThread 通过 ApplicationThread 与 AMS 进行通信,ApplicationThread 通过 Hander 与ActivityThread 进行通信
  • ApplicationThread : 提供 Binder 通讯接口,ActivityThread 的私有内部类,ActivityThread 通过代理调用此方法与本地 App 进行通信

App 的启动流程
准备:Zygote 在启动是会创建 SystemService 进程,该进程中运行着 ActivityManagerService(AMS),AMS 启动 Launcher,Zygote 稳定后等待 AMS 发送消息来创建相应的进程
当用户点击桌面图标时
a.Launcher 通过 Binder 向 AMS 发送一条要启动一个 Activity 的请求;
b.AMS 通过 Binder 告知 Launcher 进入 Pause 状态;
c.Launcher 通过 Binder 告知 AMS 已进入 Pause 状态;
d.AMS 通过 socket 通讯告知 Zygote 创建新 App 的进程;
e.Zygote 去检查是冷启动还是热启动,是否要新创建进程,用来启动一个 ActivityThread 实例,供新启动的 Activity 运行;
f.ActivityThread 创建 ApplicationThread 并通过 Binder 通讯将一个 ApplicationThread 的 Binder 传递给 AMS,便于后期通讯;
g.AMS 通过 Binder 通知 ActivityThread 一切准备就绪可以启动Activity;

6.UI 优化

a.合理选择 RelativeLayout,LinearLayout,FrameLayout
 RelativeLayout 和 FrameLayout 会让子 View 调用两次 onMeasure,当布局很复杂时效率会比较低;LinearLayout 在 weight > 0 时也会调用两次 onMeasure;
b.使用标签
布局重用,减少布局层级,需要使用,可减少一些加载时不可见的布局的实例化,提升效率
c.减少布局层级
 可以通过手机开发者选项>GPU 过度绘制查看层级,一般控制在4层以内
d.自定义 View
 重写 onDraw 方法时,不要在该方法中新建对象,容易引起 GC
e.ListView 的使用
 注意布局重用,并使用 Holder,减少布局重绘和 findViewById 的次数
f.去除不必要的背景
 getWindow().setBackgroundDrawable(null)
g.TextView的使用
 使用 leftDrawable 代替 ImageView + TextView

7.内存优化

a.读取文件资源时及时关闭
b.加载图片时注意使用缓存机制
c.使用内部类时注意使用 静态内部类 + 弱引用的方式防止内存泄漏
d.使用线程池管理线程,避免线程的重复创建消耗资源
e.在使用单例时若有 context 的引用,注意要及时释放,或者引用 ApplicationContext
f.使用 webview 时在 onDestory 方法里要及时移除和销毁,webview.removeAllViews() 和 webview.destory()
g.使用属性动画,在界面销毁时要将属性动画置空
h.使用 LeakCanary 等工具进行内存监测

8.OkHttp 原理解析

涉及到的类

  • OkHttpClient:控制整个的网络请求流程,可通过创建者模式进行创建,可设置请求超时时间,拦截器,cookie等信息
  • Request:设置请求的 url,请求方式post 或者 get,请求数据,header 头等信息
  • RealCall:网络请求的执行者,Call call = client.newCall(requst)实际返回为 RealCall,通过该类发起同步execute() 或异步请求 enqueue(),接收相应的 Response 或回调 CallBack 接口传递网络请求返回数据
    ❤ execute 方法内部实现步骤:
    a.检查该 call 是否被执行了,限制单次执行,若需要重复执行,则调用call.clone方法
    b.client.dispatcher().execute(this)关联调度器来处理资源,并调用Dispatcher的execute()方法来进行处理
    c.通过getResponseWithInterceptorChain()来获得网络请求的返回类Response response = getResponseWithInterceptorChain();
    d.client.dispatcher.finish(this)通知调度器已完成请求
    ❤ enqueue方法实现步骤:
    a.检查该 call 是否被执行了,限制单次执行,若需要重复执行,则调用call.clone方法
    b.client.dispatcher().enqueue(new AsyncCall(callBack))调用Dispatcher的异步请求方法
  • Dispatcher:调度器,主要负责网络请求的状态,维护线程池;可设置最大请求并发数,单机最大请求并发数等,内有线程池,请求队列,等待队列等属性
    ❤ execute():把RealCall放到请求队列里直接执行
    ❤ executorService():创建线程池,设置相应的最大并发数
    ❤ enqueue(AsyncCall call)实现步骤:
    a.判断同时请求并发数是否为在最大请求并发数范围内,并且没有超过单机最大请求数;
    b.若在此范围内则将该请求放到正常请求的队列里,并调用executorService().enqueue(call)创建线程池,将AsyncCall作为入参传入,交由线程池处理该方法,而AsyncCall是RealCall的内部类,最终还是调用了RealCall的execute方法
    c.若不在此范围内,则将该请求放到等候队列中
    ❤AsyncCall:Runnable子类,RealCall的内部类,在run方法中实现了网络请求的处理(即RealCall的execute方法)
    ❤finished():请求完成后调用此方法通知调度器移除此次请求,并判断是否有等待队列,如果有等待队列,且执行队列未到最大值,则将等待队列变为执行队列
  • getResponseWithInterceptorChain():真正执行网络请求,创建相应的拦截器,并通过责任链模式完成对网络请求的处理,例如取消重试,缓存处理等逻辑,最终返回Response,该方法的实现步骤为:
    a.创建拦截器Interceptor的存储list;
    b.在list中添加用户在初始化OkHttpClient时传入的拦截器;
    c.在list中添加默认的拦截器
      RetryAndFollowInterceptor:处理重试和重定向;
      BridgeInterceptor:将用户构建的网络请求转换为与后台交互的网络请求,将后台返回的数据转换成用户可读的返回格式
      CacheInterceptor:处理缓存
      ConnectInterceptor:网络连接器,负责连接服务器
      CallServiceInterceptor:执行流操作,负责向服务端发送请求数据,从服务端读取数据
    d.创建责任链,将该list作为入参传入
    e.调用责任链的chain.proceed()方法返回Response
      proceed():把list中的拦截器都遍历出来,通过迭代调用各自的proceed()方法,确认执行完所有的拦截器方法后返回相应的Response类
    流程
    1.实例化OkHttpClient,Request完成上送参数的设置
    2.实例化Call,实际是返回的RealCall
    3.调用call的execute同步请求或者enqueue异步请求
      3.1同步请求:完成如上的RealCall的execute方法流程,其中着重涉及对请求前调度器的资源分配,以及在getResponseWithInterceptorChain()方法中获取网络请求返回结果并完成拦截器中的相关方法,返回最终结果
      3.2异步请求:如上的RealCall的enqueue方法,简要概述为:管理dispatcher调度器,创建线程池,将网络请求方法最终交由线程池来处理,在线程池中由ReallCall的内部类AysncCall线程来完成网络请求方法(即调用了execute)

.属性动画

.自定义空间

-组合控件

-绘制

-自定义viewgroup

. String,StringBuffer,StringBuilder的区别

. 简述JNI

. 简述TCP,UDP,Socket

. 进程和线程的区别

. 线程池

.java 虚拟机的实现机制

.okhttp 的源码分析

.Sqlite 数据库

.java 的回收机制

.hotFix 原理

.常用的线程通信,以及异同

.事件分发机制,onTouchListener和onClickListener的执行顺序

.如何保障Service不被杀死,如何保证进程不被杀死

.sp原理,能否跨进程,如何实现

.

你可能感兴趣的:(Android面试总结)