关于 Android 的面试题

3,624 阅读26分钟
原文链接: www.jianshu.com

最近从成都来深圳了。目前在深圳这边找工作。作为一个Android开发者,来深圳这边找工作给我的感觉就是好难。可能是市场的原因,随着Android技术的成熟,各大培训机构的加入,给市场带来了不小的冲击。面试官所问的问题也越来越有难度。
在这边也面试了几家公司了,发现面试的问题很多都跟实际开发是两码事儿。是得准备一下才行,不然在这边都找不到工作了。 这两天搜罗了一些面试题,做了一下整理。这些东西可以给准备做Android的萌新一些参考。各位大牛就看个乐呵,其中有不对的地方忘指出。

正文:

最新整理面试题

1.简述synchronized,object,Monitor机制.

synchronized主要是用来进行同步操作,能帮助我们设计更安全的多线程程序。

1)当两个并发线程访问同一个对象object中的synchronized同步代码块时,一个时间内只能有一个线程得到执行。另一个线程只能等到当前线程执行完这个代码块才能执行该代码块。
2)当一个线程访问object的一个synchronized同步代码块时,其他线程对object中所有其它synchronized同步代码块的访问将被阻塞。
3)一个线程访问object中的synchronized代码块时,另外一个线程仍然可以访问object中的其它代码。

  • 对于普通的方法同步,锁是当前实例对象。
  • 对于静态方法同步,锁是当前类的class对象。
  • 对于方法块同步,锁是synchronized括号里的对象。

monitor是一个同步工具,相当于操作系统中的互斥量。它内置于每一个object对象中,相当于一个许可证。拿到许可证即可以进行操作,没有拿到需要阻塞等待。
使用synchronized时,其实是通过锁对象的monitor的取用与释放来实现的。

2.简述happen-before规则。

happen-before其实就是一个保证而已,它保证一套语句对内存的写操作对于另一条语句是可见的。

规则

1)程序顺序规则:一个线程中的每个操作,happen-befor于该线程中的任意后续操作。
2)监视锁规则:对一个监视器锁的解锁,happen-before于随后对这个监视器锁的加锁。
3)volatileb变量规则:对于一个volatile域的写,happen-before于任意后续对这个volatile域的读。
4)传递性:如果A happen-before B ,且B happen-before C,那么A happen-before C。

3.简述AQS。

AQS全称为(Abstract Queued Synchronizer),这个类是在java.util.concurrent.locks下面。
AQS的核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个就是AQS机制。

4.jvm运行时数据区域有哪几部分组成,各自作用。

jvm运行时数据区通常包括:程序计数器(Program Counter Register),Java栈(VM Stack),本地方法栈(Native Method Stack),方法区(Method Area),堆(Heap)。
作用:

  • 栈: 栈中存放的是一个个的栈帧,每个栈帧对应一个被调用的方法,在栈帧中包括局部变量表,操作数栈,指向当前方法所属的类的运行时常量池的引用,方法返回地址和一些额外的附加信息。当线程执行一个方法时,就会随之创建一个对应的栈帧必定位于java栈的顶部。

  • 堆:在java中堆是用来存储对象本身的以及数组(数组引用是存放在java栈中的),在java中程序员不用去关心空间释放的问题,java的垃圾回收机制会自动进行处理。因此这部分空间也是java垃圾收集管理的主要区域。堆是被所有线程共享的,在jvm中只有一个堆。

  • 方法区:方法区在jvm中也是一个非常重要的区域,它与堆一样,是被线程共享的区域。在方法区中,存储了每个类的信息(包括类的名称,方法信息,字段信息),静态变量,常量以及编译器编译后的代码等。还有一项信息是常量池,用来存储编译期间生成的字面量和符号引用。
  • 本地方法栈:为使用到的本地操作系统方法服务。
  • 程序计数器:一块较小的内存空间,它是当前线程所执行的字节码的行号指示器。字节码解释器工作时通过改变该计数器的值来选择下一条要执行的字节码指令。分支,跳转,循环等功能都依靠它来实现。每条线程都有一个独立的计数器,它们之间互不影响,因此该区域是线程私有的。

5.gc算法有哪些,gc收集器有哪些

gc是java的垃圾回收机制

  • 引用计数法(Reference Counting Collector):使用计数器来区分存活对象和不再使用的对象。一般来说,堆中的每个对象对应一个引用计数器。当每一次创建一个对象并赋给一个变量时,引用计数器置为1。当对象被赋给任意变量时,引用计数器每次加1当对象出了作用域后(该对象丢弃不再使用),引用计数器减1,一旦引用计数器为0,对象就满足了垃圾收集的条件。
  • 标记算法(Tracing Collector):使用了根集的概念,基于tracing算法的垃圾收集器从根集开始扫描,识别出哪些对象可达,哪些对象不可达,并用某种方式标记可达对象。
  • 整理算法(Compacting Collecotr):该算法会将所有的对象移到堆的一端。能解决堆碎片的问题。
  • 复制算法:将内存分为两个区域(from space 和 to space)。所有的对象都分配到from space。清理时先将所有标为活动对象copy到to space,然后清除from space空间。然后互换from space和to apce的身份,每次清理都重复上述过程。

gc收集器:

  • serial收集器:单线程,工作时必须暂停其他工作线程,多用于client机器上,使用复制算法。
  • ParNew收集器:serial的多线程版本,server模式下jvm首选的新生代收集器。复制算法。
  • Parallel Scavenge收集器:可控制吞吐量的收集器,吞吐量指有效运行时间。复制算法。
  • Serial Old收集器:serial的老年代版本,使用整理算法。
  • Parallel Old收集器:Parallel Scavenge收集器的老版本,多线程,标记整理。
  • CMS收集器:整理算法。最短回收停顿时间,缺点是产生碎片。
  • GI收集器:基本思想是化整为零,将堆分为多个Region,优先回收价值最大的Region。并行并发,分代收集,空间整合。整理算法。

6.简述class加载各阶段,Class Loader有哪些模型。

在java应用程序开发中,只有被jvm装载的class类型才能在程序中使用。只要生成的字节码符合jvm指令集和文件格式,就可以在jvm上运行,这为java的跨平台性提供条件。
字节码的装载过程分为3个阶段:加载,连接,初始化。其中连接包括3个步骤(验证,准备,解析)。
1)加载:处于class装载的第一个阶段,这时jvm必须完成通过类的全面获取类的二进制数据流,解析类的二进制数据流为方法区内的数据结构,创建java.lang.Class的实例。

2)连接:

  • 验证:验证字节码文件,保证加载的字节码是符合规范的。
  • 准备:正式为类变量(static修饰的变量)分配内存,并设置内变量的初始值,这些内存都将在方法区进行分配(不包括实例变量)。
  • 解析:将类,接口,方法和字段的应用转为直接引用。

3)初始化:如果前面的步骤都没有出现问题,那么表示类可以顺利的装载到系统中。这个时候才会执行java字节码。初始化阶段的主要工作是执行类的初始化方法。

Class Loader是一个对象,主要是对类的请求提供服务,当jvm需要某个类是它根据名称向ClassLoader请求这个类,然后ClassLoader返回这个类的class对象。
1)引导类加载器(Bootstrap Class Loader):它用来加载java的核心库,使用源生代码来实现的。
2)扩展类加载器(Extensions Class Loader):用来加载java的扩展库。jvm的实现会提供一个扩展库目录,该类加载器在目录中寻找并加载java类。
3)系统类加载器(System Class Loader):一般来讲,java应用的类都是由它加载完成的,是根据java的应用类路径来加载java类。

7.简述常用的JDK命令行以及工具。

jak常用工具在安装目录的bin目录下。
工具

--编译工具:javac
--运行工具:java
--压缩工具:jar
--文档生成工具:javadoc
--字符编码转换工具:native2ascii

命令
1,编译命令
编译一个文件:在目录下面输入javac a.java(java文件名),编译多个文件中间用空格隔开;javac -d cls a.java是讲编译的文件放到同目录的cls文件夹中。
2,运行命令
java my.hello运行my包下面的hello编译文件。
java -calsspath cl my.java
3,压缩命令
jar -cvf:压缩 //jar -cvf hello.jar a.class b.class c.class
jar -xvf:解压 //jar -xvf cl\hello.jar 解压cl目录下的hello.jar
4,文档生成工具 (将源文件中的doc帮助文件生成HTML文档)
javadoc hello.java
javadoc -d java\doc -version -author lcz\src\lcz.java
5.字符编码转换工具
native2ascii -encoding 编码 源文件名字 改变后的名字

8.简述字节码文件的组成

1,Class字节码中有两种数据类型:
字节数据直接量:这是基本的数据类型。共细分为u1、u2、u4、u8四种,分别代表连续的1个字节、2个字节、4个字节、8个字节组成的整体数据
表:是有多个基本数据或者其它表,按照规定顺序组成的大的数据集合。
2, 具体详解请参考www.blogjava.net/DLevin/arch… (我也不懂 ,自己去看大神写的)。

9,如何实现一个定时调度和循环调度。

1,使用Timer: Timer myTimer = new Timer(); myTimer.schedule(new Worker(),1000);//1秒后执行。
myTimer.scheduleAtFixedRate(new Worker(),5000,1000);//5秒后执行第一次,之后每隔1秒执行一次。

10,多线程,如何实现一个ThreadLoacl。

ThreadLoacl,线程安全类。关于线程安全除了synchronized关键字,还有另一种方法就是ThreadLoacl,它通过为每个线程提供一个独立的变量副本,解决了变量并发访问的冲突问题。ThreadLocal比直接使用synchronized更方便,简单并且结果程序拥有更高的并发性。
java代码

1.private static final ThreadLocal threadSession = new ThreadLocal();    
2.    
3.public static Session getSession() throws InfrastructureException {    
4.    Session s = (Session) threadSession.get();    
5.    try {    
6.        if (s == null) {    
7.            s = getSessionFactory().openSession();    
8.            threadSession.set(s);    
9.        }    
10.    } catch (HibernateException ex) {    
11.        throw new InfrastructureException(ex);    
12.    }    
13.    return s;    
14.}

可以看到在getSession()方法中,首先判断当前线程中有没有放进去session,如果还没有,那么通过sessionFactory().openSession()来创建一个session,再将session set到线程中,实际是放到当前线程的ThreadLocalMap这个map中,这时,对于这个session的唯一引用就是当前线程中的那个ThreadLocalMap,而threadSession作为这个值的key,要取得这个session可以通过threadSession.get()来得到,里面执行的操作实际是先取得当前线程中的ThreadLocalMap,然后将threadSession作为key将对应的值取出。

11,说说你了解的一个线程安全队列。

分为BlockingQueQue(阻塞队列)和 ConcurrentLinkedQueQue(非阻塞队列)。
以BlockingQueQue为例,BlockingQueQue作为线程容器,可以为线程同步提供有力的保障。
BlockingQueQue有两种实现:
1)基于数组的阻塞队列实现ArrayBlockingQueQue,内部维护了一个定长数组,以便缓存队列中的数据对象。
2)基于链表的阻塞队列LinkedBlockingAQueQue,它内部维护了一个链表结构的数据缓冲队列。
BlockingQueQue按照FIFO(先进先出)排列元素。
注意:1,必须要使用take()方法在获取时达成阻塞结果。
2,使用poll()方法将产生非阻塞效果。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;



public class BlockingDeque {
//阻塞队列,FIFO
private static LinkedBlockingQueue<Integer> concurrentLinkedQueue = new LinkedBlockingQueue<Integer>(); 


 public static void main(String[] args) {  
 ExecutorService executorService = Executors.newFixedThreadPool(2);  

 executorService.submit(new Producer("producer1"));  
 executorService.submit(new Producer("producer2"));  
 executorService.submit(new Producer("producer3"));  
 executorService.submit(new Consumer("consumer1"));  
 executorService.submit(new Consumer("consumer2"));  
 executorService.submit(new Consumer("consumer3"));  

 }  

 static class Producer implements Runnable {  
 private String name;  

 public Producer(String name) {  
     this.name = name;  
 }  

 public void run() {  
     for (int i = 1; i < 10; ++i) {  
         System.out.println(name+ "  生产: " + i);  
         //concurrentLinkedQueue.add(i);  
         try {
            concurrentLinkedQueue.put(i);
            Thread.sleep(200); //模拟慢速的生产,产生阻塞的效果
            } catch (InterruptedException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
            }

         }  
     }  
 }  

 static class Consumer implements Runnable {  
 private String name;  

 public Consumer(String name) {  
     this.name = name;  
 }  
 public void run() {  
     for (int i = 1; i < 10; ++i) {  
         try {          
                //必须要使用take()方法在获取的时候阻塞
                  System.out.println(name+"消费: " +  concurrentLinkedQueue.take());  
                  //使用poll()方法 将产生非阻塞效果
                  //System.out.println(name+"消费: " +  concurrentLinkedQueue.poll());  

                 //还有一个超时的用法,队列空时,指定阻塞时间后返回,不会一直阻塞
                 //但有一个疑问,既然可以不阻塞,为啥还叫阻塞队列?
                //System.out.println(name+" Consumer " +  concurrentLinkedQueue.poll(300, TimeUnit.MILLISECONDS));                    
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }  

     }  
     }  
 }  
}

Android基础面试题

1,什么是ANR,如何避免它。

在Android设备上,如果你的应用程序有一段时间响应不够灵敏,当响应停顿的事件达到一定的值,系统会向用户显示一个对话框,这个对话框叫做应用程序无响应(Application Not Responding)对话框,用户可以选择让程序停止或者继续运行。不同的组件发生ANR的时间不一样,主线程是5s(Activity,Service),BroadCastReceiver是10秒。
解决方法:
将所有的耗时操作,比如访问网络,Socket通信,查询,复杂逻辑运算等都放到子线程中去执行,让后通过handler.sendMessage,runonUIThread,AsyncTask等方式更新UI,确保用户操作界面的流畅度。如果耗时操作需要用户等待,可以在界面上显示进度条。

2,View的绘制流程。

View的绘制是从ViewRoot的performTraversals()方法开始的,经过measure,layout,draw这个3大步骤。

measure的过程:

measure过程是对整个view树的所有控件计算宽高
measure是冲ViewRoot类中的host.measure开始的,内部调用的是View的measure(int widthMeasureSpec,int heightMeasureSpec)方法,measure方法里面调用了onMeasure(int widthMeasureSpec,int heightMeasureSpec)方法,方法中的两个参数都是是MeasureSpec类型(指父控件对子控件宽高的期望值,它是一个32位的int类型数,前两位表示测量模式,后30位表示测量大小)
测量模式一共有3种:
1)EXACTLY 精确测量模式,xml文件中写200dp,march_parent等代表使用该模式,
2)AT_MOST 最大模式,xml文件中写wrap_content表示使用该模式。
3)UNSPECIFIED 无限大测量模式,只有在绘制特定自定义View时才用的到这个模式。
真正代表测量结束的方法是setMeasuredDimension方法,该方法传入的两个参数是宽高的SpecSize。测量结束后我们可以通过getMeasureHeight和getMeasureWidth来获取测量宽高。
自定义ViewGroup一定要重写onMeasure方法,用于测量子View的宽高,不重写的话子View没有宽高。
自定义View如果在xml中使用了wrap_content属性,就需要重写onMeasure方法来设置wrap_content的默认大小,不然会显示出match_parent的效果。

layout的过程:

ViewGroup用来将子View放在合适的位置上。
layout是从ViewRoot类中的host.layout开始的,内部调用的是ViewGroup的layout方法。在ViewGroup的layout方法中,先调用setFrame来确定自己的左上右下的位置,再调用onLayout来确定子View的位置。
自定义ViewGroup一定要重写layout方法来确定子View的位置,自定义View一般不需要重写该方法,它的位置是右父控件确定的。

draw过程:

此过程是真正将内容展示在屏幕上让我们能够看到的过程。
draw是从ViewRoot类中的host.draw开始的,内部调用的是View的draw方法。
draw的步骤:
1)绘制背景。
2)绘制内容,也就是调用onDraw方法。
3)绘制子View,调用的是dispatchDraw方法。
4)绘制装饰,如listview的滚动条等。

对于View的绘制过程,既可以说是简单的,也可以说是复杂的,简单的在于Google已经帮我们将draw框架写好了,我们在自定义ViewGroup时不用管draw过程,只需要实现measure和layout过程。复杂在于,我们写继承View的自定义控件的时候需要重写onDraw方法,这样才能绘制出你自定义的View的内容,onDraw(Canvas canvas)方法中最重要的两个东西是Paint和Canvas,这个使用起来算是比较复杂的。

3,自定义View如何考虑机型适配

这里要考虑的是屏幕的问题:

  • 合理使用warp_content,match_parent.
  • 尽可能的是使用RelativeLayout
  • 针对不同的机型,使用不同的布局文件放在对应的目录下,android会自动匹配。
  • 尽量使用点9图片。
  • 使用与密度无关的像素单位dp,sp
  • 引入android的百分比布局。
  • 切图的时候切大分辨率的图,应用到布局当中。在小分辨率的手机上也会有很好的显示效果。

4,Touch事件分发机制

一个完整的touch事件,由一个down事件,多个move事件,一个up事件组成。
Touch事件的一般传递流程Activity-->window(唯一实现类PhoneWindow)-->顶级View(DecorView)-->ViewGroup-->View
监听Touch事件有两种方式,setOnTouchListener和直接重写三个方法:dispatchTouchEvent,onInterceptTouchEvent,onTouchEvent。
使用setOnTouchListenre方式设置监听事件,该方式的优先级较高,如果在onTouchListener的onTouch方法中return true的话,那么onTouchEvent方法是接收不到该Touch事件的。而且因为onClickListener中的onClick方法实际上是在onTouchEvent中被调用的,所以Touch事件走不到onTouchEvent,Touch事件就不会生效。

直接重写三个方法:
dispatchTouchEvent:该方法是对点击事件的分发,在这个方法中我们一般return super.dispatchTouchEvent,讲该事件分发下去。
onInterceptTouchEvent:该方法表示对Touch事件进行拦截,这是ViewGroup特有的方法,View没有。在ViewGroup中如果onInterceptTouchEvent返回true,表示将该事件拦截,那么事件将传递给ViewGroup的onTouchEvent方法处理。如果onInterceptTouchEvent方法返回true,事件将传递个子View的dispatchTouchEvent进行分发。
onTouchEvent:该方法表示对Touch事件进行消费,返回true表现消费,返回false表示不消费,那么该事件该事件将传递给父控件的onTouchEvent处理。

5,Art和Dalvik的区别

在程序运行过程中Dalvik虚拟机不断的进行将字节码转换为机器码的工作。
而Art引入了AOT这种预编译技术,在应用程序的安装过程中已经将所有的字节码编译为了机器码,在运行的时候直接调用。Art极大的提高了程序的运行效率,同时减少了手机的耗电量,在垃圾回收机制上也有很大的优化,但是Art模式下应用程序的安装需要消耗更多的时间,同时也需要跟多的安装空间。
Dalvik 是Android4.4及以下平台的虚拟机。
Art 是在Android4.4以上平台使用的虚拟机。

6,ddms和traceView的区别

1, ddms:是android开发环境中的dalvik虚拟机调试监控服务;
ddms能够提供,测试设备截屏,针对特定的进程查看正在运行的线程以及堆信息,Logcat,广播状态信息,模拟电话呼叫,接收sms,虚拟地理坐标等。
2,traceView是android平台配备的性能分析的工具;它可以通过图形化让我们了解要跟踪的程序的性能,并且能具体到方法。
区别:ddms是一个程序执行查看器,在里面可以看见线程和堆栈等信息,traceView是程序性能分析器。

7,四大组件及生命周期。

1)Activity:常用的生命周期方法有 ;
onCreat->onStart->onResume->onPuase->onStop->onDestroy
当已经存在的Activity由不可见到可见会走onRestart方法。
意外销毁时会走onSaveInstanceState方法结束,由意外销毁恢复是会走onRestoreInstanceState方法。
2)BroadcastReceive:在接收到广播是执行onReceive方法在该方法返回结果后结束,如果在onReceive中做了超过10s的事情就会报ANR。
3)Service:

  • 当start一个Service时的生命周期方法:
    onCreate->onStart->onDestroy
  • 当bind一个Serive时的生命周期方法:
    onCreate->onBind->onUnbind->onDestroy
    4)ContentProvider:在Android平台上多个应用程序共享数据时会用到。 在访问ContentProvider的时候,可以通过Activity或者Context的getContentPresolver来访问。每一个ContentProvider都有一个唯一的URI来识别。
    其中的方法有:query,insert,update,delete;
    ContentProvider在接收到ContentResolver发出的请求后被激活,也仅在响应ContentResolver提出的请求是激活,我们普遍认为它在请求完毕后关闭。

8,对AIDL的理解

AIDL:android interface definition language的缩写。
AIDL是用来实现进程间通信的,可以帮我们实现发布以及调用远程服务。
使用:
1)服务端:创建一个Service用来监听客户端的连接请求,然后创建一个AIDL文件,将服务端暴露给客户端的接口在这个文件中声明,最后在Service中实现这个AIDL接口。
2)客户端:首先绑定服务端的Service,绑定成功后将服务端返回的Binder对象转成AIDL接口所属的类型,接着就可以调用AIDL中的方法。

9,简单叙述okhttp的实现原理

Android中网络请求框架比较多,常用的有HttpURLConnention,Volley,okHttp,Retrofit,RxJava等,从Android4.4开始HttpURLConnection底层实现采用的是okhttp。
进行通信的原理主要是通过dispatcher不断从requestQueue中取出请求call,根据是否已经缓存调用Cache或NetWork这两类数据获取接口之一,从内存缓存或者服务器中获取请求的数据。分为同步和异步请求,同步请求通过call.execute()直接返回当前的response,而异步请求会将当前的call.enqueue添加到请求队列中,通过回调的方式来获取最后的结果。

10,如何保证一个后台服务不被杀死,比较省电的方式是什么。

Android中通过Service实现后台任务。
方法一:

通过将Service绑定到Notification,成为一个前提服务,可以提高存活率在Service中创建一个Notification,再调用Service.startForeground(int id,Notification notification)方法运行在前台即可。这个方式使用360等如阿健管家可以杀死。

方法二:
通过定时警报来不断启动Service,这样就算Service被杀死,也能再启动。同时也可以监听网络切换,开锁屏等广播启动Service。
参考:
Intent intent = new Intent(mContext,MyService.class);
PendingIntent sender = PendingIntent.getService(mContext,0,intent,0);
AlarmManager alarm = (AlarmManager)getSystemService(ALARM_SERVICE);
alarm.setRepeating(AlarmManager.RTC_WAKEUP,System.currentTimeMillis(),5*10000,sender);
这种方式不断启动的逻辑处理起来很麻烦。

方法三:
通过jni调用c,在c语音中启动一个进程fork()。 可以保证360等手机管家不会清理。但是带来了jni交互,稍微有点麻烦。

保证一个后台不被杀死的方法还要很多,对于那种方法比较省电我就不知道了。还需要继续学习。

11,请介绍一下ContentProvider是如何实现数据共享的。

Android提供了ContentProvider,一个程序可以通过实现一个ContentProvider的抽象接口将自己的数据完全暴露出去,而且ContentProvicer是以类似数据库中表的方式将数据暴露。也就是说ContentProvider就像一个“数据库”。那么外界获取其提供的数据,也就应该与从数据库中获取数据的操作基本一样,只不过是采用URI来表示外界需要访问的“数据库”。外部访问通过ContentResolver去访问并操作这些被暴露的数据。

12,Handler机制。

Handler包括四个角色:

  • Handler:负责发送消息处理消息。
  • Message:消息实体对象,handler通过sendMessage将实体放到消息队列中。
  • MessageQueQue:存放消息的队列。
  • Looper:消息轮询器,不停的从消息队列中取出消息交给handler处理。

在主线程创建Handler,在需要发送消息的地方创建一个Message,通过handler发送。这个消息回到MessageQueQue中,然后Looper会将这个消息取出交给handler处理。

Handler可以有多个,但是在同一线程中Looper和MessageQueQue只能有一个。

13,Binder机制

Binder包含四个角色:

  • Server 服务器
  • Client 客户终端 ,获得实名Binder的引用。Server向ServiceManger注册了Binder实体及名字后,Client就可以通过名字获得该Binder的引用。例如我们申请获得名字叫张三的Binder的引用,ServiceManager收到这个连接请求,从请求数据包里获得Binder的名字。再找到该名字对应的条目,从条目中取出Binder的引用。将该引用作为回复发送给发起请求的Client。
  • ServiceManager 域名服务器(DNS),负责将字符形式的Binder名字转化成Client中对该Binder的应用,使得Client能通过Binder名字获得Server中Binder实体的引用。
  • Binder驱动 可以理解为路由器。Binder驱动负责进程之间Binder通信的建立,Binder在进程间的传递。

Binder使用Client-Server通信方式,安全性好,简单高效。再加上其面向对象的设计思想,独特的接收缓存管理和线程池管理方式,成为Android进程间通信的中流砥柱。

14,ListView中图片错位的原理,和解决方法。

错位原理: 如果我们只是简单的显示数据,没有convertView的复用和异步操作,就不会产生图片错位。重用convertView但没有异步操作也不会有错位现象。例如我们的listView中刚好显示7个item,当向下滑动时,显示出item8,而item8是重用的item1,如果此时异步网络请求item8的图片,比item1的图片慢,那么item8就会显示item1的图片。当item8下载完成,此时用户向上滑显示item1时,又复用的item8的image。这样就导致的图片错位。
解决方法: 对imageview设置tag,并预设一张图片。向下滑动后,item8显示,item1隐藏。但由于item1是第一次进来就显示所以一般情况下,item1都会比item8先下载完,此时可见的item8的tag和隐藏了的item1的url不匹配,所以就算item1的图片下载完也不会显示到item8中,因为tag标识的永远是可见图片中的url
holder.img.setTag(imgUrl);
holder.img.setImageResource(R.drawable.ic_launcher);
if(imageView.getTag() != null && imageView.getTag().equals(imageUrl)){ imageView.setImageBitmap(result);}

15,在manifest 和代码中如何注册和使用BroadcastReceiver;

<receiver android:name="包名.自己扩展的广播接收者名">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>

16,ApplicationContext和ActivityContext的区别;

这是两种不同的context,也是最常见的两种.第一种中context的生命周期与Application的生命周期相关的,context随着Application的销毁而销毁,伴随application的一生,与activity的生命周期无关.第二种中的context跟Activity的生命周期是相关的,但是对一个Application来说,Activity可以销毁几次,那么属于Activity的context就会销毁多次.至于用哪种context,得看应用场景,个人感觉用Activity的context好一点,不过也有的时候必须使用Application的context.application context

17,Serializable 和Parcelable 的区别;

在Android上应该尽量采用Parcelable,它效率更高。
Parcelabe代码比Serializable多一些。
Parcelabe比Serializable速度高十倍以上。
Serializable只需要对某个类以及它的属性实现Serializable接口即可,无需实现方法。缺点是使用的反射,序列化的过程较慢,这种机制会在序列化的时候创建许多的临时对象。容易触发GC。
Parcable方法实现的原理是将一根完整的对象进行分解,而分解后的每一部分都是Intent所支持的数据类型,这样也就实现传递对象的功能。

18,介绍一下NDK;

一 : NDK是一系列工具的集合
NDK提供了一系列的工具,帮助开发者快速开发C或C++的动态库,并能自动将so和java应用一起打包成apk。这些工具对开发者的帮助是巨大的。
NDK集成了交叉编译器,并提供了相应的mk文件隔离CPU,平台,ABI等差异。开发人员只需要简单修改mk文件(指出哪些文件需要编译,编译特性要求等)就可以创建出so。
NDK可以自动将so和java应用一起打包,极大的减轻了开发人员的打包工作。
二 : NDK提供了一份稳定,功能有限的API头文件声明。
Google明确声明该API是稳定的,在后续所有版本中都稳定支持当前发布的API。从该版本的NDK中看出,这些API支持的功能非常有限,包含:c标准库,标准数学库,压缩库,Log库。

19,混合开发;

混合开发就是在一个App中内嵌一个轻量级的浏览器,一部分源生的功能改为Html5来开发,这部分功能不仅能够在不升级App的情况下动态更新,也可以在Android或者iOS的App上同时运行,让用户的体验更好又可以节省开发的资源。
混合开发最主要的是Html5和Native的交互
在Android中WebView本来就支持js和java相互调用,只需要开启WebView的脚本执行,然后通过mWebView.addJavascriptInterface(new JsBridge(),"bxbxbai");向Html5页面注入一股Java对象,然后就可以在Html5页面中调用Native的功能了。
Android4.2以后的系统规定允许被js调用的Java方法必须以@JavascriptInterface进行注解。