携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第15天,点击查看活动详情
activity生命周期
- onCreate():activity被创建时调用
- onStart():activity由不可见变成可见时调用
- onResume():activity可交互时调用
- onPause():activity被部分遮挡时调用
- onStop():activity处于完全不可见状态时调用
- onRestart():activity从停止变为运行状态时调用
- onDestroy():销毁时调用
service生命周期
- onCreate():被创建时调用
- onStartCommand():被使用startService方法启动时调用
- onBind():被使用bindService方法启动时调用
- onUnbind():解除绑定时调用
- onRebind():从旧组件解绑并绑定新组件且onUnbind()返回true时调用
- onDestroy():销毁时调用
activity、view、window的关系
- activity是承载UI和业务逻辑的容器
- window是组件的可视范围,持有根布局
- view是组成UI的元素。
activity的启动模式和应用场景
- standard:默认模式,一个activity实例对应一个栈中对象,意味着可重复添加同一个activity入栈。大部分普通场景。
- singleTop:栈顶单例模式,当activity处于栈顶时,再次创建同一个activity的实例时,会复用栈顶activity,不会创建新的实例;activity不处于栈顶的时候与standard启动模式相同。适合防止连续点击造成创建多次activity的情景。
- singleTask:栈中单例模式,如果activity已存在于栈中,再次创建同一个activity实例时,会将该activity推到栈顶并且将该activity上的其他activity实例出栈(销毁)
- singleInstance:新建栈单例模式,不管是否存在相同的activity实例,创建该activity时都会新建一个新的栈并将该activity放入新建的栈中。
Touch事件传递机制
Touch事件的传递分成两个过程:分发和处理。 当点击发生时,会父布局最先响应,调用分发方法向子控件分发事件(其中经过一个拦截过程,viewGroup有一个是否允许拦截的属性,如果允许拦截并且确认拦截的时候将不会向子控件分发事件),一直分发到非viewGroup的view中,结束分发过程。即事件分发的过程是由父控件到子控件的。 当view分发到事件后(dispatchTouchEvent),会判断该控件是否有设置了onTouchlistener,有且listener的的onTouch方法返回为true时,该方法会返回true,否则返回onTouchEvent的返回值,返回值为true表明事件已被view消费了,不会再传递到父控件处理,反之向上传递事件(由此可见,处理的过程是从子控件到父控件的)。也就是说,当onTouch方法返回true时,onTouchEvent是不会被调用的,也因为onClick方法是在performClick方法中被调用,而performClick方法是在onTouchEvent中被调用的,所以onClick方法自然也就被屏蔽掉了。如果onTouch返回false的话,就会调用onTouchEvent,onTouchEvent中会判断该view是否可点击,不可点击几乎不会做处理,但会消费掉该action;可点击的话,会在ACTION_UP的时候调用performClick(不同的action会有不同的处理逻辑)。performClick会判断onClickListener不为空时调用onClick。
view绘制流程
view的绘制涉及几个方法:
- onMeasure:测量view的大小,从父布局到子控件递归调用measure方法,measure方法调用onMeasure。
- onLayout:确定view在父控件中的位置。
- onSizeChanged:当控件大小改变时调用,可获取新旧宽高
- onDrawBackgound:绘制背景(不能重写)
- onDraw:绘制内容
- onDrawForeground:绘制前景(滑动边缘渐变和滑动条等)
动画类别
- 帧动画:利用一张张的图片轮播形成的动画
- 属性动画:通过控制随时间变化的浮点值控制值的变化,多用于控件的属性变化(改变控件坐标)
- 补间动画:设定开始和结束的状态,中间的变化自动补充,不改变控件坐标。
Binder机制
进程的内存空间分为两种,一种是用户空间,另一种是内核控件,普通的数据是存放在用户空间的,也因为用户空间是进程独立的不能访问其他进程的用户空间,所以进程间不能直接通信。Binder就是用于进程间通信的机制。虽然用户空间不能跨进程访问,但是内核空间却可以,所以跨进程通信时,需要通过访问共用的内核空间实现,Binder驱动就是实现内核空间的通信的。
Android进程间通信的实现方式
- Binder
- Socket
- 文件共享(如sharedPreference)
- intent
- contentProvider
- AIDL
- 广播
- 服务
自定义view流程
继承view或groupView或已有原生控件 如有需要自定义属性 在onmeasure中确定view的大小 在ondraw中画出内容 如有需要暴露设置属性的方法
ANR是什么,怎么避免
ANR即Application not responsing,应用无响应 在Android系统中,activity5秒内无响应则出现ANR,广播10秒,服务20秒。 避免:将耗时的同步操作放到子线程执行(网络请求,文件读写,海量数据处理等)
消息机制
消息机制中有四个关键类,Handler、Message、Looper、MessageQueue。 Handler是用来辅助消息发送和处理的类,可通过复写handleMessage来处理Message,通过post或postDelay来发送消息。 Message是消息的载体,what属性是消息的标识,用int值来标记不同的消息类型,obj为消息中包含的信息,一般用于存放处理消息是要用到的数据。还有一些数组和数值的属性是一些便捷的存放数据的属性。 MessageQueue是消息的队列,名字虽然是队列,但是MessageQueue是用单链表实现的,因为单链表在插入和删除上表现较好。MessageQueue使用先进先出的原 则,按序取出消息。 Looper是一个不断轮询MessageQueue的类,会不断取出MessageQueue中的消息。每一个线程中只能有一个Looper。主线程的Looper在线程创建的时候就自动创建并开始轮询了。Looper的存放使用ThreadLocal实现,ThreadLocal用于创建一个线程的共享变量,当在一个已有Looper的线程中再次创建Looper,会拿到相同的Looper。
性能优化
- 布局优化:尽量将减少布局的层级,很少情况下使用的布局可以使用stub标签,使用include标签减少重复布局的编写。
- 绘制优化:自定义控件中的ondraw方法尽量不要做耗时操作和尽量少的初始化操作,保证绘制的流畅性。
- 线程优化:多线程开发时多使用线程池对线程进行管理,更好地复用线程以及对最大并发进行控制。
- 内存泄漏优化:handler、AsynTask、静态变量、广播、contentReceiver等的使用注意处理潜在的内存泄漏问题。
广播注册的方式与区别
- 静态注册:在清单文件中声明广播,这种广播是常驻型广播,长期占用内存,占用CPU资源,但这种广播可以在app不运行的时候监听,例如开机广播。
- 动态注册:在Application或其他组件中用代码动态声明的广播,非常驻型广播,广播的生命周期和组件绑定,但要在onDestroy的时候注销广播,否则容易造成内存泄漏。
进程保活,避免service死亡
- 进程保活:可以使用AlarmManager或WorkManager定时启动activity,当进程死亡后拉起;使用前台服务来提高进程的优先级,提高进程的存活率;
- service保活:设置为前台服务提高优先级;在进程中轮询service是否死亡,死亡则重新启动;在onStartCommand中返回STICKY,当service被杀死后,系统会尽量复活粘性服务;onDestroy时发送一个自定义广播,收到广播重启service。
怎么避免OOM,加载图片OOM
出现OOM一个很大的原因是因为内存泄漏,久而久之内存就会被占满从而出现OOM,代码中处理好ML的问题可以减少OOM出现的概率;避免一次性声明大量的对象或使用轻量级的对象;按需使用弱引用,使得发送GC的时候能够释放更多的内存;OOM还会出现在加载比较大图片的情况下,比如加载多张大图片到UI中,不进行处理的话,图片就会直接加载到内存中,容易发生OOM,解决方法是加载图片之前,先对图片的大小进行估计,从而判断是否需要进行图片压缩,而且不在使用中的图片可以考虑释放内存,需要用的时候重新加载。
GC
Java有自动回收机制,即GC(Garbage Collection),当jvm判断一个对象已经“无用”了,就会释放其内存。是否“无用”jvm依靠一种引用链的机制判断(弱引用除外,弱引用无论如何都会被回收)。只要这个对象在GC Root的引用链中的话,这个对象就是“有用”,反之为“无用”。GC Root有如下:正在运行的线程、静态变量、native中调用的方法、栈帧中的局部变量等。ML的发生就是一个对象其实已经是无用的了,但是缺由于某种原因还存在于GC Root的引用链中,jvm判断为“有用”,这时候就不会回收该对象从而发生内存泄漏。
Fragment与Fragment、Activity之间的通信
Fragment:Fragment之间是不能直接通信的,需要通过宿主Activity为桥梁通信。 Activity:Activity可以在添加Fragment的时候使用Bundle给Fragment传递数据;可以使用FragmentManager的方法findFragmentById和findFragmentByTag来得到Fragment实例来传递数据;Fragment可以通过在onAttach回调中获取实现了自定义接口的Activity对象从而调用接口的方法进行向Activity传输数据;也可以使用getActivity获取宿主Activity的实例。
RecyclerView和ListView的区别
RecyclerView可以实现ListView的显示效果,同时也可以实现瀑布流的效果。也可以设置滚动的方向。 RecyclerView的item复用不用手动实现,已经封装好了。(viewHolder) RecyclerView可以实现局部刷新。 需要使用动画的情况下,RecyclerView的性能比ListView好,而且RecyclerView提供了使用动画的API。 单纯显示列表数据两者区别不大。
Glide框架的优缺点
- 优点:加载的生命周期与activity的生命周期绑定、储存的图片内存占用较小、多媒体缓存、可以设置优先级加载、支持okhttp和volley获取网络图片。
- 缺点:因为格式的问题,显示的图片清晰度不足,但可以设置不同的格式。
常见网络请求框架的比较
- volley:google公司推出的一款轻量级的异步网络请求框架,优点是封装性好,使用门槛低,框架小,封装了ImageLoader。缺点是不能用于上传大数据。
- okhttp:优点是性能好、封装好了同步异步、线程池、错误处理等,API使用起来很方便。缺点是实际使用的时候一般需要再封装一层以提高便捷性。
- retrofit:基于okhttp的网络请求框架,对okhttp的进一步封装使得有更强的易用性,比如使用大量注解以减少代码量等,跟Rxjava有很好的融合性,同时也继承了okhttp的高性能。缺点是使用门槛较高。
设计模式
- 单例模式:单例模式是指某个对象在全局只存在唯一的一个实例,这样的设计模式能避免一些内存占用较大的对象被频繁创建,能在全局中提供一个唯一的实例(多用于管理类)。做法是将类构造方法私有化,然后在类中声明一个该类的对象并向外提供获取该对象的方法(一般是getInstance),该方法被调用时,先判断内部类对象是否为空,为空则创建一个实例赋值给该对象并返回,不为空则直接返回该对象。单例模式的实现方法有:枚举实现,单锁式,双锁式,加载初始式、静态内部对象式。
- 建造者模式:建造者模式能够将构建具有复杂属性的对象的过程变成链式调用,使得构建对象更加清晰明了。做法是在对象中定义一个静态内部类Builder,定义一套跟外部类属性完全相同的属性,并定义一系列的设置方法,方法的返回都是this(实现链式调用)。最后定义一个build方法,用Builder中的属性构建外部对象并返回。
- 观察者模式:观察者模式旨在响应,即被观察者发生变化是会即时通知观察者(可以是多个观察者)。
- 策略模式:策略模式强调多个具有相同功能但实现不同的逻辑应该讲功能抽象成接口。例如同一个功能支付,却有支付宝、微信、银联卡等不同的具体实现逻辑,这时候应该讲支付功能抽象成一个接口,不同的支付方式实现这个接口并定义具体的支付逻辑。好处是方便对不同实现逻辑的增删改,面对接口编程,对修改关闭,对拓展开放。但只适用于程序掌握全部所有的逻辑的情况下。
- 工厂模式:工厂模式是一种创建对象的设计模式,该设计模式识具体的构建逻辑不会暴露,相当于你需要一台汽车,你去工厂提车但并不需要知道这汽车是怎么造出来的。做法是定义一个接口对象(比如车),实现多个子类,子类里是具体的构建方法(红色的车、柴油车、汽油车等的具体生产的方法),接着创建一个能根据具体需要创建对象的工厂类(汽车厂),使用工厂(向工厂提要求,比如我要一台汽油车),工厂根据要求选择相应的子类(汽油车的生产方法),构建一个接口对象返回(工厂交付一台车)。
三级缓存
三级缓存是:磁盘缓存、内存缓存、网络缓存 加载数据时,依次从内存、磁盘、网络中获取,速度递减。