问题整理(更新至7.25)

问题整理(更新至7.25)

2018, Jul 18    

Android基础篇

onclicklistener是如何调用的

就是在UP事件中创建一个实现了Runnalbe接口的PerformClick对象,run方法中调用performClick执行点击,在handelr中post,如果post失败则执行方法performClick立即执行onClickListener的onClick方法

onTouchListenerd的onTouch方法 > onTouchEvent > onClickListener

onLongClickListener是如何实现的

CheckForLongPress实现一个Runnable接口,run方法中执行performLongClick方法执行长按的处理。
<—
CheckForLongPress在checkForLongClick方法中创建,并在私有变量mPendingCheckForLongPress中存储。
<—
checkForLongClick方法创建CheckForLongPress对象后,发送一个延迟事件,在特定时间之后执行CheckForLongPress,延迟的时间为系统设定的长按的时间-传入的延迟时间(一般为0,特殊处理时在CheckForTap中,暂时不关注)。
<—
checkForLongClick的触发在ACTION_DOWN中,ACTION_DOWN中会优先执行button的ontouchdown事件,然后依据是不是在一个scroll容器中决定是否立即去执行checkForLongClick操作。
在长按时间到了之后,如何判断当前是否还是有效的长按事件呢? 处于press状态 && 当前viewattach的view的数量相同的。 在ACTION_UP、ACTION_CANCEL和ACTION_MOVE事件中会移除掉长按的message。

if (isPressed() && (mParent != null)
        && mOriginalWindowAttachCount == mWindowAttachCount) {
    if (performLongClick(mX, mY)) {
        mHasPerformedLongPress = true;
    }
}
case MotionEvent.ACTION_UP:
    boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
    if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {                      
        if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
            removeLongPressCallback();
        }
    }
    break;
case MotionEvent.ACTION_CANCEL:
    removeLongPressCallback();
    break;
case MotionEvent.ACTION_MOVE:
    if (!pointInView(x, y, mTouchSlop)) {
        if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
            removeLongPressCallback();
        }
    }
    break;
}

如何实现流式布局

见git上的viewstudy项目

ConstrainLayout

ConstraintLayout 继承自viewgroup,比relativelayout更高级,除了友好的可视化编辑外,增加的限制属性能够帮助解决试图层级过多的问题。 新特性:(https://www.jianshu.com/p/38ee0aa654a8)

相对定位,左边右对齐,起始边尾部对齐等等。 外边距(gone margin,B向A添加约束后,如果A的可见性变为GONE,这时候B的外边距可以改变,也就是B的外边距根据A的可见性分为两种状态。) 居中和倾向

app:layout_constraintLeft_toLeftOf=”parent” app:layout_constraintRight_toRightOf=”parent layout_constraintHorizontal_bias=”0.3” 可见性的表现 一般情况下,GONG控件是不可见的,且不再是布局的一部分,但是在布局计算上,ConstraintLayout与传统布局有一个很重要的区别:传统布局下,GONE控件的尺寸会被认为是0(当做点来处理),在ConstraintLayout中,GONE控件尺寸仍然按其可见时的大小计算,但是其外边距大小按0计算 尺寸约束:通过设置比例,让宽高的其中一个随另一个变化。为了实现比例,需要让控件宽或高受约束,且尺寸设置为0dp(也可以是MATCH_CONSTRAINT) Chain:一系列双向连接的控件连接在一起的形态(如图 8所示,是最小单位的Chain,只有两个组件)

app:layout_constraintLeft_toLeftOf=”parent” app:layout_constraintRight_toRightOf=”parent”

组件化有什么好处

现象:

  1. 稍微改动一个模块的一点代码都要编译整个工程,耗时耗力
  2. 公共资源、业务、模块混在一起耦合度太高
  3. 不方便测试 https://juejin.im/entry/577bae93d342d30057970e05

    卡顿,工具(–)

    卡顿检测:工程中并没有修改代码去做这件事,网上说的方法,依据activitythread中loop的方法diapatch前后的log去计算两个log之间是不是超过了阈值,也并没有用到。
    (来源 https://www.cnblogs.com/ldq2016/p/6667381.html) 比较方便的还是在开发者模式中开启gpu绘制。 iamge
    (1)Swap Buffers:表示处理任务的时间,也可以说是CPU等待GPU完成任务的时间,线条越高,表示GPU做的事情越多;
    (2)Command Issue:表示执行任务的时间,这部分主要是Android进行2D渲染显示列表的时间,为了将内容绘制到屏幕上,Android需要使用Open GL ES的API接口来绘制显示列表,红色线条越高表示需要绘制的视图更多;
    (3)Sync & Upload:表示的是准备当前界面上有待绘制的图片所耗费的时间,为了减少该段区域的执行时间,我们可以减少屏幕上的图片数量或者是缩小图片的大小;
    (4)Draw:表示测量和绘制视图列表所需要的时间,蓝色线条越高表示每一帧需要更新很多视图,或者View的onDraw方法中做了耗时操作;
    (5)Measure/Layout:表示布局的onMeasure与onLayout所花费的时间,一旦时间过长,就需要仔细检查自己的布局是不是存在严重的性能问题;
    (6)Animation:表示计算执行动画所需要花费的时间,包含的动画有ObjectAnimator,ViewPropertyAnimator,Transition等等。一旦这里的执行时间过长,就需要检查是不是使用了非官方的动画工具或者是检查动画执行的过程中是不是触发了读写操作等等;
    (7)Input Handling:表示系统处理输入事件所耗费的时间,粗略等于对事件处理方法所执行的时间。一旦执行时间过长,意味着在处理用户的输入事件的地方执行了复杂的操作;
    (8)Misc Time/Vsync Delay:表示在主线程执行了太多的任务,导致UI渲染跟不上vSync的信号而出现掉帧的情况;

卡顿的原因:无法在16ms内完成渲染,导致丢帧现象。丢帧的原因可能是因为 (1)layout过于复杂,无法完成渲染; — 布局优化 (2)层叠太多的绘制单元,多次绘制; — 布局优化 (3)频发的GC;— 内存优化 (4)动画执行的次数过多等等。 布局优化:看文章(https://leo1992.github.io/blog/Android%E9%AB%98%E6%80%A7%E8%83%BD%E7%BC%96%E7%A8%8B-%E6%89%93%E9%80%A0%E5%B8%83%E5%B1%80%E7%AC%94%E8%AE%B0/)

layout过于复杂:
考虑是否合适的控件(1)linearlayout,relativelayout(2)constraintlayout
太多的绘制单元
**解决办法:**
用工具检测:原色,蓝色,绿色,粉色,红色。原因:(1)重叠背景(2)叠加的view(3)复杂的层级
(1)tansparent;
(2)space draw()方法为空
(3)不常出现的布局动态添加的方式加入,一层层的invisible(会计算)或者gone(不会计算,但是view还是会创建加载的)
(4)需要控制Fragment的显示和隐藏,尽量使用动态地Inflation view,它的性能要比SetVisiblity好。合并布局,相对布局,重用布局
频繁的GC:
执行GC操作的时候,任何线程的任何操作都会需要暂停,等待GC操作完成之后,其他操作才能够继续运行。GC的原因:内存抖动,瞬间产生大量的对象。
内存优化: (https://leo1992.github.io/blog/Android%E9%AB%98%E6%80%A7%E8%83%BD%E7%BC%96%E7%A8%8B-%E5%86%85%E5%AD%98%E7%AC%94%E8%AE%B0/)
(1)数据类型,sparsearray,arraymap
(2)自动装箱,也涉及存取速度的问题,比如基本类型在堆栈,对象在堆中需要分配内存比较慢
(3)集合遍历,for(item:list)
(4)对象管理,stringbuilder,本地变量和局部变量–,数组比集合高效
(5)内存设计模式,对象池模式,享元模式
(6)viewstub,不会被加载、绘制,可以延迟加载view对象,节省内存;
(7)listview中优化(https://leo1992.github.io/blog/listview%E4%BC%98%E5%8C%96/)
(8)混淆对内存的优化
加快速度
(1)findviewbyid方法,父节点
(2)系统方法 system.arraycopy
。。。。

工具
TraceView,DDMS,选择一个进程,“Start Method Profiling”,(扩展)
StrictMode 系统检测出主线程违例的情况并做出相应的反应,最终帮助开发者优化和改善代码逻辑。用来基于线程或VM设置一些策略, 一旦检测到策略违例, 控制台将输出一些警告,包含一个trace信息展示你的应用在何处出现问题.在Application或是Activity的onCreate中开启StrictMode,设置policy(https://www.cnblogs.com/yaowen/p/6024690.html这边文章跟我的思维比较契合)
Systrace

inflate

inflate的后两个参数 https://blog.csdn.net/u012702547/article/details/52628453 root为null,attachToRoot为true:将resource指定的布局添加到root中 root不为null,attachToRoot为false: 有的布局参数是要求控件在容器中时才有意义,比如layout_width和layout_height。root会协助view的根节点生成布局参数,只有这一个作用。 root为null:不需要将view添加到任何容器中,同时也没有任何容器来来协助view的根节点生成布局参数。

主线程不会因为Looper.loop()里的死循环卡死

—loop 默认情况下handler不退出,会一直处于死循环中,所以为什么所有的ui操作都在主线程中,这样的死循环并不会让界面卡死? 不希望主线程运行一段时间推出——保证能一直执行—–用死循环实现 如何保证死循环不卡死—死循环无消息时休眠 死循环如何处理其他事务—

ANR哪几种情况

只有主线程才会产生ANR

三种情况:

  1. 5s内无法响应用户输入事件(例如键盘输入, 触摸屏幕等).
  2. BroadcastReceiver在10s内无法结束
  3. Service各个生命周期函数时20秒内没有处理完毕

原因

(1)应用主线程卡住,对其他请求响应超时。
—执行耗时的操作
a.数据库操作
b.I/O
c.连接网络
d.硬件操作(比如camera)
e.调用thread的join()方法、sleep()方法、wait()方法或者等待线程锁的时候
f.service binder的数量达到上限
g.system server中发生WatchDog ANR,service忙导致超时无响应
—在主线程中执行
a. Activity的所有生命周期回调都是执行在主线程的. b. Service默认是执行在主线程的. c. BroadcastReceiver的onReceive回调是执行在主线程的. d. 没有使用子线程的looper的Handler的handleMessage, post(Runnable)是执行在主线程的。 e. AsyncTask的回调中除了doInBackground, 其他都是执行在主线程的. f. View的post(Runnable)是执行在主线程的. https://blog.csdn.net/chenhande1990chenhan/article/details/78498153 这个结构不错
(2)死锁。其他线程持有锁,导致主线程等待超时
(3)系统反应迟钝
(4)CPU负载过重

定位:

一般情况下,如果有ANR发生,系统都会在/data/anr/目录下生成traces.txt文件,通过分析traces.txt 文件,可以查看产生ANR的原因。(手机要root)

解决:

  1. 避免在主线程中执行耗时操作,开子线程,然后使用Handler+Message的方式做一些操作
  2. BroadcastReceiver要执行耗时操作时应启动一个service,将耗时操作交给service来完成。(https://leo1992.github.io/blog/broadcast%E7%9B%B8%E5%85%B3/)
  3. 避免在Intent Receiver里启动一个Activity,因为它会创建一个新的画面,并从当前用户正在运行的程序上抢夺焦点。如果你的应用程序在响应Intent广 播时需要向用户展示什么,你应该使用Notification Manager来实现。(https://www.jianshu.com/p/7fd95bc2a55c)

post和不post的区别,处理什么问题

读取高度

getMeasuerdHeight 和 getHeight的区别

getMeasuredHeight()返回的是原始测量高度,与屏幕无关,getHeight()返回的是在屏幕上显示的高度。实际上在当屏幕可以包裹内容的时候,他们的值是相等的,只有当view超出屏幕后,才能看出他们的区别。当超出屏幕后,getMeasuredHeight()等于getHeight()加上屏幕之外没有显示的高度 getMeasuredHeight是用来判断布局信息的时候使用到的,onLayout中使用。而getHeight是在onDraw中使用。

内存泄漏

  1. 单例的静态特性导致其生命周期和应用的生命周期一样长,持有的对象无法释放。 —- context用application的
  2. 非静态内部类,非静态内部类默认会持有外部类的引用,是不是有不能在生命周期内执行完的任务,是不是静态实例(静态实例的生命周期跟应用一样长) —- 将该内部类设为静态内部类或将该内部类抽取出来封装成一个单例,如果需要使用Context,就使用Application的Context。
  3. handler 原因: 匿名内部类,当Android应用程序启动时,该应用程序的主线程会自动创建一个Looper对象和与之关联的MessageQueue。当主线程中实例化一个Handler对象后,它就会自动与主线程Looper的MessageQueue关联起来。所有发送到MessageQueue的Messag都会持有Handler的引用,所以Looper会据此回调Handle的handleMessage()方法来处理消息。只要MessageQueue中有未处理的Message,Looper就会不断的从中取出并交给Handler处理。另外,主线程的Looper对象会伴随该应用程序的整个生命周期。未处理的消息持有handler的引用,而handler又持有它所属的外部类也就是MainActivity的引用。这条引用关系会一直保持直到消息得到处理,这样阻止了MainActivity被垃圾回收器回收,从而造成了内存泄漏。(https://www.jianshu.com/p/90caf813682d 这个是我看到比较好的解释)
  4. 线程(AsyncTask和Runnable)AsyncTask和Runnable都使用了匿名内部类,那么它们将持有其所在Activity的隐式引用。如果任务在Activity销毁之前还未完成,那么将导致Activity的内存资源无法被回收,从而造成内存泄漏。 解决方法:将AsyncTask和Runnable类独立出来或者使用静态内部类,这样便可以避免内存泄漏。
  5. 资源未关闭造成的内存泄漏

RecyclerView ListView

RecyclerView的优点: ViewHolder, LayoutManger, ItemDecorater, ItemAnimation

Android对集合有哪些优化

引入原因:客户端对于数据量要求不高,但是对于内存要求尽可能低
场景

  1. 大量数据不适用(二分查找缺陷)
  2. 内存优化
  3. 全部都支持索引(这是Java中的Map和Set集合不具备的)

HashMap的问题:(https://www.jianshu.com/p/aff3b8990ab3) HashMap的key和value都不是基本类型的,当插入一个object时key的hashcode会计算出来然后放入entry

  1. 自动装箱意味着需要产生额外的对象,这对于内存的使用和垃圾回收产生影响。
  2. HashMapEntity自己本身也会产生额外的对象,这同样会影响内存的使用和垃圾回收产生。
  3. 每次HashMap的存储对象减少或增加的时候,这个开销会随着Hashmap的size增加而增加。
  4. 哈希是很好的实现方案,但是如果实现的不好将会让我们的开销回到O(N)
  5. 很多人都会忽略关于哈希的另外一个缺点:我们需要存储它的key和对应的hash值。这种冗余有助于解决冲突。 非散列解决方案也可以在这方面有所帮助。

ArrayMap:
不用类似链的结构去存储,而是将object和key都存在mArray数组里,然后用一个int数组去存储hashcode,所以在查找时会增加时间复杂度(因为要在mHashes中进行二分查找,而不是直接用定位)。
查找: mHashes中找index – key在2*index而value在2*index+1

SparseArray:
可以避免自动装箱减少内存的消耗.SparseArray、SparseIntArray、SparseLongArray、SparseBooleanArray。比起ArrayMap,key单独放在一个int数组中,这样key就不用自动装箱。
查找: 用二分查找在mKeys中查找key,然后用key的index就能从mValues中找到value。
ArraySet
问题:Sparse中如何用key的index去查找value

java篇

final

final 修饰类表示一个类不能被继承 final 修饰方法表示方法不能被任何继承类修改它的含义 final 修饰基本变量。修饰基本数据类型表示初始化之后不能再更改,修改引用类型变量表示初始化之后不能再让其指向另一个对象。

SoftReference, WeakReference, PhantomReference

soft引用在内存足够的情况下不会回收,只有在空间接近临界值或者抛出oom的时候,才会回收。避免内存溢出。用于加载图片,bitmap缓存机制。在工程中的下载图片缓存中被处理过的bitmap。

week引用只要在垃圾回收扫到弱引用的对象的时,都会直接被垃圾回收器回收。

Phantom的get方法总是返回null,因此无法访问对应的引用对象;其意义在于说明一个对象已经进入finalization阶段,可以被gc回收,用来实现比finalization机制更灵活的回收操作。

回收时间:

  1. 垃圾回收线程是一个守护线程,优先级低,当应用程序空闲时,即没有应用线程在运行时,GC会被调用。
  2. 堆内存不足时。在新生代的Eden区满了,会触发新生代GC(Mimor GC),经过多次触发新生代GC存活下来的对象就会升级到老年代,升级到老年代的对象所需的内存大于老年代剩余的内存,则会触发老年代GC(Full GC)。当程序调用System.gc()时也会触发Full GC。

线程池的使用和优势

  1. 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  2. 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
  3. 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。??

    垃圾回收

    JVM

    HashMap实现原理

    技能篇

    调试有什么技巧

    如何提升编译

    如何解决卡顿

    命令行编译过项目

    开源框架源码

    其他

    如何学习

    解决过什么比较有趣的bug

  4. 分享中应用安装下载,极少情况下的跳转问题,try-catch
  5. 我的页面同步下载状态页面整个不断刷新的问题,定义状态,重写equal
  6. recyclerview tag问题