Android 面试主题集合整理
一、线程、多线程和线程池面试专题
1、开启线程的三种方式?
1)继承 Thread 类,重写 run()方法,在 run()方法体中编写要完成的任务 new Thread().start();
2)实 现 Runnable 接 口 , 实 现 run() 方 法 new Thread(new MyRunnable()).start();
3)实现 Callable 接口 MyCallable 类,实现 call()方法,使用 FutureTask 类来 包装 Callable 对象,使用 FutureTask 对象作为 Thread 对象的 target 创建并启 动线程;调用 FutureTask 对象的 get()方法来获得子线程执行结束后的返回值。
FutureTask<Integer> ft = new FutureTask<Integer>(new MyCallable()); new Thread(ft).start();
2、run()和 start()方法区别
run()方法只是线程的主体方法,和普通方法一样,不会创建新的线程。只有调 用 start()方法,才会启动一个新的线程,新线程才会调用 run()方法,线程才会 开始执行。
3、如何控制某个方法允许并发访问线程的个数?
创建 Semaphore 变量,Semaphore semaphore = new Semaphore(5, true); 当方法进入时,请求一个信号,如果信号被用完则等待,方法运行完,释放一个 信号,释放的信号新的线程就可以使用。
4、在 Java 中 wait 和 seelp 方法的不同
wait()方法属于 Object 类,调用该方法时,线程会放弃对象锁,只有该对象调 用 notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。
sleep()方法属于 Thread 类,sleep()导致程序暂停执行指定的时间,让出 CPU, 但它的监控状态依然保存着,当指定时间到了又会回到运行状态,sleep()方法 中线程不会释放对象锁。
5、谈谈 wait/notify 关键字的理解
notify: 唤醒在此对象监视器上等待的单个线程
notifyAll(): 通知所有等待该竞争资源的线程
wait: 释放 obj 的锁,导致当前的线程等待,直接其他线程调用此对象的 notify() 或 notifyAll()方法
当要调用 wait()或 notify()/notifyAll()方法时,一定要对竞争资源进行加锁,一般放到 synchronized(obj)代码中。当调用 obj.notify/notifyAll 后,调用线程依旧持有 obj 锁,因此等待线程虽被唤醒,但仍无法获得 obj 锁,直到调用线程退出 synchronized 块,释放 obj 锁后,其他等待线程才有机会获得锁继续执行。
6、什么导致线程阻塞?
(1)一般线程阻塞
1)线程执行了 Thread.sleep(int millsecond)方法,放弃 CPU,睡眠一段时间, 一段时间过后恢复执行;
2)线程执行一段同步代码,但无法获得相关的同步锁,只能进入阻塞状态,等 到获取到同步锁,才能恢复执行;
3)线程执行了一个对象的 wait()方法,直接进入阻塞态,等待其他线程执行 notify()/notifyAll()操作;
4)线程执行某些 IO 操作,因为等待相关资源而进入了阻塞态,如 System.in, 但没有收到键盘的输入,则进入阻塞态。
5)线程礼让,Thread.yield()方法,暂停当前正在执行的线程对象,把执行机会 让给相同或更高优先级的线程,但并不会使线程进入阻塞态,线程仍处于可执行 态,随时可能再次分得 CPU 时间。线程自闭,join()方法,在当前线程调用另一个线程的 join()方法,则当前线程进入阻塞态,直到另一个线程运行结束,当前 线程再由阻塞转为就绪态。
6)线程执行 suspend()使线程进入阻塞态,必须 resume()方法被调用,才能使 线程重新进入可执行状态。
7、线程如何关闭?
1)使用标志位
2)使用 stop()方法,但该方法就像关掉电脑电源一样,可能会发生预料不到的 问题
3)使用中断 interrupt()
public class Thread {
// 中断当前线程
public void interrupt();
// 判断当前线程是否被中断
public boolen isInterrupt();
// 清除当前线程的中断状态,并返回之前的值
public static boolen interrupted();
}
但调用 interrupt()方法只是传递中断请求消息,并不代表要立马停止目标线程。
8、讲一下 java 中的同步的方法
之所以需要同步,因为在多线程并发控制,当多个线程同时操作一个可共享的资 源时,如果没有采取同步机制,将会导致数据不准确,因此需要加入同步锁,确 保在该线程没有完成操作前被其他线程调用,从而保证该变量的唯一一性和准确性。
1)synchronized 修饰同步代码块或方法
由于 java 的每个对象都有一个内置锁,用此关键字修饰方法时,内置锁会保护 整个方法。在调用该方法前,需获得内置锁,否则就处于阴塞状态。
2)volatile 修饰变量
保证变量在线程间的可见性,每次线程要访问 volatile 修饰的变量时都从内存中 读取,而不缓存中,这样每个线程访问到的变量都是一样的。且使用内存屏障。
3)ReentrantLock 重入锁,它常用的方法有 ReentrantLock():
创建一个 ReentrantLock 实例 lock()获得锁 unlock()释放锁
4)使用局部变量 ThreadLocal 实现线程同步,每个线程都会保存一份该变量的 副本,副本之间相互独立,这样每个线程都可以随意修改自己的副本,而不影响其他线程。常用方法 ThreadLocal()创建一个线程本地变量;get()返回此线程局 部的当前线程副本变量;initialValue()返回此线程局部变量的当前线程的初始 值;set(T value)将此线程变量的当前线程副本中的值设置为 value
5)使用原子变量,如 AtomicInteger,常用方法 AtomicInteger(int value)创 建个有给定初始值的 AtomicInteger 整数;addAndGet(int data)以原子方式 将给定值与当前值相加
6)使用阻塞队列实现线程同步 LinkedBlockingQueue<E>
9、如何保证线程安全?
线程安全性体现在三方法:
1)原子性:提供互斥访问,同一时刻只能有一个线和至数据进行操作。
JDK中提供了很多atomic类,如 AtomicInteger\AtomicBoolean\AtomicLong,它们是通过 CAS 完成原子性。 JDK 提供锁分为两种:synchronized 依赖 JVM 实现锁,该关键字作用对象的 作用范围内同一时刻只能有一个线程进行操作。另一种是 LOCK,是 JDK 提供的代码层面的锁,依赖 CPU 指令,代表性是 ReentrantLock。
2)可见性:一个线程对主内存的修改及时被其他线程看到。
JVM 提供了 synchronized 和 volatile,volatile 的可见性是通过内存屏障和禁 止重排序实现的,volatile 会在写操作时,在写操作后加一条 store 屏障指令, 将本地内存中的共享变量值刷新到主内存;会在读操作时,在读操作前加一条 load 指令,从内存中读取共享变量。
3)有序性:指令没有被编译器重排序。
可通过 volatile、synchronized、Lock 保证有序性。
10、两个进程同时要求写或者读,能不能实现?如何防止进程的同步?
我认为可以实现,比如两个进程都读取日历进程数据是没有问题,但同时写,应 该会有冲突。
可以使用共享内存实现进程间数据共享。
二、Android 面试帮助篇
1、要做一个尽可能流畅的 ListView,你平时在工作中如何进行优化的?
①Item 布局,层级越少越好,使用 hierarchyview 工具查看优化。
②复用 convertView
③使用 ViewHolder
④item 中有图片时,异步加载
⑤快速滑动时,不加载图片
⑥item 中有图片时,应对图片进行适当压缩
⑦实现数据的分页加载
2、对于 Android 的安全问题,你知道多少
①错误导出组件
② 参数校验不严
③WebView 引入各种安全问题,webview 中的 js 注入
④不混淆、不防二次打包
⑤明文存储关键信息
⑦ 错误使用 HTTPS
⑧山寨加密方法
⑨滥用权限、内存泄露、使用 debug 签名
3、如何缩减 APK 包大小?
代码保持良好的编程习惯,不要重复或者不用的代码,谨慎添加 libs,移除使用不到的 libs。
使用 proguard 混淆代码,它会对不用的代码做优化,并且混淆后也能够减少安 装包的大小。
native code 的部分,大多数情况下只需要支持 armabi 与 x86 的架构即可。如果 非必须,可以考虑拿掉 x86 的部分。
资源
使用 Lint 工具查找没有使用到的资源。去除不使用的图片,String,XML 等等。 assets 目录下的资源请确保没有用不上的文件。
生成 APK 的时候,aapt 工具本身会对 png 做优化,但是在此之前还可以使用其 他工具如 tinypng 对图片进行进一步的压缩预处理。
jpeg 还是 png,根据需要做选择,在某些时候 jpeg 可以减少图片的体积。 对于 9.png 的图片,可拉伸区域尽量切小,另外可以通过使用 9.png 拉伸达到大图效 果的时候尽量不要使用整张大图。
策略
有选择性的提供 hdpi,xhdpi,xxhdpi 的图片资源。建议优先提供 xhdpi 的图片, 对于 mdpi,ldpi 与 xxxhdpi 根据需要提供有差异的部分即可。
尽可能的重用已有的图片资源。例如对称的图片,只需要提供一张,另外一张图 片可以通过代码旋转的方式实现。
能用代码绘制实现的功能,尽量不要使用大量的图片。例如减少使用多张图片组 成 animate-list 的 AnimationDrawable,这种方式提供了多张图片很占空间。
4、Android 与服务器交互的方式中的对称加密和非对称加密是什么?
对称加密,就是加密和解密数据都是使用同一个 key,这方面的算法有 DES。 非对称加密,加密和解密是使用不同的 key。发送数据之前要先和服务端约定生 成公钥和私钥,使用公钥加密的数据可以用私钥解密,反之。这方面的算法有 RSA。ssh 和 ssl 都是典型的非对称加密。
三、Android 面试常见题
1、java 中==和 equals 和 hashCode 的区别
基本数据类型的==比较的值相等. 类的==比较的内存的地址,即是否是同一个对象,在不覆盖 equals 的情况下, 同比较内存地址,原实现也为 == ,如 String 等重写了 equals 方法. hashCode 也是 Object 类的一个方法。返回一个离散的 int 型整数。在集合类操作 中使用,为了提高查询速度。(HashMap,HashSet 等比较是否为同一个) 如果两个对象 equals,Java 运行时环境会认为他们的 hashcode 一定相等。 如果两个对象不 equals,他们的 hashcode 有可能相等。 如果两个对象 hashcode 相等,他们不一定 equals。 如果两个对象 hashcode 不相等,他们一定不 equals。
2、int 与 integer 的区别
int 基本类型
integer 对象 int 的封装类
3、String、StringBuffer、StringBuilder 区别
String:字符串常量 不适用于经常要改变值得情况,每次改变相当于生成一个新 的对象
StringBuffer:字符串变量 (线程安全)
StringBuilder:字符串变量(线程不安全) 确保单线程下可用,效率略高于 StringBuffer
4、什么是内部类?内部类的作用
内部类可直接访问外部类的属性
Java 中内部类主要分为成员内部类、局部内部类(嵌套在方法和作用域内)、匿名 内部类(没构造方法)、静态内部类(static 修饰的类,不能使用任何外围类的 非 static 成员变量和方法, 不依赖外围类)
5、进程和线程的区别
进程是 cpu 资源分配的最小单位,线程是 cpu 调度的最小单位。 进程之间不能共享资源,而线程共享所在进程的地址空间和其它资源。 一个进程内可拥有多个线程,进程可开启进程,也可开启线程。 一个线程只能属于一个进程,线程可直接使用同进程的资源,线程依赖于进程而存在。
6、final,finally,finalize 的区别
final:修饰类、成员变量和成员方法,类不可被继承,成员变量不可变,成员方法 不可重写
finally:与 try...catch...共同使用,确保无论是否出现异常都能被调用到
finalize:类的方法,垃圾回收之前会调用此方法,子类可以重写
finalize()方法实现对 资源的回收
7、Serializable 和 Parcelable 的区别
Serializable Java 序列化接口 在硬盘上读写 读写过程中有大量临时变量的生成, 内部执行大量的 i/o 操作,效率很低。
Parcelable Android 序列化接口 效率高 使用麻烦 在内存中读写(AS 有相关插件 一键生成所需方法) ,对象不能保存到磁盘中
8、静态属性和静态方法是否可以被继承?是否可以被重写?以及原因?
可继承 不可重写 而是被隐藏
如果子类里面定义了静态方法和属性,那么这时候父类的静态方法或属性称之为 "隐藏"。如果你想要调用父类的静态方法和属性,直接通过父类名.方法或变量名 完成。
9、成员内部类、静态内部类、局部内部类和匿名内部类的理解,以及项目中的应用
Java 中内部类主要分为成员内部类、局部内部类(嵌套在方法和作用域内)、匿名内部类(没构造方法)、静态内部类(static 修饰的类,不能使用任何外围类的 非 static 成员变量和方法, 不依赖外围类)
使用内部类最吸引人的原因是:每个内部类都能独立地继承一个(接口的)实现, 所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。
因为 Java 不支持多继承,支持实现多个接口。但有时候会存在一些使用接口很 难解决的问题,这个时候我们可以利用内部类提供的、可以继承多个具体的或者 抽象的类的能力来解决这些程序设计问题。可以这样说,接口只是解决了部分问 题,而内部类使得多重继承的解决方案变得更加完整。
10、string 转换成 integer 的方式及原理
String integer Intrger.parseInt(string);
Integer string Integer.toString();
四、初级面试专题(中小厂)
1、导致内存泄露的原因有哪些?
2、理解 Activity,View,Window 三者关系
3、Handler 的原理
4、View,ViewGroup 事件分发
5、onNewIntent()什么时候调用?(singleTask)
6、mvc 和 mvp mvvm
7、自定义控件
8、Serializable 和 Parcelable 的区别
最后
因文章篇幅长度篇幅限制,后续内容我就不一一的展现出来了。我已将后续的内容上传至我的【GitHub】项目上来,如果有需要学习参考可以自行取得。