Android、Java、Kotlin面试笔记2020.11.22

四种LaunchMode使用场景

  1. standard默认模式,每次激活Activity时都会创建Activity实例,并放入任务栈。
  2. singleTop如果栈顶存在该Activity的实例,就复用该实例(会调用onNewIntent()),否则会创建新的实例并放入堆栈,即使存在该Activity实例,只要不在栈顶就会创建新的实例。
  3. singleTask如果该栈中已经有该Activity的实例,就重用该实例(会调用onNewIntent()),重用时,会将该实例回到栈顶,他上面的实例将会被移出栈,如果栈中不存在栈,就创建新的实例放入栈中。
  4. 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应用无响应

产生原因

  1. 5秒内无法响应用户输入事件
  2. BroadcastReceiver在10秒内无法结束
  3. Service在20秒内无法结束(低概率)

解决方式

  1. 不要在主线程中做耗时操作,而应放在子线程中实现。
  2. 避免再BroadcastReceiver中做耗时操作或者计算
  3. 避免再Intent Receiver里启动一个Activity,因为它会创建一个新的画面,并从当前用户正在运行的程序上抢夺焦点。
  4. 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操作,效率低
  • ParcelableAndroid序列化接口,效率高,使用麻烦,在内存中读写,(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 关键字的区别

  1. volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取;synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
  2. volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的
  3. volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性
  4. volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
  5. 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向下传递,事件传递的方向是由父类到子类,事件响应的方向是从子类到父类

处理大图,缓存大图

  1. 有效加载大图片,合理设置BitmapFactory.Options的inSampleSize值,减少图片内存占用
  2. 仅请求图片的大小inJustDecodeBounds=true,不会加载图片到内存
  3. 缓存图片,内存缓存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的区别

  1. lazy{}只能用在val类型,lateinit只能用在var类型
  2. lateinit不能用在可空的属性上和java的基本类型上
  3. lateinit可以在任何位置初始化并且可以初始化多次,而lazy在第一次被调用时就被初始化,想要被改变只能重新定义
  4. 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_OKRESULT_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
Activity

Fragment
Fragment
Service
Service

其它知识

代码段

//获取现在时间字符串
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
//延时5秒执行
TimeUnit.SECONDS.sleep(5);

layout_grivaty、grivaty的用处

  • grivaty:显示的父控件中子控件的属性
  • layout_grivaty:显示子控件在父控件中的属性

安卓四大组件

  1. 活动
  2. 服务
  3. 内容提供者
  4. 广播接受器

特征
Java:继承、封装、多态
面向对象:继承、封装、多态、抽象

RabbitMQ
RabbitMQ连接方式

  • direct直连模式:消息直接入队列
  • fanout扇形模式:消息通过exchange分发到队列
  • topic话题模式:路由键之间有规则
    • *匹配任意一个字符
    • #匹配零个或多个字符

RabbitMQ注解

  • @RabbitListener标注在类上面表示当有收到消息的时候,就交给
  • @RabbitHandler注解的方法处理,具体使用哪个方法处理,根据方法参数类型来定
  • @RabbitListener注解在方法上传入队列名可直接在方法中获取队列消息
  • @Payload注解参数,获取消息中的body
  • @Headers注解参数,获取消息中headers

控制反转(IOC)和面向切面(AOP)

  • IOC控制反转/依赖注入降低耦合,统一集中管理Bean实例
  • AOP面向切面把重复的操作封装起来,分别在目标操作的前后执行重复操作(例如登录)
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值