四种LaunchMode使用场景
standard
默认模式,每次激活Activity时都会创建Activity实例,并放入任务栈。singleTop
如果栈顶存在该Activity的实例,就复用该实例(会调用onNewIntent()),否则会创建新的实例并放入堆栈,即使存在该Activity实例,只要不在栈顶就会创建新的实例。singleTask
如果该栈中已经有该Activity的实例,就重用该实例(会调用onNewIntent()),重用时,会将该实例回到栈顶,他上面的实例将会被移出栈,如果栈中不存在栈,就创建新的实例放入栈中。singleInstance
在一个新栈中创建该Activity的实例,并让多个应用共享该栈中该Activity实例。一旦该模式的Activity实例已经存在某个栈中,任何应用再激活该Activity时都会重用该栈中实例(会调用实例的onNewIntent()),其效果相当于多个应用共享一个应用,不管谁激活该Activity都会进入同一个应用中。
Handler的原理
在Android中,一个线程有一个Looper,主线程会自动创建Looper,子线程需要在run方法中调用Looper.prepare()(创建Looper)和Looper.loop(通知Looper开始循环消费MessageQueue的消息)。
- Android中主线程是不能进行耗时操作的,子线程是不能进行更新UI的。所以就有了handler,它的作用就是实现线程之间的通信。
- Handler整个流程中,主要有四个对象,Handler,Message,MessageQueue,Looper。当应用创建的时候,就会在主线程中创建Handler对象,
- 我们通过要传送的消息保存到Message中,Handler通过调用sendMessage方法将Message发送到MessageQueue中,Looper对象就会不断的调用loop()方法
- 不断的从MessageQueue中取出Message交给Handler进行处理。从而实现线程之间的通信。
ANR应用无响应
产生原因
- 5秒内无法响应用户输入事件
- BroadcastReceiver在10秒内无法结束
- Service在20秒内无法结束(低概率)
解决方式
- 不要在主线程中做耗时操作,而应放在子线程中实现。
- 避免再BroadcastReceiver中做耗时操作或者计算
- 避免再Intent Receiver里启动一个Activity,因为它会创建一个新的画面,并从当前用户正在运行的程序上抢夺焦点。
- Service是运行在主线程的,所以在Service做耗时操作,必须要放在子线程中。
final和static使用
final
- final变量即为常量,只能赋值一次
- final方法不能被子类重用
- final类不能被继承
static
- static变量:对于静态变量在内存中只有一个拷贝,JVM只为静态分配一次内存,加载类的过程中完成静态变量内存的分配,可以用类名直接访问
- static代码块:static代码块是类加载时,初始化自动执行的
- static方法:static方法可以直接通过类名调用,任何的实例也都可以调用,因此static方法中不能用this和super关键字。
Java ==和equals的区别
==
比较的是内存的地址,基本类型比较值是否相同equals
是引用类型比较引用类型是否相同- 如果两个对象 equals,Java 运行时环境会认为他们的 hashcode 一定相等。 如果两个对象不 equals,他们的 hashcode 有可能相等。 如果两个对象 hashcode 相等,他们不一定 equals。 如果两个对象 hashcode 不相等,他们一定不 equals。
Serializable和Parcelable的区别
Serializable
是Java序列化接口,在硬盘上读写,读写过程中有大量临时变量生成,内部执行大量I/O操作,效率低Parcelable
Android序列化接口,效率高,使用麻烦,在内存中读写,(as有相关插件一键生成所需方法),对象不能保存到磁盘中
List,Set,Map的区别
List
的特征是其元素以线性方式存储,集合中可以存放重复对象ArrayList
代表长度可以改变的数组,可以对元素进行随机的访问,向ArrayList中插入和删除元素的速度慢,基于数组实现的,线程不安全LinkedList
在实现中采用链表数据结构,插入和删除速度快,访问速度慢,基于双链表实现的
Set
是最简单的一种集合,集合中对象不按特定的方式排序,并且没有重复的对象,Set接口主要实现了两个实现类:HashSet
类按照哈希算法来存取集合中的对象,存取速度较快,不能添加重复元素TreeSet
类实现了SortSet接口,能够对集合中的对象进行排序
Map
是一种把键对象和值对象映射的集合,它的每一个元素都包含一对键对象和值对象,Map没有继承于Collection接口从Map集合中检索元素时,只要给出键对象就会给出对应的值对象HashMap
基于散列表的实现,插入和查询"键值对"的开销是固定的,可以通过构造器设置容量capactity和负载因子load factor,以调整容器的性能LinkedHashMap
类似于HashMap,但是迭代遍历它时,取得“键值对”的顺序是其插入次序,或者是最近最少使用(LRU)的次序。只比HashMap慢一点。而在迭代访问时发而更快,因为它使用链表维护内部次序TreeMap
基于红黑树数据结构的实现,查看"键"或"键值对"时,它们会被排序(次序由Comparabel或Comparator决定)。TreeMap的特点在 于,你得到的结果是经过排序的。TreeMap是唯一的带有subMap()方法的Map,它可以返回一个子树HashTable
是线程安全,不允许有null的键和值,效率稍低,方法是是Synchronize的。有contains方法方法。Hashtable 继承于Dictionary类
线程同步
- synchronized关键字修改的方法。
- synchronized关键字修饰的语句块
- 使用特殊域变量(volatile)实现线程同步
synchronized 和volatile 关键字的区别
- volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取;synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
- volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的
- volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性
- volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
- volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化
数据库事务特点ACID
原子性
整个事务中的所有操作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节。一致性
一个事务可以封装状态改变(除非它是一个只读的)。事务必须始终保持系统处于一致的状态,不管在任何给定的时间并发事务有多少。隔离性
隔离状态执行事务,使它们好像是系统在给定时间内执行的唯一操作。持久性
在事务完成以后,该事务对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。
反编译重打包
apktool
资源文件获取dex2jar
源码文件获取jd-gui
源码查看- dex2jar将Apk内的dex转化转化为jar文件,然后jd-gui可以查看,保存为java文件,Apktool可以将布局图片等资源提取出来,apktool命令
apktool d test.apk
,使用jadx对classes.dex进行反编译,相对于dex2jar,jadx更加方便快捷,打开bin文件中的jadx-gui.bat然后直接选择APK即可,虽然jadx也可反编译资源文件,但有时候并不准确。- -f如果目标文件夹存在,则强制删除现有文件夹
- -o指定解码目标文件夹的名称
- -s不反编译dex文件,也就是classes.dex文件会被保留(默认将dex文件解码成smali文件)
- -r不反编译资源文件,也就是说resources.arsc文件会被保留(默认将resources.arsc解码成具体资源文件)
apktool b test
重新打包,会在目录中生成build和dist目录- 复制签名文件到dist目录下方便操作,终端进入dist目录,终端进入dist目录,执行命令jarsigner -verbose -keystore [your_key_store_path] -signedjar [signed_apk_name] [usigned_apk_name] [your_key_store_alias] -digestalg SHA1 -sigalg MD5withRSA 字段说明:
- [your_key_store_path]:密钥所在位置的绝对路径
- [signed_apk_name]:签名后安装包名称
- [usigned_apk_name]:未签名的安装包名称
- [your_key_store_alias]:密钥的别名
简述反编译
- 反编译:apktool d a.apk
- 打包:apktool b a.apk
- 创建签名:keytool -genkey -alias apk.keystore -keyalg RSA -validity 20000 -keystore apk.keystore
- 进入app文件根目录的dist/目录下执行命令打包:jarsigner -verbose -keystore apk.keystore -signedjar signed.apk a.apk apk.keystore
线程通信
我们知道线程是CPU调度的最小单位。在Android中主线程是不能够做耗时操作的,子线程是不能够更新UI的。而线程间通信的方式有很多,比如广播,Eventbus,接口回掉,在Android中主要是使用Handler。Handler通过调用sendMessage方法,将保存消息的Message发送到Messagequeue中,而Looper对象不断的调用loop方法,从messageQueue中取出Message,交给Handler处理,从而完成线程间通信。
线程池
Android中常见的线程池有四种,FixedThreadPool、CachedThreadPool、ScheduledThreadPool、SingleThreadExecutor。
FixedThreadPool
线程池是通过Executors的new FixedThreadPool方法来创建。它的特点是该线程池中的线程数量是固定的。即使线程处于闲置的状态,它们也不会被回收,除非线程池被关闭。当所有的线程都处于活跃状态的时候,新任务就处于队列中等待线程来处理。注意,FixedThreadPool只有核心线程,没有非核心线程。CachedThreadPool
线程池是通过Executors的newCachedThreadPool进行创建的。它是一种线程数目不固定的线程池,它没有核心线程,只有非核心线程,当线程池中的线程都处于活跃状态,就会创建新的线程来处理新的任务。否则就会利用闲置的线程来处理新的任务。线程池中的线程都有超时机制,这个超时机制时长是60s,超过这个时间,闲置的线程就会被回收。这种线程池适合处理大量并且耗时较少的任务。这里得说一下,CachedThreadPool的任务队列,基本都是空的。ScheduledThreadPool
线程池是通过Executors的newScheduledThreadPool进行创建的,它的核心线程是固定的,但是非核心线程数是不固定的,并且当非核心线程一处于空闲状态,就立即被回收。这种线程适合执行定时任务和具有固定周期的重复任务。SingleThreadExecutor
线程池是通过Executors的newSingleThreadExecutor方法来创建的,这类线程池中只有一个核心线程,也没有非核心线程,这就确保了所有任务能够在同一个线程并且按照顺序来执行,这样就不需要考虑线程同步的问题。
View的事件分发和View的工作原理
安卓自定义View三部曲onMeasure(),onLayout(),onDraw()
onMeasure()
测量宽高,onMeasure方法中有个setMeasureDimenSion方法来设置view的宽高测量值,而setMeasureDimenSion有个getDefaultSize()方法作为参数。一般情况下,我们只需要关注at_most和exactly两种情况,getDefaultSize的返回值就是measureSpec中的SpecSize,而这个值基本就是view测量后的大小。而UnSpecified这种情况,一般是系统内部的测量过程,它是需要考虑view的背景这些因素的。- View的测量需要MeasureSpec(测量规格),它代表一个32位的int值,高2位代表SpecMode(测量模式),低(30)位代表SpecSize(某种测量模式下的规格大小),而一组SpecMode和SpecSize可以打包为一个MeasureSpec,反之,MeasureSpec可以解包得到SpecMode和SpecSize的值,SpecMode有三类:
Unspecified
:父容器不对View有任何限制,要多大有多大Exactly
:父容器已经检测出View所需要的精度大小,这个时候,View的大小就是SpecSize所指定的值,他对应着layout布局中的match_parent或者是具体的值At_most
:父容器指定了一个可用大小的SpecSize,View的大小不能够大于这个值,它对应布局的warp_content
onLayout()
确定View在父容器上的摆布位置- 普通的View的话,可以通过setFrame方法来得到四个顶点的位置,也就确定了View在父容器的位置,接着就调用onLayout方法,该方法是父容器确定子元素的位置
onDraw()
绘制图形- 绘制背景
- 绘制自己
- 绘制child
- 绘制装饰
View的事件传递
当我们点击屏幕,Activity调用dispatchTouchEvent()方法,把事件传递给Window;Window再将事件交给DecorView(DecorView是View的根布局);DecorView再传递给ViewGroup; Activity->Window->DecorView->ViewGroup->View
dispatchTouchEvent
事件分发,会调用onInterceptTouchEvent判断是否需要拦截onInterceptTouchEvent
事件拦截,如在桌面中滑动时,返回true时父拦截事件,点击事件时,点击时返回false时,不拦截事件传递给子控件就响应了点击事件(View没有拦截方法,因为View没有子控件要拦截)onTouchEvent
事件响应- Activity首先调用dispatchTouchEvent()进行分发,接着再调用super向下传递,事件传递的方向是由父类到子类,事件响应的方向是从子类到父类
处理大图,缓存大图
- 有效加载大图片,合理设置BitmapFactory.Options的inSampleSize值,减少图片内存占用
- 仅请求图片的大小inJustDecodeBounds=true,不会加载图片到内存
- 缓存图片,内存缓存LruCache
Service生命周期
onCreate()
首次创建服务时,系统将调用此方法。如果服务已在运行,则不会调用此方法,该方法只调用一次。onStartCommand()
当另一个组件通过调用startService()请求启动服务时,系统将调用此方法。onDestroy()
当服务不再使用且将被销毁时,系统将调用此方法。onBind()
当另一个组件通过调用bindService()与服务绑定时,系统将调用此方法。onUnbind()
当另一个组件通过调用unbindService()与服务解绑时,系统将调用此方法。onRebind()
当旧的组件与服务解绑后,另一个新的组件与服务绑定,onUnbind()返回true时,系统将调用此方法。
安卓动画
逐帧动画(Frame Animation)
- 加载一系列Drawable资源来创建动画,简单来说就是播放一系列的图片来实现动画效果,可以自定义每张图片的持续时间,简单讲就是把几个静态的图片快速播放形成动画,可以使用AnimationDrawable,官方推荐使用XML文件,放在res/drawable/路径下,
补间动画(Tween Animation)
- Tween可以对View对象实现一系列动画效果,比如平移,缩放,旋转,透明度等,但是它不会改变View属性的值,只是改变了View的绘制位置,比如,一个按钮的动画过后,不在原地的位置,但是触发点击事件的仍然是原来的坐标,四个动画效果实现类:TranslateAnimation、ScaleAnimation、RotateAnimation、AlphaAnimation、AnimationSet,对应的的XML标签为translate、 scale、 rotate、alpha、set,其中set里还可以放set,然后放在放置在res/anim/目录下
属性动画(Property Animation)
- 动画的对象除了传统的View对象,还可以是Object对象,动画结束后,Object对象的属性值被实实在在的改变了, 属性动画使用Animator的子类,通常是ValueAnimator和ObjectAnimator ,ValueAnimator只能计算属性值的变化,可以设置监听然后自己处理相关逻辑;ObjectAnimator可以对对象的属性值计算后,直接应用于对象的相应属性。
Kotlin Lambda
如果接口中只存在一个回调的方法,就符合使用lambda函数可简化为
mView.setEventListener({
data: Data ->
})
//或者可以直接省略Data,借助kotlin的智能类型推导
mView.setEventListener({
data ->
})
如果上面的data参数不需要使用,可以把 “data ->” 去掉
mView.setEventListener({})
如果接口只有一个方法,可以把"()"提取到前面
mView.setEventListener(){}
方法只有一个参数,可直接去掉"()"
mView.setEventListener{}
Kotlin的let with run apply also函数的使用
let
函数的一般结构obj.let{it.todo}
,可直接在语句块最后一行写返回值,判断obj对象不为null才执行代码块,利用it代码obj对象去访问其公有的属性和方法with
函数的一般结构var str = with(StringBuilder(), { append("hello") "world" })
,一般用于对象调用多个内部方法的操作,可直接在语句块最后一行写返回值,不写则返回第一个参数run
函数的一般结构obj.run{ obj_method() }
,可直接在语句块最后一行写返回值,函数可以说是let和with两个函数的结合体,代码块可以直接调用obj对象的方法,还可以对obj对象判空决定是否运行代码块apply
函数的一般结构obj.apply{ obj_method() }
,整体作用功能和run函数很像,唯一不同点就是它返回的值是对象本身,而run函数是一个闭包形式返回,返回的是最后一行的值。also
函数的一般结构`,also和let函数很像,also函数的返回值是返回当前的这个对象。一般可用于多个扩展函数链式调用
Kotlin lateinit和by lazy的区别
lazy{}
只能用在val类型,lateinit只能用在var类型lateinit
不能用在可空的属性上和java的基本类型上- lateinit可以在任何位置初始化并且可以初始化多次,而lazy在第一次被调用时就被初始化,想要被改变只能重新定义
- lateinit有支持(反向)域
Kotlin协程
GlobalScope.launch{}
函数可以创建一个协程作用域,并且域中逻辑不会阻塞,可以通过delay()函数让程序等待协程runBlacking{}
可以让程序等待协程和它的子协程执行完再结束,可在协程域中通过launch{}函数来创建子协程,会阻塞当前线程coroutineScope{}
和runBlacking{}相似,可以让程序等待协程和它的子协程执行完再结束,并且可在协程域中通过launch{}函数来创建子协程,唯一不同的是coroutineScope{}函数只会阻塞当前协程,不阻塞线程,可在协程作用域或者挂起函数中调用launch{}
只能在协程作用域中调用val job = Job() val scope = CoroutineScope(job) scope.launch{}
推荐的写法,可通过scope.launch{}函数创建子协程,然后job.cancel()可以取消所有由scope调用launch{}而启动的协程val result = async{}.await()
函数必须在协程作用域中才可以调用,它会创建一个子协程并且返回一个Deferred对象,获取async{}函数的运行结果可以通过Deferred对象的await()方法即可,当调用await()方法时,如果代码块中还没执行完,那么await()方法将会阻塞当前协程,直到可以获得async{}函数的执行结果val result = withContext(Dispatchers.Xxx){}
相当于async的一种简化写法,Dispatchers.Xxx有三个选项Main(在安卓主线程中)、IO(高并发策略)、Default(默认低并发策略)delay()
函数只可以在协程作用域中,它可以让当前协程延迟指定时间后再运行,和Thread.sleep()方法不同,delay()函数是一个非阻塞的挂起函数cancel()
GlobalScope.launch{}和launch()函数会返回一个Job对象,只需要调用Job对象的cancel()方法取消协程
Serializable 和 Parcelable 的区别
Serializable
Java 序列化接口 在硬盘上读写 读写过程中有大量临时变量的生 成,内部执行大量的 i/o 操作,效率很低。Parcelable
Android 序列化接口 效率高 使用麻烦 在内存中读写(AS 有相关插件 一键生成所需方法) ,对象不能保存到磁盘中
Result类型回调
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
onActivityResult()
通过startActivityForResult()启动一个Activity通过setResult方法返回的数,参数:requestCode
为startActivityForResult()方法启动Activity的请求码resultCode
是setResult()方法返回的处理结果,结果有RESULT_OK
和RESULT_CANCELED
data
可通过getXxxExtra()等方法获取Activity返回数据
onRequestPermissionsResult()
通过ContextCompat.checkSelfPermission()方法检查是否有权限 通过ActivityCompat.requestPermissions()请求权限requestCode
为ActivityCompat.requestPermissions()方法请求权限的请求码permissions
为请求的所有权限grantResults
为请求通过的权限,其中PERMISSION_GRANTED
为通过的权限,PERMISSION_DENIED
为拒绝的权限。
MVC、MVP、MVVM
MVC
- Model和数据有关操作,例如读取数据库、网络请求
- View负责显示,例如xml、view、laytout
- Controller负责处理用户交互,例如Activity、Fragment
MVP
- Model和数据有关操作,例如读取数据库、网络请求
- View数据显示和交互,例如Activity、Fragment
- Presenter转发Model或者View的请求
MVVM
- Model和数据有关操作,例如读取数据库、网络请求
- View数据显示和交互,例如Activity、Fragment
- ViewModel是通过DataBinding把View和Model绑定在一起,配合LiveData、Lifecycle组件
生命周期
Activity
Fragment
Service
其它知识
代码段
//获取现在时间字符串
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
//延时5秒执行
TimeUnit.SECONDS.sleep(5);
layout_grivaty、grivaty的用处
grivaty
:显示的父控件中子控件的属性layout_grivaty
:显示子控件在父控件中的属性
安卓四大组件
- 活动
- 服务
- 内容提供者
- 广播接受器
特征
Java:继承、封装、多态
面向对象:继承、封装、多态、抽象
RabbitMQ
RabbitMQ连接方式
direct
直连模式:消息直接入队列fanout
扇形模式:消息通过exchange分发到队列topic
话题模式:路由键之间有规则*
匹配任意一个字符#
匹配零个或多个字符
RabbitMQ注解
@RabbitListener
标注在类上面表示当有收到消息的时候,就交给@RabbitHandler
注解的方法处理,具体使用哪个方法处理,根据方法参数类型来定@RabbitListener
注解在方法上传入队列名可直接在方法中获取队列消息@Payload
注解参数,获取消息中的body@Headers
注解参数,获取消息中headers
控制反转(IOC)和面向切面(AOP)
IOC控制反转/依赖注入
降低耦合,统一集中管理Bean实例AOP面向切面
把重复的操作封装起来,分别在目标操作的前后执行重复操作(例如登录)