内存泄露的根本原因:长生命周期的对象持有短生命周期的对象。短周期对象就无法及时释放。情况如下:静态集合类引起内存泄露,各种数据链接没有关闭,内部类,单例
避免 OOM 异常:
程序需要申请一段“大”内存,但是虚拟机没有办法及时的给到,即使做了GC操作以后
android为每个app设置了一个内存上限。
减少内存对象的占用,内存对象的重复利用
ANR
也就是在规定的时间内,没有响应。处理:避免在UI线程,BroadcastReceiver 还有service主线程中,处理复杂的逻辑和计算
layout_gravity和gravity的区别:
layout_gravity:设置控件本身相对于父控件的显示位置。
gravity:设置的是控件自身上面的内容位置。
四大引用:
强引用:Object obj =new Object();属于不可回收的资源,垃圾回收器绝不会回收它
软引用:属于可有可无的,内存不足了才会回收
弱引用:可有可无,拥有更短的生命周期,只要垃圾回收器扫描到它,不管内存空间充足与否,都会回收它的内存
虚引用:不影响对象的生命周期,任何时候都可能被垃圾回收器回收
Java反射查看类信息
1.获得Class对象 2.获取class对象的属性、方法、构造函数等 3. 获取class对象的信息
代理模式的分类:静态代理:代理类是在编译时就实现好的。也就是说 Java 编译完成后代理类是一个实际的 class 文件。动态代理:代理类是在运行时生成的。也就是说 Java 编译完之后并没有实际的 class 文件,而是在运行时动态生成的类字节码,并加载到JVM中。
- 代理:动态代理:newProxyInstance:1.创建了一个新的类【代理类】2. 传入的InvocationHandler作为参数创建一个代理类的实例并返回
- IO:FileNameFilter:文件过滤器,包含了一个accept(File dir,String name)方法,该方法依次对指定File的所有子目录或者文件进行迭代,按照指定条件,进行过滤,过滤出满足条件的所有文件。
字节流和字符流区别:
(1)读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
(2)处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。只要是处理纯文本数据,就优先考虑使用字符流。 除此之外都使用字节流。RandomAccessFile既可以读取文件内容,也可以向文件输出数据
对象的内存布局分为3个区域:对象头(对象自身的运行时数据和类型指针),实例数据(是对象真正存储的有效信息),对齐填充。对象的访问定位:句柄访问和直接指针访问。
Java内存区域:方法区(公有,包含常量池),堆(公有,是JVM所管理的内存中最大的一块),虚拟机栈(线程私有,描述的是java方法执行的内存模型),本地方法栈(线程私有,与虚拟机栈所发挥的作用相似),程序计数器(线程私有,当前线程所执行的字节码的行号指示器)。
类加载机:把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型。类的生命周期:加载,验证,准备,解析,初始化,使用和卸载。
垃圾收集算法:1.标记-清除算法,2.复制算法,3.标记-整理算法,4.分代收集算法
JVM中分为年轻代(Young generation)和老年代(Tenured generation)。
安卓:
事件分发的本质:将点击事件(MotionEvent)向某个View进行传递并最终得到处理。事件在哪些对象之间进行传递:Activity、ViewGroup、View。dispatchTouchEvent(),onTouchEvent()
IntentService处理异步请求,实现多线程,内部是了HandlerThrea
LRU缓存算法:当缓存满时,会优先淘汰那些最近最少使用的缓存对象。原理是把最近使用的对象用强引用(即我们平常使用的对象引用方式)存储在 LinkedHashMap 中。当缓存满时,把最近最少使用的对象从内存中移除,并提供了get和put方法来完成缓存的获取和添加操作
DecorView是FrameLayout的子类,它可以被认为是Android视图树的根节点视图
View的绘制是从上往下一层层迭代下来的。DecorView-->ViewGroup(--->ViewGroup)-->View
Measure:测量每个控件的大小。调用measure()方法,进行一些逻辑处理,然后调用onMeasure()方法,在其中调用setMeasuredDimension()设定View的宽高信息,完成View的测量操作。MeasureSpec由两部分组成,一部分是测量模式,另一部分是测量的尺寸大小。
Layout流程:其中布局也是自上而下,不同的是ViewGroup先在layout()中确定自己的布局,然后在onLayout()方法中再调用子View的layout()方法,让子View布局。在Measure过程中,ViewGroup一般是先测量子View的大小,然后再确定自身的大小。View的测量、布局和绘制原理来看,要实现自定义View :onMeasure()方法,onLayout()方法,onDraw()方法
Bitmap的高效加载策略①将BitmapFactory.Options的inJustDecodeBounds参数设为true并加载图片。②从BitmapFactory.Options中取出图片的原始宽高信息,它们对应于outWidth和outHeight参数。③根据采样率的规则并结合目标View的所需大小计算出采样率inSampleSize。④将BitmapFactory.Options的inJustDecodeBounds参数设为false,然后重新加载图片。
进程优先级 前台-可视-服务-后台-空
Parcelable和Serializable的作用、效率、区别及选择:Parcelable的性能比Serializable好,在内存开销方面较小,在需要保存或网络传输数据时选择Serializable
启动流程:创建进程-绑定Application-显示Activity界面-Binder通信
Android性能优化的方面:1.布局优化2.绘制优化3.内存泄漏优化(通过一些分析工具比如MAT来找出潜在的内存泄露)4.响应速度优化5. ListView /RecycleView及Bitmap优化6.线程优化7.其他性能优化的建议
ListView优化:复用 convertView,ViewHolder 的使用,数据分页加载,管理 adapter 的数据集合 list ,
RecycleView优化:数据优化,布局优化
框架
Okhttp3: execute()方法(同步方法)和enqueue()方法(异步方法)
1)支持http2,对一台机器的所有请求共享同一个socket
2)内置连接池,支持连接复用,减少延迟
3)支持透明的gzip压缩响应体
4)通过缓存避免重复的请求
5)请求失败时自动重试主机的其他ip,自动重定向
6)好用的API
OkhttpClient :对外的API
RealCall类:集成Call类,从源代码中,可看到使用Call类,发送出(同步/异步)请求.RealCall的主要作用
Dispatcher类通过维护一个线程池,来维护、管理、执行OKHttp的请求。
Interceptor类:拦截器是Okhttp中提供的一种强大机制,它可以实现网络监听、请求、以及响应重写、请求失败重试等功能
运用到的设计模式:单例模式,建造者模式,工厂方法模式,责任链模式
如何使用OkHttp进行异步网络请求,并根据请求结果刷新UI:
第一步,创建一个OkHttpClient对象
第二步,创建携带请求信息的Request对象
第三步,创建Call对象
第四步,call.enqueue()
OkHttp对于网络请求都有哪些优化,如何实现的
通过连接池来减少请求延时
无缝支持GZIP来减少数据流量
缓存响应数据来减少重复的网络请求
可以从很多常用的连接问题中自动恢复
Http请求神器RxHttp:任意请求,任意返回数据类型,皆遵循请求三部曲
retrofit2
涉及到的设计模式
外观模式,构建者模式,工厂模式,代理模式,适配器模式,策略模式,观察者模式
Retrofit就是一个网络请求框架的封装,底层的网络请求默认使用的Okhttp,本身只是简化了用户网络请求的参数配置等,还能与Rxjava相结合,使用起来更加简洁方便。
EventBus
开销小,代码优雅。将发送者和接受者解耦
EventBus的使用步骤分为定义事件、订阅事件、发送事件、处理事件、取消订阅五步。
EventBus从3.0开始使用注解@Subscribe配置事件订阅方法
注册事件:EventBus.getDefault().register(this);
取消注册:EventBus.getDefault().unregister(this);
发送事件:EventBus.getDefault().post("Hello World!")
粘性事件:先发送事件,后续再准备订阅事件的方法、注册事件
EventBus.getDefault().postSticky("Hello World!");
RxJAVA
处理异步,当你的业务需要访问数据库,访问网络,或者任何耗时的操作,都可以借助Rxjava来实现
处理异步任务可以用Handler,AsyncTask等来实现,我们为什么还要用Rxjava?
采用链式调用,代码简洁优雅有美感,并且可读性增强。
rxjava中采用观察者模式。模块之间划定了清晰的界限,降低了模块间的耦合性,提高了代码的可维护性和重用性。
rxjava中提供了强大的操作符。
Glide
Glide的缓存设计可以说是非常先进的,考虑的场景也很周全。在缓存这一功能上,Glide又将它分成了两个模块,一个是内存缓存,一个是硬盘缓存
Glide.with(this)
.load("http://www.baidu.com/img/bdlogo.png")
.into(imageView);
加载同一张图片,Picasso的内存开销仍然远大于Glide。
Image质量的细节:Glide默认的是Bitmap格式是RGB-565,Picasso默认ARGB_8888格式,
Glide加载的图片没有Picasso那么平滑,但是很难察觉
磁盘缓存:Picasso缓存的是全尺寸的。而Glide缓存的跟ImageView尺寸相同
Glide可以加载Gif动图,Picasso不可以加载动图
Gson
toJson序列化(对象转换成字符串),fromJson反序列化(字符串转化为对象)
@SerializedName()可解决字段名不一致的问题
限定某个字段不参加序列化或反序列化@Expose()注解
ButterKnife
依托Java的注解机制来实现辅助代码生成的框架
Mvvm:
M---Model (数据)
V---View (视图)
VIewModel主要做两件事:
1、把 Model 中的数据绑定到View(视图层)。
2、监听VIew (视图层),把事件,界面操作,回调给Model中的JavaScript中的对象,函数。
Android和H5交互: webView, 只是这样调用mWebView.loadUrl()加载会在默认浏览器打开,要设置setWebViewClient,设置为可调用js方法webSettings.setJavaScriptEnabled(true); 若调用的js方法没有返回值,调用mWebView.loadUrl("javascript:do()");有返回值时我们可以调用mWebView.evaluateJavascript()方法:js调用Android本地Java方法:addJavascriptInterface
实现某一栏滚动到某一位置就置顶不会消失:
CoordinatorLayout和AppbarLayout的配合, 以及实现了NestedScrollView的布局或控件.
AppbarLayout是一种支持响应滚动手势的app bar布局, CollapsingToolbarLayout则是专门用来实现子布局内不同元素响应滚动细节的布局.
需要设置 app:layout_behavior = "@string/appbar_scrolling_view_behavior",否则将不会响应滚动布局的滚动事件.
进程间通信:
Intent(activity启动另一个activity)
文件共享
Messenger(轻量级的 IPC 方案,底层实现是 AIDL,以串行的方式处理客户端发来的消息,大量并发请求就不适合用 Messenger,只适合传递消息,不能跨进程调用服务端的方法,只是传递数据,不涉及方法调用,也不需要高并发)
Binder通信采用C/S架构。
Binder通信的四个角色:
Client进程:使用服务的进程。
Server进程:提供服务的进程。ServiceManager进程:ServiceManager的作用是将字符形式的Binder名字转化成Client中对该Binder的引用,使得Client能够通过Binder名字获得对Server中Binder实体的引用。
Binder驱动:驱动负责进程之间Binder通信的建立,Binder在进程之间的传递,Binder引用计数管理,数据包在进程之间的传递和交互等一系列底层支持。
AIDL:能够实现进程间通信,其内部是通过Binder机制来实现的,后面会具体介绍,现在先介绍AIDL的使用。Binder机制的运行主要包括三个部分:注册服务、获取服务和使用服务
binder机制(AIDL) 解决并发和跨进程调用方法的问题,支持的数据类型:基本数据类型,string,arraylist,hashmap,AIDL。只有不同应用的客户端调用远程方法并在服务中处理多线程才调用
ContentProvider(不同应用间数据共享)一对多的进程间数据共享
Socket:分为流式 Socket(SOCK_STREAM)和数据报式 Socket(SOCK_DGRAM)。流式 Socket是面向连接的socket,针对面向连接的TCP;数据报式 Socket针对无连接的UDP服务,连接应用层与传输层之间接口,一对多的并发实时通信
TCP/IP 五层网络模型:
应用层:规定应用程序的数据格式,主要的协议 HTTP,FTP,WebSocket,POP3 等;
传输层:建立“端口到端口” 的通信,主要的协议:TCP,UDP;
网络层:建立”主机到主机”的通信,主要的协议:IP,ARP ,IP 协议的主要作用:一个是为每一台计算机分配 IP 地址,另一个是确定哪些地址在同一子网;
数据链路层:确定电信号的分组方式,主要的协议:以太网协议;
物理层:负责电信号的传输。、
线程间通信:基本状态:就绪、阻塞、运行
线程主要分为:守护线程(垃圾回收线程)、非守护线程
开启线程三种方式:
继承·thread类,实现runnable接口,实现callable接口, Callable接口和Runnable接口的区别: Callable接口的call()方法可以有返回值(通过Future接口的get()方法,不过此方法是阻塞性的),而Runnable接口的run()方法没有返回值. Callable接口的call()方法可以声明抛出异常,而Runnable接口的run()方法不可以声明抛出异常。
三种线程间通信方式:共享变量(内存), 管道, handle机制
两个子线程间通信:在一个子线程中创建一个Handler, 绑定looper,在另一个子线程中调用此handler来发送消息就。
更新ui
- Handel:主线程中定义Handler,子线程发消息,通知Handler完成UI更新,主线程实例化MyHandler,子线程调用Handler.sendMessage发送消息,回调主线程的Handler.handleMessage
- runOnUiThread:子线程中通过runOnUiThread()方法更新UI
- View.post(Runnable r) 适用于view的更新
- AsyncTask:主线程中实例化对象异步线程处理
消息机制:MessageQueue(消息队列),Handler(处理者)和Looper(循环执行,只能创建一个)这三大部分,以及Message(消息)。运行流程:Handler发送消息调用MessageQueue向消息队列中添加消息通过Looper循环读取消息,再调用目标Handler的dispatchMessage传递消息后返回所在线程,目标Handler调用handleMessage获取消息。在子线程中通过Handler的post()方式或send()方式发送消息,最终都是调用了sendMessageAtTime()方法。
synchronized关键字:
原子性:执行操作要么都执行要么都不执行。synchronized修饰的方法和代码块,作用于调用的对象。synchronized修饰的静态方法和类,作用于这个类的所有对象。
可见性:当多个线程访问同一个变量时,一个线程修改了变量的值,其他的线程能立即看到
有序性:程序的执行顺序会按照代码先后顺序进行执行
volatile和synchronized的作用和区别:
1.volatile只能作用于变量,使用范围较小。synchronized可以用在变量、方法、类、同步代码块等,使用范围比较广。
2.volatile只能保证可见性和有序性,不能保证原子性。而可见性、有序性、原子性synchronized都可以包证。
3.volatile不会造成线程阻塞。synchronized可能会造成线程阻塞。
出现死锁问题: 互斥条件:一个资源每次只能被一个线程使用。
请求与保持条件:一个线程因请求资源而阻塞时,对已获得的资源保持不放。
不剥夺条件:线程已获得的资源,在未使用完之前,不能强行剥夺。
循环等待条件:若干线程之间形成一种头尾相接的循环等待资源关系。
多线程实现方式主要有:
实现Thread的run()方法或者实现Runable接口
HandlerThread用来执行多个耗时操作,而不需要多次开启线程,里面是采用Handler和Looper实现的。1.创建HandlerThread的实例对象2. 启动创建的HandlerThread线程3. 将handlerThread与Handler绑定在一起
AsyncTask(两个线程池)
LoaderManager(执行一些耗时的异步数据加载操作,并根据Activity生命周期对异步处理进行调整)
线程池
使用线程池好处:降低资源消耗,提高响应速度,提高线程的可管理性
一个线程池管理了一组工作线程,同时它还包括了一个用于放置等待执行的任务队列。
ThreadPoolExecutor:线程池的核心实现类
种类:
- FixedThreadPool:只有核心线程,没有非核心线程
- CachedThreadPool:没有核心线程,只有非核心线程
- SingleThreadExecutor:只有单个工作线程
- ScheduledThreadPool:实现定时和周期性任务的线程池
同步块比同步方法好,同步方法会锁对象,同步块不会锁对象
线程池工作队列
1、ArrayBlockingQueue
是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
2、LinkedBlockingQueue
一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列
3、SynchronousQueue
一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
4、PriorityBlockingQueue
一个具有优先级的无限阻塞队列。
分配线程池:线程等待时间所占比例越高,需要越多线程。线程CPU时间所占比例越高,需要越少线程。
线程池运行机制:假如有一个工厂,工厂里面有10个人,每个工人同时只能做一件事情。因此只要当10个工人中有工人是空闲的,来了任务就分配给空闲的工人做;当10个工人都有任务时,如果还来任务,就把任务进行排队等待。
数据结构
List,Set,Map的区别:
Set集合中的对象无序,没有重复对象:
HashSet:哈希算法来存取集合中的对象,存取速度比较快
TreeSet:能够对集合中的对象进行排序
List线性方式,存储有序,可以存放重复对象
ArrayList() : 读取快,增删慢,线程不安全
LinkedList():读取慢,增删快
Map:把键对象和值对象映射的集合
HashMap:基于散列表的实现,插入和查询“键值对”的开销是固定的
LinkedHashMap:迭代访问时发而更快,因为它使用链表维护内部次序。
TreeMap:基于红黑树数据结构
ArrayMap和HashMap的对比:
- 存储方式不同
- 添加数据时扩容时的处理不一样,ArrayMap用的是copy数据,所以效率相对要高。
- ArrayMap提供了数组收缩的功能
- ArrayMap采用二分法查找;
HashMap 和 HashTable 的区别:
1:继承不同。
2:HashMap 不是线程安全的,HashTable 是线程安全。
3:HashTable的key和value都不允许为null值,而HashMap的key和value则都是允许null
HashSet与HashMap怎么判断集合元素重复:Object的hashCode方法判hashCode是否已经存在
Hashmap原理:利用key的hashCode重新hash计算出当前对象的元素在数组中的下标。
获取HashMap的元素:
1.首先根据hashCode()做hash,然后确定bucket的index;
2.如果bucket的节点的key不是我们需要的,则通过keys.equals()在链中找。
1. 什么时候会使用HashMap?他有什么特点?
是基于Map接口的实现,存储键值对时,它可以接收null的键值,是非同步的,HashMap存储着Entry(hash, key, value, next)对象。
2. 你知道HashMap的工作原理吗?
HashMap存储键值对时调用键对象的hashCode()方法来计算hashcode,让后找到bucket位置来储存值对象;HashMap使用链表来解决碰撞问题,当发生碰撞了,对象将会储存在链表的下一个节点中,即 HashMap是通过链表结构存储hashcode相同的键值对。
3. 你知道get和put的原理吗?equals()和hashCode()的都有什么作用?
通过对key的hashCode()进行hashing,并计算下标( (n-1) & hash),从而获得buckets的位置。如果产生碰撞,则利用key.equals()方法去链表或树中去查找对应的节点
LruCache 使用一个 LinkedHashMap 简单的实现内存的缓存,没有软引用,都是强引用。如果添加的数据大于设置的最大值,就删除最先缓存的数据来调整内存
栈与堆的区别:一些基本类型的变量和对象的引用变量都是在方法的栈内存中分配的,堆内存用来存放所有由 new 创建的对象(包括该对象其中的所有成员变量)和数组
Android版本的适配和调用
Android6.0以后敏感权限需要动态申请
Android7.0出于安全考虑关闭了网络/拍照/录像系统广播,应用间共享文件限制
Android8.0
软件安装适配:去掉了允许安装未知来源软件,需要手动确认,安装过程中需要加上REQUEST_INSTALL_PACKAGES权限。通知需要提供通知渠道,新增悬浮窗,
广播适配:注册广播不能用隐式,需要明确指定
权限适配:读取权限分离
Android9.0:全面屏、刘海屏的适配,限制了明文流量的网络请求,非加密的流量请求都会被系统禁止
Android10.0:引入了大量更改和限制以增强对用户隐私的保护。
横竖屏切换:onRestoreInstanceState(不用非空判断),onSaveInstanceState
四大组建
Acticity:onCreate onStart onResume onPause onStop onDestory
BrocastReceiver:
静态注册:在AndroidManifest.xml中用标签注册
动态注册:在onStart中注册registerReceiver(),onStop中取消unregisterReceiver
Service启动方式:
- bindService,调用unbindService()方法来关闭。生命周期:onCreate,onBind,onUnbind,onDestroy,生命周期在onUnbind方法调用后结束
- startService,无限地运行下去stopSelf()方法。生命周期:onCreate,onStartCommand,onDestroy,生命周期和整个活动的生命周期一同结束
保证Service不被杀死:提升Service优先级,在onDestory()中发送广播开启自己
ContentProvider:不同的应用程序之间共享数据
提供的方法:query:查询
insert:插入
update:更新
delete:删除
getType:得到数据类型
onCreate:创建数据时调用的回调函数
网络请求
post与get区别:
get明文传输,大小限制1024k,有缓存,post暗文传输无限制无缓存。post将参数置于请求数据中,get则跟在url链接后面。GET一般用于获取/查询资源信息,而POST一般用于更新资源信息
tcp/udp的区别:(TCP)传输控制协议,提供可靠数据传输的通用协议。
(UDP)用户数据报协议,面向无连接的协议
TCP三次握手:第一次握手:建立连接。客户端发送连接请求报文段SYN,等待服务器确认。第二次握手:服务器收到客户端报文段SYN+ACK,确认后将所有的信息放入报文段中发送给客户端。第三次握手:客户端收到报文段后向服务器发送ACK报文段
为什么要三次握手?为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。
四次挥手:第一次分手:主机1向主机2发送FIN报文。第二次分手:主机2收到了主机1发送的FIN报文段,向主机1回一个ACK报文段,主机2告诉主机1同意关闭请求。第三次分手:主机2向主机1发送FIN报文段,请求关闭连接。第四次分手:主机1收到主机2发送的FIN报文段,向主机2发送ACK报文段,,等待没有数据主机1就关闭
状态码:1xx:指示信息——表示请求已接收,继续处理。
2xx:成功——表示请求已被成功接收、理解、接受。
3xx:重定向——要完成请求必须进行更进一步的操作。
4xx:客户端错误——请求有语法错误或请求无法实现。
5xx:服务器端错误——服务器未能实现合法的请求。
HTTPS其实是有两部分组成:HTTP + SSL / TLS,也就是在HTTP上又加了一层处理加密信息的模块
Kotlin
Kotlin和Java的区别:1. Kotlin更安全(没有空指针异常);2. 简洁,可靠,有趣;3. 函数式支持;扩展函数;
fun 关键字用来定义一个函数,
var 关键字是 variable 的简称,表可变变量定义
lateinit var延迟初始化,防止出现空的
val 关键字是 value 的简称,不可变变量定义
if 语句来代替 三目运算符
使用 when 来代替 switch
Data代替大量的set get方法和复写基础方法
三个等号 === 表示比较对象地址,两个 == 表示比较两个值大小
我是将近两年工作经验的安卓开发,因为来公司做的大部分都是电商类app,所有很多东西其实很少接触到,准备了福州的联迪公司面试,他们公司用的进程线程和kotlin flutter多所以面试准备的都是这部分内容,朋友帮忙内推进去连面试都没给还是对自己很自责和失望,所以准备开始学习学习kotlin和flutter基础了,不然都没啥竞争力了,可能是简历还是水平低了点。希望我总结的这些点能帮到大家。