2024 android面试题总结部分

进程间通信方式

  1. 主要有intent,只支持Bundle支持的数据类型;
  2. Messenger 支持一对多串行通信,用于发送消息及Bundle数据;
  3. AIDL 功能强大,可调用服务端方法;
  4. ContentProvider 主要支持进程间共享数据;
  5. BroadCastReceiver 广播也可实现进程间通信,发送消息及数据

线程间通信方式

我们知道线程是CPU调度的最小单位。在Android中主线程是不能够做耗时操作的,子线程是不能够更新UI的。而线程间通信的方式有很多,比如广播,Eventbus,接口回掉,在Android中主要是使用handler。handler通过调用sendmessage方法,将保存消息的Message发送到Messagequeue中,而looper对象不断的调用loop方法,从messageueue中取出message,交给handler处理,从而完成线程间通信。

  1. 使用object类的wait()和notify()方法配合synchronized使用
  2. 使用Lock类或者LockSupport类实现线程间的阻塞和唤醒
  3. 使用volatile关键字(修饰基本类型数据),实现线程间共享内存,保证数据的可见性;

    如何保证数据的可见性,先学习下Java Memory Model内存模型:

  • JVM中存在主存区(Main Memory或Java Heap Memory),所有变量都是在主存中,对于所有线程进行共享.

  • 每个线程又存在自己的工作内存(Working Memory),线程对所有变量的操作并非发生在主存区,而是发生在工作内存中.

  • 所以我们会发现一个线程中修改了一些变量的值,但是在另外一个线程未必生效,在主内存中变量值也未必同步。

  • 除了我们平常用synchronized锁定对象可保证对象值同步,我们使用volatile关键字可以标记变量,使用的是CPU级别的memory barrier的write,read指令保证数据同步,所以多个线程都能获取到一致的数据。

线程池

 Android中常见的线程池有四种,FixedThreadPool、CachedThreadPool、ScheduledThreadPool、SingleThreadExecutor。

 FixedThreadPool线程池是通过Executors的new FixedThreadPool方法来创建。它的特点是该线程池中的线程数量是固定的。即使线程处于闲置的状态,它们也不会被回收,除非线程池被关闭。当所有的线程都处于活跃状态的时候,新任务就处于队列中等待线程来处理。注意,FixedThreadPool只有核心线程,没有非核心线程。

CachedThreadPool线程池是通过Executors的newCachedThreadPool进行创建的。它是一种线程数目不固定的线程池,它没有核心线程,只有非核心线程,当线程池中的线程都处于活跃状态,就会创建新的线程来处理新的任务。否则就会利用闲置的线程来处理新的任务。线程池中的线程都有超时机制,这个超时机制时长是60s,超过这个时间,闲置的线程就会被回收。这种线程池适合处理大量并且耗时较少的任务。这里得说一下,CachedThreadPool的任务队列,基本都是空的。

ScheduledThreadPool线程池是通过Executors的newScheduledThreadPool进行创建的,它的核心线程是固定的,但是非核心线程数是不固定的,并且当非核心线程一处于空闲状态,就立即被回收。这种线程适合执行定时任务和具有固定周期的重复任务。

SingleThreadExecutor线程池是通过Executors的newSingleThreadExecutor方法来创建的,这类线程池中只有一个核心线程,也没有非核心线程,这就确保了所有任务能够在同一个线程并且按照顺序来执行,这样就不需要考虑线程同步的问题。

Activity的生命周期以及启动模式

生命周期如下图

2024 android面试题总结部分_第1张图片

启动模式分为四种

  1. Standard 标准模式,每次启动都会生成新的实例,应用于邮件,manifest无配置默认为此种启动模式
  2. SingleTop 栈顶复用 在栈顶存在该实例便复用,否则新建,应用于登录页面,推送消息栏,复用拉起时走onNewIntent()方法
  3. SingleTask 栈内复用模式,在栈内存在该实例则复用,上面的实例清出栈,主要用于主页面,支付页面,webview页面
  4. SingleInstance  栈内只存在一个实例,主要用于系统Launcher,锁屏键,来电显示等

Service的两种启动方式及其区别

startService 启动后的方法是onStartCommand和bindService 启动后的方法是onBind

service的两种启动方式:Service的两种启动方式_service启动方式-CSDN博客

IntentService和Service的区别:IntentService和Service区别 - 简书

关于==和equals的区别和联系

1)对于==,比较的是值是否相等

  (1)如果作用于基本数据类型的变量,则直接比较其存储的 “值”是否相等;

(2)如果作用于引用类型的变量,则比较的是所指向的对象的地址

2)对于equals方法,注意:equals方法不能作用于基本数据类型的变量,equals继承Object类,比较的是是否是  同一个对象

(1)如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;

(2)诸如String、Date等类对equals方法进行了重写的话,比较的是所指向的对象的内容。

Thread和Runnable的区别以及Thread的start和run的区别

  1. Thread和Runnable都可以实现多线程(还可以用Executors实现线程池和callable)
  2. Thread是类,Runnable是接口,runnable可以实现复用
  3. Start方法开启子线程,run是Thread内部的方法,开启线程调用run()无法实现多线程

    thread的几种状态:https://www.cnblogs.com/jhxxb/p/10821483.html

MVC MVP MVVM 的区别及特点

  1. MVC中view承担了部分controller的功能,无法达到逻辑的复用
  2. MVP 通过view和p层即Presenter的相互持有,p调用module,将逻辑方法抽象成接口,presenter中去具体实现,view层只是调用p的方法;优点是模型视图完全分离,presenter中的方法可以重用,缺点是presenter与view联系紧密,视图改变,presenter也要随之改变,实现方法过多,不易维护
  3. MVVM 采用双向绑定(data-binding),优点是代码简单,view的变动自动反应在viewModel中,反之亦然,缺点是维护性差,代码不易阅读

关于View的事件传递

可以参考这篇文章View事件传递机制 - 简书;

具体代码可看迅速理解Android事件传递 - 星际怪物 - ITeye博客

view的事件传递:图解 Android 事件分发机制 - 简书

String,StringBuffer,StringBuilder的区别

  1. String不可改变对象,一旦创建就不能修改;
  2. StringBuffer创建之后,可以去修改,当多个线程同步操作数据,使用StringBuffer
  3. StringBuilder也可修改,执行效率高于StringBuffer,线程不安全
  4. 字符赋值频繁使用StringBuilder

简述JNI

是java和c语言之间的桥梁,由于java是一种半解释语言,可以被反编译出来,一种重要涉及安全的代码就使用了C编程,再者很多底层功能调用C语言都实现了Java没必要重复造轮子,所以定义了JNI接口的实现

Android性能优化

  1. 布局优化: 减少布局层级,使用ViewStub提高显示速度,布局服用,尽可能少使用warp_content,删除空间中无用的属性,避免过度绘制移除window默认背景,按需显示占位图,自定义View优化,使用canvas.clipRect()识别可见区域
  2. 启动速度:采用分布加载,异步加载,延期加载提高应用初始化速度,采用线程初始化数据等,合理的刷新机制
  3. 内存方面:防止内存泄露,使用一些第三方工具检测解决
  4. 代码优化:遵循Java生命周期
  5. 安装包优化:删除无用资源,优化图片,代码混淆,避免重复库存在
  6. 网路优化:避免过多的网络请求,做好数据缓存;减小数据传输,对传输数据进行压缩
  7. 绘制优化:绘制优化是指在View的onDraw()方法内避免执行大量的操作。不要在在onDraw()方法内创建大量的局部对象,这不仅会占用过多内存还会频繁gc,降低程序的效率;不要在onDraw()方法内执行耗时操作,绘制时间要保持在16ms以内,防止界面卡顿。
  8. 内存泄漏优化:内存泄漏,是指一段内存在该释放的时候却得不到释放,导致App内内存越用越少。一,静态变量导致的内存泄漏,静态变量的生命周期是整个程序的生命周期,所以不要让静态变量引用生命周期比程序生命周期短的对象;二,单例模式导致的内存泄漏,单例模式对象引用局部对象导致内存泄漏;三,属性动画或线程导致的内存泄漏,属性动画和线程不去主动停止,会一直执行下去的,要在适当的时候停止属性动画和线程。
  9. 响应速度优化和ANR:响应速度优化是指避免在主线程做耗时操作,因为操作都是在主线程响应的 ;解决ANR问题,如果不能在代码中找到导致ANR的原因,则可以分析traces.txt找到原因,traces.txt文件会记录导致ANR的原因的。
  10. 线程优化:不要开启大量线程,采用线程池。

详细可看:Android浅析之性能优化 - 知乎

Android 常见性能优化总结 - 简书

Java常用数据结构

HashMap HashTable区别

  1. HashTable方法同步,方法用synchronized修饰,多线程场合
  2. HashTable不允许null值
  3. 遍历方式,HashTable使用Enumeration遍历,HashMap使用Iterator遍历
  4. hash数组大小默认11,HshTable增加为old*2 + 1,HashMap默认16,增加为2的指数倍
  5. HashTable线程安全,但是需要获得对象锁,一般使用CurrentHashMap
  6. HashTable竞争同一把锁,ConcurrentHashMap

HashSet和HashMap的区别
  *HashMap*     *HashSet*
  HashMap实现了Map接口     HashSet实现了Set接口
  HashMap储存键值对     HashSet仅仅存储对象
  使用put()方法将元素放入map中     使用add()方法将元素放入set中
  HashMap中使用键对象来计算hashcode值     HashSet使用成员对象来计算hashcode值,对于两个对象来说hashcode可能相同,所以equals()方法用来判断对象的相等性,如果两个对象不同的话,那么返回false
  HashMap比较快,因为是使用唯一的键来获取对象     HashSet较HashMap来说比较慢

LinkedList ArrayList区别

  1. 双向链表&Object数组
  2. 插入删除&随机查找
  3. 都是不同步的,不保证线程安全
  4. 内存空间占用,ArrayList在结尾预留容量空间,LinkedList每一个元素消耗更多的空间

Android的动画类型

主要分为帧动画,补间动画和属性动画;

属性动画和补间动画区别

  1. 补间动画可以设置4个方向的动画, 属性动画指定任意动画
  2. 补间动画是针对UI控件执行的动画, 属性动画是针对对象进行的动画
  3. 补间动画移动后, 控件的实际位置没有变化 ;
  4. 属性动画会随着位置的变化而变化

Post和get的区别

  1. get请求的参数是拼接在url中的,浏览器对get请求的参数长度是限制的
  2. Post请求的参数是放在http请求的body当中的,相比get请求来说相对安全,post请求的可以携带的数据相比get请求在浏览器中可以更大

内存泄露如何查看和解决

内存泄漏的例子:https://www.cnblogs.com/andashu/p/6440944.html

关于非静态内部类的持有 深入理解Java中为什么内部类可以访问外部类的成员_java中局部内部类为什么能获取父类的成员变量-CSDN博客

概念:有些对象只有有限的生命周期,当他们的任务完成之后,它们将被垃圾回收,如果在对象的生命周期本该结束的时候,这个对象还被一系列的引用,着就会导致内存泄露。
解决方法:使用开源框架LeakCanary检测针对性解决
常见的内存泄露有:
1)单例造成的内存泄露,例如单例中的Context生命周期大于本身Context生命周期
线程使用Hander造成的内存卸扣,当activity已经结束,线程依然在运行更新UI非静态类2)使用静态变量导致无法回收释放造成泄露
3)WebView网页过多造成内存泄露
4)资源未关闭造成泄露,例如数据库使用完之后关闭连接

深层拷贝和浅层拷贝的区别

浅层拷贝指的是对引用的复制例如list = originList,要想让原数据即originList在复制到list集合中并且改变其中的值后,原数据不受影响,这时就需要用到深层拷贝,即将原数据中的对象集合进行clone,这样即使对list中的数据进行改变的操作,也不会影响到originList的数据

Okhttp面试相关

如何使用okhttp进行网络异步请求,并根据请求结果刷新UI

第一步,创建一个OkHttpClient对象 OkHttpClient mClient = new OkHttpClient.Builder().build();
第二步,创建携带请求信息的Request对象 Request request = new Request.Builder().url("http://www.baidu.com").get().build();
第三步,创建Call对象  Call call = mClient.newCall(request);
第四步,call.enqueue()
需要注意的是,不能直接在Callback中更新UI,否则会报出异常

Okhttp面试简答-CSDN博客

Glide面试相关

读取的顺序是:Lru算法缓存、弱引用缓存、磁盘缓存

Android面试题:Glide_android glide原理面试-CSDN博客

类加载机制及原理

类加载机制:https://www.cnblogs.com/yixianyixian/p/8145506.html

JAVA虚拟机、Dalvik虚拟机和ART虚拟机简要对比

虚拟机简要对比 :JAVA虚拟机、Dalvik虚拟机和ART虚拟机简要对比_java虚拟机 dalvik虚拟机 art虚拟机-CSDN博客

常用的设计模式

1.单例模式:双重验证锁

public class SingleInstance {

    private static volatile SingleInstance instance;

    public static SingleInstance getInstance() {

        //第一次校验

        if (instance == null) {

            synchronized (SingleInstance.class) {

                //第二次校验

                if (instance == null) {

                    instance = new SingleInstance();

                }

            }

        }

        return instance;

    }

}

为什么需要两次判空

第一次校验:由于单例模式只需要创建一次实例,如果后面再次调用getInstance()方法时,则直接返回之前创建的实例,因此大部分时间不需要执行同步方法里面的代码,大大提高了性能。如果不加第一次校验的话,那跟上面的普通懒汉模式没什么区别,每次都要去竞争锁。

第二次校验:如果没有第二次校验,假设线程A执行了第一次校验后,判断为null,这时线程B也获取了CPU执行权,也执行了第一次校验,判断也为null。接下来线程B获得锁,创建实例。这时线程A又获得CPU执行权,由于之前已经进行了第一次校验,结果为null(不会再次判断),获得锁后,直接创建实例。结果就会导致创建多个实例。所以需要在同步代码里面进行第二次校验,如果实例为空,则进行创建。

2.适配器模式,如GridView,ListView,Recycleview对应的列表加载的适配器刷新模式

3.建造者模式,如AlertDialog的创建方法

4.观察者模式,如EventBus使用到的观察者模式以及BroadCastReceiver的观察者模式

你可能感兴趣的:(开发日常问题,android)