MeasureSpec是干什么的?有什么作用?
作用:更节省内存
MeasureSpec通过将Specmode和SpecSize打包成一个int值来避免过多的对象内存分配。
synchronized的机制
在同一时刻只能有一个线程操作,其他的线程阻塞或者轮询等待,在线程竞争激烈的情况下,这种方式的效率会非常的低下。( Hashtable)
View滑动冲突如何解决
1.同向滑动冲突 2.不同向滑动冲突
比如ListView头部添加viewpager, 用户在ViewPager上左右滑动,而页面缺出现了ListView上下滑动的情况
解决办法:通过事件分发的onInterceptTouchEvent方法进行判断
weakHashmap
WeakHashMap的键是“弱键”。在WeakHashMap中,当某个键不再正常使用时,会从WeakHashMap中被自动移除
LinkedHashMap
· LinkedHashMap是继承于HashMap,是基于HashMap和双向链表来实现的。
· HashMap无序;LinkedHashMap有序,可分为插入顺序和访问顺序两种。如果是访问顺序,那put和get操作已存在的Entry时,都会把Entry移动到双向链表的表尾(其实是先删除再插入)。
· LinkedHashMap存取数据,还是跟HashMap一样使用的Entry[]的方式,双向链表只是为了保证顺序。
· LinkedHashMap是线程不安全的
MVP、MVC
模型层(Model):程序需要操作的数据或信息(系统中的业务逻辑部分),比如数据的存储、网络请求等,
视图层(View):
直接面向于最终用户的视图层,并提供给用户操作界面,是Model的具体表现形式,是程序的外壳
控制器层(Controller):
根据View的事件逻辑修改对应的Model,
MVP 框架模式
数据的存取(Model):
Model角色主要是提供数据的存取功能,Presenter需要通过Model层存取、获取数据,
用户界面(View):
View通常指Activity、Fragment或者某个View控件,它含有一个Presenter成员变量。通常View需要实现一个逻辑接口,将View上的操作转交给Presenter进行实现,
交互中间人(Presenter):
Presenter主要作为View和Model的桥梁,它从Model层检索数据后,返回给View,使得View和Model之间没有耦合,也将业务逻辑从View层上抽离出来。
通过Presenter将View和Model解耦合、降低类型复杂度、各个模块可以独立测试、独立变化
MVP框架是如何实现的?都充当什么角色?
M层提供数据上的存取,比如数据库存取操作、网络操作、复杂的算法等操作;
V层负责显示数据部分,显示Model层的数据结果,Activity、Fragment或者某个View控件可以视为是V层,;
P层更多的是作为V层和M层的桥梁,主要负责相关的业务逻辑。
优点:
- 通过P层的转接能避免业务逻辑被塞进View中,有效的降低View的复杂性;
- View层和Model层完全解耦,他们之间互不干扰,带来了良好的可拓展性、可测试性,保证了系统的灵活性和整洁性;
- Activity、Fragment等仅仅为View层,不会再出现MVC那种Activity、Fragment即做View层又做Controller层的窘境,View层及Model层大大提高复用性;
- 我们可以将一个Presenter用于多个视图,而不需要改变Presenter的逻辑。这个也是我上面提到的理想的MVP模式;
- 由于各层分工明确,极便于单元测试;
MVC 的优缺点及适用场景
优点:
- 耦合性低,MVC本质是分层解耦,将表现层与表现逻辑很好的分离,减少模块代码之间的相互影响.
- 可扩展性好。由于耦合性低,添加需求,扩展代码就可以减少修改之前的代码,降低bug的出现率.
- 模块职责划分明确。主要划分层M,V,C三个模块,利于代码的维护.
缺点:
- View层和Model层是相互可知的,这意味着两层之间存在耦合;
- 在Android开发中,xml作为View层,控制能力实在太弱了,只能把代码写在Activity中,造成了Activity既是Controller层,又是view层这样一个窘境,而且Activity并不是一个标准的MVC模式中的Controller,它的首要职责是加载应用的布局和初始化用户界面,并接受和处理来自用户的操作请求,进而作出响应。随着应用内界面及其逻辑的复杂度不断提升,Activity类的职责不断增加,以致变得庞大而臃肿。
适用场景:适用于功能较少、业务逻辑简单、界面不复杂的小型项目。
Https协议
HTTPS在HTTP的基础上加入了SSL协议,SSL依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密。
http与https的区别
- HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全,为了保证这些隐私数据能加密传输,于是网景公司设计了SSL(Secure Sockets Layer)协议用于对HTTP协议传输的数据进行加密,从而就诞生了HTTPS。简单来说,HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。
HTTPS和HTTP的区别主要如下:
1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
客户端在使用HTTPS方式与Web服务器通信时有以下几个步骤
- 客户端向服务端发送url,要求与服务端建立ssl连接
- 服务端会将证书信息并携带公钥发送给客户端
- 客户端通过公钥加密信息给服务端
- 服务端通过私钥进行解密
Http协议
https 使用数字签名证书SSL提供了数据传输安全通道的HTTP协议
HTTP是超文本传输协议,是客户端浏览器或其他程序与Web服务器之间的应用层通信协议。
okHttp原理
OkHttp的底层是通过Java的Socket发送HTTP请求与接受响应的(这也好理解,HTTP就是基于TCP协议的),但是OkHttp实现了连接池的概念,即对于同一主机的多个请求,其实可以公用一个Socket连接,而不是每次发送完HTTP请求就关闭底层的Socket,这样就实现了连接池的概念。而OkHttp对Socket的读写操作使用的OkIo库进行了一层封装。
OkHttpClient算是执行调用请求Call的工厂,这个工厂将会被用来发送Http请求和读取他们的返回这里强调OkHttp的使用最好创建一个单例OkHttpClient实例,并且重复使用。这是因为每一个Client都有自己的一个连接池connection pool和线程池thread pools。重用这些连接池和线程池可以减少延迟和节约内存。
Interceptor 是 OkHttp 最核心的一个东西,不要误以为它只负责拦截请求进行一些额外的处理(例如 cookie),实际上它把实际的网络请求、缓存、透明压缩等功能都统一了起来,每一个功能都只是一个 Interceptor,它们再连接成一个 Interceptor.Chain,环环相扣,最终圆满完成一次网络请求。
Java7 ConcurrentHashMap:分段锁
ConcurrentHashMap 是一个 Segment 数组,Segment 通过继承 ReentrantLock 来进行加锁,所以每次需要加锁的操作锁住的是一个 segment,这样只要保证每个 Segment 是线程安全的,也就实现了全局的线程安全。默认是 16,也就是说 ConcurrentHashMap 有 16 个 Segments,所以理论上,这个时候,最多可以同时支持 16 个线程并发写,只要它们的操作分别分布在不同的 Segment 上。这个值可以在初始化的时候设置为其他值,但是一旦初始化以后,它是不可以扩容的。
Java 8 ConCurrentHashMap()的实现
高效的实现并发环境下的线程安全.
ConcurrentHashMap不仅实现了HashMap支持的所有功能,并且保持了和HashMap一样的高效的前提下,还实现了线程安全,ConcurrentHashMap是基于CAS来实现线程安全的,CAS是一种轻量级的锁,它不会阻塞线程,而是会等待直到获得变量,然后进行业务操作,这和锁需要阻塞线程来实现线程安全来比较,是一种很大的改良,因为基于锁的同步控制需要让线程获得锁,而获得锁之前是要阻塞等待的,它需要另外的线程来唤醒它让他再次去竞争锁,
- java基础面试知识点
- java中==和equals和hashCode的区别?
“==” :比较的是结果值
“equals” :比较的将是对象的引用是否指向同一块内存地址
“hashCode”:hashCode()方法相当于是一个对象的编码,它与equals()方法的不同之处就在于它返回的是int型,比较起来不直观。
- int、char、long各占多少字节数
Java基本类型占用的字节数:
1字节: byte , boolean
2字节: short , char
4字节: int , float
8字节: long , double
3.int与integer的区别
1、Integer是int的包装类,int则是java的一种基本数据类型
2、Integer变量必须实例化后才能使用,而int变量不需要
3、Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值
4、Integer的默认值是null,int的默认值是0
4.谈谈对java多态的理解
多态的实现的必要条件:继承,重写,父类引用指向子类对象(即,声明是父类,实际指向的是子类的一个对象)
- String、StringBuffer、StringBuilder区别
在这方面运行速度快慢为:StringBuilder > StringBuffer > String
- String对象一旦创建之后该对象是不可更改的,但后两者的对象是变量,是可以更改的
- 在线程安全上,StringBuilder是线程不安全的,而StringBuffer是线程安全的
(StringBuffer中很多方法可以带有synchronized关键字,所以可以保证线程是安全的,StringBuilder的方法则没有该关键字,所以不能保证线程安全)
- 如果要进行的操作是多线程的,那么就要使用StringBuffer,
但是在单线程的情况下,还是建议使用速度比较快的StringBuilder。
- 什么是内部类?内部类的作用
内部类就是写在类中或方法中的类,它还是一个类,与其他类的不同就是他只为这个外部类(含内部类的类)类所用
内部类的作用:
1、内部类可以很好的实现隐藏
2、内部类拥有外围类的所有元素的访问权限
3、可以实现多重继承
4、可以避免接口中的方法和同一个类中的方法同名的问题
7.抽象类和接口区别
1、抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。
2、抽象类要被子类继承,接口要被类实现。
3、接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现
4、接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
5、抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,一个实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。
6、抽象方法只能申明,不能实现,接口是设计的结果 ,抽象类是重构的结果
7、抽象类里可以没有抽象方法
8、如果一个类里有抽象方法,那么这个类只能是抽象类
9、抽象方法要被实现,所以不能是静态的,也不能是私有的。
10、接口可继承接口,并可多继承接口,但类只能单根继承。
8.抽象类是否可以没有方法和属性?
是 有抽象属性或者抽象方法的必须为抽象类,相反不一定,不然你定义抽象类干嘛,又不用它的特点,属性和普通类属性一样
- 接口的意义
解耦,可扩展这是设计接口的主要原因之一
- 泛型中extends和super的区别
<? extends T>限定参数类型的上界:参数类型必须是T或T的子类型 只能读取, 不能写入,
<? super T> 限定参数类型的下界:参数类型必须是T或T的超类型 只能写入, 不能读取,
- 父类的静态方法能否被子类重写
不能
因为静态方法从程序开始运行后就已经分配了内存,也就是说已经写死了。所有引用到该方法的对象(父类的对象也好子类的对象也好)所指向的都是同一块内存中的数据,也就是该静态方法。子类中如果定义了相同名称的静态方法,并不会重写,而应该是在内存中又分配了一块给子类的静态方法,没有重写这一说
- final,finally,finalize的区别
final修饰符(关键字)。被final修饰的类,不能作为父类而被子类继承。因此一个类不能既被abstract声明,又被final声明。将变量或方法声明为final,可以保证他们在使用的过程中不被修改。被声明为final的变量必须在声明时给出变量的初始值,而在以后的引用中只能读取。被final声明的方法也同样只能使用,不能重载
finally是在异常处理时提供finally块来执行任何清除操作。不管有没有异常被抛出、捕获,finally块都会被执行。
finalize是方法名。java技术允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在object类中定义的,因此所有的类都继承了它。子类覆盖finalize()方法以整理系统资源或者被执行其他清理工作。finalize()方法是在垃圾收集器删除对象之前对这个对象调用的。
- Serializable 和Parcelable 的区别
1、作用
Serializable的作用是为了保存对象的属性到本地文件、数据库、网络流、rmi以方便数据传输,当然这种传输可以是程序内的也可以是两个程序间的。而Android的Parcelable的设计初衷是因为Serializable效率过慢,为了在程序内不同组件间以及不同Android程序间(AIDL)高效的传输数据而设计,这些数据仅在内存中存在,Parcelable是通过IBinder通信的消息的载体。
从上面的设计上我们就可以看出优劣了。
2、效率及选择
Parcelable的性能比Serializable好,在内存开销方面较小,所以在内存间数据传输时推荐使用Parcelable,如activity间传输数据,而Serializable可将数据持久化方便保存,所以在需要保存或网络传输数据时选择Serializable,因为android不同版本Parcelable可能不同,所以不推荐使用Parcelable进行数据持久化
- 静态属性和静态方法是否可以被继承?是否可以被重写?以及原因?
父类的静态属性和方法可以被子类继承,不可以被子类重写。
- 静态内部类的设计意图
一个与外围对象有关系,一个没有关系,静态内部类不依赖外部类的实体
静态内部类与非静态内部类之间存在一个最大的区别:非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围内,但是静态内部类却没有。
没有这个引用就意味着:它的创建是不需要依赖于外围类的。
它不能使用任何外围类的非static成员变量和方法。
- 闭包和局部内部类的区别
内部类使用外部的局部变量,实际上形成了闭包,也就是捕获了所在方法内的变量,这个被捕获的变量不能随着方法的执行完毕而消失,因为内部类的实例可能还会用到这个变量,所以需要 final 关键字
- List,Set,Map的区别
Set(公共特点;无序,唯一) list(有序,可重复) map(公共特点特点:以键值对的形式存储,键唯一,值可重复):
- 开启线程的三种方式?
1.继承Thread类,并复写run方法,创建该类对象,调用start方法开启线程。
2.实现Runnable接口,复写run方法,创建Thread类对象,将Runnable子类对象传递给Thread类对象。调用start方法开启线程。
第二种方式好,将线程对象和线程任务对象分离开。降低了耦合性,利于维护
3.创建FutureTask对象,创建Callable子类对象,复写call(相当于run)方法,将其传递给FutureTask对象(相当于一个Runnable)。
创建Thread类对象,将FutureTask对象传递给Thread对象。调用start方法开启线程。这种方式可以获得线程执行完之后的返回值。
- run()和start()方法区别
start与run方法的主要区别在于当程序调用start方法一个新线程将会被创建,并且在run方法中的代码将会在新线程上运行,然而在你直接调用run方法的时候,程序并不会创建新线程,run方法内部的代码将在当前线程上运行。
- 如何控制某个方法允许并发访问线程的个数?
构造函数创建了一个 Semaphore 对象,并且初始化了 5 个信号。这样的效果是控件 test 方法最多只能有 5 个线程并发访问,对于 5 个线程时就排队等待,走一个来一下;
请求一个信号(消费一个信号),如果信号被用完了则等待;
释放一个信号,释放的信号新的线程就可以使用了.
- 在Java中wait和seelp方法的不同;
sleep()方法,属于Thread类中的。而wait()方法,则是属于Object类中的。
sleep()方法导致了程序暂停执行指定的时间,让出cpu给其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。
在调用sleep()方法的过程中,线程不会释放对象锁。
而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后重新获取对象锁进入运行状态。如果不调用notify()方法,该线程会永远处于挂起状态
22.谈谈wait/notify关键字的理解
wait():交出锁和 CPU 控制权,线程进入阻塞状态,等待 notify() 唤醒;
notify():等线程交出锁(synchronized 代码块执行完毕 / 执行 wait())后,唤醒一个 wait();
notifyAll():等线程交出锁(synchronized 代码块执行完毕 / 执行 wait())后,唤醒所有 wait();
- 什么导致线程阻塞?
阻塞状态的线程的特点是:该线程放弃CPU的使用,暂停运行,只有等到导致阻塞的原因消除之后才恢复运行。或者是被其他的线程中断,该线程也会退出阻塞状态,同时抛出InterruptedException
1)线程执行了Thread.sleep(intmillsecond);方法,当前线程放弃CPU,睡眠一段时间,然后再恢复执行
2)线程执行一段同步代码,但是尚且无法获得相关的同步锁,只能进入阻塞状态,等到获取了同步锁,才能回复执行。
3)线程执行了一个对象的wait()方法,直接进入阻塞状态,等待其他线程执行notify()或者notifyAll()方法。
4)线程执行某些IO操作,因为等待相关的资源而进入了阻塞状态。比如说监听system.in,但是尚且没有收到键盘的输入,则进入阻塞状态。
23.线程如何关闭?
//触发条件设置中断 thread.interrupt();
关于线程的关闭,可能会用stop() 方法,但是stop是线程不安全的,一般采用interrupt,判断线程是否中止采用isInterrupted,
- 讲一下java中的同步的方法
使用synchronized关键字
由于java的每个对象都有一个内置锁,当用此关键字修饰方法时, 内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。
注: synchronized关键字也可以修饰静态方法,此时如果调用该静态方法,将会锁住整个类。
注:同步是一种高开销的操作,因此应该尽量减少同步的内容。通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。
- 如何保证线程安全?
同步与锁
为了避免多个线程同时读写一个数据而产生不可预料的后果,开发人员要将各个线程对同一个数据的访问同步,也就是说,在一个线程访问数据未结束的时候,其他线程不得对同一个数据进行访问。
同步的最常用的方法是使用锁(Lock),它是一种非强制机制,每个线程在访问数据或资源之前首先试图获取锁,并在访问结束之后释放锁;在锁已经被占用的时候试图获取锁时,线程会等待,直到锁重新可用。
25.string 转换成 integer的方式及原理
parseInt 构造一个Integer对象 通过char 转换
- synchronized与Lock的区别
类别 | synchronized | Lock |
存在层次 | Java的关键字,在jvm层面上 | 是一个类 |
锁的释放 | 1、以获取锁的线程执行完同步代码,释放锁 2、线程执行发生异常,jvm会让线程释放锁 | 在finally中必须释放锁,不然容易造成线程死锁 |
锁的获取 | 假设A线程获得锁,B线程等待。如果A线程阻塞,B线程会一直等待 | 分情况而定,Lock有多个锁获取的方式,具体下面会说道,大致就是可以尝试获得锁,线程可以不用一直等待 |
锁状态 | 无法判断 | 可以判断 |
锁类型 | 可重入 不可中断 非公平 | 可重入 可判断 可公平(两者皆可) |
性能 | 少量同步 | 大量同步 |
26.死锁的四个必要条件?
- 互斥条件:资源是独占的且排他使用,进程互斥使用资源,即任意时刻一个资源只能给一个进程使用,其他进程若申请一个资源,而该资源被另一进程占有时,则申请者等待直到资源被占有者释放。
- 不可剥夺条件:进程所获得的资源在未使用完毕之前,不被其他进程强行剥夺,而只能由获得该资源的进程资源释放。
- 请求和保持条件:进程每次申请它所需要的一部分资源,在申请新的资源的同时,继续占用已分配到的资源。
- 循环等待条件:在发生死锁时必然存在一个进程等待队列{P1,P2,…,Pn},其中P1等待P2占有的资源,P2等待P3占有的资源,…,Pn等待P1占有的资源,形成一个进程等待环路,环路中每一个进程所占有的资源同时被另一个申请,也就是前一个进程占有后一个进程所深情地资源。
- 怎么避免死锁?
死锁避免的基本思想:系统对进程发出的每一个系统能够满足的资源申请进行动态检查,并根据检查结果决定是否分配资源,如果分配后系统可能发生死锁,则不予分配,否则予以分配,这是一种保证系统不进入死锁状态的动态策略。
- 对象锁和类锁是否会互相影响?
不会,类锁用于所有对象,对象锁只用于本对象。
- 什么是线程池,如何使用?
多线程的异步执行方式,虽然能够最大限度发挥多核计算机的计算能力,但是如果不加控制,反而会对系统造成负担。线程本身也要占用内存空间,大量的线程会占用内存资源并且可能会导致Out of Memory。即便没有这样的情况,大量的线程回收也会给GC带来很大的压力。
为了避免重复的创建线程,线程池的出现可以让线程进行复用。通俗点讲,当有工作来,就会向线程池拿一个线程,当工作完成后,并不是直接关闭线程,而是将这个线程归还给线程池供其他任务使用。
线程池的正确使用
以下阿里编码规范里面说的一段话:
ThreadPoolExecutor是可以拓展的
线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors各个方法的弊端:
1)newFixedThreadPool和newSingleThreadExecutor:
主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
2)newCachedThreadPool和newScheduledThreadPool:
主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。
- 多线程断点续传原理
HTTP/1.1 开始就支持了。一般断点下载时才用到 Range 和 Content-Range 实体头。 而Range支持的话,客户端应该记录了之前已经读取的文件范围,网络恢复之后,则向服务器发送读取剩余Range的请求,服务端只需要发送客户端请求的那部分内容,而不用整个文件发送回客户端,以此节省网络带宽。
30.断点续传的实现
- Android一些优化方案
布局优化
1、<include>标签避免重复布局
2、<merge>标签和<include>合并使用减少层级关系
3、使用ViewStub进行按需加载布局。比如网络异常页面,只在网络异常的情况下加载,避免多余的布局和绘制时间。
绘制优化
1、避免在onDraw中做过于复杂的事情。如heavy calculate,for循环等
内存泄露优化
1、内存泄露可以使用MAT或LeakCanary检测
2、注意以下几种场景导致的内存泄露
1)单例模式,生命周期和Application一致。内部最好不要持有activity等引用实例
2)静态变量导致内存泄露,静态变量和类的生命周期一致
响应速度和ANR优化
1、核心思想:避免在UI线程做过多操作
2、可以采用RxJava和RxAndroid进行优雅的线程切换来避免
网络优化
1、客户端缓存ip列表,避免DNS请求
2、数据按需拉取,用到了才请求
3、时间戳优化。当服务端有新数据才进行优化
4、日志上报优化:本地合并日志,延迟上报
Bitmap优化
1、采用BitmapFactory.Options来压缩图片
2、采用LruCache和DiskLruCache进行图片缓存
ListView/GridView优化
1、采用ViewHolder
2、滑动时不宜启动大量异步任务。比如异步加载图片。首先会开启大量线程(可以用线程池优化),其次会在短时间内有大量的UI更新操作,造成卡顿。
3、不要在getView中的主线程内做耗时操作。
线程优化
1、可以采用线程池代替线程。
2、使用Excutor、ExcutorService、ThreadPoolExecutor等现成的javaapi代替。这也是Effective Java中推荐的做法。
架构优化
1、使用mvp、mvvm分层解耦。
2、针对复杂的APP可以使用MultiDex进行模块化解耦。
- 什么是过渡绘制,如何防止过渡绘制
蓝色(1x次绘制)-》浅绿色(2x绘制)-》淡红色(3x绘制)-》红色(4x绘制)。
GPU在android4.2以上才有此功能
Android过度绘制指的是在屏幕一个像素上绘制多次(超过一次),最好把绘制控制在2次以下,3次绘制有时候是不能避免的,尽量避免,4次的绘制基本上是不允许的
- 事件分发机制
核心的图解说明
- ListView的优化
答:1、如果自定义适配器,那么在getView方法中要考虑方法传进来的参数contentView是否为null,如果为null就创建contentView并返回,如果不为null则直接使用。在这个方法中尽可能少创建view。
- 给contentView设置tag(setTag()),传入一个viewHolder对象,用于缓存要显示的数据,可以达到图像数据异步加载的效果。
3、如果listview需要显示的item很多,就要考虑分页加载。比如一共要显示100条或者更多的时候,我们可以考虑先加载20条,等用户拉到列表底部的时候再去加载接下来的20条。
使用ViewHolder模式来提高效率
Viewholder模式充分了ListView的视图缓存机制,避免了每次在调用getView的时候都去通过findViewById实例化数据。
异步加载:耗时的操作放在异步线程中
一种是通过多线程方式通过Handler+Message进行异步加载,另一种是通过AsyncTask来进行异步操作
listView错位加载问题
使用findViewWithTag
在Adapter中为ImageView设置Tag标识位:
为图片设置缓存
ListView的滑动时停止加载和分页加载
- Binder机制,Stub类中asInterface函数作用,BnBinder和BpBinder区别。
asInterface的作用:是根据调用是否属于同进程而返回不同的实例对象,同进程时,调用asInterface返回的是Stub.Proxy对象。
服务端跨进程的类都要继承Binder类。我们所持有的Binder引用(即服务端的类引用)并不是实际真实的远程Binder对象,我们的引用在Binder驱动里还要做一次映射。也就是说,设备驱动根据我们的引用对象找到对应的远程进程。客户端要调用远程对象函数时,只需把数据写入到Parcel,在调用所持有的Binder引用的transact()函数,transact函数执行过程中会把参数、标识符(标记远程对象及其函数)等数据放入到Client的共享内存,Binder驱动从Client的共享内存中读取数据,根据这些数据找到对应的远程进程的共享内存,把数据拷贝到远程进程的共享内存中,并通知远程进程执行onTransact()函数,这个函数也是属于Binder类。远程进程Binder对象执行完成后,将得到的写入自己的共享内存中,Binder驱动再将远程进程的共享内存数据拷贝到客户端的共享内存,并唤醒客户端线程。
- 为什么要使用Binder?
在传统的Linux上,我们还是有很多选择可以用来实现进程间通信,如管道、SystemV、Socket等。那么Android为什么不使用这些原有的技术,而是要使开发一种新的叫Binder的进程间通信机制呢?
主要有两个方面的原因:
性能方面
在移动设备上(性能受限制的设备,比如要省电),广泛地使用跨进程通信对通信机制的性能有严格的要求,Binder相对出传统的Socket方式,更加高效。Binder数据拷贝只需要一次,而管道、消息队列、Socket都需要2次,共享内存方式一次内存拷贝都不需要,但实现方式又比较复杂。
安全方面
传统的进程通信方式对于通信双方的身份并没有做出严格的验证,比如Socket通信ip地址是客户端手动填入,很容易进行伪造,而Binder机制从协议本身就支持对通信双方做身份校检,因而大大提升了安全性。
- 在多进程中,Application会启动几次
Android中多进程方式导致的全局Application的OnCreate多次被调用,
- 单例模式,双锁原理,volatile原理,静态内部类实现单例的原理。
单利模式原理:
1.对象只创建一个实例,并且提供一个全局的访问点。
2.每次从getInstance()都能返回一个且唯一的一个对象。
3.能适应多线程同时(并发)访问。
4.实现懒加载(Lazy Load),在需要的时候才被构造。
优点:客户端使用单例模式的实例的时候,只需要调用一个单一的方法即可生成一个唯一的实例,有利于节约资源。
缺点:首先单例模式很难实现序列化,这就导致采用单例模式的类很难被持久化,当然也很难通过网络传输;其次由于单例采用静态方法,无法在继承结构中使用。
饿汉式:线程安全:构造方法私有化:推荐使用,缺点:上来就创建对象,会比较消耗内存
public class Singleton{
private static Singleton singleton = new Singleton ();//类创建时,就创建一个静态对象,之后不会再改变
private Singleton (){}
public Singleton getInstance(){return singletion;}
}
懒汉式:线程不安全:若生成实例的时候若不加synchronized(线程同步),会发生线程不安全:如:当一个线程执 行到(2)时,还未生成对象,另一个线程执行(1);这时就会发生线程不安全
public class Singleton{
private static Singleton singleton = null;
public static synchronized getInstance(){
if(singleton==null){//(1)
//(2)
singleton = new Singleton();//(3)
}
return singleton;
}
}
双锁原理:对getInstance()做了同步处理,synchronized将导致性能开销。如果getInstance()被多个线程频繁的调用,将会导致程序执行性能的下降。反之,如果getInstance()不会被多个线程频繁的调用,那么这个延迟初始化方案将能提供令人满意的性能。
volatile原理
Volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性”。可见性的意思是当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。(volatile是处理多线程锁的替代方案,对应有时需要实时的修改共享资源的变量,被volatile修复的变量的值可以立刻被业务方取得最新的值。)Volatile变量修饰符如果使用恰当的话,它比synchronized的使用和执行成本会更低,因为它不会引起线程上下文的切换和调度。
静态内部类实现单例的原理
不仅能保证线程安全,也能够保证对象的唯一性,同时也延迟了单例的实例化,所以这是推荐使用的单例模式实现方式。
好处:懒加载,不需要使用synchronized加同步锁。只有在第一次调用的时候才会加载。
原理:静态内部类只有在被调用时才会加载,且类的加载过程是线程保护的所以不存在线程安全问题。而且这个类的实例化是在静态内部类中且使用了final修饰,只能赋值一次,static修饰随着类的加载而加载。
Tips:外部类加载的时候并不会加载内部静态类。
- Java多线程,synchronized
实现同步机制注意以下几点:
在多线程用,安全性高,性能低。
在单线程用,性能高,安全性低。
1,不要对线程安全类的所有方法都进行同步,只对那些会改变共享资源方法的进行同步。
2,如果可变类有两种运行环境,当线程环境和多线程环境则应该为该可变类提供两种版本:线程安全版本和线程不安全版本(没有同步方法和同步块)。在单线程中环境中,使用线程不安全版本以保证性能,在多线程中使用线程安全版本.
为什么要使用线程通讯?
当使用synchronized 来修饰某个共享资源时(分同步代码块和同步方法两种情况),当某个线程获得共享资源的锁后就可以执行相应的代码段,直到该线程运行完该代码段后才释放对该 共享资源的锁,让其他线程有机会执行对该共享资源的修改。当某个线程占有某个共享资源的锁时,如果另外一个线程也想获得这把锁运行就需要使用wait() 和notify()/notifyAll()方法来进行线程通讯了。
Java.lang.object 里的三个方法wait() notify() notifyAll()
wait方法导致当前线程等待,直到其他线程调用同步监视器的notify方法或notifyAll方法来唤醒该线程。
wait(mills)方法
都是等待指定时间后自动苏醒,调用wait方法的当前线程会释放该同步监视器的锁定,可以不用notify或notifyAll方法把它唤醒。
notify()
唤醒在同步监视器上等待的单个线程,如果所有线程都在同步监视器上等待,则会选择唤醒其中一个线程,选择是任意性的,只有当前线程放弃对该同步监视器的锁定后,也就是使用wait方法后,才可以执行被唤醒的线程。
notifyAll()方法
唤醒在同步监视器上等待的所有的线程。只用当前线程放弃对该同步监视器的锁定后,才可以执行被唤醒的线程。
- SQLite的相关操作
- 在AndroidManifest.xml中设置关于数据操作的权限
- 创建类a继承 SQLiteOpenHelper
- 在a类构造方法中创库
- 在oncreate中,通过SQLiteDataBase创建实例建表
- 创建a类的实例helper,通过helper.getWritableDatabase()执行sql语句
- 网络相关的问题,网络的五层模型,又问了TCP和UDP
五层协议的网络体系结构各层的主要功能。
⑴物理层: 物理层的任务就是透明地传送比特流,确定连接电缆插头的定义及连接法。
⑵数据链路层: 数据链路层的任务是在两个相邻结点间的线路上无差错地传送以帧(frame)为单位的数据。每一帧包括数据和必要的控制信息。
⑶网络层:网络层的任务就是要选择合适的路由,使发送站的运输层所传下来的分组能够正确无误地按照地址找到目的站,并交付给目的站的运输层。
⑷运输层:运输层的任务是向上一层的进行通信的两个进程之间提供一个可靠的端到端服务,使它们看不见运输层以下的数据通信的细节。
⑸应用层:应用层直接为用户的应用进程提供服务。
socket通信其实就是“套接字”,通过ip地址与端口进行连接的
创建tcp服务端步骤:
1.创建一个serverSocket对象
2.调用accept()方法接受客户端请求
3。从Socket中获取i/o流
4。对I/O流进行读写操作,完成与客户端的交互。
5。关闭I/O流和Socket
创建tcp客户端步骤:
1.创建一个Socket对象
2.从Socket中获取I/o流
3.对I/O流进行读写操作,完成与服务端的交互
4.关闭I/O流和socket
客户端写 服务端就读,
服务端读,客户端就写
基于UDP协议的Socket编程
创建发送端
1.建立DategramSocket对象。该断点建立,系统会随机分配一个端口
如果不想随机配置,可以手动指定。
2.将数据进行packet包的封装,必须要指定目的地地址和端口
3.通过socket服务的send方法将该包发出。
创建接收端
1.建立DategramSocket对象。要监听一个端口。
2.通过socket的receiver方法将数据存入数据包中。
3.通过数据包中dp的方法getDate()、getAddress()、getPort()等方法
获取包中的指定信息。
4。将socket关闭
- Acticity的生命周期以及四种启动模式,各自特点
①. standard(备注:standard是系统默认的启动模式。)
标准启动模式,每次激活Activity时都会创建Activity,并放入任务栈中。每个窗体的
getTaskId()保持不变,但是this.hashCode()发生改变。
②. singleTop
如果在任务的栈顶正好存在该Activity的实例, 就重用该实例,而不会创建新的Activity对象,不过它会调用onNewIntent()方法。如果栈顶部不存在就会创建新的实例并放入
栈顶(即使栈中已经存在该Activity实例,只要不在栈顶,都会创建实例)。会回调onNewIntent()方法。
③. singleTask
如果在栈中已经有该Activity的实例,就重用该实例(会调用实例的onNewIntent())。
重用时,会让该实例回到栈顶,因此在它上面的实例将会被移除栈。如果栈中不存在该实
例,将会创建新的实例放入栈中。 和singleTop在名字上即可看出区别,即singleTop每次只检测当前栈顶的Activity是
否是我们需要请求创建的,而singleTask则会检测栈中全部的Activity对象,从上向下,
如果检测到是我们所请求的,则会消灭此Activity对象上面的对象,直接把检测到的我们需
要的Activity置为栈顶。
④. singleInstance
与singleTask模式的区别是,在singleInstance模式中,存放activity实例的(窗口对象)的
回退栈不能有其他任何窗口对象。因此如果该窗口不存在,则要新建任务来存放该singleI
nstance模式窗口。也就是说getTaskId()会发现任务id发生了变化。
此启动模式和我们使用的浏览器工作原理类似,在多个程序中访问浏览器时,如果当
前浏览器没有打开,则打开浏览器,否则会在当前打开的浏览器中访问。此模式会节省大
量的系统资源,因为他能保证要请求的Activity对象在当前的栈中只存在一个。总之,在
开发Android项目时,巧妙设置Activity的启动模式会节省系统开销和提高程序运行效率。
- Service的两种启动模式生命周期,如果同时使用两种启动
· StartService的生命周期为:onCreate --> onStartCommand(可多次调用) --> onDestroy。
【详细说明:】
在程序中调用:context.startService() 会触发执行Service生命周期中的onCreate()、onStartCommand()回调方法,此时服务就开始正式运行;
如果Service还没有运行,则android先调用onCreate()然后调用onStartCommand();如果Service已经运行,则只调用onStartCommand(),所以一个Service的onStartCommand方法可能会重复调用多次;
如果在程序中调用:context.stopService()会触发执行Service生命周期中的onDestroy()回调方法,会让服务停止;
stopService()的时候直接onDestroy,如果是调用者自己直接退出而没有调用stopService()的话,Service会一直在后台运行。该Service的调用者再启动该Service后可以通过stopService关闭Service;stopSelf()
· BindService的生命周期为:onCreate --> onBind(只一次,不可多次绑定) --> onUnbind --> onDestory。
【详细说明:】
在程序中调用:context.bindService()会触发执行Service生命周期中的onCreate()、onBind()回调方法,此时服务开始运行;
onBind将返回给客户端一个IBind接口实例,IBind允许客户端回调服务的方法,比如得到Service运行的状态或其他操作。此后调用者(Context,例如Activity)会和Service绑定在一起;
如果调用Service的调用者Context退出了,那么会依次调用Service生命周期中的onUnbind()、onDestroy()回调方法,会让服务停止;
所以BindService的生命周期为:onCreate --> onBind(只一次,不可多次绑定) --> onUnbind --> onDestory。
【备注:】
Service是不能自己启动的,只有通过 Context 对象调用startService() 和bindService() 方法来启动。
在Service每一次的开启关闭过程中,只有onStartCommand()可被多次调用(通过多次startService调用),其他onCreate()、onBind()、onUnbind()、onDestory()在一个生命周期中只能被调用一次。
Service可以在和多场合的应用中使用,比如播放多媒体的时候用户启动了其他Activity这个时候程序要在后台继续播放,比如检测SD卡上文件的变化,再或者在后台记录你地理信息位置的改变等等,总之服务总是藏在后头的。
- volley的源代码原理。
// Volley使用步骤:3步走:
// 1.创建一个请求队列;
RequestQueue queue = Volley.newRequestQueue(this);
// 2.创建一个请求对象:字符串请求;json请求;image请求.
StringRequest request = new StringRequest(Method.GET, url,
new Listener<String>() {// 请求成功时候的监听
public void onResponse(String result) {
tvInfo.setText(result);
}
}, new ErrorListener() {// 请求失败时的监听
public void onErrorResponse(VolleyError error) {
Log.e("TAG", "error=" + error.getMessage());
}
});
// 3.把请求对象添加到请求队列中.
queue.add(request);
- http中缓存机制,Last-Modify的作用等。
Last-Modified :用于记录页面最后修改时间的 HTTP 头信息,Last-Modified 是由服务器往客户端发送的 HTTP 头.
对比缓存
强制缓存如果生效,不需要再和服务器发生交互,而对比缓存不管是否生效,都需要与服务端发生交互。 两类缓存规则可以同时存在,强制缓存优先级高于对比缓存,也就是说,当执行强制缓存的规则时,如果缓存生效,直接使用缓存,不再执行对比缓存规则。
- fragment的生命周期
1.onAttach() 作用:fragment已经关联到activity,这个时候 activity已经传进来了, 获得activity的传递的值 就可以进行 与activity的通信里, 当然也可以使用getActivity(),前提是这个fragment已经和宿主的activity关联,并且没有脱离,有且只有调用一次。
2.onCreate()
系统创建fragment的时候回调他,在他里面实例化一些变量 这些个变量主要是:当你 暂停 停止的时候 你想保持的数据 他只调用一次。
3.onCreateView()
第一次使用的时候 fragment会在这上面画一个layout出来, 为了可以画控件 要返回一个 布局的view,也可以返回null j 就什么都没有显示。
当系统用到fragment的时候 fragment就要返回他的view,越快越好 ,所以尽量在这里不要做耗时操作,比如从数据库加载大量数据
4onActivityCreated()
当Activity中的onCreate方法执行完后调用。
注意了: 从这句官方的话可以看出:当执行onActivityCreated()的时候 activity的onCreate才刚完成。 所以在onActivityCreated()调用之前 activity的onCreate可能还没有完成, 所以不能再onCreateView()中进行 与activity有交互的UI操作,UI交互操作可以在onActivityCreated()里面进行。 所以呢,这个方法主要是初始化那些你需要你的父Activity或者Fragment的UI已经被完 整初始化才能初始化的元素。
5.onStart()
和activity一致,启动Fragement 启动时回调,,此时Fragement可见。 6.onResume()
和activity一致 在activity中运行是可见的。激活, Fragement 进入前台, 可获取焦点时激活。
7.onPause()
和activity一致 其他的activity获得焦点,这个仍然可见第一次调用的时候,指的是 用户 离开这个fragment(并不是被销毁) 通常用于 用户的提交(可能用户离开后不会回来了) 8.onStop()
和activity一致, fragment不可见的, 可能情况:activity被stopped了或者 fragment被移除但被,加入到回退栈中,一个stopped的fragment仍然是活着的如果长时间不用也会被移除。
9. onDestroyView()
Fragment中的布局被移除时调用。表示fragemnt销毁相关联的UI布局, 清除所有跟视图相关的资源。
然后这个知识移除视图 并没有销毁而且还没有脱离activity
10.onDestroy()
销毁fragment对象, 跟activity类似了。 11.onDetach()
Fragment和Activity解除关联的时候调用。 脱离activity。
- Handler机制
Handler的消息处理主要有五个部分组成,Message,Handler,Message Queue,Looper和ThreadLocal。
Message:Message是在线程之间传递的消息,它可以在内部携带少量的数据,用于线程之间交换数据。Message有四个常用的字段,what字段,arg1字段,arg2字段,obj字段。what,arg1,arg2可以携带整型数据,obj可以携带object对象。
Handler:它主要用于发送和处理消息的发送消息一般使用sendMessage()方法,而发出的消息经过一系列的辗转处理后,最终会传递到Handler的handleMessage方法中。
MessageQueue:MessageQueue是消息队列的意思,它主要用于存放所有通过Handler发送的消息,这部分的消息会一直存在于消息队列中,等待被处理。每个线程中只会有一个MessageQueue对象。
Looper:每个线程通过Handler发送的消息都保存在,MessageQueue中,Looper通过调用loop()的方法,就会进入到一个无限循环当中,然后每当发现Message Queue中存在一条消息,就会将它取出,并传递到Handler的handleMessage()方法中。每个线程中只会有一个Looper对象。
ThreadLocal:MessageQueue对象,和Looper对象在每个线程中都只会有一个对象,怎么能保证它只有一个对象,就通过ThreadLocal来保存。Thread Local是一个线程内部的数据存储类,通过它可以在指定线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储到数据,对于其他线程来说则无法获取到数据。
- gradle中buildToolsVersion和TargetSdkVersion的区别是什么
CompileSdkVersion是你SDK的版本号,也就是API Level,例如API-20、API-21等等。buildeToolVersion是你构建工具的版本,其中包括了打包工具aapt、dx等等。这个工具的目录位于sdk/build-tools/(19.0.2,26.0.1,27.0.1);
- 手机适配一些方案
- 百分比布局
- AutoLayout框架 (扩展性差)
- ConstraintLayout(拖拽 android studio 2.0以上支持)
- android-percent-support-lib-sample框架
- 多套布局 多套图 设置dimens
- hashmap的实现原理
HashMap允许key和value为null;是非同步的,线程不安全;存取速度更快,效率高
HashMap的底层是基于数组和链表实现的,HashMap就是Hash表的Map实现。 Hash表就是Hash数组,Map实现是指实现了Map接口。
- 静态方法是否能被重写
非静态方法属于类的实例,是可以被子类重写,从而达到多态的效果; 静态方法属于类,是不能被重写,故而也不能实现多态。
- Tcp链接,3次握手和4次挥手的原因,以及为什么需要这样做。
要3次握手的原因:防止已过期的连接请求报文突然又传送到服务器,因而产生错误
为什么需要3次握手:确认客户端与服务端是不是相应的服务都准备好了,只有通过了3次握手才能直接传输数据并且满足了数据可靠性的传输。
4次挥手原因:先由客户端向服务器端发送一个FIN,请求关闭数据传输。
为什么要4次挥手?确保数据能够完成传输。
- Hashmap是如何解决hash冲突的
当关键字数量比较大的时候,难免就会造成一个问题,就是不一样的关键字隐射到同一个地址上,这样就造成了一个问题,就是hash冲突。
拉链法(链地址法):就是在冲突的位置上简历一个链表,然后将冲突的元素插入到链表尾端,
- 进程与线程区别
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源,但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.
- 写了一个二分查找和单例模式
二分查找
冒泡排序
选择排序
- http中的同步和异步
同步:提交请求->等待服务器处理->处理完毕返回 这个期间客户端浏览器不能干任何事
异步: 请求通过事件触发->服务器处理(这是浏览器仍然可以作其他事情)->处理完毕
- Android是如何进行资源管理的。
所有的资源都在res目录下存放,包括drawable,layout,strings,anim等等,当我们向工程中加入任何一个资源时,会在R类中相应会为该 资源分配一个id,我们在应用中就是通过这个id来访问资源的
- java比较重要的几个特性
面向对象主要有四大特性:封装、抽象、继承和多态。
封装:在面向对象语言中,封装特性是由类来体现的,我们将现实生活中的一类实体定义成类。
抽象:抽象就是将一类实体的共同特性抽象出来,封装在一个抽象类中,所以抽象在面向对象语言是由抽象类来体现的。
继承:继承就像是我们现实生活中的父子关系,儿子可以遗传父亲的一些特性,在面向对象语言中,就是一个类可以继承另一个类的一些特性,从而可以代码重用
多态:多态就是通过传递给父类对象引用不同的子类对象从而表现出不同的行为 ...
- OpenGL、GLSurfaceView和Shader,主要聊了OpenGL绘制方法
现在基本上用到的是OpenGL ES 2.0,检查系统是否实际支持2.0版本。
Shader:着色器
专门用于显示OpenGL的3D图像View:GLSurfaceView。GLSurfaceView会处理OpenGL初始化过程中比较基本的操作,如配置显示设备,以及在后台线程中渲染。继承自SurfaceView。
通过GLSurfaceView.setRenderer()函数,传入Renderer对象,Renderer是一个接口包含三个抽象方法onSurfaceCreated、onDrawFrame、onSurfaceChanged。
- 写一个死锁,死锁是怎样产生的,怎样防止死锁
死锁:死锁是指两个或两个以上的进程进在执行过程中,由于资源竞争或由于相互通信而造成的一种阻塞式现象,如果没有外力影响。那么它们将永远的持续下去, 此事称系统产生死锁现象,这种永远互相在等待的进程成为死锁。
防止死锁:尝试获取锁的过程中若超过了这个时限该线程则放弃对该锁请求。若一个线程没有在给定的时限内成功获得所有需要的锁,则会进行回退并释放所有已经获得的锁,然后等待一段随机的时间再重试。
- 给定一个int型 n,输出1~n的字符串例如 n = 4 输出“1 2 3 4”
if(int i=1;i<=a;i++){System.out.print(i)}
- 类的加载和内部类
- Serializable和Parcelable的区别。
- View的绘制流程,详细到framework代码
- 线程同步,CopyOnWriteArrayList怎样实现
- Volley源码,EventBus源码
- gc是根据什么来回收一个对象的,引用计数和gc root分别怎么实现
- 怎样会产生内存泄露,举一个具体的例子,使用什么检查内存泄露
- 什么是ANR,Activity、BroadcastReceiver、Service对ANR时间限制分别是多少,怎么处理ANR,除了系统生成trace.txt文件,怎么在程序中检测ANR。写出伪代码
- 编解码一些知识,I帧,B帧,P帧区别
- 类的加载过程,Person person = new Person();为例进行说明。
- JVM相关知识,GC机制。
JVM主要包括四个部分:
1.类加载器(ClassLoader):在JVM启动时或者在类运行时将需要的class加载到JVM中。
2.执行引擎:负责执行class文件中包含的字节码指令;
3.内存区(也叫运行时数据区):是在JVM运行的时候操作所分配的内存区。
4.本地方法接口:主要是调用C或C++实现的本地方法及返回结果。
GC机制
垃圾收集器一般必须完成两件事:检测出垃圾;回收垃圾。
- 类的加载器,双亲机制,Android的类加载器。
- Android中进程的级别,以及各自的区别。
- 插件化原理。
- 集合框架,list,map,set都有哪些具体的实现类,区别都是什么。
- concurrentHashmap原理,原子类。
- View的绘制流程
- Interger中的128(-128~127),这里考的是常量池的概念,这个没回答上来。
- 线程池的相关知识。
- Handler机制,HandlerThread实现等等。
- LRUCache算法是怎样实现的。
会优先淘汰那些近期最少使用的缓存对象
- 强引用:直接的对象引用。
- 软引用:当一个对象只有软引用存在时,系统内存不足时次对象会被gc回收。
- 弱引用:当一个对象只有弱引用时存在时,此对象会随时被gc回收。
- LRUCache的实现原理
LruCache的核心思想很好理解,就是要维护一个缓存对象列表,其中对象列表的排列方式是按照访问顺序实现的,即一直没访问的对象,将放在队尾,即将被淘汰。而最近访问的对象将放在队头,最后被淘汰。这个队列是由LinkedHashMap来维护。
而LinkedHashMap是由数组+双向链表的数据结构来实现的。其中双向链表的结构可以实现访问顺序和插入顺序,使得LinkedHashMap中的<key,value>对按照一定顺序排列起来。
- 怎样计算一张图片的大小,加载bitmap过程(怎样保证不产生内存溢出),二级缓存。
- 简单介绍一下java中的泛型,泛型擦除以及相关的概念。
- SQL相关的知识优化的方案
1、 减少数据访问(减少磁盘访问)
2、 返回更少数据(减少网络传输或磁盘访问)
3、 减少交互次数(减少网络传输)
4、 减少服务器CPU开销(减少CPU及内存开销)
5、 利用更多资源(增加资源)
- 插件化相关技术,热修补技术是怎样实现的,和插件化有什么区别。
Binder面试题
1、Linux内核的基本知识
- 进程隔离/虚拟地址空间:进程间是不可以共享数据的,相当于被隔离,每个进程被分配到不同的虚拟地址中
- 系统调用:Linux内核对应用有访问权限,用户只能在应用层通过系统调用,调用内核的某些程序
- binder驱动:它负责各个用户的进程,通过binder通信内核来进行交互的模块
2、为什么使用Binder
- 性能上,相比传统的Socket更加高效
- 安全性高,支持协议双方互相校验
3、Binder通信原理
- Service端通过Binder驱动在ServiceManager的查找表中注册Object对象的add方法
- Client端通过Binder驱动在ServiceManager的查找表中找到Object对象的add方法,并返回proxy对象的add方法,add方法是个空实现,proxy对象也不是真正的Object对象,是通过Binder驱动封装好的代理类的add方法
- 当Client端调用add方法时,Client端会调用proxy对象的add方法,通过Binder驱动去请求ServiceManager来找到Service端真正对象,然后调用Service端的add方法
4、AIDL
- 客户端通过aidl文件的Stub.asInterface()方法,拿到Proxy代理类
- 通过调用Proxy代理类的方法,将参数进行封包后,调用底层的transact()方法
- transact()方法会回调onTransact()方法,进行参数的解封
- 在onTransact()方法中调用服务端对应的方法,并将结果返回
5、BpBinder和BBinder
BpBinder(客户端)对象和BBinder(服务端)对象,它们都从IBinder类中派生而来,BpBinder(客户端)对象是BBinder(服务端)对象的代理对象,关系图如下
- client端:BpBinder.transact()来发送事务请求
- server端:BBinder.onTransact()会接收到相应事务
- Webview与js交互
Android调用Js 如果想4.4以上与4.4以下都能兼容的话可以做判断
- 获取系统版本号 int version=Build.VERSION.SDK.INT
- If(version>18){使用WebView.evaluateJavascript()}else{WebView.loadUrl()}
addjavascriptInterface() 4.2以上在 被掉用的android方法上面加上@javascriptInterface注解就好了
addjavascriptInterface() 4.2以下存在漏洞 当JS拿到Android这个对象后,就可以调用这个Android对象中所有的方法,包括系统类(java.lang.Runtime 类),从而进行任意代码执行。
具体获取系统类的描述:(结合 Java 反射机制)
- Android中的对象有一公共的方法:getClass() ;
- 该方法可以获取到当前类 类型Class
- 该类有一关键的方法: Class.forName;
- 该方法可以加载一个类(可加载 java.lang.Runtime 类)
- 而该类是可以执行本地命令的
解决办法: 采用拦截prompt()进行漏洞修复。
每次当 WebView 加载页面前加载一段本地的 JS 代码,原理是:
- 让JS调用Javascript方法:该方法是通过调用prompt()把JS中的信息(含特定标识,方法名称等)传递到Android端;
- 在Android的onJsPrompt()中 ,解析传递过来的信息,再通过反射机制调用Java对象的方法,这样实现安全的JS调用Android代码。
- Rxjava操作符
事件创建:
1. Create() 完整的创建被观察者对象;
2. Just() 快速创建被观察者对象 并且直接发送事件;
3. fromIterabe() 快速创建一个被观察者对象,直接发送list集合;
4.fromArray() 快速创建一个被观察者对象,直接发送array数组;
5. defer() 动态获取最新的observable对象数据;
6.Timer() 快速创建一个被观察者对象 延时指定时间后,发送一个long类型数据;
7.intervalRange() 每隔指定时间发送数据,可以指定发送的数据与数量;
range(3,10) 连续发送一个事件序列,可以指定范围 参数1:从3开始, 参数2:到10结束 输出结果:3,4,5,6,7,8,9,10;
事件转换:
8.Map() 将被观察者发送的事件转换为任意类型事件;
9.FlatMap() 将每个事件拆分放到一个新的observable中, 再将这些新observable放到一个总的observable中,将这个总的observable事件无序的发送给观察者;
10.Concatmap() 与flatmap()一样,但它是有序的;
11.Buffer() 将要发动的事件中,回去一定数量的事件放到缓存区中,这里放入的事件数量是可以指定的;
事件合并:
12.Concat ()/concatArray() 将多个被观察者合并后一起发动,串行执行, Concat()合并的被观察者<=4个,ConcatArray()合并的事件>4个 ;
13.Merge()/MergeArray() 将多个被观察者合并后一起发动,并行执行 merge() <=4个 mergearray()>4个;
14.Zip() 合并多个被观察者Observable事件,一起发动;
事件过滤:
15.Filter() 过滤特定的条件事件;
16.OfTypr() 过滤特定的数据类型数据;
17.skip() / skipLast()比如1,2,3,4,5 skip(1)跳过事件的前1项,skipLast(2)跳过事件的后两项 结果为2,3;
18.distinct() / distinctUntilChanged() 过滤事件序列中重复的事件 / 连续重复的事件;
19.Take(2)指定观察者最多能接收到2个事件数量;
20.takeLast(3)指定观察者只能接收到被观察者发送的最后3个事件;21.firstElement() / lastElement()仅选取第1个元素 / 最后一个元素
22.elementAt() 通过索引获取某个元素