自己总结的安卓面试题(一)

目录

安卓的多线程

Java基础知识

集合

网络相关面试题

性能优化

View相关

进程

其他的补充知识点


  • 安卓的多线程

  1. 进程和线程的区别
    1. 进程是CPU资源分配的最小单位,线程的CPU调度的最小单位;
    2. 进程之间不能共享资源,线程之间可以共享所在进程的地址空间和资源;
    3. 一个进程中可以有多个线程,一个线程只能属于一个进程;
    4. 进程可以开启线程和其他进程
  2. 创建线程的三种方式
    1. 继承Thread类重写run方法;
    2. 实现Runnable接口重写run方法;
    3. 实现Callable重写call方法;

可以提供一个返回值

Call方法可以抛出一个异常

可以通过运行Callable得到的Future对象监听目标线程调用call方法的结果,得到返回值;

  1. 对线程池的理解
    1. 线程池在任务未到来之前会创建一定数量的线程放在空闲队列中,这些线程都是处于睡眠状态;当请求到来之后,会给其分配一个空闲线程,然后运行;如果没有空闲线程,会创建新线程,如果最大线程数满了,就会抛出异常.
    2. 作用:
      • 是为了尽可能减少对象创建和销毁的次数,提高性能,减少CPU资源的消耗,可以控制线程数量,避免内存消耗过度.
      • 当线程调度任务发生异常时,会重新创建一个线程来代替异常线程;
    3. 如何创建:使用ThreadPoolExecutor
  2. 线程的状态
    1. New:新建状态,还没有调用start
    2. Runnable:可运行状态,调用start进入可运行状态
    3. Blocked:阻塞状态,被锁阻塞,暂时不活动
    4. Synchronized:关键字修饰的方法或代码块获取锁时的状态
    5. Waiting:等待状态,不运行任何代码,等待线程调度器调度,sleep,wait用来暂停当前线程的执行,任何其他线程都可以中断当前线程的睡眠;
    6. Timed Waiting:超时等待,在指定时间自行返回;
    7. Terminated:终止状态,包括正常终止和异常终止;
  3. wait和sleep的区别
    1. Wait用于线程间的通信,如果等待且其他线程被唤醒时,它会释放锁;
    2. Sleep仅是释放CPU资源或者让当前线程停止执行一段时间,并不会释放锁;
  4. 线程中断
    1. Java中提供了线程中断机制相关的interrupt()方法,他将中断标识位设置为true,具体是否要中断由程序来判断;
    2. New和Terminal不理会线程中断请求;
    3. Runnable和Blocked调用interrupt会将标志位设置为true;
    4. Waiting和Timed Waiting会发生InterruptedException异常;
  5. Thread为什么不能用stop方法停止线程
    1. 会抛出ThreadDeath异常;
    2. 释放该线程持有的所有的锁,数据可能会被破坏;

 

 

  1. 同步方法和同步代码块

当多个线程同时操作一个可共享的资源变量时,就会导致数据不准确,解决方法:

    1. 同步方法.即用synchronized关键字修饰的方法,在调用此方法前,需要获得内置锁,否则处于阻塞状态;synchronized也可以修饰静态方法,调动此静态方法,会锁住整个类;
    2. 同步代码块:synchronized修饰的语句块,
    3. 使用特殊域变量volatile实现线程同步,他会保证修改的值立即更新到主存中,即内存可见性;
    4. 使用重入锁:
      • ReentrantLock()方法,创建一个ReentrantLock实例;
      • lock()获得锁;
      • unlock()释放锁;
    5. 使用局部变量实现线程同步:使用ThreadLocal来管理变量,每一个线程都有该变量的副本,副本之间独立
  1. 线程通信机制
    1. 四大角色
      • Message:消息的载体,常用的四个字段:arg1,arg2,what,obj

Obj携带Object对象,其他三个携带整形数据

      • MessageQueue:消息队列,每个线程只有一个
      • Looper:调用loop()方法,进入到一个无限循环(没有新消息就阻塞),每当MessQueue有消息就取出来,每个线程只有一个Looper
      • Handler:发送和处理消息
      • ThreadLocal:每个线程私有的本地存储区域,存储对象
  1. 原子性,可见性,有序性
    1. 原子性:某一个操作不可分割,比如a=0是原子操作,a++不是原子操作,非原子操作都存在线程安全问题
    2. 可见性:指线程之间的可见性,如用volatile修饰的内容具有可见性,但不能保证其原子性,因为volatile只能使用在变量级别,不能对方法进行修饰;
    3. 有序性:线程执行的顺序按代码的先后顺序,java内存模型中,允许编译器和处理器对指令进行重排序,这样会影响到多线程并发执行的正确性.所以可以通过volatile和synchronized以及Lock保证一定的有序性.
  2. 死锁产生的四个必要条件
    1. 互斥条件,一个资源一段时间内仅能够被一个进程所占有;
    2. 请求和保持条件:进程已经持有了至少一个资源,但又提出了新的资源请求,而该资源被其他进程所占有,此时请求会被阻塞,但自己的资源又保持不放;
    3. 不可剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,只能自己主动释放;
    4. 循环等待条件:若干进程形成循环等待资源的关系,环路中每一个进程所占有的资源被另一个进程所申请.
  3. 死锁的避免

系统对进程所发出的每一个能够满足的资源申请进行动态检查,并根据检查结果决定是否分配资源.

 

 

  1. 控制线程的个数
    1. 使用semphore来控制,acquire来请求一个线程,信号量-1,release()释放一个信号量,信号量+1
    2. 使用线程池executor,new一个CachedThreadPool固定线程;
    3. ThreadPoolExecutor的六个参数:
      • CorePoolsize:核心池大小
      • Maxnumpoolsize最大池大小,工作队列满了之后放这里,满了就会报异常
      • Workqueue:工作队列:核心池放不下的会先放在这里
      •  

 

  • Java基础知识

  1. Java中堆和栈的理解
    1. 堆内存:
      • 用于存储java中的对象和数组,当我们new一个对象或者创建一个数组,就会在堆内存中开辟一块空间;
      • 生存期不必告诉编译器,存取速度较慢;
      • 堆是所有线程共享的一块公共区域,对象都在堆里创建,但为了提升效率,线程会从堆中拷贝一个缓存到自己的栈中;
    2. 栈内存:
      • 主要用于执行程序,比如基本类型的变量和对象的引用变量;
      • 栈数据数据大小和生存期是确定的,存取速度比堆快;
      • 栈是一块和线程相关的内存区域,每个线程都有自己的栈内存,用于存储本地变量,方法参数和栈调用;
  2. JVM运行时的内存分布
    1. 线程共享:
      • 方法区:存放已被加载的类信息,常量,静态常量;
      • 堆:java内存最大的一块,所有对象实例,数组都存放在java堆,gc回收的地方;
    2. 线程私有:
      • 虚拟机栈:java虚拟栈:存放基本数据类型,对象的引用,方法出口等;
      • 本地方法栈
      • 程序计数器
  3. 四种引用:
    1. 强引用:若内存中的对象具有强引用,即时内存不足,抛异常,垃圾回收器也不会回收;如果显示地设置为null,或超出生命周期范围,就可以考虑回收;
    2. 软引用:如果内存空间足够,GC不会回收,内存空间不足,就会回收;如果软引用被gc回收,虚拟机就会把这个软引用加入到与之关联的引用队列中;
    3. 弱引用:gc一旦发现只具有弱引用的对象,就会回收;并把这个弱引用加入到与之相关联的引用队列中;
    4. 虚引用:并不会决定对象的生命周期,且必须和引用队列联合使用;
  4. equals和hashCode的区别:
    1. 两个对象equals,hashCode一定相等
    2. HashCode不相等,一定不equals
  5. String,StringBuffer,StringBuilder的区别
    1. String字符串常量,不可变,每次改变相当于生成一个新的对象;
    2. StringBuilder字符串变量,线程不安全,效率高于StringBuffer;
    3. StringBuffer,字符串变量,线程安全;
  6. 内部类
    1. 成员内部类:位于外部类成员位置的类,可以使用外部类中的成员变量和成员方法;
      • class Outer {
      •     private int age = 20;
      •     //成员位置
      •     class Inner {
      •         public void show() {
      •             System.out.println(age);
      •         }
      •     }
      • }
    2. 局部内部类:定义在在方法和作用域中
      • class Outer {
      •     public void method(){
      •         class Inner {
      •         }
      •     }
      • }
    3. 匿名内部类:无构造方法
    4. 静态内部类:由static修饰的类,不能使用外围类的非static的变量和方法
    5. 作用:每个内部类都能独立地继承一个接口的实现,内部类解决java只能单继承
  7. 静态代理和动态代理的区别:
    1. 静态代理:由程序员创建或者由特定工具自动生成源代码,在程序运行前,代理类的.class文件就存在;
    2. 在程序运行时,运用反射机制动态创建;
  8. Java反射机制
    1. 在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个属性和方法;

 

 

  • 集合

  1. HashMap和HashTable的区别
    1. HashMap支持key和value为null,HashTable不允许,是因为HashMap对null进行了特殊处理,将null的hashCode值定为0
    2. hashMap不是线程安全,HashTable是线程安全的;
    3. hashMap实现线程安全的方式为Collections.synchronized(new HashMap());
    4. HashMap默认长度为16,扩容为原先的2倍,HashTable默认长度为11,扩容为原来的2n+1;
    5. HashMap继承AbstractMap,HashTable继承自Dictionary
  2. HashMap和HashSet的区别
    1. HashMap实现了map接口,hashmap存储键值对;hashset实现了set接口,仅存储对象;
    2. Hashmap使用put()方法添加,使用键对象来计算hashcode值;hashset是使用add()方法将元素放入set中,使用成员对象来计算hashcode值;
    3. Hashmap比较快,因为是使用唯一的键来获取对象,hashset比较慢,因为要比较两个对象是否相同;
  3. Hashmap和Hashset如何判断元素重复
    1. Hashmap可以判断key是否相同,也可以判断value是否相同;
    2. Hashset不能添加重复的元素,当调用add()方法时,首先会计算hashCode值,如果相同就调用equals方法看是否返回true,如果为true说明元素已经存在;
  4. Arraylist和vector的区别
    1. Vector加入了同步机制,线程安全,arraylist线程不安全;
    2. Vector加入了synchronized同步机制,效率相对慢一点;

 

  • 网络相关面试题

  1. TCP/IP五层模型:应用层,传输层,网络层,数据链路层,物理层
  2. 三次握手
    1. 第一次:建立连接时,客户端发送syn包(seq=j)到服务器,并进入SYN_SENT状态,等待服务器确认;
    2. 第二次:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(seq=k);
    3. 客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
    4. 为什么是三次而不是两次:为了数据传输,TCP协议的通信双方都必须维护一个序列号,三次握手的过程即通信双方互相告知序列号起始值的过程;
    5. 握手过程中包中不包括数据,握手完毕之后,才开始传送数据.
  3. 四次挥手
    1. 第一次:客户端发送报文告诉服务器没有数据要发送了;
    2. 第二次:服务器收到,告诉客户端收到
    3. 第三次:服务端向客户端发送报文,请求关闭连接;
    4. 第四次:客户端收到关闭连接的请求,向服务端发送报文,服务端关闭连接;
    5. 为什么不是三次:第二次挥手只是确认客户端的结束报文段,不代表服务端的数据已经传输完毕了,这个传输完毕的时间不确定,可能会使得客户端长时间得不到响应,造成资源浪费
  4. TCP和UDP的区别
    1. TCP面向连接,UDP无连接;
    2. TCP提供可靠的服务,UDP不保证可靠交付;
    3. TCP面向字节流,UDP面向报文;
    4. TCP全双工可靠信道,UDP是不可靠信道;
    5. TCP连接是点到点,UDP是支持一对一,多对多;
  5. http协议
    1. 基于请求和响应模式的无连接,无状态,应用层协议
    2. 简单快速:协议简单,通信速度快;
    3. 灵活:成功传输任意类型的数据对象,由Content-Type标记;
    4. 无连接:每次处理一个请求,处理完成即断开;
    5. 无状态:对事物处理没有记忆功能;
    6. http是应用层的协议,底层基于TCP/IP协议
  6. get和post的区别
    1. Get参数通过url传递,post是在requset body中传递;
    2. Get在url传递的参数有长度限制,post没有;
    3. Get参数会暴露在url上,安全性不如post;
    4. Get只接受ASCII字符,post无限制;
    5. Get回退时无害,post会再次请求;
    6. Get只能进行url编码,post支持多种编码方式;
  7. https
    1. 是http的安全版;
    2. 应用层http和传输层tcp中间加多了一层TLS/SSL加密套件,https就是应用层将数据给到TLS/SSL,然后将数据加密后,再给到TCP进行传输;
  8. 加密算法
    1. 对称加密:加密和解密是同一个密钥;(代表:DES)
    2. 非对称加密:A和B各有两把钥匙,一把公钥,一把私钥,A用B公钥进行加密,B用B的私钥进行解密,反过来一样;(代表:RSA)
  9. Volley
    1. 2.3之前使用HttpClient,之后使用HttpUrlConnection;
    2. 适合进行数据量不大但通信频繁的网络操作;对于大数据量的操作,比如文件下载,表现很糟糕,因为volley会把返回的流导入内存中,下载大文件会发生内存溢出;
    3. Volley执行的过程:默认情况下,volley会开启四个网络调度线程和一个缓存调度线程;首先请求会加入缓存队列,缓存调度线程如果找到该请求的缓存就直接读取该缓存并解析,否则,这个请求就加入网络队列;

 

    1. Volley为什么不适合上传大文件:适合小且频率高的?
      • Volley的网络请求线程池默认大小为4,可以并发4个请求,大于4个,会排在队列中,并发量小所以适合数据小频率高的请求;
      • 下载文件会把流存入内存中,导致内存溢出;
  1. Okhttp
    1. 最大并发量为64
    2. 使用连接池技术,支持5个并发的socket连接,默认keepAlive时间为5分钟,解决tcp握手和挥手的效率问题;
    3. 利用相应缓存避免重复的网络请求;
    4. 请求失败,自动重连;
    5. 支持SPDY协议,是一种基于TCP的应用层协议;
    6. Okhttp的缺点:
      • 消息回来需要切到主线程自己去写;
      • 调用比较复杂,需要进行封装;
      • 缓存失败:为了信息传输的安全性,对请求进行加密.可能导致缓存系统失效;
    7. Okhttp用到哪些设计模式:
      • Builder设计模式,如构建对象OkhttpClient,参数类型相同且很多参数可以为空时,可以用Builder模式完成;
      • 工厂方法模式:如源码中的接口Call;
      • 单例模式
      • 观察者模式如EventListener,监听请求和响应;
      • 策略模式;
      • 责任链模式,如拦截器;
  2. Retrofit
    1. Retrofit底层是基于okhttp实现的,使用运行时注解的方式工作;
    2. 通过java接口与注解来描述网络请求,并用动态代理生成网络请求的request,然后通过client来调用相应的网络框架(默认为okhttp)去发起网络请求,并将返回的response通过converterFactory转换为相应的数据model,最后通过callAdapter转换成其他数据方式;
    3. 流程:
      • 解析注解->配置网络请求参数;
      • 动态代理->生成请求对象;
      • 网络请求适配器->将请求对象进行平台适配;
      • 网络请求执行器->发送请求;
      • 数据转换器->解析返回数据;
      • 回调执行器->切换线程;
      • 主线程->处理结果;
    4. 优点
      • 可以配置不同的http client来实现网络请求,如okhttp,httpclient;
      • 参数注解可以定制;
      • 支持同步,异步,rxjava;
      • 超级解耦;
      • 可以配置不同的反序列化工具来解析数据,如json,xml;

 

  • 性能优化

  1. 图片如何三级缓存?
    1. 三级缓存:加载某张图片时,先去LruCache中寻找图片,没有则去SoftReference中取出图片使用,没有则去文件系统中寻找,没有则去网上加载图片;
    2. Lrucache是存储最近,最后使用的图片,最近最少使用的会被移除出去;当被移除的时候,会将图片添加到softreference中,软引用不会影响系统运行;
  2. Bitmap如何处理大图,如何预防OOM?
    1. 调整图片大小;
      • BitMapFactory的Options参数,通过inSampleSize参数可以对一个图片进行采样缩放;
    2. 及时回收不用的图片资源:将暂时不用的图片recycle();
    3. 缓存图片到内存中:LruCache专门处理图片缓存;
  3. 内存回收机制和GC算法?
    1. 内存判定对象可回收的两种机制:
      • 引用计数算法:被引用计数加一,引用失效,计数减一,计数器为0就是不可以再被使用的,难以解决对象之间相互循环引用;
      • 可达性分析法:通过一系列GCRoots的对象作为起始点,从这些节点向下搜索,剩余的就是不可达的;
    2. GC回收算法:
      • 分代收集算法:根据对象存活周期,将Java堆分为新生代和老年代
        1. 新生代:大批对象死去,只有少量存活,使用复制算法只需复制少量存活对象即可;
          1. 复制算法:把可用内存把容量划分为大小相同的两块,每次只使用其中一块,当这一块内存用尽后,把还存活着的对象复制到另一块,再把这一块内存空间一次清理掉,
        2. 老年代:对象存活率高,使用标记-清理算法或者标记整理算法,只需标记较少的回收对象即可
          1. 标记-清理算法:首先标记出所有需要回收的对象,然后统一清理;但会导致导致大量的内存碎片;
          2. 标记-整理算法:标记出所有需要回收的对象,然后把所有存活的对象整理到一端,不会产生内存碎片;
  4. 内存溢出和内存泄漏的区别:
    1. 内存溢出:程序在申请内存时,没有足够内存空间供其使用,出现out of memory,Android系统为每一个应用申请到的内存有限;
    2. 内存泄漏:指程序在申请内存后,被某个对象一直持有,无法释放已经申请的内存空间;内存泄漏的累积后果很严重;
  5. 内存溢出的原因
    1. 发生地点:堆内存和java虚拟栈中
    2. 堆内存溢出:
      • 生产者消费者模型,
        1. 如注册后忘记注销;
        2. 添加到队列,忘记控制队列大小;
      • 循环引用
        1. Fastjson转json
    3. 栈内存溢出:递归
  6. 内存泄漏的原因
    1. 根本原因:对象的生命存活时间不一致
    2. 发生地点:方法区,堆,java虚拟栈
    3. 单例模式
      • 案例:单例构造方法中传递的context为某一个activity,在activity中调用了单例的构造方法;
      • 泄漏原因:activity生命周期比单例类短,无法释放,导致泄漏;
      • 解决:Context传递应用Application的Context,即单例对象的生命周期和Application一样长,不会出现内存泄漏的问题;
    4. 静态变量导致的内存泄漏:
      • 案例:一个activity定义了一个静态变量;
      • 泄漏原因:静态变量存储在方法区,生命周期从类加载到整个进程结束,一旦静态变量初始化后,他所持有的引用,知道进程结束后才会释放;
      • 解决方法:在onDestroy中将静态变量赋值为null;
    5. 非静态内部类导致的内存泄漏:
      • 案例:
        1. Handler里面调用Activity的方法,Handler会被底层的ThreadLocal绑定,所以生命周期比Activity周期长;
        2. activity中开了一个线程去请求数据,然后退出activity ,因为线程持有activity的引用,导致activity不能及时回收;
      • 泄漏原因:非静态内部类默认会持有外部类的引用,当非静态内部类对象的生命周期比外部类对象的生命周期长时,就会导致内存泄漏;
      • 解决方法:Handler提取出来,引用Activity时用WeakReference包装起来;或者把Handler声明为静态类.因为静态内部类不会持有外部类的引用;
    6. 未取消注册回调导致的内存泄漏
      • 在activity中注册广播,在销毁activity时没有取消注册;
      • 泄漏原因:广播会一直存在系统中,一样持有activity的引用,导致内存泄漏;
      • 解决方法:及时取消注册;
    7. 集合中的对象未清理造成内存泄漏:
      • 案例:一个对象被添加到了集合中,该对象是不能被gc回收的;
    8. 资源未关闭导致内存泄漏:
      • 案例:WebView
    9. 没有有效利用已有的对象,容易造成泄漏,例如,使用过的Bitmap对象没有调用recycle()方法释放内存,构造Adapter没有使用缓存的convertView对象;

 

  1. 哪些工具检测内存泄漏:
    1. DDMS-Heap
      • 实时查看程序中的内存的使用信息;
    2. MAT分析方式
      • Histogram,列出内存中每个对象的名字,数量和大小;
      • Dominator Tree将所有内存中的对象按大小排序;
  2. 如何优化ListView
    1. 复用convertView
      • 当convertView不为空时对其进行复用;
    2. 定义存储控件引用类ViewHolder
      • 在第一次创建时convertView将控件找出,在第二次复用时,直接使用convertView的getTag()获得这些控件;
    3. 数据的分批和分页加载
      • 分批加载
      • 分页加载
    4. 图片的处理
      • 要用WeakReference来存储与获取图片信息;
      • 获取到图片时,对图片进行压缩存在内存中;
      • 在getView()方法中做图片转换时,产生的中间变量要及时释放;

 

  • View相关

  1. View的绘制过程:
    1. OnMeasure,测量视图大小,从顶层父View到子View递归调用measure方法,measure又回调onMeasure();
    2. OnLayout():确定View的位置,进行页面布局;从顶层父View向子View递归调用view.layout方法,即父View根据上一步measure子View所得到的布局大小和布局参数,将子View放在合适的位置上;
    3. onDraw():绘制视图,
      • 绘制背景background.draw(canvas)
      • 绘制自己(onDraw)
      • 绘制children(dispatchDraw)
      • 绘制装饰(onDrawScrollBars)
  2. View事件分发机制:
    1. DispatchTouchEvent:用于进行事件的分发;如果事件能够传递给当前View,那么此方法一定会被调用,返回结果受当前View的onTouchEvent和下级View的dispatchTouchEvent的影响,表示是否消耗当前事件;
    2. onInterceptTouchEvent:在上诉方法内部调用,对事件进行拦截,该方法只在ViewGroup中有;一旦拦截,则执行ViewGroup中的onTouchEvent,在ViewGroup中处理事件,而不接着分发View.
    3. onTouchEvent,在dispatchTouchEvent中调用,用来处理点击事件,返回结果表示是否消耗当前事件;
  3. 如何解决View的事件冲突:
    1. 外部拦截法:点击事件都要经过父容器的拦截处理,如果父容器需要此事件就拦截,否则不拦截,需要重写父容器的onInterceptTouchEvent方法,在内部做出相应的拦截;
    2. 内部拦截法:指父容器不拦截任何事件,将所有事件都传递给子容器,如果子容器需要就直接消耗,否则交由父容器进行处理,需要配合requestDisallowInterceptTouchEvent方法;
  4. Scroller怎么实现View的弹性滑动?
    1. 在MotionEvent.ACTION_UP事件触发时调用startScroll()方法,该方法并没有进行实际的滑动操作,而是记录滑动相关量
    2. 接着调用invalidate/postinvalidate()方法,请求view重绘,导致view.draw方法被执行;
    3. 在view重绘后会在draw方法中调用computeScroll方法,而computeScroll又会向Scroller获取当前的scrollX和scrollY,然后通过scrollTo实现滑动;接着又调用postInvalidate方法进行第二次重绘,和之前一样,反复导致View进行小幅度的滑动;
  5. Invadate和postinvalidate的区别:
    1. Invalidate和postinvalidate都用于刷新View,主要区别是:
    2. invalidate在主线程中调用,若在子线程中需要配合handler;
    3. Postinvalidate可在子线程中直接调用;
  6. SurfaceView和View的区别
    1. View要在UI线程进行刷新,SurfaceView可以在子线程中进行刷新;
    2. View适合于主动刷新,Surface适合于被动刷新,如频繁刷新,而View频繁刷新可能会阻塞主线程;
    3. SurfaceView底层双缓冲机制,而View没有,适合于频繁刷新,刷新时数据处理量很大的页面(如视频播放界面)
  7. 自定义View如何考虑机型适配
    1. 合理使用wrap_content,match_parent
    2. 尽可能使用RelativeLayout
    3. 针对不同的机型,使用不同的布局文件放在对应的目录下,android会自动匹配;
    4. 尽量使用.9图片;
    5. 使用与密度无关的像素单位dp,sp;
    6. 切图的时候切分辨率大的图;

 

  • 进程

  1. 如何开启多进程?
    1. 在AndroidManifest中给四大组件指定属性,android:process开启多进程模式;
    2. 在内存允许的条件下可以开启N个进程;
  2. 为何需要IPC?多进程通信可能出现的问题
    1. 通过开启多进程获取更大内存空间,两个或多个应用之间共享数据
    2. 造成的问题:
      • 静态成员和单例模式完全失效;独立的虚拟机造成;
      • 线程同步机制完全失效;独立的虚拟机造成;
      • SharePreferences的可靠性下降;Sp不支持两个进程并发进行读写,有一定机率导致数据丢失;
      • Application会多次创建;新进程会分配独立的虚拟机;
  3. 进程间通信的方式:
    1. Intent:只能传输Bundle所支持的数据类型,四大组件之间的通信
    2. AIDL:Android中最常用的,使用稍微复杂,需要注意线程同步;
    3. ContentProvider:进程间的大量数据共享,主要对外提供数据的CRUD操作;
    4. Socket:常用于网络通信中,只能传输原始的字节流;
  4. Binder的优点
    1. 传输效率高,可操作性高:传输效率影响因素主要是内存拷贝的次数,Binder只需一次拷贝;
    2. 实现C/S架构方便:Server和Client端独立,稳定性较好;
    3. 安全性较高;

 

  • 其他的补充知识点

  1. Service启动方式
    1. 通过startService(),会经历onCreate->onStartCommand,stopService的时候会调用onDestroy,如果是调用者直接退出而没有调用stopService时,Service会一直在后台运行,下次调用者起来仍然可以stopService;
    2. 通过bindService,Service只会运行onCreate,这个时候调用者和Service绑定在一起,调用者退出Service也会onUnbind->onDestroy;
    3. 如果先是start,然后bind时就直接onbind
    4. 如果先是bind,再start,就直接startCommand
    5. 两个同时用,需要先onUnbind -> 然后StopService();
  2. EventBus的理解
    1. EventBus是一款Android优化的发布/订阅消息总线,它简化了组件和组件,组件和后台线程间的通信
    2. 三个元素:
      • Event:事件
      • Subscriber:事件订阅者,接收特定的Event事件
      • Publisher:事件发布者,同于通知Subscriber有事件发生
    3. 实现了观察者设计模式,优点为代码简洁优雅,使用方便,开销小,有助于代码解耦;
  3. 生命周期的执行:
    1. Activity A到Activity B的:A的onPause() -> B 的onCreate() -> onStart() -> onResume() -> A的onStop();
    2. 从B返回A:B的onPause() -> A的onRestart() -> onStart() -> onResume() -> B的onStop() -> onDestroy();
    3. 退出A : onPause() -> onStop() ->onDestroy();
  4. 属性动画(Property Animation)和(Tween Animation)的区别:
    1. 补间动画只是改变View的显示效果,不会真正改变View的属性;属性动画会改变View的实际属性值;
    2. 补间动画只是针对于View,属性动画可以不作于View;
  5. ListView和RecyclerView的区别;
    1. ViewHolder:ListView中ViewHolder需要自己定义,也可以不定义;recyclerview中viewholder必须定义;
    2. LayoutManager:R提供了根据了更加丰富的布局管理,包括linearlayoutmanager支持水平和竖直方向;StaggeredGridLayoutManager支持瀑布流;GridLayoutManager支持网格展示;
    3. ItemAnimator:R中用于添加,删除,移动的动画效果;
    4. ItemDecoration:R默认不加间隔符,使用这个来实现间隔符;
  6. Java中的集中变量修饰符
    1. Public修饰的变量为公有变量,如果公有变量又在一个共有类中,这个变量可以被所有包中的所有类访问;
    2. Protected保护访问修饰符:若在一个共有类中,可以被所在类本身,同一个包中的所有类,其他包中该类的子类;
    3. 默认访问修饰符:同一个包中的所有类;
    4. Private:所在类访问;

 

  1. ANR
    1. Application Not Responsing ,即应用无响应;
    2. 原因:
      • 当前的事件没有机会得到处理;
      • 当前事件正在处理,没有及时完成;
    3. 案例:
      • 点击事件或者键盘输入事件在5s内无法得到响应;
      • BroadCastReceiver的onReceive()函数运行在主线程中,10s内无法得到处理;
      • 前台服务在20s无法完成处理,后台服务200s内没有执行完毕;
      • ContentProvider的publish在10s内没有完毕;
    4. 解决:
      • 把io操作放在工作线程中处理,减少耗时操作和错误操作,如网络请求,数据库操作,Socket操作,文件读写等放在子线程;

 

  1. 主线程的Looper.loop()一直无限循环为什么不会造成ANR?
    1. looper.loop() 不断地接收事件、处理事件,每一个点击触摸或者说Activity的生命周期都是运行在 Looper.loop() 的控制之下,如果它停止了,应用也就停止了。只能是某一个消息或者说对消息的处理阻塞了 Looper.loop();也就说我们的代码其实就是在这个循环里面去执行的,当然不会阻塞了。

 

  1. 一个线程中有几个Handler,几个Looper,几个MessageQueue
    1. 一个线程只能有一个Looper,因为Thread中使用ThreadLocal<Looper>去存储当前线程的Looper,ThreadLocal底层是hashmap,key值是线程本身,所以只有一个looper;
    2. 一个messageQueue,因为一个线程只有一个looper
    3. 一个线程可以存在多个handler,可以在发送消息的时候使用message.setTarget(handler)来设置当前message的target handler;

 

 

  1. 数据库如何高效插入数据
    1. 在try中开启事务
    2. 在finally中关闭事务
  2. Activity的状态保存和恢复
    1. 保存:当Activity A 在前台时,按下home键,切换其他的应用,横竖屏切换,系统会调用onSavedInstanceState()方法;
    2. 恢复:onRestoreInstanceState()
  3. Fragment的add和remove
    1. Remove是移除fragment,当fragment不加入back stack时,remove会一直调用到onDetach,加入到back stack时,会走到onDestroyView;
    2. 当fragment被另一个fragment调用replace时,并且压入back stack时,View会销毁,fragment本身没有销毁,也就是调用到了ondestroyView();
    3. Fragment的oncreateview多次执行,需要用一个rootView记录一下oncreateView返回的View,下一次调用判断一下rootView是否为null
  4. Fragment之间数据的传递
      •  MainFragment mainFragment =
      •            (MainFragment) getActivity()
      •            .getSupportFragmentManager()
      •            .findFragmentByTag("mainFragment");//另一个fragment的tag
    1. 接口回调
    2. EventBus
      • 引入EventBus:添加依赖
      • 注册事件接收者,在接收Fragment中进行注册EventBus.getDefault().register(this);
      • 发送事件: EventBus.getDefault().post(mDatas.get(position));
      • 定义事件类型
      • 接收事件并处理:
        1. @Subscribe
        2. public void onEvent(String data) {
        3.     bt_main.setText(data);
        4. }
      • 注销事件接收:
        1. @Override
        2. public void onDestroy() {
        3.     super.onDestroy();
        4.     EventBus.getDefault().unregister(this);
        5. }
  5. Activity与Service之间
    1. 通过startService()来启动服务,不过不好掌控
    2. 使用bindService(),就可以在ServiceConnection中获取Service的实例,就实现了activity中获取到service的实例对象,就可以调用service方法;
  6. 对ContentProvider的理解
    1. 结构化方式存放的数据,以相对安全的方式来封装数据并且提供简易的处理机制,提供了不同进程间数据交互的标准化接口;
  7. 说说ContentProvider、ContentResolver、ContentObserver 之间的关系
    1. ContentProvider是内容提供者,对其他应用进行数据共享,其他应用可以通过ContentProvider对你应用中的数据进行增删查改;
    2. ContentResolver内容解析者,作用是按照一定的规则对内容提供者的数据解析;
    3. ContentObserver:内容观察者,目的是观察特定uri引起的数据库的变化,既而做一些相应的处理,类似于数据库中的触发器;
  8. 描述一下BroadcastReceiver的理解
    1. 使用了设计模式中的观察者模式,基于消息的发布/订阅事件模型,将广播的发送者和接收者解耦;
    2. 注册的方式:
      • 静态注册:在AM中注册,不受任何组件的生命周期影响;
      • 动态注册:组件结束前,必须注销;
      • 最好在onresume中注册,onpause中注销,否则造成内存泄漏;
    3. 方式广播:intent通过sendBroadCast发送出去;
    4. 类型:
      • 普通广播:开发者自身定义intent的广播
      • 系统广播:涉及到手机的开机,网络变化,拍照等都会相应的广播;
      • 有序广播:广播接收者按照先后顺序来接收;按照priority属性大小排序,相同的话就是动态注册的优先;
      • 本地广播:安全性高,效率高;exported设置为false,非本应用的广播不接收,指定该广播接收器所在的包名,使用封装好的LocalBroadcastManager使用方式与全局广播相同,只是注册和注销的context变为了LocalBroadcastManager的实例;
  9. 如何导入已有的数据库
    1. 把源数据库放入res目录下,建立一个dbmanager,然后通过FileinputStream读取数据库,再用FileOutputStream把读取到的数据写入到data/data/包名下;
  10. LinearLayout和RelativeLayout的区别:
    1. RelativeLayout比LinearLayout慢是因为会让子View调用两次measure过程,而LinearLayout只需要一次,如果LinearLayout有weight属性时,也需要两次;
    2. 不影响层级深度的情况下,使用LinearLayout和FrameLayout而不是Relat..;
    3. 尽量使用padding而不是margin
  11. 屏幕适配的知识点
    1. 屏幕尺寸:指对角线的长度,单位是英寸,一英寸=2.54厘米
    2. 屏幕分辨率:1px=1像素,屏幕分辨率越高,显示效果越好;
    3. 屏幕像素密度:每英寸的像素点数;计算公式为竖的分辨率的平方+横的分辨率的平方开根号然后除以英寸数;
    4. 常见的
      • Mdpi:像素密度:120-160   hdpi 160 -240 xhdpi 240 -320  xxhdpi 320 - 480 xxxdpi 480-640
    5. 常用适配方案:
      • 使用wrap_content,match,weight
      • 自定义像素适配,以美工的设计尺寸为原始尺寸,根据不同设备的密度来计算出宽和高;
      • 百分比适配
  12. 插值器和估值器
    1. 插值器:Interpolator:一个接口,设置属性值从初始值到结束值的变化规律,
      • 在动画效果的XML代码中设置插值器属性
      • 在java代码中设置
    2. 估值器:TypeEvaluator,一个接口,插值器决定值的变化规律,即决定的是变化趋势,接下来的具体变化数值交给估值器;
      • 属性动画特有的属性;
      • 协助插值器实现非线性运动的动画效果;
  13. Android的数据存储方式
    1. SharePreference,SQLite,Content Provider,File,网络存储
    2. SharePreference本质是一个xml文件,存储一些参数设置
    3. SQLite轻量级的数据库支持SQL语法,Android还提供了一个SQLIteDataBaseHelper的类,提供了一些操作数据库的api;
    4. Content Provider 实现数据共享;
    5. FIle: IO存储方式,用于存储数量大的数据,更新数据很难;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章