Android知识(个人记录和整理)

https://github.com/LRH1993/android_interview

screen adapt屏幕适配https://juejin.im/post/5bce688e6fb9a05cf715d1c2

rxjavahttp://gank.io/post/560e15be2dca930e00da1083

刘海屏分为危险区和安全区,危险区小于等于状态栏高度,不适配会出现顶部黑边,应用下移或者顶部被遮挡,首先判断是否是刘海屏,在根据厂商api获取高度,在根据需求适配。

继承DaoMaster.OpenHelper重写onUpgrade方法,//生成临时表,复制表数据 恢复数据,然后使用MyOpenHelper替代DaoMaster.DevOpenHelper。

Glide:二级内存缓存:从Lrucache中没有取到就去弱引用,如果都没有取到则从磁盘取,分别是原始图片或者解码后的。

Fresco:使用三级缓存,已解码内存缓存;未解码内存缓存;磁盘缓存。

1,第一级缓存就是保存bitmap,直接存的就是bitmap对象,5.0 以下,这些位于ashmem,5.0以上,直接位于java的heap上。

2,第二级缓存保存在内存,但是没有解码,使用时需要解码。

3,第三级缓存就是保存在本地文件,同样文件也未解码,使用的时候要先解码。

static:类被加载时创建,被卸载时销毁。存在于方法区。

局部变量:存在于方法或代码块,执行完后就销毁,如果被返回回去且被引用那生命周期将被延续。

在多线程环境下,每个线程拥有一个栈。

okhttp

程序里直观请求是由request和okhttpclient一起组成,request包含请求的参数,url,header等等服务端需要的一些信息,okhttp声明的时候实例化dispatcher,调用newcall方法会返回reallcall,发起请求实则是由realcall开始,声明reallcall的构造传入okclient,而okclient里有dispatcher,realcall会在执行同步和异步请求时将call对象会存到dispatcher里,dispatcher里面包含了三个请求队列:1,异步等待call队列readyasynccalls,2,异步运行call队列runningasynccalls,3,同步运行call队列runningsynccalls,加入1和2的是asynccall(reallcall的内部类),3是reallcall,1和2的评判是根据如果最大请求数目64或者同一host下请求数超过5个则放入1。

reallcall里的enqueue调用的线程池的execute方法(需要runnable对象)传入asyncall,asynccall继承namedrunnable(实现runnable接口在run方法里加入抽象方法execute)重写了execute;

reallcall同步方法直接执行的是execute方法并返回response,不管同步还是异步都会调用核心方法:getResponseWithInterceptorChain(),里面声明一个集合按顺序依次放入各种interceptor,用户自己定义的放第一,其次是:失败和重定向(RetryAndFollowUpInterceptor),客户端与服务端桥接(BridgeInterceptor),读取缓存和更新缓存(CacheInterceptor),与服务器建立连接(ConnectInterceptor),从服务器读取响应数据(CallServerInterceptor)等拦截器;接着声明RealInterceptorChain,调用它的第一次proceed(request)方法,该方法通过拦截器集合取出各个interceptor并调用intercept()方法传入再次声明的RealInterceptorChain对象,因为每个拦截器里的具体方法都调用了RealInterceptorChain的proceed(request)方法,这样通过递归的方式(next索引每次加1)依次执行完所有的Interceptor来共同驱动okhttp工作。

HashMap

通过映射的方式存取键值对数据,数组加链表的结构1.8超过8个后修改为红黑树效率高,超过 16(默认数量)*0.75(负载因子)12个需要扩容,扩容时(双倍),耗性能,最好能提前传入大小,并且扩容时的resize方法,并不是线程安全,并发操作容易形成环形链表(本来就不支持同步),获取一个不存在的key时计算出的index正好是环形链表的下标就会出现死循环;使用entrySet迭代器while同时遍历出key和value,而keySet则需要先查出key在查value效率低。

1.7put:判断table是否需要初始化,key为null则put一个null进去,根据key的hashcode方法计算出hash值,在利用hash值与size-1算出在table中的索引,将索引处Node的key和hash与传入key(内存和值随便一个相等)和key的hash比较相同则赋给临时Node变量,不相同则判断是否TreeNode或链表并赋给临时Node变量,最后将临时Node写入,此时需要判断扩容resize。

1.7get:key为null返回null,不为null和put一样根据key计算出hash,取出在table中的第一个Node first,将first的hash,key与传入的key,计算出的hash比较,相同则取出并返回value,不同则去数或链表中找,都没有找到返回null。

2的幂次方原因:用key计算出的hash值和数组的长度-1做与运算得到下标索引,如果数组长度为2的幂次方,那么数组长度-1的二进制末尾值都是1,与key的hash进行&运算之后的结果,除了超过数组长度-1数值的高位部分,低位部分都与key的hash值一致。如果数组长度不是2的幂次方,计算出的索引二进制末尾值永远是0,也就是不会有末尾为1的情况,比如1.3.5.7.9.11等索引永远不会存储值,造成空间浪费;比如数组长度16(16-1,1111)和15(15-1,1110)分别和hashcode8(1000)和9(1001)相与,16得到的index就是hashcode低位值8,9,但是15得到的index是相同的8,这样定位到同一位置上了这就产生了碰撞。

concurrenthashmap

同样是数组加链表的结构,如果链表结构中元素超过TREEIFY_THRESHOLD阈值,默认为8个,则把链表转化为红黑树,提高遍历查询效率。采用分段锁,其中 Segment 继承于 ReentrantLock。不会像 HashTable 那样不管是 put 还是 get 操作都需要做同步处理,解决了HashMap的并发问题。


          java8中 是用尾插法扩容 java7以前都是头插法 所以java8不会有 死循环问题。

Lrucache 近期最少使用算法

核心思想优先淘汰那些近期最少使用的缓存对象,内部使用的是LinkedHashMap双向链表结构,LHM的构造传入accessOrder为true时这个集合的顺序就会是访问顺序,也就是将访问后的数据放到列表最前面处于安全位置,put方法每次都会调用sizeof方法累加大小,需要用户自己计算并返回,默认为1,并且会根据key查找之前关联过的value,找到就返回并且相应的size减掉(重复算过了)最后调用trimTosize(maxsize)判断是否超过maxsize,超过就将LHM里存放时间最久的取出并移除,size减掉;get方法最重要的是LHMget方法里afterNodeAccess方法,这个方法的作用就是将刚访问过的元素放到集合的最后一位,所以没有用到的会被移到LHM的头部,trimToSize方法将其移除。


创建对象时构造器的调用顺序是:

  • 父类静态变量
  • 父类静态代码块
  • 子类静态变量
  • 子类静态代码块
  • 父类普通变量
  • 父类普通代码块
  • 父类构造函数
  • 子类普通变量
  • 子类普通代码块
  • 子类构造函数

sleep() 和 wait() 有什么区别:1来自的类不同,2,sleep不让出系统资源(占着资源睡觉)没有释放锁对象,wait让出资源释放锁对象进入等锁池,3,wait要调用notify和notifyAll方法唤醒,没有wait就调用会抛异常,sleep需要try


垃圾收集算法

标记-清除算法:分为标记和清除两部阶段,首先标记所需要回收的对象,在标记完成后统一回收所有被标记的对象,1:效率低,标记和清除过程都不高,2:空间问题,标记清除后会产生大量不连续的内存碎片,空间碎片太多导致在需要分配大对象时无法找到足够的内存而不得不在提前触发一次垃圾回收动作。

复制算法:按内存容量划分为大小相等的两块,每次只需要其中的一块,当一块内存用完了将存活的对象复制到另一块上面,然后再把刚刚用完的内存空间一次清理掉,解决了内存碎片问题,但是可用内存就缩小为一半。

标记整理算法:标记过程同标记-清除算法一样,但是在后续步骤不是直接对对象进行清理,而是让所有存活的对象都向一侧移动,然后直接清理掉端边界以外的内存。

分代收集算法:当前商业虚拟机的GC都是采用分代收集算法,根据对象存活周期的不同将堆分为:新生代和老年代,方法区为永久代(新版本已经将永久代废弃,引入元空间概念,永久代使用JVM内存而元空间直接使用物理内存),根据各个年代的特点采用不同的收集算法,新生代中的对象产生与死亡比较频繁,使用复制算法。新生代区又分为Eden区和Survivor区(Survivor from Survivor to)比例8:1:1。新产生的对象优先进入Eden去,当Eden区满了之后在使用Survivor from区,当Survivor from区也满了之后就进行Minor GC(新生代GC):GC的时候Eden和Survivor from中存活的对象copy进入Survivor to,然后清空Eden和survivor from,这个时候的Survivor from就成了新的Survivor to(满状态未使用),原来的Survivor to就成了Survivor from(有存活对象,等待装满去Survivor to),Survivor  from和Survivor  to不断的交换自身角色进行对象的存储和清除,如果Survivor to无法容纳全部存活对象根据老年代的分配担保将对象copy进去老年代,如果还是无法容纳则进行Full GC(老年代GC)。大对象直接进入老年代,JVM中的参数:-XX:PretenureSizeThreshold,大于这个值进入老年代,目的是为了避免Eden和Survivor区之间进行大内存复制。长期存活的对象进入老年代:JVM给每个对象一个年龄计数器,如果对象在Eden出生并经过第一次新生代GC后,并且能在Survivor区中容纳,那么该对象年龄设定为1,每熬过一次新生代GC,年龄就加1,直到一定程度(默认为15,可以通过XX:MaxTenuringThreshold来设定),就会移入老年代。如果Survivor区中相同年龄(如年龄为x)所有对象大小总和大于Survivor的一半,年龄大于等于x的所有对象直接进入老年代,无需等到最大年龄要求。

RecyclerView控件回收复用原理简述
四级缓存:mAttachedScrap,mCachedViews,mRecycledPool,mViewCacheExtenion
mViewCacheExtension:是系统预留的没有做任何操作,一般忽略;
mAttachedScrap:LayoutManger在布局item时会调用detachAndScrapAttachedViews①,该方法的作用是将HolderView从RV上分离出来,并存在mAttachedScrap这个List集合里,该集合没有大小限制,单存的是存放剥离下来的HolderView然后在重新布局上去,这些HolderView不参与回收复用也就是不会走adapter的绑定数据方法,对于新布局时没有用到的HV会移动到mCachedViews集合中,它的大小为2,如果满了会将旧的HV放入
mRecyclerPool里,它本身是一个类而且这里面是按照itemType分开存储的,每个itemType类型缓存的数目默认是5,里面的HV清掉了所有绑定痕迹所有缓存的HV要走adapter的绑定方法,,
所以RV复用过程:要从RV中拿一个HV,会调用recycler.getViewForPosition(position)②,它会先在mAttachedScrap中找是不是刚才从剥离的如果是直接利用,若不是则在mCachedViews里寻找,这2次都是精确查找不需要走adapter的绑定方法,若还是没有则到mRecyclerPool里搜索,这里面的HV需要重新绑定数据,如果还是没有找到则会走onCreateViewHolder重新创建HV实例。removeAndRecycleView(child, recycler)③该方法会在item滚出屏幕的时候调用并将HV标记为Removed,然后在放到mCahchedView集合里,同样的满了会放入mRecyclerPool。

 AsyncTask

下载类,必须在UI线程创建和开启,继承AT三个泛型参数:请求参数,进度,结果(可用Void代替),onPreExecute()方法UI准备,doInBg()子线程执行调用pulishProgress将进度发送到onProgressUpdate()回调方法,onPostExecute()接受结果

封装了SerialExecutor和Thread_Pool_Executor2个线程池以及internalhandler,前者使得任务排队,真正工作的是后者的线程池。构造方法初始化了mWorker、mFuture,InternalHanlder对象,mWorker作为mFuture的参数,mWorker实现了call()方法,里面有doInBackground()和postResult()。通过调用AsyncTask的execute()方法开启,该方法里默认调用executeOnExecutor(SerialExecutor)方法即默认串行,这里可以直接调用executeOnExecutor通过接受Thread_Pool_Executor对象变成并行,executeOnExecutor调用onPreExecute(),接着线程池的excute(mFuture),mFuture的run()方法里调用了mWorker的call()方法,正式开启耗时任务,里面的数据传递通过internalhandler
:注意内存泄漏,onDestory()里cannel(),使用弱引用。由于InternalHanlder需要用MainLooper作为参数所以AsyncTask类必须是在主线程初始化

HandlerThread

开辟子线程(子looper),handlerMessage()方法处于子线程,通过handler发送消息开启子looper线程
①初始化时传入工作线程的名称随便填或者优先级,②调用start()方法,③初始化处理线程任务的handler并传入handlerthread的getLooper作为参数,顺序不能倒,因为run()方法里调用Looper.prepare()初始化looper在取出looper和调用notifyall,通知getLooper里的wait方法共同完成对looper的同步初始化工作,run()方法里最后调用loop()开启任务。handlerthread有quit和quitsafelf方法,都是调用的messagequene的quit,区别是前者不管任务有没执行完都调用removeAllmessagelocked(循环遍历message链表移除所有消息回调最后重置null)直接终止任务,而后者调用removeAllFuturemessage,该方法通过message.when来判断消息是否处理完毕,处理完就调用removeAllMessagelocked,没有就等待消息处理完毕。

IntentService抽象类继承至service,需要注册。区别service:1,自带工作线程,2,停止服务不需要手动调用。区别其他工作线程:1,内部采用的是handlerthread类似于工作线程,2,由于是继承service是属于后台服务优先级高保证任务执行。

onCreate方法里初始化handlerthread于上面使用handlerthread的步骤一样,调用startservice时执行到onStartcommand里的onstart方法,onstart里开始使用servicehandler发送message消息传递intent对象和一个整型startid标记来判断是不是最后一个任务消息,handler里的handlermessage调用onhandleintent该方法自行实现子线程,其次通过调用stopself(startid)自动停止,多次调用startservice不会多次创建oncreate所以多个任务会多次调用onstart来依次处理任务。

onMeasure

单子view测量流程源码:measure(final)→onmeasure(需要复写)→setMeasuredDemission→getDefaultsize(有背景则使用背景提供的宽高,没有则使用minxx(默认为0));具体使用:复写后根据测算模式和测量size以及实际需求大小来决定单子view的宽高

viewgroup测量流程源码:measure(final)→onmeasure(需要复写,前2个都是view的方法)→measurechildren(for循环调用measurechild)→measurechild→getChildMeasureSpec(获得宽高的测量规格后调用child.measure传入测量规格)→最后合并子view宽高调用setMeasureDemission;具体使用:复写后每个子view都要调用child.measurechild(),然后累加子view的getMeasureXXX,最后根据父view的测量模式和测量size决定最后的viewgroup测量宽高。

测量规格MeasureSpec由测量mode和测量size决定,而mode,size和父view的specMode以及当前子view的layoutparam有关,规则:1若layoutparam为wrapcontent,父mode无论如何子view的mode为at_most ,size不超过父view剩余空间。2若layoutparam为具体值则子view的 mode为exactly,size为具体值;3若为matchparent,①父view measurespec mode为exactly则子view measure mode为exactly 子view size为父view剩余空间大小,②父view measurespec mode为at—_most则子view measspec mode为at_most ,子view size不超过父view剩余空间;

onLayout

单子view源码:layout(不能恶意重写,调用setFrame布局自身)→onlayout(空实现,单子view不建议重写)。

viewgroup源码:layout(不能恶意重写,调用setFrame布局自身)→onlayout(必须重写抽象方法,for循环调用单子view的布局流程)。具体使用:在onlayout中for循环getMeasureXX方法调用child.layout来摆放每个子view。

注意:在非人为情况改变layout里返回的数值时getXX和getMeasureXX永远相等,getXX是在onLayout方法执行后才有值,getMeasureXX是获得测量宽高一般在onLayout中使用,使用场景不同要分先后。

onDraw

单子view:调用draw()→里面有drawbackground(绘制背景)→其次onDraw(绘制自身内容复写自行实现)→dispatchDraw(绘制子view,没有子view,空实现)→onDrawforeground(绘制前景,比如绘制scrollbar,indicator)。使用:重写onDraw,使用canvas绘制

viewgroup绘制源码:没有实现draw(),最主要的是实现dispatchDraw(),里面循环调用drawChild(),里接着面调用child.draw(),后面的与子view绘制相同,使用:重写onDraw,遍历每个子view调用draw(canvas)绘制。


View分发机制
https://www.jianshu.com/p/de7faf9dab6f
顺序:dispatchOnTouchEvent→onIntercepteTouchEvent(viewGroup才有)→onTouchEvent
下传递down,都无消费,down通过onTouchEvent往上回传回去,给Activity消费
子view消费,down转到子view时无回溯,而是下传递up,到子view的onTouchEvent up消费
父view消费,下传递down到子view,并回溯到该父view的onTouchEvent ,接着向下传递up达到该父view,但是消费事件的父view的onIntercepteTouchEvent up不走,上父的走。
父拦截,down只传到该父不到子view,若该父消费则接着开始下传递up,该父onIntercepteTouchEvent up不走,若不消费,从该父的onTouchEvent 开始回传down

mvc:一般的是view→contraller→model→view,view由xml或者java view,contraller由activity承担,由于xml功能太弱所以activity承担了view和contraller太多的工作,model业务模型,主要是网络数据获取,数据库,io等。主要操作:在activity声明model,触发view事件后调用model的业务操作并通过activity实现的接口来接受具体数据,然后展示。

mvp:view和presenter双向,persent和model双向,通过中间层presenter让view和model完全解耦,让view以接口的形式存在并让activity实现相应更新界面的方法,主要操作:在activity里声明presenter传入实现了view的实现类activity,调用presenter的方法,presenter里有model和view的引用,model执行业务操作(构造里传入CallBack将数据传递出去),presenter里实现CallBack后用view更新,里面数据传递都使用接口的方式。也可以使用Contract契约类将实现basepresenter的具体presenter和实现baseview的具体view装载起来,好处是通过规范的命名和注释可以清晰的看到整个界面的逻辑。缺点是业务多的情况下接口类和实现类也会过度增加。

mvvm kotlin版:VM集成系统的ViewModel和LifecycleObserver可以感知生命周期,并且持有M层respository,主要提供数据源,可以网络,网络使用协程切换线程也可以数据库,通过LiveData保存数据,最后在V层即activity界面使用VM开启请求数据的方法并注册观察者,观察数据的变化来更新界面。
:以上的二种设计模式具体使用是需要注意生命周期,当销毁的需要销毁

只需要在gradle文件中添加如下代码:
android{
dataBinding{
enabled=true
}
}
ActivityXXBinding自动生成的xx是layout布局名称,可以ActivityXXBinding.控件id获取控件,通过@BindingAdapter("bind:imgurl")注解可以自定义属性在图片加载方法上面,然后布局里app:imgurl并绑定,java代码里赋值,实体继承BaseObservable并在想要改变的字段get的方法加上@Bindable注解,然后给需要改变的字段的set方法加上notifyPropertyChanged(org.lenve.databinding3.BR.description);一句即可。

EventBus3.0有四种线程模型,分别是:

POSTING (默认) 表示事件处理函数的线程跟发布事件的线程在同一个线程。
MAIN 表示事件处理函数的线程在主线程(UI)线程,因此在这里不能进行耗时操作。
BACKGROUND 表示事件处理函数的线程在后台线程,因此不能进行UI操作。如果发布事件的线程是主线程(UI线程),那么事件                            处理函数将会开启一个后台线程,如果果发布事件的线程是在后台线程,那么事件处理函数就使用该线程。ASYNC 表示无论事件发布的线程是哪一个,事件处理函数始终会新建一个子线程运行,同样不能进行UI操作。
在EventBus3.0之前我们必须定义以onEvent开头的那几个方法,分别是onEvent、onEventMainThread、onEventBackgroundThread和onEventAsync,而在3.0之后事件处理的方法名可以随意取,不过需要加上注解@subscribe(),并且指定线程模型threadmode=xxx,默认是POSTING。在接受事件的类需要注册与反注册,Eventbus,.getdefault.register/unregister,发送事件post(对象)或者粘性事件postSticky(即后注册的订阅者能接受到先发送的事件)。

:局部变量里的基本数据类型和引用,但是引用的对象实体是在堆中。
:成员变量(基本数据类型,引用和引用的对象实体)
方法区:方法和static变量

linux系统fork出init进程,init进程fork出zygote进程,Launcher(Launcher本质上也是一个应用程序继承至activity)进程通过Binder通知system_server(zygote fork出)进程,里面初始化了各种系统service,ams,pms,wms,system_server通过socket通信告知zygote 再fork出app进程,app进程里初始化applicationthread,activitythread,最后回调Activity.onCreate()开始了生命周期。

断点续传,以上次结束的地方作为起点接着下载,httpURLconnection.setRequestProperty("Range","bytes="+50+"-"+100)设置从服务端请求参数,本地文件写入使用的是RandomAccessFile对象的seek(100)方法,可以从文件的指定位置开始写入数据,写的方法和outputstream一样。多线程断点续传就是在断点续传上延伸的,将文件分成多个部分每个部分都按照断点续传的方式进行,每个线程的起始点需要根据文件的大小除以线程数目做分隔,最后一个线程的终点是文件总长度为了防止分隔不均匀,下载的进度、停止、完成等状态需要做同步处理因为是多线程同时访问的。实际项目中的下载实体要做数据库存储和文件本地校验。
数组与集合:数组长度固定,集合是可变的;数组可存入基本数据类型如int,而集合需要转成相应的包装类。

选择排序思想:int temp=0;int min=0;
                          for(int i=0;i<arrs.length-1;i++)(

                         for(int j=i+1;j<arrs.length;j++)(

                         if(arrs j< arrs min)(

                          min=j;//注意这里的min是会被替换动态赋值的。 找出最小的数

          temp =arrs i ;  arrs i=arrs min;arrs min=temp.//替换位置
 找出最小的数与第一个数交换位置,在剩下的数中找出最小的数与第二个数交换位置,以此类推。

泛型:语法糖,在编译时期就完成类型转换的工作,防止在运行时出现类转换异常,在编译时期发现错误总比在运行时要好。

coordinatorlayout design包下,constraintlayout 约束布局,i++ 先赋值在自增,++i先自增在赋值,

synchronized修饰static方法时需要获得类锁,当所有该类的对象(多个对象)在不同线程中调用这个static同步方法时,线程之间会形成互斥,因为该方法只有一个;若不是静态的方法则不会发生互斥因为他们拥有的是不同的锁。

newfixedthreadpool(core) 核心线程数和最大线程数一样,也就是只有核心线程,0L,空闲不会被回收,若超出则等待。
newsinglethreadexecutor(无),核心线程为1,最大线程数为1,也就是只有一个核心线程,0L,空闲不会被回收,若超出则等待。
newcachethreadpool (无)核心线程为0,总数目为最大值,60L,没有空闲线程则创建线程,空闲超60s则回收。
newscheduledthreadpool(core),核心线程数目固定,总数目为最大值,0L,作用是定时开启线程执行任务。

http 状态码 2xx:成功,3xx重定向,4xx:客户端错误,其中403禁止,404未找到,5xx服务器内部错误常见的500,http 使用的默认端口是80;https使用的默认端口是 443

对称加密:加密和解密都是相同的秘钥,常见的有DES,AES;非对称加密:有一组秘钥对分为公钥和私钥,公钥加密只有私钥才能解密确保内容安全,私钥加密公钥解密接受者确认发送者身份,常见的RSA。

性能分析:自带的android profiler分析内存 cpu network等,leakcanary分析内存泄漏(单利,asynctask,handler,非静态内部类),BlockCanary分析卡顿,loop的分发消息里有before和after记录时间,若时间差超过阈值表示卡顿发生。
布局优化:①LL和RL都能完成的使用LL,②使用include标签加载公共布局,③使用viewstub按需加载比如出错页面和空页面,                      viewstub.inlfate或者viewstub.setvisiable为可见来初始化,只能初始化一次,之后只能操作viewstub里的内容因为初                     始化完成后移除自身了④使用merge标签减少布局嵌套层次比如include里和外都是LL可以使用merge,⑤使用                               constraintlayout 约束布局
网络优化:①大量数据采用分页,②大图片需要压缩推荐使用webp,③加入网络数据缓存
安装包优化:①使用混淆或者使用lint工具扫描剔除无用资源文件,②能使用shape代替图片就使用shape,使用图片的话优先使                      用.9图,本地大图使用webp格式,③使用7zip压缩apk里的resoures.arsc,需要zipalign对齐,然后签名。
内存优化: 避免内存泄漏(asynctask界面销毁时cancel,handler静态加弱引用结束removecallbacks),扩大内存
代码优化:①使用lint工具进行静态代码块分析②字符串拼接使用stringbuilder。https://juejin.im/post/5ae582fff265da0b7e0c099f

安全:https,证书签名校验,核心秘钥和算法使用NDK放到native层,混淆,加壳(360,梆梆,乐固,爱加密等)
RecyclerView优化:
((SimpleItemAnimator) rv.getItemAnimator()).setSupportsChangeAnimations(false)关闭默认动画,
设置 RecyclerView.addOnScrollListener(listener); 来对滑动过程中通过scrollState停止加载的操作
使用DiffUtil进行局部刷新:DiffUtil.calcaulateDiff(new CustomDiffUillCallBack(oldlist,newlist)),CustomDiffUillCallBack里实现4个方法,根据需求返回数值,返回DiffUtil.DiffResult对象调用dispatchUpdatesTo(adapter)        


android中的类加载器,pathclassloader加载dex,dexclassloader加载jar/apk,findclass方法,for循环elements,每个element就是一个dex,找到对应名称的类就返回,热修复就是将新类插队到bug类之前,注意引用到bug类的类的标记问题。


属性动画和补间动画区别:补间动画是父容器不断的绘制 view,实则还是在原地,而属性动画改变了view内部属性值真正的改变view,通过设置点击事件可以证明。

TaskTrackBuilder:MainActivity已退出的情况下点击通知进入通知详情后想要回到MainActivity的关键,通知详情界面清单需配置parentActivityName。

 提高进程优先级:监听锁屏和开屏广播,锁屏时打开一个1px透明的activity,开屏时finish;使用service和该service的内部service的startForeground发送相同id的通知, 内部的service在stopself通知全部消失。

onTouch与onTouchEvent:都是在dispatchTouchEvent中执行,但是onTouch优于onTouchEvent,看到有touchlistener的判断,如果onTouch返回true那么onTouchEvent不会得到执行,如果控件是非enable的那么注册的onTouch无效,想要监听touch必须重写onTouchEvent来实现。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值