安卓基础

124 阅读13分钟
recyclerview
缓存机制(四级缓存)(https://blog.csdn.net/jb_home/article/details/111647931) 
    1 ArrayList<ViewHolder> mAttachedScrap(屏幕内) 用于屏幕内itemView快速重用,不需要重新createViewhe和bindView
    2 ArrayList<ViewHolder> mCachedViews(屏幕外)保存最近移除屏幕的viewHolder,包含数据和position信息,复用时必须相同位置,
        应用场景需要来回滑动的列表,当往回滑动时,直接复用viewHolder数据,不需要重新bindView
    3 ViewCacheExtension mViewCacheExtension(自定义缓存)不直接使用,需要自定义实现,默认不实现
    4 RecycledViewPool mRecyclerPool(缓存池)当cacheview满了后或者adapter被更换,将cacheview中的viewHolder放到pool中,
        放之前会把viewHolder数据清除,复用时需要重新bindView
保存缓存流程
    1 插入或者删除itemView时,先把屏幕内的viewHolder保存到attachedScrop中
    2 滑动屏幕的时候,先消失的itemview会保存到cacheView(大小默认是2),超过数量的话先进先出原色,移除头部的itemView保存到recyclerPool缓冲池
        缓冲池会按照itemView的itemType进行保存,每个itemType个数不超过5个
获取缓冲流程
    attachedScrap中获取通过position匹配holder->
    获取失败,从CacheView中获取,也是通过position获取holder->
    获取失败从自定义缓冲汇总获取->
    获取失败,从mRecyclerPool中获取->
    获取失败,从新创建viewHolder->createViewHolder并bindView
和listview的区别
    listview布局单一 recyclerview可以实现横向 纵向 9宫格 瀑布流
    listview需要自己定义viewholder
    listview需要设置settag和gettag recyclerview自己处理
RecyclerView的回收复用机制
    回收和复用的都是ViewHolder对象,在RecyclerView的内部类Recycler中,可以看到四重缓存中的关键数据结构都和ArrayList<ViewHolder>有关,ViewHolder是itemView的封装。
    说明无论回收还是复用,都是以ViewHolder为单位去存取。
什么时候回收?什么时候复用?
    我们追踪程序,是以 RecyclerView的 onTouchEvnet move事件为起点。结合追踪到的源码,可以发现,回收发生在 itemView消失的时候,复用则发生在 itemView由不可见到可见的时候,内容改变时也有回收和复用。
优化
    减少onCreateViewHolder执行次数
        swapAdapter代替setAdapter->setAdapter直接清空rv上所有缓存,swapAdapter会将rv上的holder缓存到pool
        共用回收池->多个RV使用同一个adapter
        添加rv的缓存数量,默认是5个
    减少onCreateViewHolder执行时间
        减少item过渡绘制->层级
        局部刷新->DiffUtil工具类
四大组件 ==activity==
Activity 的生命周期	startActivityForResult
oncreate->onstart->onResume->onpause->onstop->ondestory
	activity 被回收
		重写onSaveInstanceState()方法,在此方法中保存需要保存的数据,该方法将会在activity被回收之前调用。在Onstop方法之前 onuse方法之后
		通过使用onConfigurationChanged方法代替onRestoreInstanceState()方法可以从中提取保存好的数据
	A跳转B
		A OnPause  B OnCeater onStart OnResume A onStop
	B返回A
		B OnPause A OnRestart onStart OnResume B onStop OnDestory

Activity 的四种启动模式
	standard 可以有多个相同的实例 也允许多个相同的 activity 叠加
	singleTop 可以有多个相同的实例 不允许多个相同的 activity 叠加
	singleTask 只能有一个实例.在一个应用启动它时,
		若activity 不存在会在当前task中创建一个新的实例,
		若activity 存在会把task中在其之上的其它activitydestory掉并调用它的onNewIntent方法
	singleInstance 只能有一个实例,这个实例独自运作在一个task中一个task只有这个实例,不允许有别的 activity存在 

横竖屏切换时候activity的生命周期
    1 不设置 android:configChanges时 会重新调用个生命周期 切横屏执行1次 切竖屏执行22 设置了 configChanges="orientation"时 切横竖屏会执行13 设置 configchange="orientation|keyboardHidden" 切换不会执行
[多个activity传数据](https://zhuanlan.zhihu.com/p/268717257)
    代替onActivityResult
    Activity Results API 中两个重要的组件:ActivityResultContract和ActivityResultLauncher。
    registerForActivityResult(ActivityResultContracts.StartActivityForResult()){
        if(it.resultCode == Activity.RESULT_OK){
            val result = it.data?.getStringExtra("result")
            text1.text = result
        }
    }


子线程更新UI
    ViewRootImpl会在checkThread()中检测更新UI是否在主线程->onResume之后创建ViewRootImpl对象   
[intent 传递大数据](https://mp.weixin.qq.com/s/N6GUTtu0cazfvotGieriCg)
    intent最大传递200k
    使用binder最大可以传递8M
    单进程中
        //定义一个IntentBinder,此方法仅在『同一个进程』下有效哦,切记切记!!!!
        class IntentBinder(val imageBmp:Bitmap? = null): Binder()
        //------------------------使用如下--------------------------//
        //com.xxx.xxx.MainActivity
        val bitmap = BitmapFactory.decodeStream(...)
        startActivity(Intent(this,SecondActivity::class.java).putExtras(Bundle().apply {
                putBinder("myBinder",IntentBinder(bitmap))
        }))
        
        //------------------------获取Bitmap并显示如下--------------------------//
        //com.xxx.xxx.SecondActivity
        val bundle: Bundle? = intent.extras
        val imageBinder:IntentBinder? = bundle?.getBinder("myBinder") as IntentBinder?
        //拿到Binder中的Bitmap
        val bitmap = imageBinder?.imageBmp

==fragment==

fragment 的生命周期 添加newInstance方法
	onCreate() onCreateView() onStart onResume -> onPause onStop->onDestroyView onDestroy
fragment和activity通信
如果activity没有fragment的实例 可以通过getFragmentManager.findFragmentByTag()或者findFragmentById()获得任何Fragment实例
fragment事务管理机制
通过handler发送消息,放到消息队列里面

==service==

服务 Service 生命周期
	startService-->onCreate-->onStartCommand-->onDestroy
	bindService-->onCreate-->onBind-->onUnbinf-->onDestroy
	
startService --> onCreate --> onStart --> stopService的时候调用onDestroy方法(如果没有调用stopService Service会一直在后头运行)
bindService --> onCreate --> onBind--> onUnbind->onDestroyed

1 通过startService
    Service会经历 onCreate 到onStart,然后处于运行状态,stopService的时候调用onDestroy方法。
    如果是调用者自己直接退出而没有调用stopService的话,Service会一直在后台运行。
2 通过bindService   
    Service会运行onCreate,然后是调用onBind, 这个时候调用者和Service绑定在一起。调用者退出了,Srevice就会调用onUnbind->onDestroyed方法。
    所谓绑定在一起就共存亡了。调用者也可以通过调用unbindService方法来停止服务,这时候Srevice就会调用onUnbind->onDestroyed方法。
IntentService
    有handler,handlerThread,绑定looper,在onStart()发送handler回调抽象方法->执行完抽象方法自动调用关闭服务
JobIntentService
    Android O 对后台限制,8.0提供了JobIntentService,简单的将任务交给JobScheduler调度  
    任务的提交则是在enqueueWork中。
多次启动同一个service
    service创建时,也就是第一次启动时会调用oncreate,如果还在运行,你继续启动的话就不调用oncreate了,而是调用onstartcommand方法

==BroadcastReceiver==

广播接受者
	开启方式
		写一个类 继承 BroadcastReceiver
			代码进行注册	不是常驻型广播,也就是说广播跟随程序的生命周期。
			 清单文件	 是常驻型,也就是说当应用程序关闭后,如果有信息广播来,程序也会被系统调用自动运行。
			
	有序和无序广播	
		无序广播 广播不能被终止,数据不可以被修改sendBroadcast(Intent)或sendBroadcast(Intent, String)
		有序广播 按照优先级接受,可以被终止,数据可以被修改sendOrderedBroadcast(Intent, String, BroadCastReceiver, Handler, int, String, Bundle)
LocalBroadcastManager 只能在应用内部发送广播,利用handler实现的
本地广播和全局广播
    本地只有自己的APP能收到
    别人也不能给你的APP发广播

==ContentProvider的概念==

数据库在安卓中是私有的
一个程序可以通过实现一个 contentprovider 抽象接口将自己的数据暴露出来
为储存和获取数据提供统一的接口可以在不同的应用程序之间共享数据
用表的形式组织数据 提供删增改查的方法
每个都有一个公共的URI 这个 URI用于表示所提供的数据
(新添加) 系统下不同程序默认是数据不共享的 白自己的数据通过URI的形式共享出去

Intent 和 Intent-Filter的区别

 IntentFilter就是用于描述intent的各种属性
 Intent负责对应用中一次操作的动作、涉及数据、附加数据进行描述

属性动画原理可以看出:ObjectAnimator与 ValueAnimator类的区别:

ValueAnimator 类是先改变值,然后 手动赋值 给对象的属性从而实现动画;是 间接 对对象属性进行操作;
ObjectAnimator 类是先改变值,然后 自动赋值 给对象的属性从而实现动画;是 直接 对对象属性进行操作;

context讲解

context是个抽象基类有两个子类,分别是contextWrapper(功能的封装类)和contexttextImpl(功能的实现类)
context一共有3中类型 分别是activity service application 这3个类分别承担着不同的作用,他们3个的context是不一样的都属于context的一种
activity view window 三者关系
activity在attach中初始化一个window
window是一个view或者viewgroup
view 通过addview来添加不同的view
1:Activity中调用attach创建一个Window,准确的说是PhoneWindow。
2:这个PhoneWindow有一个“DecorView”,在setContentView中会将layout填充到decorView。
3: 一个应用进程有一个windowManagerGlobal对象,
4: 每个phonewindow对应一个ViewRootImple对象
5: windowMangerGlobal.addView调用ViewRootImpl的setView方法,完成window的添加过程
6: ViewRootImpl的setView方法中吧任务交给底层wms->桥接模式
View的绘制流程
activityThread.handleResumeActivity()中decorView与window绑定->WindowManagerImp里面vindowManagerGlobal实例化ViewRootImpl
->viewRootImpl通过setView添加view,requestLayout()设置消息屏障拦截所有同步消息->编舞者开始执行view的3个方法
ViewRootImpl如何执行View的渲染操作
    measure->layout->draw->软件绘制和硬件加速->view的两种刷新方式 invalidate(UI线程)和postInvalidate(非UI线程)

measure过程、layout过程、draw过程
第一步 OnMeasure测量视图的大小从父view到子view递归调用measure测量模式,exactly精确值模式,at_most最大值模式,unspecified不指定大小测量模式
    在ViewGrounp中 measureChildren()会遍历测量viewGroup中所有的View,当view处理Gone状态不对其进行测量,将测量出来的信息传入子View的measure
第二步 OnLayout 测量视图的位置
    通过setFrame设定View的四个顶点位置,执行onLayout方法
第三步 OnDraw绘制视图
    背景->view内容->子view->装饰
    根据activity的布局进行绘制->从viewroot开始->从上到下绘制在整个视图树->每个view负责绘制自己
    viewgroup还要通知子view->分为3个步骤->measure有测量模式,分发给viewgroup
    在viewgroup中传递给子view,遍历自身所有view
    逐个调用子view的measure()->layout确认view在父容器的位置,父容器获取view的位置参数传递给子view->draw
开始->构造函数(view初始化)->onMeasure(测量view大小)->onSizeChanged(确定view大小)->onlayout(确定子view布局)->ondraw(会话)
->视图改变,用户操作或者自身改变->invalidate()->onDraw

getMeasuredWidth和getWidth的区别
    getMeasuredWidth 在measure()执行后就会有数据
    getWidth 在layout()执行后才有
    
子线程不能更新UI
    执行完onResume才会decorView与activity绑定
    有个checkThread()来检查是否是主线程,oncreate没有检查因为onCreate处于view创建期->onstart界面看见
    ->onResume与用户交互->onResume方法之后才算是更新UI->setcontentview只是创建了一个View树->只有onResume之后才是真正的渲染
事件分发 onInterceptTouchEvent拦截
1 activity->phoneWindow->DocerView->ViewGrounp->View,View不触发返回由父级处理
2 要经历的3个方法  dispatchTouchEvent(分发)、onInterceptTouchEvent(拦截)、onTouch(处理)
大体流程
    1 activity中走window的dispatch->DocerView的dispatch->分发给ViewGroup->dispatchTouchEvent()中事件为down,调用onInterceptTouchEvent判断是否拦截
    ->遍历子view,判断点击坐标是否在子view范围呢.
    2 如果拦截->onTouchEvent(自己处理mFirstTouchTarget)->OnclickListerer->结束
    3 如果不拦截->将mFirstTouchTarget向下传递->如果是view就消费 ,如果是viewGroup就向下分发

setonTouchListener->ontouch() 返回false并且在抬起执行 onclick,返回true就不执行
mFirstTouchTarget是一个touchTarget类型的链表->因为安卓可以多指点击操作
requestDisallowInterceptTouchEvent方法进行干预父元素的事件分发->会导致父 down中的 disallowintercept 发生变化

外部拦截onInterceptTouchEvent,内部拦截dispatchTouchEvent

public boolean dispatchTouchEvent(MotionEvent event) {
  boolean consume = false;
  if (onInterceptTouchEvent(event)) {
    consume = onTouchEvent(event);
  } else {
    consume = child.dispatchTouchEvent(ev);
  }
  return consume;
}
Android中Service和Thread的区别
线程 是分配CPU基本单位  子线程  可以在service和activity中被创建
服务 组件之一	被用来执行长时间的后台任务		  主线程		可以独立运行
系统可能在内存不足的时候优先杀死后台的Activity或者Thread,而不会轻易杀死Service组件

直播

采集->处理(美颜,水印)->压缩(h265,h264)->推流->流媒体服务器分发->拉流观看
采集 分为视频和音频不同的输入源和数据格式
    音频 将信号采集成PCM编码的原始数据,之后压缩成mp3格式
    视频 拍摄成YUV编码的数据,之后经过编码压缩成H264格式,常见的格式比如mp4,3GP
处理 一般在解码压缩前进行处理,比如水印,降噪,美颜
编码和封装 将巨大的原始数据编码成很小的视频,核心就是去除冗余信息
推到流媒体 RTMP实时消息传输协议 RTSP实时流传输协议
    rtmp是主流的是基于TCP的是一种设计用来进行实时数据通信的协议
整个过程分为3部分
    1 主播端 采集 编码 封装 推流
    2 流媒体 接受到流进行合并处理分发给观众
    3 观众端 拉流 解码 播放
多媒体 总结
    视频 分辨率 帧率 编码 封装
    MediaPlayer
        透过JNI调用Native层 mediaService进程独立存在处理多媒体 APP和mediaService进程通过binder交互
        MediaPlayer和SurfaceView	
jetpack

viewModel

需要向viewModel类传数据
    class Factory implements ViewModelProvider.Factory
屏幕旋转保存数据
    activity实现了ViewModelStoreOwner接口 重写了ViewModelStore->Destroy()
    ->onRetainNonConfigurationInstance保存了viewModuleStore->在activity启动时调用了 attach中恢复
    ->viewModuleStore内部维护了 hashmap储存了viewModel实例
viewModel销毁
    在activity中componentActivity的构造方法中->lifecycle中加了onDestroy的监听->getViewModelStore.clear->从map中删除
fragment销毁
    FragmentManagerViewModel.clearnonConfigState-> viewModelStore.clear();

Lifecycle

观察者添加observer ->在activity的oncreate中初始化LifecycleRegistry-> activity生命周期发送变化->通过mLifecycleRegistry.markState方法标记相应的状态	
对于26.1.0以后的supportActivity
    activity的oncreate的时候添加了reportFragment(代理)->代理在onActivityCreated.dispatch进行分发生命周期
setValue和postValue
    postValue同步方法 不放到主线程执行->之后调用setValue	

liveData

addObserve(this),this 就是lifecycle监听生命周期
处于激活状态->判断mVersion最新版本->不是最新的就把上一次缓存的数据通知observer
调用setValue的时候,mVersion+1->处于激活状态,直接处理->不是激活状态等回调
observeForever 当activity出于不活跃状态也可以发送数据->需要remove obsever否则内存泄漏
Dalvik 和ART
    dalvik 可以支持已转换为.dex格式的Java应用程序运行->dalvik虚拟机是寄存器 jvm是堆栈
    art 取代了dalvik->新的虚拟机采用的AOT->可以将应用程序的字节码转换为机器码->运行过程无需进行试试编译->占用空间增大
加载so库
    直接system.loadLibrary加载工程中的libs下的so库

SharedPreferences

每次读取key对应的值都是对文件进行一次读的操作,IO操作
线程安全加了3把锁
    1 读map加锁
    2 写加锁,防止没有提交数据丢失,editor持有map,当提交才将editor中的map和真map合并
    3 文件更新加锁
进程不安全
    添加读写锁
第一次初始化时,会对xml进行一次读取,将文件内所有内容缓存到内存的map中
    put()更新map中的数据,commit时将map中的数据更新到xml中
    应该将不同业务的值,放到不同的xml文件中
sharedPreferences.edit().putint().putstring.commit() 也是一次提交一次io操作
apply()异步执行 commit主线程执行