Android面试主题整理合集(二)

Android框架知识

框架知识相对来说就比较难了,希望大家认真学习。


1.buttnife实现原理

通过注解处理器动态生成java文件,在java文件中进行findViewById和setOnClickListener操作

2. EventBus实现原理

通过观察者设计模式,先通过注册的方式将指定的类加到一个表里面,等发送消息时轮训那个表,依据注解和注解的value找到匹配的方法,然后执行该方法

3.LiveData原理

LiveData通知其他组件原理主要是观察者设计模式。在android里用的比较多的是MutableLiveData

 public class MutableLiveData<T> extends LiveData<T> {
    //非主线程中使用
    @Override
    public void postValue(T value) {
        super.postValue(value);
    }

    //主线程中使用
    @Override
    public void setValue(T value) {
        super.setValue(value);
    }
}

通过LiveData的postValue或者setValue方法,通知观察者Observer数据的变化并请可观察的变化数据通过Observer的onChanged传导出来

其优点有

  • 遵从应用程序的生命周期,如在Activity中如果数据更新了但Activity已经是destroy状态,LivaeData就不会通知Activity(observer)
  • 不会造成内存泄漏(LiveData仅通知活跃的Observer去更新UI。 非活跃状态的Observer,即使订阅了LiveData,也不会收到更新的通知)

LiveData的observe方法

    @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            // ignore
            return;
        }
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        if (existing != null && !existing.isAttachedTo(owner)) {
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        if (existing != null) {
            return;
        }
        //将LifecycleBoundObserver和activity或者fragment的lifeCycle相关联
        owner.getLifecycle().addObserver(wrapper);
    }

装饰器LifecycleBoundObserver

 class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
        @NonNull
        final LifecycleOwner mOwner;

        LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
            super(observer);
            mOwner = owner;
        }

        @Override
        boolean shouldBeActive() {
            return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
        }

        @Override
        public void onStateChanged(@NonNull LifecycleOwner source,
                @NonNull Lifecycle.Event event) {
                //数据发生变化,如果activity或者fragment已经销毁,就解除订阅,避免了内存泄露
            if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
                removeObserver(mObserver);
                return;
            }
            activeStateChanged(shouldBeActive());
        }
......
public interface Observer<T> {
    /**
     * Called when the data is changed.
     * @param t  The new data
     */
    void onChanged(@Nullable T t);
}

第一个参数可以直接传activity,如果activity已经销毁直接return,否则owner.getLifecycle().addObserver(wrapper);,LiveData调用postValue或者setValue方法就会回调Observer的onChanged方法

  • LiveData的observe方法接收activity的getLifeCycle(可感知activity生命周期)和Observer(添加一个观察者到LiveData的内部集合里)。
  • LiveData的setValue方法将数据通知到所有观察者,如果actiivty已经销毁则之间解除订阅。否则所有观察者的onChange(T t)方法会被执行

这数据处理逻辑放在ViewModel里,需要更新UI时给观察者发送通知即可,同时不用关心内存泄漏

4.ViewModel

  • 处理逻辑
  • 存储数据,ViewModel储存的数据activity旋转后数据不会丢失
  • 数据共享

Android面试主题整理合集(二)

 

5.Flowable和Observable

RxJava1中使用Observable和Observer建立起订阅关系,但会产生背压问题。Rxjava2使用Flowable和Subscriber替换RaJava1的Observable和Observer。Flowable是在Observable的基础上优化后的产物,Observable能解决的问题Flowable也都能解决。但是并不代表Flowable可以完全取代Observable,Flowable运行效率要比Observable慢得多。 只有在需要处理背压问题时,才需要使用Flowable

  • 当上下游在不同的线程中,通过Observable发射,处理,响应数据流时,如果上游发射数据的速度快于下游接收处理数据的速度,这样对于那些没来得及处理的数据就会造成积压,这些数据既不会丢失,也不会被垃圾回收机制回收,而是存放在一个异步缓存池中,如果缓存池中的数据一直得不到处理,越积越多,最后就会造成内存溢出,这便是响应式编程中的背压(backpressure)问题
  • 如果能够确定:
  1. 上下游运行在同一个线程中
  2. 上下游工作在不同的线程中,但是下游处理数据的速度不慢于上游发射数据的速度
  3. 上下游工作在不同的线程中,但是数据流中只有一条数, 则不会产生背压问题,就没有必要使用Flowable,以免影响性能。

6.app优化

  • 内存优化:使用leakcanary抓取内存泄露,或者使用android studio抓取内存信息,通过Profiler分析内存泄露情况
  • 体积优化 不复杂图片使用svg代替png。换肤时使用着色器,可减少图片资源 build文件配置 保留指定语言 保留指定so库架构 开启混淆压缩

Android面试主题整理合集(二)

 

7.Rxjava中关于Disposable

Rxjava容易遭层内存泄漏。在订阅了事件后没有及时取阅,导致在activity或者fragment销毁后仍然占用着内存,无法释放。而disposable,可以用来取消订阅

8.Glide

  • Glide的缓存 Glide缓存机制大致分为三层:Lru算法缓存、弱引用缓存、磁盘缓存 读取的顺序是:Lru算法缓存、弱引用缓存、磁盘缓存(据说glide最新版改了,先从弱引用中取,没有的话再从Lru中取,再放进弱引用中)
  • Glide.with(this) .load("http://www.baidu.com/img/bdlogo.png") .into(imageView); with()方法是对RequestManager进行配置 public static RequestManager with(FragmentActivity activity) { return getRetriever(activity).get(activity); } 复制代码 load()方法是对RequestBuilder进行配置 public RequestBuilder<Drawable> load(@Nullable Object model) { return asDrawable().load(model); } 复制代码 into()方法是通过线程池给imageView进行图片的加载设置
  • 占用内存较小 默认使用RGB_565格式
  • 支持gif
  • 与Activity生命周期绑定,不会出现内存泄露 Glide绑定Activity时,生成一个无UI的Fragment 将无UI的Fragment的LifeCycle传入到RequestManager中 在RequestManager的构造方法中,将RequestManager存入到之前传入的Fragment的LifeCycle,在回调LifeCycle时会回调到Glide的相应方法

9.Android串口

通过串口编程可以让Android应用和外设进行通信,通过谷歌提供的一个库来开发。
通过那个库,打开串口。获取输入输出流,就可以利用串口接收数据和发送数据了
核心参数:

path:为串口的物理地址,一般硬件工程师都会告诉你的例如ttyS0、ttyS1等,或者通过SerialPortFinder类去寻找得到可用的串口地址。
baudrate:波特率,与外接设备一致
flags:设置为0,原因较复杂,见文章最底下

10.断点续传实现

  • 在本地下载过程中要使用数据库实时存储到底存储到文件的哪个位置了
  • 下次继续传递时,才能通过HTTP的GET请求中的setRequestProperty("Range","bytes=startIndex-endIndex");方法可以告诉服务器,数据从哪里开始,到哪里结束
  • 同时在本地的文件写入时,RandomAccessFile的seek()方法也支持在文件中的任意位置进行写入操作
  • 最后通过广播或事件总线机制将子线程的进度告诉Activity的进度条。关于断线续传的HTTP状态码是206

11.RxJava 变换操作符 map flatMap concatMap zip buffer

  • map:【数据类型转换】将被观察者发送的事件转换为另一种类型的事件。适用于一对一转换
  • flatMap:【化解循环嵌套和接口嵌套】将被观察者发送的事件序列进行拆分 & 转换 后合并成一个新的事件序列,最后再进行发送。应用场景:一个接口调用时依赖另一个接口的返回值,适用于一对多
  • concatMap:【有序】与 flatMap 的 区别在于,拆分 & 重新合并生成的事件序列 的顺序与被观察者旧序列生产的顺序一致。
  • zip: 应用场景:两个接口的数据相互结合才能显示UI
  • buffer:定期从被观察者发送的事件中获取一定数量的事件并放到缓存区中,然后把这些数据集合打包发射。

12.热修复

  • 什么是dex分包 把一个apk解压后,会有一个classes.dex的文件,它包含了我们项目中所有的class文件 dvm中存储方法id用的是short类型,所以就导致dex中方法不能超过65535个
  • 分包的原理 就是将编译好的class文件,拆分打包成多个dex 除了第1个dex文件外(正常apk中存在的唯一的dex文件),其他的所有dex文件都以资源的形式放到apk里面,并在Application的onCreate回调中通过系统的ClassLoader加载它们。 注意:在注入之前就已经引用到的类,则必须放到第一个dex文件中,否则会提示找不到该文件
  • ClassLoader动态加载 每个dex文件是一个Element,多个dex文件排列成一个有序的数组就是dexElements 将我们修复Bug后的dex文件,通过反射加入到dexElements数组最前面,就可以了

13.消息机制

  • MessageQueue的数据结构 --> 链表(因为要不停的插入和取出消息,链表插入和删除是O(1),数组是O(n))
  • Message插入的位置 --> 遍历MessageQueue,比较插入消息和MessageQueue中消息的when,插入后保证MessageQueue的when从小到大排列即可
  • 什么时候取出消息 --> 会有个循环从MessageQueue中取消息,会根据第一个消息的when进行阻塞,直到阻塞时间到,就会唤醒next方法取消息
  • Handle怎么发送Runnable --> 封装成Message,Message有一个Runnable类型的成员变量
  • 主线程怎么向子线程中发消息 --> 在子线程中Looper.prepare(),Looper.loop(),开启子线程中的MessageQueue
  • 为什么子线程向主线程发消息不需要Looper.prepare(),Looper.loop() --> Activity启动时调用了prepare()和loop(),开启过了主线程的MessageQueue
  • 不同线程怎么单独维护一个Looper的 --> 每个线程都有ThreadLocal成员变量, Looper.prepare()的时候用他来存储Loop实例。这样不同的线程就有了不同的Looper,不同的MessageQueue(MessageQueue就是Loop的成员变量)
  • Handle使用 ActivityThread的main方法中,Looper.prepareMainLopper() (子线程调用Looper.prepare()),Looper.loop()在当前线程中存了Looper实例,同时开始轮训自己的MessageQueue Handler handle = new Handler(),取出主线程中的Looper,取出Looper中MessageQueue(sendMessage时需要将messgae放到MessageQueue)

最后

在这里我也分享一份由几位大佬一起收录整理的 Flutter进阶资料以及Android学习PDF+架构视频+面试文档+源码笔记 ,并且还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料……

这些都是我闲暇时还会反复翻阅的精品资料。可以有效的帮助大家掌握知识、理解原理。当然你也可以拿去查漏补缺,提升自身的竞争力。
如果你有需要的话,可以前往 GitHub 自行查阅。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值