1.自定义view有哪些方式
1. 组合控件,将几种控件组合起来形成一个其他地方需要复用的控件,如标题栏。自定义一个**view继承五大布局,通过inflater.inflate将写了几个控件的layout加到控件里面去。
2. 继承系统控件,如很多地方使用的edittext需要加背景,就自定义一个控件继承edittext,然后给他设置上背景。
3.自定义View继承view,复写View的生命周期函数来完成View的渲染,先复写构造函数,再复写其余几个方法。
- onMeasure:该方法就是测量绘制view所需要的大小。参数三种模式 at_most 限定了最大值,具体看实现 EXACTLY有精确值了 UNSPECIFIED 不限制大小
- onLayout:就是放置每个子view的位置。
- onDraw:就是将子view绘制到画布上面。
- 设置点击事件要用onTouchEvent.
4. 自定义viewgroup继承viewgroup。
-
只需要onMeasure与onLayout,与自定义view相同
-
在onLayout()计算完每个子View的坐标之后,可以直接通过addView(child)的方式直接将该子View添加到ViewGroup中,无需复写onDraw()来将子View绘制到canvas。
-
在实现每个子View的点击事件,可以直接通过child.setOnClickListener来设置点击事件。
-
onInterceptTouchEvent()是ViewGroup的一个方法,目的是在系统向该ViewGroup及其各个childView触发onTouchEvent()之前对相关事件进行一次拦截,Android这么设计的想法也很好理解,由于ViewGroup会包含若干childView,因此需要能够统一监控各种touch事件的机会,因此纯粹的不能包含子view的控件是没有这个方法的,如LinearLayout就有,TextView就没有。 true表示拦截了 false不拦截
总结:
-
1.requestLayout()和invalidate() (1)requestLayout():会将mPrivateFlags 设置为PFLAG_FORCE_LAYOUT,从而调用到父控件的mParent.requestLayout()并标记为PFLAG_FORCE_LAYOUT,最后调用到ViewRootImpl的requestLayout(),这些被标记为PFLAG_FORCE_LAYOUT的View会重新调用到onMeasure()、onLayout()、onDraw(); (
-
2)invalidate():并没有将mPrivateFlags设置为PFLAG_FORCE_LAYOUT,所以只会调用到onDraw(),而不会调用到onMeasure()、onLayout()。 这个问题出现就是在增加了ImageView的个数刷新View的时候,发现该View的所占的空间并没有发生变化,正是由于这两个方法没有合理使用引起的。
-
2.在复写onMeasure()、onLayout()、onDraw()的时候,要把padding计算在View控件中,否则设置的padding不起作用
-
3.在复写onLayout()的时候,其实可以直接将子View从paddingTop、paddingLeft开始放置子View,onLayout传入的left/top/right/bottom用处不大,因为这四个值返回的是该自定义控件本身在父容器中的坐标
-
4.继承于View在复写onDraw()的时候,需要canvas.translate,才能将子View绘制到canvas上,并且canvas平移的时候都是相对于当前位置平移。
2.glide如何做自动回收
glide会自己监听生命周期
3.MVVM
view-model-viewmodel
viewmodel存放与界面相关数据,减轻activity负担
当发生横竖屏切换、白天黑夜切换、语言切换等时,activity会重建,viewmodel不会,保持数据不损失,viewmodel有自己独立的生命周期,所有不能在activity中创建,要用ViewModelP rovider获取。
4.lifecycle
生命周期组件,让类自己监听某个活动的生命周期
5.livedata
数据绑定,数据主动同步组件
6.mvp
presenter操作视图和数据
7.arraylist hashmap linkedHashmap
按照存和取的顺序来讲,ArrayList和LinkedList就属于有序集合,因为ArrayList底层是动态数组实现的,而数组是一块连续的空间,每次存的时候都是找到索引,一个接着一个的存储,取的时候也要按照索引遍历出来。
hashmap的容量必须是2的n次方,不管正反插入,hashmap会对key按一定规则来排序,调用interator.next方法时,不按存入的顺序取出。
linkedHashmap双向链表。即每个数据节点带有直接前后驱两个指针。跟hashmap都存在线程不安全的问题。容易死循环。不死循环的有HashTable和SynchronizedMap,都是对put get方法加锁或对map对象本身加锁。影响效率。推荐****ConcurrentHashMap。
8.前段实体类对象有规定的命名与后端返回的字段不一致,如何处理
Gson 使用 @SerializedName 注解解决
@SerializedName("code") private String password;
9.mvvm 数据如何双向绑定和刷新界面,原理是什么
使用livedata来进行双向绑定。数据更新时livedata通知所有被观察者,dababinding生成的BindingImpl中有onFieldChange方法来更新界面。
10.recycleview 间距怎么设置的
mRecyclerView.addItemDecoration
11.view如何自定义布局的
12.内存优化
打开Android Studio性能检测工具Profiler,可以获取到当前应用的内存动态使用情况。然后看那一部分内存使用多,进行相应的控制。图片优化,数据量优化。
13.OKhttp retrofit RxJava 原理
14.viewstub merge include 区别
viewstub是一个默认宽高为0的控件,通常用于缓加载,先判断需不需要这部分内容再加载。
include是一个引用其他地方布局的控件。但会引入额外层级。
merge也可以在其中写其他控件,但不会引入层级。通常配合include,减少层级。
15.intent大小限制原因
Intent 传输数据的机制中,用到了 Binder。Intent 中的数据,会作为 Parcel 被存储在 Binder 的事务缓冲区(Binder transaction buffer)中的对象进行传输。
传 512K 以下的数据的数据可以正常传递。
传 512K~1024K 的数据有可能会出错,闪退。
传 1M以上的数据会报错:TransactionTooLargeException
16.哪些类不该被混肴
四大组件不能被混肴,因为要在ams.xml里面注册 混肴了找不到
17.intent四种启动模式
1.standard 系统默认的启动模式,栈结构,先进后出.只要你创建了Activity实例,一旦激活该Activity,则会向任务栈中加入新创建的实例,退出Activity则会在任务栈中销毁该实例。
2.singleTop 如果某个Activity自己激活自己,即任务栈栈顶就是该Activity,则不需要创建,其余情况都要创建Activity实例; 推送详情页和商品详情页这种多次重复打开的 只刷数据
3.singleTask 如果要激活的那个Activity在任务栈中存在该实例,则不需要创建,只需要把此Activity放入栈顶,并把该Activity以上的Activity实例都pop 一般app首页
4. singleInstance 如果我们将某个activity设置成这个singleStance启动模式,则当激活这个activity之后单独放到一个栈,下次再使用的时候,直接使用这个栈,比如打电话应用就是一个singleStance模式启动的activity 电话 闹钟 日历
18.kotlin为什么可以在jvm运行
19.单例模式有哪几种
20.如何让某些线程在别的线程之后执行
countdownlatch
21.安卓线程有几种状态
1)、新建状态(New):新创建了一个线程对象。
2)、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
3)、运行状态(Running):就绪状态的线程获取了CPU,执行run()方法。
4)、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种: (一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。 (二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。 (三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
5)、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。 当调用start方法的时候,该线程就进入就绪状态。等待CPU进行调度执行,此时还没有真正执行线程。 当调用run方法的时候,是已经被CPU进行调度,执行线程的主要任务。
22.handler为什么会造成内存泄漏
1、handler创建时候就对持有当前Activity得引用,同时message持有对handler的引用。MessageQueue持有Message;Message持有了Handler;handler是匿名内部类,持有this Activity,Activity持有大量内存,就会造成内存泄漏。
2、 发送一个延迟消息时候,消息被处理前,该消息一直保存在消息队列中,在持续保存这段时间,messageque持有对message的引用,Message持有了handler;在我们的activity中建立一个Handler的实例,该handler实例默认持有了外部的对象acticity的引用,当我们调用acticity的ondestroy方法时,activity销毁了,但是根据可达性分析,我们的需要的activity存在被handler引用,只要handler不被释放,没办法会销毁,就造成了内存泄漏。
解决办法:
ondestroy方法中,清除消息队列中的消息;mHandler.removeCallbacksAndMessages(null)
或者将handler定义为静态内部类 但是需要将handler中需要使用的外部类或对象定义为静态或使用弱引用持有。使用第二种方法时,需要注意弱引用的生命周期
23.线程安全
1. synchronized加锁
2. volatile关键字能保证内存可见性,即不管那个线程使用该变量都是最新的。且能解决指令重排序问题。
24. 进程间通信方式
1. bundle,startactivity的时候传
2. 文件共享
3. contentprovider
4. aidl
25.内存优化
主要三方面,内存泄漏、内存抖动、内存溢出
当内存频繁分配和回收导致内存不稳定,出现内存抖动,内存抖动通常表现为频繁 GC、内存曲线呈锯齿状。
并且,内存抖动的危害严重,会导致页面卡顿,甚至 OOM。
内存抖动的优化:
1.使用StringBuilder拼接字符串。因为StringBuilder可以提前创建确定大小,避免扩容。
2.不在循环中创建变量
3.资源复用
4.使用合理的数据结构
内存泄漏可使用android profiler检测 开着该工具 运行一遍逻辑 然后看memory类别 会显示泄漏个数 再看具体的。
26. 弱引用
通常安卓中都是强引用。
如果一个对象只具有弱引用,则内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。弱引用通常用在对内存敏感的程序中,比如高速缓存就有用到软引用,内存够用的时候就保留,不够用就回收。
关键字 WeakReference。
27.音频焦点
requestAudioFocus()请求音频焦点
为 requestAudioFocus 方法传入的第3个参数:
如果计划在将来一段时间内播放音频,并且希望前一个持有音频焦点的应用停止播放,则应该请求永久性的音频焦点 (AUDIOFOCUS_GAIN)。
如果只希望在短时间内播放音频,并且希望前一个持有音频焦点的应用暂停播放,则应该请求暂时性的焦点 (AUDIOFOCUS_GAIN_TRANSIENT)。
如果只希望在短时间内播放音频,并允许前一个持有焦点的应用在降低音量的情况下继续播放,则应该请求“降低音量”的暂时性焦点(AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK) 。这两个音频会混合到音频流中同时输出。
abandonAudioFocus()放弃音频焦点
AudioManager.OnAudioFocusChangeListener ,以便接收回调并管理自己的音量
AUDIOFOCUS_GAIN:获取得到音频焦点。
AUDIOFOCUS_LOSS:永久性失去音频焦点,后续不会再收到 AUDIOFOCUS_GAIN 回调。应用应立即暂停播放,此时其他应用会播放音频。
AUDIOFOCUS_LOSS_TRANSIENT:暂时性失去音频焦点,应用应暂停播放。当抢占焦点的应用放弃焦点时、自己的应用可以收到 AUDIOFOCUS_GAIN 回调。
AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:暂时性失去音频焦点,应用应降低音量。当抢占焦点的应用放弃焦点时、自己的应用可以收到 AUDIOFOCUS_GAIN 回调。
28.小卡片
AppWidget 是通过 BroadCastReceiver 的形式进行控制的,开发 AppWidget 的主要类为 AppWidgetProvider, 该类继承自 BroadCastReceiver。为了实现桌面小部件,开发者只要开发一个继承自 AppWidgetProvider 的子类,并重写它的 onUpdate() 方法即可。重写该方法,一般来说可按如下几个步骤进行:
1、创建一个 RemoteViews 对象,这个对象加载时指定了桌面小部件的界面布局文件。
2、设置 RemoteViews 创建时加载的布局文件中各个元素的属性。
3、创建一个 ComponentName 对象
4、调用 AppWidgetManager 更新桌面小部件。
29.安卓bug监控 使用bugly平台
30.Android为什么要设计出Bundle而不是直接使用HashMap来进行数据传递
-
性能优化:
Bundle
内部使用的是ArrayMap
实现,这种数据结构在内存使用和数据操作速度上相比HashMap
更适合小数据量操作,这是因为ArrayMap
内部使用两个数组来存储键值对,并通过二分法进行数据查找,而HashMap
内部是数组加链表结构,适合处理大量数据。在Android中,组件间传递数据通常涉及的数据量较小,因此ArrayMap
更适合这种场景 -
序列化效率:在Android中,
Intent
用于在组件间传递数据时,推荐使用的数据序列化方式是Parcelable
,而不是Serializable
。Bundle
实现了Parcelable
接口,这意味着它可以更高效地处理数据的序列化和反序列化过程。相比之下,HashMap
默认使用Serializable
接口进行序列化,这在Android平台上通常效率较低 -
兼容性和集成:
Bundle
作为Android框架的一部分,与Android的组件通信机制(如Intent
)紧密集成。它提供了一套专门为Android设计的API,这些API考虑了Android应用的特定需求,如跨进程通信和内存管理。直接使用HashMap
可能需要额外的工作来确保与Android框架的兼容性 -
内存优化:Android对内存使用非常敏感,因此在设计框架时会特别关注内存效率。
Bundle
的设计考虑到了这一点,通过使用ArrayMap
和Parcelable
接口,它能够在不牺牲性能的前提下,提供一种轻量级的数据传递方式
31. Parcelable与Serializable的区别
-
性能:
Parcelable
通常比Serializable
更高效。Parcelable
是 Android 特有的接口,专门优化了在 Android 中的数据序列化和反序列化,适合在进程间传递数据。而Serializable
是 Java 的标准接口,性能较低,因为它涉及到反射和较为复杂的对象图处理。 -
实现复杂度:
Parcelable
需要手动实现序列化和反序列化过程,代码较为复杂,但提供了更高的灵活性和性能,需要实现writeToParcel()和CREATOR字段。而Serializable
只需要实现一个接口,只需添加implements Serializable即可,自动处理大部分序列化工作,但可能会在性能上有所妥协。 -
数据传递:
Parcelable
适合用于 Android 的Intent
、Bundle
和IPC
数据传递,而Serializable
更适合于普通的 Java 对象存储和传递。
32 anr
输入事件在5秒内没有处理完成发生ANR。
前台服务在20秒内,后台服务在200秒内没有处理完成发生ANR。
BroadcastReceiver的onReceive方法在处理广播时,前台广播在10秒内,后台广播在60秒内没有处理完成发生ANR。
ContentProvider在发布数据时在10秒内没有处理完成发生ANR。
分析anr步骤:
1. 查看日志和traces文件分析anr的原因和大概位置
2. 确定是什么原因导致的anr
3. 死锁、拿不到某些资源导致一直等待,同时对已拿资源保持不放
4. 主线程耗时操作
5. 系统资源不足(内存太小了)
int type = (((int) bytes[0] ) & 0xff) | (((int) bytes[1]&0xff)<<8);
33.应用启动流程
1.向 System_Server 进程发起 startActivity
请求
2.System_Server 向 Zygote 进程发送创建新进程的请求
3.新进程实例化ActivityThread,ActivityThread
是应用进程的主线程类,它负责管理应用的生命周期和消息循环等。在这一步骤中,会创建 ApplicationThread
、Looper
和 Handler
对象,并开启主线程的消息循环 Looper.loop()
4.
绑定 Application:ActivityThread
的 main
方法调用 attach
方法进行 Binder 通信,通知 System_Server 进程执行 ActivityManagerService#attachApplication(mAppThread)
方法。在 System_Server 进程中,ActivityManagerService#attachApplication(mAppThread)
会依次初始化 Application
和 Activity
34.setContentView流程
1.根据传入的布局id,将其加载到内存中,递归生成view对象,然后将其设置给当前的window。
2.window将其传给ViewRootImpl, ViewRootImpl会执行measure(确定大小),layout(确定位置),draw以将view显示出来。