各大企业面试笔记

德丰集团面试:

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).队列:应用的范围也不一样,线程池,消息传递机制,以及系统中的各种资源的管理等用到的一般都是队列。
栈的:问题的求解,函数调用和递归实现,计算机中断,数据保存和恢复。


image.png

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 文件,它以 为根。我们可能让 path 或 group 的属性进行动画,因此需要对进行动画的 path 或 group 命名。

(2). android.graphics.drawable.AnimatedVectorDrawable 对应的 XML 文件,它以 为根。 这里定义需动画的 path 或 group 的 的 animation 属性指定为一般的 ObjectAnimator 或 AnimatorSet 对应的 XML。

(3). android.graphics.drawable.Animator 对应的 XML 文件,它以 等为根, 对应 AnimatorSet 和 ObjectAnimator。
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


image.png

Android自带的Lint代码检查工具


image.png

image.png
image.png

SonarLint 实时分析每个文件

image.png

厦门与梦公司面试:

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协议。(寻址和路由)
第四层:数据链路层 (物理介质访问)
第四层:接口和电缆 (二进制数据流传输)


image.png

Socket:https://www.jianshu.com/p/fb4dfab4eec1

image.png

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()));
    }

image.png
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

你可能感兴趣的:(各大企业面试笔记)