德丰集团面试:
1.热更新的实现原理
andfix直接在native层替换原有的方法。由于底层替换原理只支持方法替换,不支持方法的增加
和减少,成员字段的增加和减少
在android5.0之前,每个android应用只含有一个dex文件,dex的方法数量被限制在了65535之内
,导致apk引入大量第三方sdk后方法数量超过限制无法编译通过。为了解决这个问题,Google推
出多dex文件的解决方案multidex,一个apk可以包含多个dex文件。通过Multidex.install
(this)完成dex文件的加载。
在编译时通过新旧两个Dex生成差异patch.dex,将差异patch.dex重新和原始安装包的旧Dex合并
还原为新的Dex,这个过程是在后台进程中进行,由于采用ClassLoader机制,所以需要app重启。
2.推送的实现原理
常驻型的广播,onReceiver
(JPushInterface.ACTION_MESSAGE_RECEIVED.equals(intent.getAction()))
Logger.d(TAG, "[MyReceiver] 接收到推送下来的自定义消息: " + bundle.getString
(JPushInterface.EXTRA_MESSAGE));
在app登录的时候,一般把用户的id作为唯一的推送标识,进行绑定,然后推送的时候,可以根
据这个id进行推送。
3.JNI使用
4.Inflate解析文件的原理
(1).LayoutInflater其实就是使用Android提供的pull解析方式来解析布局文件的
(2).会调用inflate(XmlPullParse parse ,ViewGruop root ,boolean AttachToRoot)
创建根View ,createViewFromTag(节点名称,参数),根据节点名称创建View对象,在
createViewFromTag()方法内部有会去调用CreateView方法,然后使用
反射的方式创建View的实例并返回。
(3).会递归性的调用rInflate(XmlPullParse ,ViewGroup,AttributeSet)这个根布局下面
的子元素,createViewFromTag()方法来创建View的实例,然后还会在rInflate()方法来查找这
个View下的子元素,每次递归完成后则将这个View添加到父布局当中
Root为null,attachToRoot将失去作用
Root不为null,attachToRoot为true,这时为给该布局指定一个布局root
Root不为null,attachToRoot为false,布局文件最外层的所有layout属性进行属性将会失效。
5.java虚拟机和Delvik虚拟机的区别
A:JVM运行的java字节码(节码码按照class文件格式的规范组成了class文件),但是Dalvik运
行的dex文件(dx工具转换而来)
B:JVM是基于栈,必须使用指令来载入和操作数据,所以需要的指令会更多,而Dalvik是基于寄
存器 的,(有效的减少指令的分发和减少内存的读写访问)
C:Dalvik可执行文件的体积更小(Class文件包含多个不同的方法签名,如果A类文件引用B类文
件中的方法,方法签名也会被复制到A类文件中,同样的大量的字符串常量也会在多个多个文件
中被重复使用,这些冗余信息会增加文件体积),SDK中有个Dx工具负责将Java字节码转换
dalvik字节码(class文件转换为dex文件),所有的类文件共享一个常量池,使得相同的字符串
,常量在dex文件中只出现一次,较小文件的体积。
6.Context 有哪几种,都有什么区别
Applicaton,Activity,Service 一共三种
区别:
(1)Application继承自ContextTheamWrap(带主题的相关类,内部包含了主题相关的接口,可
以加在主题),Activity和Service继承自ContextWrap,并不需要加载主题。
(2)生命周期不一样,applicaton和应用的生命周期绑定,但是activity和service的自身的生
命周期绑定。
(3)使用的场景也会有不一样,比如启动一个activity,show一个dialog,或者
layotuInflate解析一个文件都只能使用activigty用了,application和service不可以。
(4)applicaton一个,activity和service有多个。
7.线程中sleep和wait有啥区别。
(1).一个来自Thread类,一个来自Object类
(2).sleep没有释放锁而wait方法释放了锁,使得其他的线程可以使用同步代码块,或者方法
。
sleep不出让系统资源,wait进入线程池等待,出让系统资源,一直等到notify/notifyAll
唤醒等待池中的所有线程,才会进入就绪队列等待OS分配系统资源。
(3).使用范围不同,wait,notify和notifyAll只能在同步代码块,或者同步方法中使用,但是
sleep可以在任何地方使用。
(4).sleep必须要捕获异常,但是wait,notify和notifyAll不需要捕获异常。
8.queue和Stack有啥不同
(1).队列先进先出,栈先进后出
(2).队列只能在表尾进行插入,在表头的进行删除,栈只能在表尾(栈顶)进行插入和删
除操作
(3).遍历数据的速度不同
队列遍历的速度更快,从队头或者队尾开始遍历,它是基于地址指针进行遍历,不需要另外的开
辟临时空间,不影响数据的结构。
栈遍历的速度比较慢,只能从栈顶开始遍历,为了保证遍历前的一致性,需要另外开辟临时空间。
(4).队列:应用的范围也不一样,线程池,消息传递机制,以及系统中的各种资源的管理等用到的一般都是队列。
栈的:问题的求解,函数调用和递归实现,计算机中断,数据保存和恢复。
9.ArrayList和LinkedList有啥区别
(1).arrayList是基于动态数组,linkedList是基于双向链表。
(2).对于随机访问get和set,ArrayList优于LinkedList,因为ArrayList可以随机定位,而LinkedList要移动指针一步一步的移动到节点处。
(3).对于新增和删除操作add和remove,LinedList比较占优势,只需要对指针进行修改即可,而ArrayList要移动数据来填补被删除的对象的空间.
性能上的区别:
1.对ArrayList和LinkedList而言,在列表末尾增加一个元素所花的开销都是固定的。对 ArrayList而言,主要是在内部数组中增加一项,指向所添加的元素,偶尔可能会导致对数组重新进行分配;而对LinkedList而言,这个开销是 统一的,分配一个内部Entry对象。
2.在ArrayList集合中添加或者删除一个元素时,当前的列表所所有的元素都会被移动。而LinkedList集合中添加或者删除一个元素的开销是固定的。
3.LinkedList集合不支持 高效的随机随机访问(RandomAccess),因为可能产生二次项的行为。
4.ArrayList的空间浪费主要体现在在list列表的结尾预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗相当的空间。
对于需要快速插入,删除元素,应该使用LinkedList,如果需要快速随机访问元素,应该使用ArrayList。
六号网络面试:
1.低耦合高内聚(面向对象原则:低耦合高内聚,多聚合,少继承)
内聚:每个模块尽可能独立完成自己的功能,不依赖于模块外部的代码。
耦合:模块与模块之间接口的复杂程度,模块之间联系越复杂耦合度越高,牵一发而动全身。
2.Android项目安全性(混淆,加固)
minifyEanble true
proguard-android.txt
-keep 保留某个类不被混淆
-dontwarn去除警告
android四大组件不应该被混淆
使用了自定义控件那么要保证它们不参与混淆
对第三方库中的类不进行混淆
使用了 Gson 之类的工具要使 JavaBean 类即实体类不被混淆
定义的Application* 实体类
JNI中调用的类
3.Android模块化开发
4.Android动画(https://github.com/OCNYang/Android-Animation-Set)
视图动画
帧动画
属性动画 ObjectAnimator ValueAnimator,Animator
触摸反馈动画,ripple ,selectableItemBackground,selectableItemBackgroundBorderless
转场动画,ActivityOptions.makeSceneTransitionAnimation(),transitionName
Slide,Fade 在activity的onCreate方法中。
揭露动画
createCircularReveal
SVG矢量动画 Vector
添加动画的矢量图片可为
完成一个AnimatedVectorDrawable需要创建3个文件
1). android.graphics.drawable.VectorDrawable 对应的 XML 文件,它以
(2). android.graphics.drawable.AnimatedVectorDrawable 对应的 XML 文件,它以
(3). android.graphics.drawable.Animator 对应的 XML 文件,它以
ConstraintSet 动画
PathData语法:https://blog.csdn.net/n4167/article/details/79184286
5.Google的一些最新的框架
6.策略模式(人上楼,通过楼梯,电梯上楼,可以构建出那些对象)
http://baijiahao.baidu.com/s?id=1601547440739500969&wfr=spider&for=pc
7.适配器模式
https://www.cnblogs.com/V1haoge/p/6479118.html
8.Git tag 指向的是我们的最后一次提交,就像被我封存起来一样,它其实是一个独立的分支,
或者说是一个不可变的分支.指向特定提交对象的引用使用git 创建一个tag ,这样一个不可修改的历史代码版本就像被我们封存起来一样,不论是运维发布拉取,或者以后的代码版本管理,都是十分方便的
9.像素点换算成byte字节
1个byte字节占用8位,1个像素点如果加载的是24位图片,就是占用3个字节,如果加载16位图片就是占用2个字节。
10.builder模式
https://blog.csdn.net/ljcITworld/article/details/80387378
11.责任链模式
https://blog.csdn.net/u012810020/article/details/71194853
12.代理模式
https://www.cnblogs.com/gonjan-blog/p/6685611.html
13.在自定义View中用到哪些设计模式
点击事件,观察者模式
事件传递,dispatchOnTouchEvent,onIntereceptTouchEvent,onTouchEvent
类似责任链模式,
模板方法模式
方圆集团面试:
内存优化管理,有哪些工具可以分析内存,怎么优化。
https://blog.csdn.net/tuke_tuke/article/details/52316285
FindBugs_IDEA --->Analyze Project files
Android自带的Lint代码检查工具
SonarLint 实时分析每个文件
厦门与梦公司面试:
IT线上公司面试:
TCP(传输控制协议)
TCP传输控制协议,为应用层提供可靠的,面向连接的基于流的服务
TCP有确认机制和三次握手机制,容易被人拦击利用攻击。
UP协议()
与TCP协议相反,为应用层提供不可靠的,无连接和基于数据报的服务。(网络质量差容易丢包)
TCP三次握手:
ONE:客户端发送Syn包到服务器端,并进入SYN_SEND状态.
TWO:服务器端收到SYN包,会发送一个SYN+ACK包给到客户端,并进入SYN_RECEIVED状态。
THREE:客户端收到服务器发送的SYN+ACK包,并发送ACK包到服务器端,自身进入established状态。
三次握手协议是为了防止失效的的连接请求被发送到服务器。
TCP四次挥手:
ONE:客户端发送一个FIN,用来关闭客户端到服务器的数据传送。
TWO:服务器端收到这个FIN,并发回一个ACK
THREE:与此同时,服务端还会关闭自身连接,发送一个FIN给客户端
FORU:客户端在向服务端发回ACK报文确认。
第一层:应用层 http(HypertextTransfer Protocol),ftp(File TransferProtocol) 协议,TELNET远程终端,POP3邮件协议版本。(为应用程序提供网络接口)
第二层:传输层 TCP和UDP协议。(建立端到端的连接)
第三层:网络层 IP协议。(寻址和路由)
第四层:数据链路层 (物理介质访问)
第四层:接口和电缆 (二进制数据流传输)
Socket:https://www.jianshu.com/p/fb4dfab4eec1
TCP编程的服务端流程:
1.创建ServerSocket类对象-serverSocket
2.使用serverSocket开始一直阻塞监听accept,等待客户端发送来数据并且得到socket
3.根据socket的输入流读取客户端的数据,根据socket的输出流返回给客户端数据
4.关闭socket以及IO流
public class Server {
public static void main(String[] args) {
try {
// 为了看流程,我就把所有的代码都放在main函数里了,也没有捕捉异常,直接抛出去了。实际开发中不可取。
// 1.新建ServerSocket对象,创建指定端口的连接
ServerSocket serverSocket = new ServerSocket(12306);
System.out.println("服务端监听开始了~~~~");
// 2.进行监听
Socket socket = serverSocket.accept();// 开始监听9999端口,并接收到此套接字的连接。
// 3.拿到输入流(客户端发送的信息就在这里)
InputStream is = socket.getInputStream();
// 4.解析数据
InputStreamReader reader = new InputStreamReader(is);
BufferedReader bufReader = new BufferedReader(reader);
String s = null;
StringBuffer sb = new StringBuffer();
while ((s = bufReader.readLine()) != null) {
sb.append(s);
}
System.out.println("服务器:" + sb.toString());
// 关闭输入流
socket.shutdownInput();
OutputStream os = socket.getOutputStream();
os.write(("我是服务端,客户端发给我的数据就是:"+sb.toString()).getBytes());
os.flush();
// 关闭输出流
socket.shutdownOutput();
os.close();
// 关闭IO资源
bufReader.close();
reader.close();
is.close();
socket.close();// 关闭socket
serverSocket.close();// 关闭ServerSocket
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
注意:我们手机端发送完一个请求后,服务端(Server)拿到数据,解析数据,返回给客户端数据,关闭所有资源,也就是服务器关闭了。这时,如果另一个客户端再想跟服务器进行通信时,发现服务器已经关闭了,无法与服务器再次进行通信。换句话说,只能跟服务器通信一次,服务端 只能支持单线程数据处理。也就是说,上面的服务器的代码无法实现多线程编程,只能进行一次通信。
作者:徐爱卿
链接:https://www.jianshu.com/p/fb4dfab4eec1
來源:
著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
TCP编程的客户端对象
1.创建客户端的socket对象
2.使用客户端的socket对象的输出流发送给服务器数据,使用客户端的socket对象的输入流得到服务端的数据
public void onClick(View view){
new Thread(){
@Override
public void run() {
super.run();
try {
//1.创建监听指定服务器地址以及指定服务器监听的端口号
Socket socket = new Socket("111.111.11.11", 12306);//111.111.11.11为我这个本机的IP地址,端口号为12306.
//2.拿到客户端的socket对象的输出流发送给服务器数据
OutputStream os = socket.getOutputStream();
//写入要发送给服务器的数据
os.write(et.getText().toString().getBytes());
os.flush();
socket.shutdownOutput();
//拿到socket的输入流,这里存储的是服务器返回的数据
InputStream is = socket.getInputStream();
//解析服务器返回的数据
InputStreamReader reader = new InputStreamReader(is);
BufferedReader bufReader = new BufferedReader(reader);
String s = null;
final StringBuffer sb = new StringBuffer();
while((s = bufReader.readLine()) != null){
sb.append(s);
}
runOnUiThread(new Runnable() {
@Override
public void run() {
tv.setText(sb.toString());
}
});
//3、关闭IO资源(注:实际开发中需要放到finally中)
bufReader.close();
reader.close();
is.close();
os.close();
socket.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
TCP的多线程编程
public class MultiThreadServer {
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(12306);
//死循环
while(true){
System.out.println("MultiThreadServer~~~监听~~~");
//accept方法会阻塞,直到有客户端与之建立连接
Socket socket = serverSocket.accept();
ServerThread serverThread = new ServerThread(socket);
serverThread.start();
}
} catch (IOException e) {
e.printStackTrace();
} catch(Exception e){
e.printStackTrace();
}
}
}
public class ServerThread extends Thread{
private Socket socket;
//在构造中得到要单独会话的socket
public ServerThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
super.run();
InputStreamReader reader = null;
BufferedReader bufReader = null;
OutputStream os = null;
try {
reader = new InputStreamReader(socket.getInputStream());
bufReader = new BufferedReader(reader);
String s = null;
StringBuffer sb = new StringBuffer();
while((s = bufReader.readLine()) != null){
sb.append(s);
}
System.out.println("服务器:"+sb.toString());
//关闭输入流
socket.shutdownInput();
//返回给客户端数据
os = socket.getOutputStream();
os.write(("我是服务端,客户端发给我的数据就是:"+sb.toString()).getBytes());
os.flush();
socket.shutdownOutput();
} catch (IOException e2) {
e2.printStackTrace();
} finally{//关闭IO资源
if(reader != null){
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(bufReader != null){
try {
bufReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(os != null){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
UDP协议
//客户端
private void udp() {
byte[] bytes = et.getText().toString().getBytes();
try {
/*******************发送数据***********************/
InetAddress address = InetAddress.getByName("192.168.232.2");
//1.构造数据包
DatagramPacket packet = new DatagramPacket(bytes, bytes.length, address, 12306);
//2.创建数据报套接字并将其绑定到本地主机上的指定端口。
DatagramSocket socket = new DatagramSocket();
//3.从此套接字发送数据报包。
socket.send(packet);
/*******************接收数据***********************/
//1.构造 DatagramPacket,用来接收长度为 length 的数据包。
final byte[] bytes1 = new byte[1024];
DatagramPacket receiverPacket = new DatagramPacket(bytes1, bytes1.length);
socket.receive(receiverPacket);
runOnUiThread(new Runnable() {
@Override
public void run() {
tv.setText(new String(bytes1, 0, bytes1.length));
}
});
// socket.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
//服务端
public class UDPServer {
public static void main(String[] args) throws IOException {
byte[] buf = new byte[1024];
// 一:接受数据
// 1.创建接受数据的数据包
DatagramPacket packet = new DatagramPacket(buf, buf.length);
// 2.创建UPD 的 socket
DatagramSocket socket = new DatagramSocket(12306);
// 3.接收数据
System.out.println("服务端开始监听!~~~~");
socket.receive(packet);
// 4.处理数据
System.out.println("服务端:" + new String(buf, 0, buf.length));
// 二:返回数据
DatagramPacket p = new DatagramPacket(buf, buf.length, packet.getAddress(), packet.getPort());
socket.send(p);
socket.close();
}
}
线程池的类型:
(1).newFixedThreadPool
这是一种数量固定的线程池,当线程处于空闲的时候,并不会被回收,除非线程池被关闭.
当所有的线程都处于活动状态时,新任务都会处于等待状态,直到有线程空闲出来.
由于FixedThreadPool中只有核心线程并且这些核心线程不会被回收,这意味着它能够更加快速地响应外界的请求
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
(2).newScheduledThreadPool
//核心线程数是固定的,非核心线程无限大,并且非核心线程数有10s的空闲存活时间,这类线程池主要用于执行定时任务和具有固定周期的重复任务.
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSzie) {
return new ScheduledThreadPoolExecutor(corePoolSzie);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
(3).newCachedThreadPool
无核心线程,最大线程数为Integer.MAX_VALUE(2147483647),当线程池中所有线程都处于活动的状态时,线程池会创建新的线程来处理新任务,否则就会复用空闲线程来处理
(3-1)比较适合执行大量的耗时较少的任务.
(3-2)当整个线程都处于闲置状态时,线程池中的线程都会超时而被停止,这时候的CacheThreadPool几乎不占任何系统资源的.
public static ExecutorService newCacheThreadPool(){
return new ThreadPoolExecutor(
0,Integer.MAX_VALUE,
60L,TimeUnit.SECONDS,
new SynchronousQueue()
);
}
(4).newSingleThreadExecutor
这类线程池内部只有一个核心线程,它确保所有的任务都在同一个线程中按顺序执行.
SingleThreadExecutor的意义在于统一外界所有任务到一个线程,这使得这些任务之间不需要处理线程同步的问题.
public static ExecutorService newSingleThreadExecutor() {
return Executors.newSingleThreadExecutor();
}
//特点:
//线程中只有一个核心线程
//并且无超时时间
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
public class MainTest {
public static void main(String[] args){
//线程池中只允许同时存在2个线程
System.out.println("****************************newFixedThreadPool*******************************");
ExecutorService newFixedThreadPool=Executors.newFixedThreadPool(2);
for(int j=0;j<4;j++){
final int index=j;
newFixedThreadPool.execute(new MyRunnable(index));
}
//创建一个定长线程池,延时执行某个代码
System.out.println("****************************newScheduleThreadPool*******************************");
ScheduledExecutorService newScheduleThreadPool= Executors.newScheduledThreadPool(2);
for(int k=0;k<4;k++){
final int index=k;
//执行结果:延迟三秒之后执行,除了延迟执行之外和newFixedThreadPool基本相同,可以用来执行定时任务
newScheduleThreadPool.schedule(new MyRunnable(index),3, TimeUnit.SECONDS);
}
System.out.println("****************************newFixedThreadPool*******************************");
ExecutorService newSingleThreadPool=Executors.newSingleThreadExecutor();
for(int l=0;l<4;l++){
final int index=l;
//执行结果:只存在一个线程,顺序执行
newSingleThreadPool.execute(new MyRunnable(index));
}
System.out.println("****************************newCachedThreadPool*******************************");
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
for(int i=0;i<4;i++) {
final int index=i;
newCachedThreadPool.execute(new MyRunnable(index));
}
}
}
public class MyRunnable implements Runnable {
private Integer index;
public MyRunnable(Integer index) {
this.index = index;
}
@Override
public void run() {
/***
* 业务......省略
*/
try {
System.out.println("开始处理线程!!!");
Thread.sleep(index*1000);
System.out.println("我的线程标识是:"+this.toString());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
天下科技集团面试:
自定义ViewGrop绘制自身,requestlayout的会导致子控件的重新绘制
TY:
HTTPS原理,进程间的通信,有序和无序集合
Seek Top:MVVM模式中,RecycleView如何进行绑定一下
天猫集团:4中启动模式:https://www.cnblogs.com/claireyuancy/p/7387696.html