Android面试

1. Linux进程和Dalvik进程区别

  • Dalvik虚拟机是运行在Linux系统上的,是Linux的一个进程。
  • 每个应用程序都有一个Dalvik虚拟机。好处是一个应用程序一个进程,相互不影响。
1.1 什么是进程、什么是线程
  • 一个应用程序(进程)拥有独立的内存空间
  • 线程即java中的Thread类,线程共享应用程序(进程)的内存
  • 一个应用程序运行在Dalvik虚拟机(Linux进程)
  • 一个应用程序(进程)都必须有个主线程(UI线程)
  • 一个应用程序(进程)需要拥有其他线程提高并发性(网络操作、IO、等等)

2. 什么是内存泄漏、内存溢出

  • 内存泄漏:保存了不可能再被访问的引用,导致gc无法回收。
  • 内存溢出:Dalvik内存耗尽,无法为新对象分配空间。
  • 当某个界面存在内存泄漏,我们反复进入该界面,导致对象创建无法被回收,最终导致内存溢出。
2.1 什么情况下会发生内存泄漏
  • 长期引用Activity的Context
  • 不关闭IO流
  • 对象过大,如XML文件、Bitmap
  • static关键字标识的成员变量
  • 内部类持有Activity引用
2.2 如何进行内存优化
  1. 对图片进行内存优化
  • 对Bitmap进行等比缩放(一张图片解析成Bitmap,会导致占用内存变大,导致OOM)
    • BitmapFactory.Options类的inSampleSize属性设置缩放倍数
  • 设置Bitmap.Config.图片解码格式
    • Bitmap.Config.RGB_565 色彩值16位 2字节
    • Bitmap.Config.ARGB_4444 色彩值16位 2字节
    • Bitmap.Config.ARGB_8888 色彩值32位 4字节
    • Bitmap.Config.ALPHA_8 色彩值8位 1字节
  • BitmapRegionDecocder可以部分加载图片
  1. 使用ThreadPool替代new Thread()
  • 每次new Thread()都会消耗性能,单独使用Thread缺乏管理,可能无限制new Thread(),相互竞争、占用过多的系统资源导致OOM。
  • 而且缺乏一些常见功能,如定时任务、定期执行、线程中断
    • ThreadPool的简单使用方法
    //Java自带线程池
    ExecutorService cachedThreadPool = Executors.newCacheThreadPool();
    ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
    ExecutorService singleThread = Executors.newSingleThreadExecutor();
    //使用方法
    cachedThreadPool.execute(runnable);
    fixedThreadPool.execute(runnable);
    singleThread.execute(runnable);
    
    • 自定义线程池ThreadPoolExecutor
    //ThreadPoolExecutor构造函数
    ThreadPoolExecutor(int corePoolSize,//核心线程数
                        int maximumPoolSize,//最大线程数
                        long keepAliveTime,TimeUnit unit,//大于corePoolSize部分的线程在执行完毕超过多少时间,杀死线程
                        BlockingQueue workQueue,//任务队列
                        ThreadFactory threadFactory//创建线程池的工厂
                        )
    
2.3 UI Review
  • 减少视图层级嵌套(可以减少内存消耗),因为视图是树状结构,并且每次渲染都会遍历一次
    • include
    • merge
    • ViewStub
2.4 使用int而不是Integer,因为每次创建对象都要花费时间(同理 long、double、byte、char)
2.5 使用StringBuilder、StringBuffer代替String的拼接操作
    - StringBuilder是非线程安全的、StringBuffer是线程安全的
2.6 ListView、RecyclerView优化
- item中有图片时候异步加载
- 快速滑动时,不加载图片
- item中对图片进行压缩
- 数据分页加载
2.7 减少不必要的static变量
2.8 Cursor及时回收
2.9 Receiver、registerReceiver和unregisterReceiver要同时出现
2.10 I/O流及时关闭
2.11 javabean尽量不要用get/set方法,直接public效率会高
2.12 for循环中不要放业务逻辑比如
for(int i=0;i < list.size();i++)

改成

int size = list.size;
for(int i=0;i < size;i++)
2.13 Android启动优化
  • Application中onCreate进行第三方库初始化的时候使用异步(Thread、Service都可以)
  • 启动Activity设置windowsBackground属性,进行预加载提高用户体验

3 XMPP协议(即时通讯时候用)

  • XMPP消息是XML格式的,预定义了3个标签
  • 用于确定用户状态
  • 消息

4 微信网页登陆原理

服务器生成一个二维码(包含UUID),与浏览器创建一个长连接,微信扫描二维码调用登陆接口,并且传递用户信息和UUID给服务器,服务器进行校验。

5 第三方登陆原理

用户 第三方应用 QQ/微信
|--request_token------->|
|<--grant request_token-|
|<-redirect to auth page----|
|---authorize request_token------------------------>|
|<-acknowledge authorization------------------------|
|-redirect to-------------->|
|--access_token-------->|
|<-grant access_token---|

6 在Android应用程序中有几个Context

  • Context = Activity个数 + Service个数 + Application个数

7 线程与进程区别

进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。而线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。
线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信的方式(IPC)进行。不过如何处理好同步与互斥是编写多线程程序的难点。

8 Android多进程之间通信方式

Bundle、文件共享、Handler、Binder(AIDL)

9 http协议

请求:请求行、请求头部、请求数据

请求行包括请求方法(如GET、POST)、URL(如www.baidu.com)、协议版本(如HTTP/2.0)合起来就是GET www.baidu.com HTTP/2.0
请求头部包括一些控制字段(如时间、缓存控制、长连接控制)
请求数据就是POST请求时候携带的数据、如果是GET请求的话,请求数据直接在URL里面

响应:状态行、首部行、实体

状态行包括版本(如HTTP/2.0)、状态码(如200)、原因短语(如OK),合起来就是 HTTP/2.0 200 OK
首部行与请求头部类似都是一些控制字段
实体的话就是服务器处理后返回给我们的数据

HTTP协议基于TCP/IP,经历IP协议寻址、TCP协议3次握手连接上服务器。然后开始使用http协议通信,发送请求行、请求头部(发送空白行表示结束)、请求数据(头部中的Content-Length定义了长度)。接收响应行、响应头部(发送空白行表示结束)、响应数据(头部中的Content-Length定义了长度)。最后TCP四次挥手关闭断开,如果控制字段设置了keep-alive会保持连接下次复用。

10 https协议

https就是http协议加上了ssl,http协议默认端口80,https协议默认端口443。https在连接时候会进行ssl认证,客户端向服务端请求https的连接。服务器返回证书(包含公钥)给客户端;客户端认证证书没有问题之后,使用公钥对随机生成的密匙进行加密(非对称加密),然后返回给服务。之后双方通信才有随机生成的密匙进行加密(对称加密)。

非对称加密公钥加密的私钥才能解开,私钥加密的公钥才能解开,所以安全性高;但是这种方式慢。

对称加密只有一个密匙,所以安全性低;但是快。

所以https采用了服务器下发对称加密(公钥),客户端使用公钥加密了非对称加密的密匙传再给服务器,这样提高了安全性和速度。

11. TCP三次握手和四次挥手流程

三次握手:

  • 客户端发送SYN报文段给服务器表示建立连接
  • 服务端收到SYN报文段后开始配置TCP连接需要用到的东西,之后返回SYNAKC报文段给客户端
  • 客户端收到SYNACK报文段后也开始配置TCP连接需要用到的东西,返回给ACK给服务端

四次挥手:

  • 客户端发送FIN给服务端表示关闭连接,同时客户端进入等待关闭的状态
  • 服务端收到FIN后返回客服端ACK,同时服务端进入等待关闭状态。客户端收到ACK后进入半关闭状态,这时候客户端还是可以接收服务端的数据。
  • 服务端发送FIN给客户端表示关闭连接。服务端进入等待最后一个ACK状态。
  • 客户端收到FIN返回ACK给服务端。客户端和服务端完全断开TCP连接。

为什么握手三次挥手要四次?因为FIN和ACK要分开,不像SYN和ACK那样可以同步发送。

12. 垃圾回收机制

垃圾回收机制涉及到几个回收算法

  1. 标记回收算法
    从GC Roots集合开始,将内存整个遍历一次,保留所有可以被GC Roots直接或者间接引用到的对象,其他的当作垃圾回收,这个算法会产生内存碎片。
  2. 复制算法
    将内存分为两快,每次只使用其中一块。垃圾回收时候将在使用的那一块中存活的对象复制到另一块中,之后清理这一块中的所有对象。两块内存交互角色。
  3. 标记-压缩算法
    从根节点开始对所有可达对象做一次标记,之后将存活对象压缩到内存到一端。然后清理掉边界外到所有空间。

所有新建到对象放在新生代(新生代特点是会很快回收,所以使用复制算法),当一个对象经过多次回收后依旧存活,对象会被放入老生代(采用标记-压缩算法)。

复制算法与标记-压缩算法对区别在于复制算法的复制与移动会一起执行,标记-压缩算法会分开。同时压缩阶段会暂停应用。

13. Activity四种形态

  • Active(Activity处于栈顶)
  • Paused(可见但是不可以交互)
  • Stopped(不可见,被另一个Activity完全覆盖,内存足够时候,数据、状态、变量都被保存,内存不足时候会被回收)
  • killed(系统回收掉)

14. Activity启动模式

  • Standard(默认启动方式,Activity可以被多次实例化,一个任务栈中有多个实例)
  • SingleTop(分2种情况,如果Activity在栈顶,会复用Activity通过onNew
    Intent传递Intent。如果Activity不在栈顶或者不存在时候会重新实例化)
  • SingleTask(会去查看当前栈种是否有Activity,如果有则会销毁这个Activity上的所有Activity,然后复用改Activity;如果没有,则实例化)
  • SingleInstance(如果Activity不存在,会开启新的任务栈去实例化。如果任务栈种存在这个Activity,则会销毁他上面的所有Activity,复用这个Activity)

15. Activity生命周期

  • onCreate(创建)
  • onRestart(重运行,从onPause回来)
  • onStart(运行)
  • onRestoreInstanceState(恢复之前保存的状态)
  • onResume(获取焦点)
  • onPause(失去焦点)
  • onSaveInstnaceState(做一些保存状态的操作)
  • onStop(暂停)
  • onDestroy(销毁)

16. Activity与Fragment生命周期关系

  • onAttach(Fragment关联到Activity)
  • onCreate(系统创建Fragment)
  • onCreateView(绘制View)
  • onActivityCreate(Activity的onCreate方法执行之后调用)
  • onStart(运行)
  • onResume(获取焦点)
  • onPause(失去焦点)
  • onStop(暂停)
  • onDestroyView(Fragment中的布局被移除)
  • onDestroy(销毁)
  • onDetach(Fragment与Activity解除关系)

Activity onStart方法执行后Fragmenton开始执行Attach-->onCreate-->onCreateView-->onActivityCreate-->onStart
Activity onResume方法执行后Fragment开始执行onResume
Activity onPause方法执行后Fragment开始执行onPause
Activity onStop方法执行后Fragment开始执行onStop
Activity onDestroy方法执行后Fragment开始执行onDestroyView-->onDestroy-->onDetach

17. Service生命周期

  1. startService启动
    onCreate-->onStartCommand-->onDestroy
    一旦通过这种方式开启Service,这个Service会在后台一直运行,即使创建这个Service的Activity或者Broadcast被销毁,也会一直运行下去除非手动关闭这个Service。第一次调用startService时候,onCreate方法和onStartCommand方法将依次调用,多次调用startService时候,只有onStartCommand会被调用。可以通过调用stopSelf或者其他组建调用stopService来停止这时候走onDestroy方法。
  2. bindService启动
    onCreate-->onBind-->onUnbind-->onDestroy
    这种方法启动的Service会把Service和开启他的组件进行绑定,多次调用bindService只有第一次会触发onBind其余的不会触发。可以通过unbindService来解除绑定走onUnbind方法。多个组件都解除绑定之后会被系统销毁走onDestroy方法。

19. IntentService与Service区别

Service不是独立的线程,也不是独立的进程,他依赖于主线程即Service也会产生ANR。

IntentService继承Service,区别在于onCreate的时候IntentService创建了一个HandlerThread去执行耗时操作。

20. Service中onStartCommond四种返回值策略

  • START_STICKY(系统默认的值,Service进程被杀,保留Service的状态为开始状态,但不保留传递的Intent对象,之后系统会尝试重新创建Service调用onStartCommand)
  • START_NOT_STICK(Service进程被杀掉,系统不会自动启动这个服务)
  • START_REDELIVER_INTENT(如果在执行完onStartCommand后被异常杀掉,则会重启并将Intent值传入)
  • START_STICK_COMPATBILITY(START_STICKY的兼容版本,不保证一定重启)

18. Handler机制

Handler用来发送消息,创建的时候获取默认或者传递过来的Looper对象,并持有Looper对象包含的MessageQueue。发送消息时使用该MessageQueue对象来插入Message,并把自己分装到Message中。

Looper用来为某个线程做消息循环。Looper持有MessageQueue,循环获取Message并且使用Message封装的handler对象进行处理。如果没有Message的话就会阻塞。

Message包含了传递的信息。

19. Binder机制

20. Activity启动流程

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