1.Hanlder:
Handler,Message,looper和MessageQueue构成了安卓的消息机制,handler创建后可以通过sendMessage将消息加入消息队列,然后looper不断的将消息从MessageQueue中取出来,回调到Hander的handleMessage方法,从而实现线程的通信。
从两种情况来说,第一在UI线程创建Handler,此时我们不需要手动开启looper,因为在应用启动时,在ActivityThread的main方法中就创建了一个当前主线程的looper,并开启了消息队列 ,消息队列是一个无限循环,为什么无限 循环不会ANR?
我们可以想想什么情况下会发生ANR,第一,事件没有得到处理,第二,事件正在处理,但是没有及时完成,而对事件进行处理的就是looper,所以只能说事件的处理如果阻塞会导致ANR,而不能说looper的无限循环会ANR
另一种情况就是在子线程创建Handler,此时由于这个线程中没有默认开启的消息队列,所以我们需要手动调用looper.prepare(),并通过looper.loop开启消息
主线程Looper从消息队列读取消息,当读完所有消息时,主线程阻塞。子线程往消息队列发送消息,并且往管道文件写数据,主线程即被唤醒,从管道文件读取数据,主线程被唤醒只是为了读取消息,当消息读取完毕,再次睡眠。因此loop的循环并不会对CPU性能有过多的消耗。
详解
1,Message 是线程之间传递的消息,它可以在内部携带少量信息,用于在不同线程之间交换数据。
2. Handler 是处理者,它主要用于发送和处理消息。 发送消息一般使用 handler 的 sendMessage()方法,处理消息会调用 handleMessage() 方法。
3. MessageQueue 是消息队列,它主要用于存放所有由 Handler 发送过来的消息,这部分消息会一直在消息队列中,等待被处理。每个线程中只会有一个 MessageQueue 对象。
4. Looper 是每个线程中 MessageQueue /的管家, 调用 loop() 方法后,就会进入到一个无限循环当中,然后每当发现 MessageQueue 中存在一条消息,就会将其取出,并传递到 handleMessage()方法当中。每个线程中也只会有一个Looper对象。
内部实现:
ThreadLocal 是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,其他线程
则无法获取。Looper、ActivityThread 以及 AMS 中都用到了 ThreadLocal。当不同线程访问
同一个 ThreadLocal 的 get 方法,ThreadLocal 内部会从各自的线程中取出一个数组,然后
再从数组中根据当前 ThreadLcoal 的索引去查找对应的 value 值
Looper
loop 方法是一个死循环,唯一跳出循环的方式是 MessageQueue 的 next 方法返回了 null。
当 Looper 的 quit 方法被调用时,Looper 就会调用 MessageQueue 的 quit 或者
qutiSafely 方法来通知消息队列退出,当消息队列被标记为退出状态时,它的 next 方法就会
返回 null。loop 方法会调用 MessageQueue 的 next 方法来获取新消息,而 next 是一个
阻塞操作,当没有消息时,next 会一直阻塞,导致 loop 方法一直阻塞。Looper 处理这条
消息: msg.target.dispatchMessage(msg),这里的 msg.target 是发送这条消息的 Handler
对象。
HandlerThread
HandlerThread 集成了 Thread,却和普通的 Thread 有显著的不同。普通的 Thread 主要用
于在 run 方法中执行一个耗时任务,而 HandlerThread 在内部创建了消息队列,外界需要
通过 Handler 的消息方式通知 HanderThread 执行一个具体的任务。
2.AsyncTask
AsyncTask 内部也是 Handler 机制来完成的,只不过 Android 提供了线程池来执行相应地任务,因为线程池的大小问题,所以 AsyncTask 只应该用来执行耗时时间较短的任务,
比如 HTTP 请求,大规模的下载和数据库的更改不适用于 AsyncTask,因为会导致线程池堵塞,没有 线程来执行其他的任务,导致的情形是会发生 AsyncTask 根本执行不了的问题。
因为asynctask实际上是一个线程池,最大只支持5个并发。如果有线程长时间占用,且没有空闲,则其他线程只能处于等待状态,会造成阻塞。
EventBus优点:开销小,代码优雅。将发送者和接受者解耦
在EventBus3.0之后,事件处理的方法可以随便取名,但是需要添加一个注解@Subscribe,并且要指定线程模型(默认为POSTING)
普通的方法是先注册(register),再post,才能接受到事件;
如果你使用postSticky发送事件,那么可以不需要先注册,也能接受到事件,也就是一个延迟注册的过程。
粘性事件就是为了解决这个问题,通过 postSticky 发送粘性事件,这个事件不会只被消费一次就消失,而是一直存在系统中,直到被 removeStickyEvent 删除掉。
那么只要订阅了该粘性事件的所有方法,只要被register 的时候,就会被检测到,并且执行。
IntentService
Service运行在主线程 ,不能做耗时操作,在执行完毕之后,会放置,并不会自动关闭。
IntentService他运行在子线程中,可以做耗时操作,他会同步执行我们所有的任务,当所有任务执行完毕之后,自动关闭自己。
BindService和StartService区别
使用startService()方法启用服务,调用者与服务之间没有关连,即使调用者退出了,服务仍然运行。
onCreate()- >onStartCommand()->startService()->onDestroy()
使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止,大有“不求同时生,必须同时死”的特点。
onCreate()->onBind()->onUnbind()->onDestroy()
4.线程:
线程和进程的区别:进程是cpu资源分配的最小单位,线程是cpu调度的最小单位。
进程之间不能共享资源,而线程共享所在进程的地址空间和其它资源。
一个进程内可拥有多个线程,进程可开启进程,也可开启线程。
一个线程只能属于一个进程,线程可直接使用同进程的资源,线程依赖于进程而存在。
介绍:线程:是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位. 线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计 数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的 全部资源.
三种方式:
方式1:继承Thread类定义Thread类的子类,并重写该类的run()方法
注意:千万不要调用run方法,如果调用run方法好比是对象调用方法,依然还是只有一个线程,并没有开启新的线程.
方式2:实现Runnable接口
方式3:实现Callable接口
区别:
1,Runnable没有返回值,Callable可以返回执行结果,是个泛型和Future、FutureTask配合可以用来获取异步执行结果
注:Callalbe接口支持返回执行结果,需要调用FutureTask.get()方法得到,此方法会阻塞不能继续往下走
2,Callable接口的call()方法润许抛出异常;Runnable的run()方法只能在内部消化,不能往上继续抛
线程的状态(生命周期)
1、新建状态(New):新创建了一个线程对象。
2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于“可运行线程池”中,变得可运行,只等待获取CPU的使用权。即在就绪状态的线程除CPU之外,其它的运行所需资源都已全部获得。
3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。
5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
阻塞的情况分三种:
1、等待阻塞:运行的线程执行wait()方法,该线程会释放占用的所有资源,包括锁标记及CPU,JVM会把该线程放入“等待池”中。进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify()或notifyAll()方法才能被唤醒,
2、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入“锁池”中,等待获取锁标记(这时的锁池里也许已经有了其他线程在等待获取锁标记,这时它们处于队列状态,既先到先得),一旦线程获得锁标记后,就转入就绪状态。
3、其他阻塞:运行的线程执行sleep()或其他线程执行join()方法,或者发出了I/O请求时,让出CPU,JVM会把该线程置为阻塞状态,但是不会释放所占用的资源,锁标记。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
线程安全(多线程数据同步问题)
在单线程中不会出现线程安全问题,而在多线程编程中,有可能会出现同时访问同一个资源的情况,这种资源可以是各种类型的的资源:一个变量、一个对象、一个文件、一个数据库表等,而当多个线程同时访问同一个资源的时候,就会存在一个问题:
由于每个线程执行的过程是不可控的,所以很可能导致最终的结果与实际上的愿望相违背或者直接导致程序出错。
这里面,这个资源被称为:临界资源(也有称为共享资源)。
是在访问临界资源的代码前面加上一个锁,当访问完临界资源后释放锁,让其他线程继续访问。
在Java中,提供了两种方式来实现同步互斥访问:synchronized和Lock。
sleep 方法和 wait 方法有什么区别?
这个问题常问,sleep 方法和 wait 方法都可以用来放弃 CPU 一定的时间,不同点
在于如果线程持有某个对象的监视器,sleep 方法不会放弃这个对象的监视器,
wait 方法会放弃这个对象的监视器
synchronized
synchronized是Java中的关键字,是一种同步锁,临界资源使用完毕之后会自动。它修饰的对象有以下几种:
1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
3. 修饰一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
4. 修饰一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。
流程:两个线程ab,A获取锁,执行代码,释放A锁 线程b获取锁,执行代码,b释放
线程池
多线程的异步执行方式,虽然能够最大限度发挥多核计算机的计算能力,但是如果不加控制,反而会对系统造成负担。线程本身也要占用内存空间,大量的线程会占用内存资源并且可能会导致Out of Memory。即便没有这样的情况,大量的线程回收也会给GC带来很大的压力。
为了避免重复的创建线程,线程池的出现可以让线程进行复用。当有工作来,就会向线程池拿一个线程,当工作完成后,并不是直接关闭线程,而是将这个线程归还给线程池供其他任务使用。
常见的四种线程池
newFixedThreadPool
固定大小的线程池,可以指定线程池的大小,该线程池corePoolSize和
maximumPoolSize相等,阻塞队列使用的是LinkedBlockingQueue,大小为整数最大值。
当任务提交十分频繁的时候,LinkedBlockingQueue
迅速增大,存在着耗尽系统资源的问题。而且在线程池空闲时,即线程池中没有可运行任务时,它也不会释放工作线程,还会占用一定的系统资源,需要shutdown。
newSingleThreadExecutor
单个线程线程池,只有一个线程的线程池,阻塞队列使用的是LinkedBlockingQueue,若有多余的任务提交到线程池中,则会被暂存到阻塞队列,待空闲时再去执行。按照先入先出的顺序执行任务。
newCachedThreadPool
缓存线程池,缓存的线程默认存活60秒。线程的核心池corePoolSize大小为0,最大线程数为Integer.MAX_VALUE,阻塞队列使用的是SynchronousQueue。是一个直接提交的阻塞队列, 他总会迫使线程池增加新的线程去执行新的任务。在没有任务执行时,当线程的空闲时间超过keepAliveTime(60秒),则工作线程将会终止被回收,当提交新任务时,如果没有空闲线程,则创建新线程执行任务,会导致一定的系统开销。如果同时又大量任务被提交,而且任务执行的时间不是特别快,那么线程池便会新增出等量的线程池处理任务,这很可能会很快耗尽系统的资源。
newScheduledThreadPool
定时线程池,该线程池可用于周期性地去执行任务,通常用于周期性的同步数据
5.内存泄漏ANR
--什么情况导致内存泄漏?
1.内存泄露
应该被回收的内存,因为某些原因,无法被回收
常见场景:
(1)该关闭的没有关闭:io流,cursor,bitmap等
(2)长生命周期对象持有短生命周期对象的引用(单例模式的上下文、activity中的子线程、handler、非静态内部类、mvp等等很多)
解决办法:
(1)在结束使用时候关闭相关流
(2)如果必须使用上下文,则考虑在长生命周期的地方使用applicationContext;
(3)在activity的onDestory中,销毁或者移除可能未完成的任务(handler.removeAllMessage、mvp解绑等等)
(4)使用静态内部类
使用的工具:点击工具栏中的 Profile 图标
6.内存溢出OOM
1.Bitmap过大
解决办法:
二次采样:第一次采样
第一次采样我主要是想要获得图片的压缩比例,假如说我有一张图片是200*200,那么我想把这张图片的缩略图显示在一个50*50的ImageView上,那我的压缩比例应该为4,那么这个4应该怎么样来获得呢?这就是我们第一步的操作了,我先加载图片的边界到内存中,这个加载操作并不会耗费多少内存,加载到内存之后,我就可以获得这张图片的宽高参数,然后根据图片的宽高,再结合控件的宽高计算出缩放比例。
第二次采样
在第一次采样的基础上,我来进行二次采样。二次采样的时候,我把第一次采样后算出来的结果作为一个参数传递给第BitmapFactory,这样在加载图片的时候系统就不会将整张图片加载进来了,而是只会加载该图片的一张缩略图进来,这样不仅提高了加载速率,而且也极大的节省了内存,而且对于用户来说,他也不会有视觉上的差异。
2.列表中图片过多,快速滑动时
解决办法:
通过滑动距离和时间判断滑动的速度,当速度过快的时候停止图片框架加载,当速度恢复慢速的时候开始加载
3.内存抖动:
短时间创建和销毁大量对象,造成内存频繁增加和销毁
常见场景:for循环 while循环 频繁刷新的view中onDraw方法里
三级缓存分为:
1.内存缓存 :优先加载,速度最快
2.本地缓存 :次优先加载,速度较快
3.网络缓存 :最后加载,速度较慢
二、使用图片缓存的原因
1.提高用户体验:如果每次启动都从网络下载图片,势必会加载很慢,图片无法显示,或需要很久才能完全显示,用户体验及其不好
2.节约流量:如果每次加载页面,甚至只是滑动控件浏览就会下载的话,会消耗很多流量,占用网络资源的同时,也会因为应用耗流量而用户数量级受到影响
mvvm
博客地址:https://blog.csdn.net/qq_42234894/article/details/85268600
MVP和MVC最大的区别就是:在MVP中View并不会直接使用Model,他们的之间的所有通信都是通过Presenter层进行,所有的交互都发生的presenter内部,在MVC中View会直接从Model中读取数据而不是通过Controller.
MVC:
优点:
1.耦合性低
视图层和业务层分离,这样就允许改变视图,不用重新编译模型和控制器的代码,同样当业务流程和业务需求改变也只需要改变模型层,应为M、V、C的分离所以很容易改变应用程序的数据层和业务规则。
2.重用性高
允许不同样式的视图共享一个模型
3.生命周期成本低
使开发和维护用户接口的技术含量降低
4.部署快
他可以让一个开发人员专注于业务逻辑的开发,而另一个开发人员专注于界面的开发。
5.维护性高
因为M、V、C层的分离所以更易于维护和修改
缺点:
1.不适合小型和中型规模的应用程序
2.视图和控制器之间过于紧密的连接
视图不能失去控制器,控制器也不能失去视图。
MVP:
优点:
降低耦合度
1、模型与视图完全分离,我们可以修改视图而不影响模型
2、可以更高效地使用模型,因为所有的交互都发生在一个地方——Presenter内部
3、我们可以多个视图层复用一个presenter层,而不需要改变Presenter的逻辑。这个特性非常的有用,因为视图的变化总是比模型的变化频繁。
缺点:视图和Presenter的交互会过于频繁。还有一点需要明白,如果Presenter过多地渲染了视图,往往会使得它与特定的视图的联系过于紧密。一旦视图需要变更,那么Presenter也需要变更了
MVVM
1. 双向绑定技术,当Model变化时,View-Model会自动更新,View也会自动变化。很好的做到数据的一致性
2. 由于控制器的功能大都移动到View上处理,大大的对控制器进行了瘦身
缺点
1. 数据绑定也使得bug很难被调试。比如你看到页面异常了,有可能是你的View的代码有bug,也可能是你的model的代码有问题。数据绑定使得一个位置的Bug被快速传递到别的位置,要定位原始出问题的地方就变得不那么容易了。
2. 数据双向绑定不利于代码重用。
3. 虽然使用方便了也很容易保证数据的一致性,但是长期持有,不释放内存就造成话费更多的内存。
8.Glide,Fresco
Fresco
优点:
1. 图片存储在安卓系统的匿名内存, 而不是虚拟机的堆内存中
所以, 应用程序有更多的内存使用, 不会因为图片加载而导致 oom, 同时也减少垃圾回收器频繁调用回收 Bitmap 导致的界面卡顿, 性能更高.
2. 渐进式加载图片, 支持图片从模糊到清晰加载
3. 图片可以以任意的中心点显示在 ImageView, 而不仅仅是图片的中心.
4. 图片改变大小也是在 native 进行的, 不是在虚拟机的堆内存, 同样减少 OOM
5. 很好的支持 GIF 图片的显示
缺点:
1. 框架较大, 影响 Apk 体积
2. 使用较繁琐
Glide
优点
Glide 不仅是一个图片缓存,它支持 Gif、WebP、缩略图。甚至是 Video
生命周期集成
通过设置绑定生命周期,我们可以更加高效的使用Glide提供的方式进行绑定,这样可以更好的让加载图片生命周期动态管理起来
高效的缓存策略
A. 支持Memory和Disk图片缓存
B. Glide 会根据你 ImageView 的大小来缓存相应大小的图片尺寸
比如你 ImageView 大小是200200,原图是 400400 ,而使用 Glide 就会缓存 200200 规格的图,非常灵活 & 加载速度快
C. 内存开销小
默认的 Bitmap 格式是 RGB_565 格式
Android关于图片内存计算,共有四种,分别是:
ALPHA_8:每个像素占用1byte内存
ARGB_4444:每个像素占用2byte内存
ARGB_8888:每个像素占用4byte内存(默认,色彩最细腻=显示质量最高=占用的内存也最大)
RGB_565:每个像素占用2byte内存(8bit = 1byte)
举例说明:一个32位的PNG=ARGB_8888=1204x1024,那么占用空间是:1024x1024x(32/8) =
4.2 缺点
使用方法复杂
由于Glide其功能强大,所以使用的方法非常多,其源码也相对的复杂
包较大
区别
对于一般App来说,Glide完全够用,而对于图片需求比较大的App,为了防止加载大量图片导致OOM,Fresco 会更合适一些。并不是说用Glide会导致OOM,Glide默认用的内存缓存是LruCache,内存不会一直往上涨。
9.设计模式与使用场景
a.建造者模式:
使用多个简单的对象一步一步构建成一个复杂的对象,这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式
b.观察者模式:当对象间存在一对多关系时,则使用观察者模式,比如当一个对象被修改时,则会自动通知依赖它的对象,观察者模式属于行为型模式
C.代理模式:代理模式的定义:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。
静态代理是由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。
D.单例模式:
单例模式是一种常用的软件设计模式,其定义是单例对象的类只能允许一个实例存在。
静态内部类的原理是:当 SingleTon 第一次被加载时,并不需要去加载 SingleTonHoler,只有当 getInstance() 方
法第一次被调用时,才会去初始化 INSTANCE,这种方法不仅能确保线程安全,也能保证单
例的唯一性,同时也延迟了单例的实例化。getInstance 方法并没有多次去 new 对象,取的
都是同一个 INSTANCE 对象。
工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。
AlertDialog、Notification 源码中使用了 Bulider(建造者)模式完成参数的初始化:
在 AlertDialog 的 Builder 模式中并没有看到 Direcotr 角色的出现,其实在很多场
景中,Android 并没有完全按照 GOF 的经典设计模式来实现,而是做了一些修
改,使得这个模式更易于使用。这个的 AlertDialog.Builder 同9时扮演了上下文中
提到的 builder、ConcreteBuilder、Director 的角色,简化了 Builder 模式的设计。
当模块比较稳定,不存在一些变化时,可以在经典模式实现的基础上做出一些精
简,而不是照搬 GOF 上的经典实现,更不要生搬硬套,使程序失去架构之美。
定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不
同的表示。即将配置从目标类中隔离出来,避免过多的 setter 方法。
优点:
1、良好的封装性,使用建造者模式可以使客户端不必知道产品内部组成
的细节。
2、建造者独立,容易扩展。
缺点:
会产生多余的 Builder 对象以及 Director 对象,消耗内存。
日常开发的 BaseActivity 抽象工厂模式:
定义:为创建一组相关或者是相互依赖的对象提供一个接口,而不需要指定它们
的具体类。
10.HTTP和HTTPS
Socket:
Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,才能使用TCP/IP协议。 socket连接:socket连接就是所谓的长连接,理论上客户端和服务器端一旦建立起连接将不会主动断掉;但是由于各种环境因素可能会是连接断开,比如说:服务器端或客户端主机down了,网络故障,或者两者之间长时间没有数据传输,网络防火墙可能会断开该连接以释放网络资源。
HTTP协议
HTTP是基于传输层的TCP协议,而TCP是一个端到端的面向连接的协议。所谓的端到端可以理解为进程到进程之间的通信。所以HTTP在开始传输之前,首先需要建立TCP连接,而TCP连接的过程需要所谓的“三次握手”。
HTTPS协议
HTTPS协议可以理解为HTTP协议的升级,就是在HTTP的基础上增加了数据加密。在数据进行传输之前,对数据进行加密,然后再发送到服务器。这样,就算数据被第三者所截获,但是由于数据是加密的,所以你的个人信息让然是安全的。这就是HTTP和HTTPS的最大区别。
简述Https与Http的区别
1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
. 什么是 socket?
所谓 Socket 通常也称作“套接字”,用于描述 IP 地址和端口,是一个通信连的句柄,应用程序
通常通过“套接字”向网络发送请求或者应答网络请求,它就是网络通信过程中端点的抽象表示。
它主要包括以下两个协议:
TCP (Transmission Control Protocol 传输控制协议):传输控制协议,提供的是面向连接、可靠 的字节流服务。当客户和服务器彼此交换数据前,必须先在双方之间建立一个 TCP 连接,之后才能 传输数据。TCP 提供超时重发,丢弃重复数据,检验数据,流量控制等功能保证数据能从一端传到 另一端。
UDP (User Datagram Protocl 用户数据报协议):用户数据报协议,是一个简单的面向数据报 的运输层协议。UDP 不提供可靠性,它只是把应用程序传给 IP 层的数据报发送出去,但是并不能保 证它们能到达目的地。由于 UDP 在传输数据报前不用在客户和服务器之间建立一个连接,且没有超 时重发等机制,故而传输速度很快。
TCP和UDP
TCP/IP模型是互联网的基础,它是一系列网络协议的总称。这些协议可以划分为四层,分别为链路层、网络层、传输层和应用层。
链路层:负责封装和解封装IP报文,发送和接受ARP/RARP报文等。
网络层:负责路由以及把分组报文发送给目标网络或主机。
传输层:负责对报文进行分组和重组,并以TCP或UDP协议格式封装报文。
应用层:负责向用户提供应用程序,比如HTTP、FTP、Telnet、DNS、SMTP等。
TCP和UDP的区别
TCP UDP
11.Android 五种存储方式
File,SharedPrefrence,Sqlite,ContentProvider,Net
SharedPreferences存储方式,它是Android提供的用来存储一些简单配置信息的一种机制,例如:登录用户的用户名与密码。其采用了Map数据结构来存储数据,以键值的方式存储,可以简单的读取与写入
文件存储方式是一种较常用的方法,在Android中读取/写入文件的方法,与Java中实现I/O的程序是完全一样的,提供了openFileInput()和openFileOutput()方法来读取设备上的文件。
网络存储方式利用HttpURLConnection对象,我们可以从网络中获取网页数据.
ContentProvider:当应用继承ContentProvider类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据。虽然使用其他方法也可以对外共享数据,但数据访问方式会因数据存储的方式而不同,如:采用文件方式对外共享数据,需要进行文件操作读写数据;采用sharedpreferences共享数据,需要使用sharedpreferences?API读写数据。而使用ContentProvider共享数据的好处是统一了数据访问方式。
Sqlite
https://zhidao.baidu.com/question/686566121438933012.html
12.优化
23、app优化
app优化:(工具:Hierarchy Viewer 分析布局 工具:TraceView 测试分析耗时的)
App启动优化
布局优化
响应优化
内存优化
电池使用优化
网络优化
用户体验优化其实就是让用户界面绘制更快
1.减少布局层级
复杂布局使用相对布局或者约束布局,尽量不要使用线性布局
view绘制原理:https://www.jianshu.com/p/5a71014e7b1b
2.使用三种标签
<include />标签能够重用布局文件
<merge />标签在UI的结构优化中起着非常重要的作用,它可以删减多余的层级,优化UI。<merge/>多用于替换FrameLayout或者当一个布局包含另一个时,<merge/>标签消除视图层次结构中多余的视图组。例如你的主布局文件是垂直布局,引入了一个垂直布局的include,这是如果include布局使用的LinearLayout就没意义了,使用的话反而减慢你的UI表现。这时可以使用<merge/>标签优化。
<ViewStub />标签最大的优点是当你需要时才会加载,使用他并不会影响UI初始化时的性能。各种不常用的布局想进度条、显示错误消息等可以使用<ViewStub />标签,以减少内存使用量,加快渲染速度。
3.避免过度绘制
过度绘制就是意味着这一帧被绘制多次。如果是静态的布局,可能影响不是很大,如果是动态的,比如ListView,GridView,ViewPager等在性能上就会差一点
第一:如果是相对比较复杂的布局,建议使用相对布局。
第二:如果需要多层嵌套,我们可以使用merge标签来减少嵌套。
第三:减少背景颜色的多次绘制
4.使用Fragment懒加载机制
这个很好理解
5.快速启动
冷启动白屏
将初始化背景设置成透明的或者app相关欢迎页
在Application类中减少初始化内容或放置到子线程中初始化,建议使用IntentService
三、流量的优化:
1.合理的合并网络请求
2.value为null的参数我们可以考虑不传key
3.开启gzip
4.post请求尽量使用json格式或者x-www-form-urlencoded格式
四、电量的优化:
1.使用完毕的硬件设备及时关闭(GPS、BT、NFC、重力感应器)
2.背景颜色尽量不要使用白色
13.自定义view
自定义View对于一个Android开发者来说是必须掌握的知识点,也是Android开发进阶的必经之路。
为什么要自定义View?主要是Android系统内置的View无法实现我们的需求,我们需要针对我们的业务需求定制我们想要的View。
自定义View的最基本的三个方法分别是: onMeasure()、onLayout()、onDraw();
View在Activity中显示出来,要经历测量、布局和绘制三个步骤,分别对应三个动作:measure、layout和draw。
测量:onMeasure()决定View的大小;
MeasureSpec是View的内部类,它封装了一个View的尺寸,在onMeasure()当中会根据这个MeasureSpec的值来确定View的宽高。
MeasureSpec 的数据是int类型,有32位。 前两位表示模式,后面30位表示大小size。则MeasureSpec = mode+size
UNSPECIFIED:不指定测量模式, 父视图没有限制子视图的大小,子视图可以是想要的任何
尺寸
EXACTLY:精确测量模式,视图宽高指定为 match_parent 或具体数值时生效,表示父
视图已经决定了子视图的精确大小
AT_MOST最大值测量模式,当视图的宽高指定为 wrap_content 时生效,此时子视图
的尺寸可以是不超过父视图允许的最大尺寸的任何尺寸
布局:onLayout()决定View在ViewGroup中的位置;
View.java的onLayout方法是空实现:因为子View的位置,是由其父控件的onLayout方法来确定的。
· onLayout(int l, int t, int r, int b)中的参数l、t、r、b都是相对于其父
控件的位置。
· 自身的mLeft, mTop, mRight, mBottom都是相对于父控件的位置。
绘制:onDraw()决定绘制这个View。
优秀模式
Android事件分发机制
三个重要角色
1、Activity:接收Down点击事件,传递给Phonewindow和DecorView
2、ViewGroup:拦截事件,或者继续传递给子0View
3、View:决定消费这个事件或者不消费从而返回给上一级
三个核心事件
1、dispatchTouchEvent():分发点击事件,false返回activity的ontouchevent ture消费去viewgroup的ontouchEvent
Super正常走
2、onTouchEvent() : return false super是不消费事件,并让事件往父控件的方向从下往上流动。return true 是消费事件。
3、onInterceptTouchEvent():拦截点击事件, return false super不拦截,允许事件向子View传递, return true拦截事件,不在向子View传递事件。
https://www.jianshu.com/p/e99b5e8bd67b
比如ScrollView嵌套ListView都是竖向滑动的时候,滑动方向相同。就需要使用到拦截事件的方法onInterceptTouchEvent(),来决定listview要不要滑动。ViewGroup的onInterceptTouchEvent方法是默认返回true的,所以我们这里要返回super方法才可以。
15.打包流程
6.简述如何瘦身
1.注释无用代码
2.删除无用图片
3.删除无用依赖
4.使用代码代替简单图片
5.使用webP格式图片
6.使用网络加载的形式获取一些资源
7.混淆
16.加密
MD5:不可逆
BASE64:将任意对象转成字符串
AES/DES:对称加密,加解密使用同一秘钥
RSA:非对称加密,通常使用公钥加密,私钥解密
17.第三方支付流程
微信支付:
客户端发起下单请求到我们服务器
我们服务器发起生成预订单请求到微信服务器
微信服务器生成预订单返回给我们服务器再返回给我们客户端
我们把预订单传给微信SDK
微信SDK使用预订单完成支付
微信服务器把支付结果同时通知微信SDK及我们服务器
微信SDK把支付结果告知我们客户端
我们客户端拿着支付结果与我们服务器做结果验证
21Serializable 和Parcelable 的区别
Serializable Java 序列化接口 在硬盘上读写 读写过程中有大量临时变量的生成,内部执行大量的i/o操作,效率很低。
Parcelable Android 序列化接口 效率高 使用麻烦 在内存中读写(AS有相关插件 一键生成所需方法) ,对象不能保存到磁盘中
22List,map,set
List , Set, Map都是接口,前两个继承至Collection接口,Map为独立接口
Set下有HashSet,LinkedHashSet,TreeSet
List下有ArrayList,Vector,LinkedList
Map下有Hashtable,LinkedHashMap,HashMap,TreeMap
Collection接口下还有个Queue接口,有PriorityQueue类
List 有序,可重复
ArrayList
优点: 底层数据结构是数组,查询快,增删慢。
缺点: 线程不安全,效率高
ArrayList,LinkedList扩容后的大小= 原始大小+原
始大小/2 + 1。(例如:原始大小是 10 ,扩容后的大小就是 10 + 5+1 = 16)
Vector
优点: 底层数据结构是数组,查询快,增删慢。
缺点: 线程安全,效率低
LinkedList
优点: 底层数据结构是链表,查询慢,增删快。
缺点: 线程不安全,效率高
—Set 无序,唯一
HashSet
底层数据结构是哈希表。(无序,唯一)
如何来保证元素唯一性?
1.依赖两个方法:hashCode()和equals()
LinkedHashSet
底层数据结构是链表和哈希表。(FIFO插入有序,唯一)
1.由链表保证元素有序
2.由哈希表保证元素唯一
TreeSet
底层数据结构是红黑树。(唯一,有序)
1. 如何保证元素排序的呢?
自然排序
比较器排序
2.如何保证元素唯一性的呢?
set 集合从原理上如何保证不重复?
1)在往 set 中添加元素时,如果指定元素不存在,则添加成功。
2)具体来讲:当向 HashSet 中添加元素的时候,首先计算元素的 hashcode 值,
然后用这个(元素的 hashcode)%(HashMap 集合的大小)+1 计算出这个元
素的存储位置,如果这个位置为空,就将元素添加进去;如果不为空,则用 equals
方法比较元素是否相等,相等就不添加,否则找一个空位添加。
Map接口有三个比较重要的实现类,分别是HashMap、TreeMap和HashTable。
TreeMap是有序的,HashMap和HashTable是无序的。
LinkedHashMap保存了记录的插入顺序
Hashtable的方法是同步的,HashMap的方法不是同步的。这是两者最主要的区别。
这就意味着:
Hashtable是线程安全的,HashMap不是线程安全的。
Hashtable效率较低 ,HashMap效率较高。
如果对同步性或与遗留代码的兼容性没有任何要求,建议使用HashMap。 查看Hashtable的源代码就可以发现,除构造函数外,Hashtable的所有 public 方法声明中都有 synchronized关键字,而HashMap的源码中则没有。
Hashtable不允许null值,HashMap允许null值(key和value都允许)
父类不同:Hashtable的父类是Dictionary,HashMap的父类是AbstractMap
23链表
介绍:链表是存储数据元素单元通过指针串接起来形成的,因此每个单元至少有两个域,一个域用于数据元素的存储,另一个或两个域是指向其他单元的指针
单向链表
单向链表只有一个指针域,在整个节点中数据域用来存储数据元素,指针域用于指向下一个具有相同结构的节点。
双向链表
双向链表也叫双链表。双向链表中不仅有指向后一个节点的指针,还有指向前一个节点的指针。这样可以从任何一个节点访问前一个节点,当然也可以访问后一个节点,以至整个链表。一般是在需要大批量的另外储存数据在链表中的位置的时候用
循环链表
头节点和尾节点被连接在一起的链表称为循环链表,这种方式在单向和双向链表中皆可实现。循环链表中第一个节点之前就是最后一个节点,反之亦然。
哈希表
哈希表是最基础的数据结构之一,利用键值对存储并检索数据的一种非线性结构。
直接取余法: f(x)=(x+MAX)mod size 其中MAX是一个比较大的数,size是存储空间
平方取中法:
除留余数法:
哈希冲突解决策略:开放寻址法
1.当插入新的元素时,使用哈希函数在哈希表中定位元素位置;
2.检查哈希表中该位置是否已经存在元素。如果该位置内容为空,则插入并返回,否则转向步骤 3。
3.如果该位置为 i,则检查 i+1 是否为空,如果已被占用,则检查 i+2,依此类推,直到找到一个内容为空的位置。
保活
方法:
1.开启前台Service(效果好,推荐)
2.Service中循环播放一段无声音频(效果较好,但耗电量高,谨慎使用)
3.双进程守护(Android 5.0前有效)
4.JobScheduler(Android 5.0后引入,8.0后失效)
通过使用 startForeground()方法将当前Service置于前台来提高Service的优先级。需要注意的是,对API大于18而言 startForeground()方法需要弹出一个可见通知,如果你觉得不爽,可以开启另一个Service将通知栏移除
一是在onStartCommand方法中返回START_STICKY,其作用是当Service进程被kill后,系统会尝试重新创建这个Service,且会保留Service的状态为开始状态,但不保留传递的Intent对象,onStartCommand方法一定会被重新调用。其二在onDestory方法中重新启动自己,也就是说,只要Service在被销毁时走到了onDestory这里我们就重新启动它。
博客:https://blog.csdn.net/LVXIANGAN/article/details/85776130
AIDL
应用场景:
1.什么是aidl?aidl英文全称Android interface definition language,通过英文名可知道是Android内部通信描述语言,通过它我们可以定义进程间的通信接口 IPC(interprocess communication)内部进程通信
2.2.aidl做了什么事情?
3.我是这样认为的,可能描述会有点错误,aidl会涉及到客户端和服务端,在服务端定义好aidl文件,
将服务的 aidl 放到对应的 src 目录,工程的 gen 目录会生成相应的接口类 在客户端绑定服务端的service,客户端就可以获取服务端的数据了!
4.3.aidl用在什么场景?
就拿我的需求说的:在Android手机中有两个应用程序分别为A,B,应用A想从应用B中获取数据(应用B中的数据可能从网络或者缓存中去取),当应用A开启时,向应用B取数据,此时应用B没有启动,但是应用A不想在那里等待应用B启动完成后,应用B去网络或者缓存中去数据返回个应用A,这个过程需要时间,可能会导致应用A出现ANR。所以就运用到了aidl技术
5.aidl可以实现Android手机上边应用间的相互访问,可以以实现不类似其中一个app有改动的时候通知其他所有的app的功能(类似广播功能),还可以通过远程aidl实现html上边跟客户端的交互,其中就需要用到类似监听器。
过程:
我们通过 bindService(Intent,ServiceConnect,int)方法绑定远程服务,在 bindService
中 有 一 个 ServiceConnec 接 口 , 我 们 需 要 覆 写 该 类 的
onServiceConnected(ComponentName,IBinder)方法,这个方法的第二个参数 IBinder 对象其实
就是已经在 aidl 中定义的接口,因此我们可以将 IBinder 对象强制转换为 aidl 中的接口类。
我们通过 IBinder 获取到的对象(也就是 aidl 文件生成的接口)其实是系统产生的代理对象,该
代理对象既可以跟我们的进程通信,又可以跟远程进程通信,作为一个中间的角色实现了进程间通信。
IPC
20、谈谈Android的IPC(进程间通信)机制
IPC 是内部进程通信的简称,是共享"命名管道"的资源。Android 中的 IPC 机制是为了让 Activity 和 Service 之间可以随时的进行交互,在 Android 中该机制,只适用于 Activity 和 Service 之间的通信,类似于远程方法调用,类似于 C/S 模式的访问。通过定义 AIDL 接 口文件来定义 IPC 接口。Servier 端实现 IPC 接口,Client 端调用 IPC 接口本地代理。
JNI及NDK
博客:https://blog.csdn.net/yyg_2015/article/details/72229892?utm_source=blogxgwz4
JNI的全称是Java Native Interface(Java本地接口)是一层接口,是用来沟通Java代码和C/C++代码的,是Java和C/C++之间的桥梁。通过JNI,Java可以完成对外部C/C++库函数的调用,相对的,外部C/C++也能调用Java中封装好的类和方法。
Java的优点是跨平台,和操作系统之间的调用由JVM完成,但是一些和操作系统相关的操作就无法完成,JNI的出现刚好弥补了这个缺陷,也完善了Java语言,将java扩展得更为强大。
JNI的应用场景:
实际中的驱动都是C/C++开发的,通过JNI,Java可以调用C/c++实现的驱动,从而扩展Java虚拟机的能力。另外,在高效率的数学运算、游戏的实时渲染、音视频的编码和解码等方面,一般都是用C开发的。
1.NDK的理解
NDK 提供了一系列的工具,帮助开发者迅速的开发 C/C++的动
态库,并能自动将 so 和 java 应用打成 apk 包。 NDK 集成了交叉编译器,只需简单的修改 mk 文件就可以创建出 so
2.
JNI开发步骤:
1.创建一个Android工程,在Java代码中中声明一个native方法。
2.使用javah命令生成带有native方法的头文件。
3.在该Android工程中创建JNI目录,并在jni目录中创建一个Hello.c文件,根据头文件实现C代码。写C代码时,结构体JNIEnv*对象对个别object对象很重要,在实现的C代码的方法中必须传入这两个参数
4.在JNI的目录下创建一个Android.mk文件,并根据需要编写里面的内容
5.在工程的根目录下执行ndk_build命令,编译.so文件
6.在调用Native()方法前,加载.so的库文件
热修复
在热修复出现之前,一个已经上线的app中如果出现了bug,即使是一个非常小的bug,不及时更新的话有可能存在风险,若要及时更新就得将app重新打包发布到应用市场后,让用户再一次下载,这样就大大降低了用户体验,当热修复出现之后,能让用户无感知修复了bug
java基础
Java继承封装多态
封装
将抽象得到的属性和方法组合在类中,隐藏内部实现细节,对外提供接口的机制
属性的封装
1.成员变量私有化(private修饰)
2.对外提供接口(提供setter / getter方法)
3.隐藏内部实现细节(在setter/getter方法中加入控制语句)
继承
继承是子类复用父类的属性和方法的机制
继承分类有哪些
单继承 多继承 多级继承 层次继承 混合继承
继承使用的关键字: extends
继承有什么特点
是面向对象的一大特征;关系是传递的;子类复用父类的属性和方法,并能拓展新功能
什么是方法重写
重写是子类重新编写父类方法以提升代码拓展性的机制
方法重写的特点
重写的方法与父类方法,方法名相同,参数列表相同,返回值相同(或为子类)
重写方法不能使用比父类更严格的访问权限
多态
同一个操作(行为或方法)因作用对象不同(子类与父类)而表现出不同的实现方式(方法重写)的机制
多态分为
编译时多态: 在编译时确定实现方式 通过重载实现
运行时多态: 在运行时确定实现方式 通过重写实现
重写(override)与重载(overload)的区别【重点】
OKHttp
1、 OKHttp 的特点
1.相较于 Volley,它的最大并发量为 64
2.使用连接池技术,支持 5 个并发的 socket 连接默认 keepAlive 时间为 5 分钟,
解决 TCP 握手和挥手的效率问题,减少握手次数
3.支持 Gzip 压缩,且操作对用户透明,可以通过 header 设置,在发起请求的时
候自动加入 header,Accept-Encoding: gzip,而我们的服务器返回的时候 header
中有 Content-Encoding: gzip
4.利用响应缓存来避免重复的网络请求
5.很方便的添加拦截器,通常情况下,拦截器用来添加,移除,转换请求和响应
的头部信息,比如添加公参等
6.请求失败,自动重连,发生异常时重连,看源码调用 recover 方法重连了一次
7.支持 SPDY 协议(SPDY 是 Google 开发的基于 TCP 的应用层协议,用以最小化网
络延迟,提升网络速度,优化用户的网络使用体验。SPDY 并不是一种用于替代
HTTP 的协议,而是对 HTTP 协议的增强。新协议的功能包括数据流的多路复用、
请求优先级以及 HTTP 报头压缩。谷歌表示,引入 SPDY 协议后,在实验室测试
中页面加载速度比原先快 64%)
8.使用 Okio 来简化数据的访问与存储,提高性能
2、 OkHttp 的缺点
1.消息回来需要切到主线程,主线程要自己去写。
2.调用比较复杂,需要自己进行封装。
3.缓存失效:网络请求时一般都会获取手机的一些硬件或网络信息,比如使用的
网络环境。同时为了信息传输的安全性,可能还会对请求进行加密。在这些情况
下 OkHttp 的缓存系统就会失效了,导致用户在无网络情况下不能访问缓存。
3、 OkHttp 框架中都用到了哪些设计模式
1.最明显的 Builder 设计模式,如构建对象 OkHttpClient,还有单利模式
2.工厂方法模式,如源码中的接口 Call
3.观察者模式如 EventListener,监听请求和响应
4.策略模式
5.责任链模式,如拦截器
14.
Retrofit
Retrofit 底层是基于 OkHttp 实现的,与其他网络框架不同的是,它更多使用运行
时注解的方式提供功能
1、 原理
通过 java 接口以及注解来描述网络请求,并用动态代理的方式生成网络请求的
request,然后通过 client 调用相应的网络框架(默认 okhttp)去发起网络请求,
并将返回的 response 通过 converterFactorty 转换成相应的数据 model,最后通过
calladapter 转换成其他数据方式(如 rxjava Observable)
2、 Retrofit 流程
(1)通过解析 网络请求接口的注解 配置 网络请求参数
(2)通过 动态代理 生成 网络请求对象
(3)通过 网络请求适配器 将 网络请求对象 进行平台适配
(4)通过 网络请求执行器 发送网络请求
(5)通过 数据转换器 解析服务器返回的数据
(6)通过 回调执行器 切换线程(子线程 ->>主线程)
(7)用户在主线程处理返回结果
3、 Retrofit 优点
1.可以配置不同 HTTP client 来实现网络请求,如 okhttp、httpclient 等;
2.请求的方法参数注解都可以定制;
3.支持同步、异步和 RxJava;
4.超级解耦;
5.可以配置不同的反序列化工具来解析数据,如 json、xml 等
6.框架使用了很多设计模式
registerActivityLifecycleCallbacks监听所有Activity生命周期
Android和JS交互
1.通过WebView的loadUrl()
2.通过WebView的evaluateJavascript()
Js调Android
关键字
1.通过WebView的addJavascriptInterface()进行对象映射
2.通过 WebViewClient 的shouldOverrideUrlLoading ()方法回调拦截 url
3.通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截JS对话框alert()、confirm()、prompt() 消息
匿名内存
它可以让多个进程操作同一块内存区域,并且除了物理内存限制,没有其他大小限制。 Java 层使用匿名共享内存的4个点: 1. 通过 MemoryFile 开辟内存空间,获得 FileDescriptor; 2. 将 FileDescriptor 传递给其他进程; 3. 往共享内存写入数据; 4. 从共享内存读取数据。
Android版本不一样之处
5.0:Material Design
6.0:动态权限
7.0:多窗体支持(分屏模式)
8.0:Android 8.0 安装 APK 权限
9.0:必须强制使用https
动画
属性动画才是真正的实现了 view 的移动,补间动画对 view 的移动更像是在不
同地方绘制了一个影子,实际对象还是处于原来的地方。
抖音视频
讲下大概思路,使用Recycleview配合自定义LinearLayoutManager来实现这个功能,这里着重说下自定义LinearLayoutManager的实现可以看到每当下一个item滑入屏幕时,上面的item会继续播放视频,而滑入的item只有当全部进入屏幕才会播放,而且当手指抬起时,当前item会根据滑动的距离相应的自动滑入滑出,针对这种情形,就会想到使用SnapHelper
RecyclerView在24.2.0版本中新增了SnapHelper这个辅助类,用于辅助RecyclerView在滚动结束时将Item对齐到某个位置。特别是列表横向滑动时,很多时候不会让列表滑到任意位置,而是会有一定的规则限制,这时候就可以通过SnapHelper来定义对齐规则了。
SnapHelper是一个抽象类,官方提供了一个LinearSnapHelper的子类,可以让RecyclerView滚动停止时相应的Item停留中间位置。25.1.0版本中官方又提供了一个PagerSnapHelper的子类,可以使RecyclerView像ViewPager一样的效果,一次只能滑一页,而且居中显示,也就是说使用SnapHelper可以帮助RecyclerView滑动完成后进行对齐操作,让item的侧边对齐或者居中对齐,这样实现上下滑动进行视频切换。这里有SnapHelper的详解