某视频app公司面试题
- Activity启动模式。
standard(标准模式)
默认启动模式,启动activity时直接创建新的实例并压入启动它的任务栈顶
singleTop(栈顶复用模式)
启动singleTop模式的activity时发现当前任务的栈顶已经存在着这个activity的实例,那么就不会创建新的实例,而是调用该实例的onNewIntent()方法。其他的跟标准模式一样。
singleTask(栈内复用模式)
没有它要的任务栈,系统会新创建一个任务,并将该activity实例化作为该任务的根activity。
有它要的任务栈,这时候系统会找到该任务栈,如果任务栈里只有它自己则直接调用该activity实例的onNewIntent()方法。如果任务栈中它的上方还存在别的activity,那么这些activity会被全部弹出栈。
singleInstance(单例模式)
基本上跟singleTask相同,会为activity单独创建一个任务并能够复用。但是该模式的activity不允许其他activity跟自己存在于同一个任务中,由此 activity 启动的任何 activity 均会被在其他的任务中打开。 Activity生命周期,从A页面打开B页面,两个页面的生命周期。
activity状态
running->当前显示在屏幕的activity(位于任务栈的顶部),用户可见状态。
poused->依旧在用户可见状态,但是界面焦点已经失去,此Activity无法与用户进行交互。
stopped->用户看不到当前界面,也无法与用户进行交互 完全被覆盖.
killed->当前界面被销毁,等待这系统被回收
oncreate()->onstart()->onResume()->onRestart()->onPause()->onStop()->onDestory()
当AActivity切换BActivity的所执行的方法:
AActivity:onCreate()->onStart()->onResume()->onPause()
BActivity:onCreate()->onStart()->onResume()
AActivity:onStop()->onDestory()
当AActivity切换BActivity(此activity是以dialog形式存在的)所执行的方法:
AActivity:onCreate()->onStart()->onResume()->onPause()
BActivity:onCreate()->onStart()->onResume()
activity的进程优先级。
前台进程>可见进程>service进程>后台进程>空进程前台进程: 1.当前进程activity正在与用户进行交互。 2.当前进程service正在与activity进行交互或者当前service调用了startForground()属于前台进程或者当前service正在执行生命周期(onCreate(),onStart(),onDestory()) 3.进程持有一个BroadcostReceiver,这个BroadcostReceiver正在执行onReceive()方法 可见进程: 1. 进程持有一个activity,这个activity不再前台,处于onPause()状态下,当前覆盖的activity是以dialog形式存在的。 2. 进程有一个service,这个service和一个可见的Activity进行绑定。 service进程: 当前开启startSerice()启动一个service服务就可以认为进程是一个服务进程。 后台进程: activity的onStop()被调用,但是onDestroy()没有调用的状态。该进程属于后台进程。 空进程: 改进程没有任何运行的数据了,且保留在内存空间,并没有被系统killed,属于空进程。该进程很容易
onStartCommand的返回值和参数的作用。
在startService启动一个Service 时会执行onStartCommand(Intent intent,int flags,int startId)
参数:
intent:是startService时传过来的 如:startService(new Intent(this,TestService.class));
flags:是系统传入 有如下三种值:1.通过startService启动时,flags为0; 2.onStartCommand返回为START_STICKY_COMPATIBILITY或者START_STICKY并且服务异常杀死后由系统启动;flags为START_FLAG_REDELIVERY=1: 3.onStartCommand方法未被调用或者没有正常返回的异常情况下, 再次尝试创建,传入的flags就为START_FLAG_RETRY=2。 startId:传入的这个startId 用来代表这个唯一的启动请求。我们可以在stopSelfResult(int startId)中传入这个startId,用来终止Service。
返回值
START_STICKY:如果service进程被kill掉,保留service的状态为开始状态,但不保留递送的intent对象。随后系统会尝试重新创建service,由 于服务状态为开始状态,所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令被传 递到service,那么参数Intent将为null。 START_NOT_STICKY:“非粘性的”。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统不会自动重启该服务 START_REDELIVER_INTENT:重传Intent。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将Intent的值传入。 START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保证服务被kill后一定能重启。
Synchronized关键字。一个线程访问某个类的Synchronized成员方法A,同时另一个线程可以去访问这个类的Synchronized成员方法B吗?
synchronized的用法:synchronized修饰方法和synchronized修饰代码块
synchronized只是一个内置锁的加锁机制,当某个方法加上synchronized关键字后,就表明要获得该内置锁才能执行,并不能阻止其他线程访问不需要获得该内置锁的方法- App的启动优化。
- Handler,Looper,MessageQueue的关系。Handler通过sendMessageDelayed发送延时消息, 消息什么时候入队列?
- 主线程的Looper死循环为什么不会导致ANR?
- Android 动画的实现方式。
- 项目中有没有用到组件化,如何实现组件化。
- Hybrid开发中Android和H5的交互有哪些方式。
- 项目是怎样的架构,为什么这么搭建。
某物流公司的面试题
- 本地广播和全局广播的区别,本地广播的实现原理。
- 常见第三方框架及其实现原理。
- SQLite数据库操作。
- Http请求中包含哪些内容,上传文件和发起请求的post请求的区别。
- 线程池有哪几种,如何运用。
- 设计实现大批量数据上传的方式。
- MVC,MVP,MVVM架构的区别。
- 如何解决Handler导致的内存泄漏。
- 进程间通信的方式。
某厂电商及物流岗位的面试题
- View的绘制流程。
- 实现延时任务的几种方式,postDelay,AlarmManager,TimerTask的区别。
- 做过哪些优化性能的工作?如何考量优化的成果。
- http和http2.0,以及https的不同。
- Art虚拟机和Dalvik虚拟机。
- 三级缓存的原理。
- LruCache算法的原理。
- 项目中用到了哪些设计模式,Android源码中用到设计模式的场景。
- Java8有什么新特性。
- Java类如何加载,类加载的过程。
- 页面卡顿如何解决,viewHolder起什么作用。
- 如果有大数据量网络请求,该如何优化
另某厂电商KL应用岗位面试题
- 在浏览器输入网址到打开网页,中间的过程。
- 如何统计工程的代码量?如何去掉代码注释的影响?
- 模式匹配
- apt ,aop
- 标签注解的原理
- 热修复的原理。
- 线程池的原理,如果核心线程数5,最大线程数10,新来的线程会怎样?
- 如何在服务器接口未准备好的情况下调试接口?
- 如何抓取Https传输的数据?
- Android 与Js的交互,接口存在的安全隐患及修复
- 不同方式创建字符串,通过==和quuals比较的结果
某Y厂多媒体岗位面试题
- 对第三方框架的了解。EventBus可以跨进程通信吗?
- Android P有什么新特性,后台限制策略是通过怎样的API来处理的?
- HashMap的原理,hashCode的作用,重写equals为什么要重写hashCode方法
- Synchronizd修饰静态方法也修饰了非静态方法,可以在两个线程同时被调用吗?
- padding和margin的区别。给固定大小及wrap_content的textView设置padding,它的可点击区域会变化吗?
- 如何自定义view, Canvas的作用
- onMesure()方法的参数,MeasureSpec的结构
- 动画有哪几种?属性动画怎么用,插值器什么作用
- 讲一下include,merge 和viewstub。 viewstub可以被重复加载吗?在view树里是怎样的存在?
- 对线程的理解,线程同步有哪几种方式
- final关键字的作用,为什么匿名内部类里用到的变量必须是final类型的?
- handler, Looper, MessageQueue的关系,这三个可以一对多吗?怎么保证message能被其对应的handler处理?
- 触摸事件传递过程。有个竖向滑动的ViewGroup里嵌套了横向滑动的view,如何处理滑动冲突?
- onSavedInstanceState触发的时机。
某母婴平台部分面试题
- 自定义view时如何刷新布局,invalidate会改变宽高吗
- Java泛型是伪泛型吗
- new Integer(1) == 1会执行拆箱吗
- Looper无限循环为什么不会造成卡死
- 组件之间,activity如何跳转
- HTTPS及其双重检验
补充
synchronized的实现原理,和lock的区别。
synchronized是基于jvm底层实现的数据同步,lock是基于Java编写,主要通过硬件依赖CPU指令实现数据同步。
一、synchronized的实现方案
1.synchronized能够把任何一个非null对象当成锁,实现由两种方式:a.当synchronized作用于非静态方法时,锁住的是当前对象的事例,当synchronized作用于静态方法时,锁住的是class实例,又因为Class的相关数据存储在永久带,因此静态方法锁相当于类的一个全局锁。 b.当synchronized作用于一个对象实例时,锁住的是对应的代码块。
2.synchronized锁又称为对象监视器(object)。
3.当多个线程一起访问某个对象监视器的时候,对象监视器会将这些请求存储在不同的容器中。Contention List:竞争队列,所有请求锁的线程首先被放在这个竞争队列中 Entry List:Contention List中那些有资格成为候选资源的线程被移动到Entry List中 Wait Set:哪些调用wait方法被阻塞的线程被放置在这里 OnDeck:任意时刻,最多只有一个线程正在竞争锁资源,该线程被成为OnDeck Owner:当前已经获取到所资源的线程被称为Owner !Owner:当前释放锁的线程
4.synchronized在jdk1.6之后提供了多种优化方案:
自旋锁 jdk1.6之后默认开启,可以使用参数-XX:+UseSpinning控制,自旋等待不能代替阻塞,且先不说对处理器数量的要求,自旋等待本身虽然避免了线程切换的开销,但它是要占用处理器时间的,因此,如果锁被占用的时间很短,自旋等待的效果就会非常好,反之,如果锁被占用的时候很长,那么自旋的线程只会白白消耗处理器资源,而不会做任何有用的工作,反而会带来性能上的浪费。自旋次数的默认值是 10 次,用户可以使用参数 -XX:PreBlockSpin 来更改。 自旋锁的本质:执行几个空方法,稍微等一等,也许是一段时间的循环,也许是几行空的汇编指令。 锁消除 即时编译器在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行消除,依据来源于逃逸分析的数据支持,那么是什么是逃逸分析?对于虚拟机来说需要使用数据流分析来确定是否消除变量底层框架的同步代码,因为有许多同步的代码不是自己写的。 锁粗化 将同步块的作用范围限制得尽量小——只在共享数据的实际作用域中才进行同步,这样是为了使得需要同步的操作数量尽可能变小,如果存在锁竞争,那等待锁的线程也能尽快拿到锁。 轻量级锁 加锁过程:在代码进入同步块的时候,如果此同步对象没有被锁定(锁标志位为 “01” 状态)虚拟机首先将在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的 Mark Word 的拷贝,这时候线程堆栈与对象头的状态如图 13-3 所示 然后,虚拟机将使用 CAS 操作尝试将对象的 Mark Word 更新为指向 Lock Record 的指针。如果这个更新动作成功了,那么这个线程就拥有了该对象的锁,并且对象 Mark Word 的锁标志位 (Mark Word 的最后 2bit)将转变为 “00”,即表示此对象处于轻量级锁定状态,这时线程堆栈与对象头的状态如图13-4 如果上述更新操作失败,则说明这个锁对象被其他锁占用,此时轻量级变为重量级锁,标志位为“10”,后面等待的线程进入阻塞状态。 解锁过程:也是由CAS进行操作的,如果对象的 Mark Word 仍然指向着线程的锁记录,那就用 CAS 操作把对象当前的 Mark Word 和线程中复制的 Displaced Mark Word 替换回来,如果替换成功,整个同步过程就完成了。如果替换失败,说明有其他线程尝试过获取该锁,那就要释放锁的同时,唤醒被挂起的线程。 轻量级锁能提升程序同步性能的依据是 “对于绝大部分的锁,在整个同步周期内都是不存在竞争的”,这是一个经验数据。如果没有竞争,轻量级锁使用 CAS 操作避免了使用互斥量的开销,但如果存在锁竞争,除了互斥量的开销外,还额外发生了 CAS 操作,因此在有竞争的情况下,轻量级锁会比传统的重量级锁更慢。 偏向锁 偏向锁也是 JDK 1.6 中引入的一项锁优化,它的目的是消除数据在无竞争情况下的同步原语,进一步提高程序的运行性能。如果说轻量级锁是在无竞争的情况下使用 CAS 操作去消除同步使用的互斥量,那偏向锁就是在无竞争的情况下把整个同步都消除掉,连 CAS 操作都不做了。 实质就是设置一个变量,判断这个变量是否是当前线程,是就避免再次加锁解锁操作,从而避免了多次的CAS操作。坏处是如果一个线程持有偏向锁,另外一个线程想争用偏向对象,拥有者想释放这个偏向锁,释放会带来额外的性能开销,但是总体来说偏向锁带来的好处还是大于CAS的代价的。在具体问题具体分析的前提下,有时候使用参数 -XX:-UseBiasedLocking 来禁止偏向锁优化反而可以提升性能。
二、lock的实现方案
与synchronized不同的是lock是纯java手写的,与底层的JVM无关。在java.util.concurrent.locks包中有很多Lock的实现类,常用的有ReenTrantLock、ReadWriteLock(实现类有ReenTrantReadWriteLock),其实现都依赖java.util.concurrent.AbstractQueuedSynchronizer类(简称AQS),实现思路都大同小异,因此我们以ReentrantLock作为讲解切入点。
分析之前我们先来花点时间看下AQS。AQS是我们后面将要提到的CountDownLatch/FutureTask/ReentrantLock/RenntrantReadWriteLock/Semaphore的基础,因此AQS也是Lock和Excutor实现的基础。它的基本思想就是一个同步器,支持获取锁和释放锁两个操作。
要支持上面锁获取、释放锁就必须满足下面的条件:1、 状态位必须是原子操作的 2、 阻塞和唤醒线程 3、 一个有序的队列,用于支持锁的公平性
场景:可定时的、可轮询的与可中断的锁获取操作,公平队列,或者非块结构的锁。
主要从以下几个特点介绍:1.可重入锁 如果锁具备可重入性,则称作为可重入锁。像synchronized和ReentrantLock都是可重入锁,可重入性在我看来实际上表明了锁的分配机制:基于线程的分配,而不是基于方法调用的分配。 2.可中断锁 可中断锁:顾名思义,就是可以相应中断的锁。 在Java中,synchronized就不是可中断锁,而Lock是可中断锁。 如果某一线程A正在执行锁中的代码,另一线程B正在等待获取该锁,可能由于等待时间过长,线程B不想等待了,想先处理其他事情,我们可以让它中断自己或者在别的线程中中断它,这种就是可中断锁。 3.公平锁和非公平锁 公平锁以请求锁的顺序来获取锁,非公平锁则是无法保证按照请求的顺序执行。synchronized就是非公平锁,它无法保证等待的线程获取锁的顺序。而对于ReentrantLock和ReentrantReadWriteLock,它默认情况下是非公平锁,但是可以设置为公平锁。 参数为true时表示公平锁,不传或者false都是为非公平锁。 ReentrantLock lock = new ReentrantLock(true); 4.读写锁 读写锁将对一个资源(比如文件)的访问分成了2个锁,一个读锁和一个写锁。 正因为有了读写锁,才使得多个线程之间的读操作不会发生冲突。 ReadWriteLock就是读写锁,它是一个接口,ReentrantReadWriteLock实现了这个接口。 可以通过readLock()获取读锁,通过writeLock()获取写锁。
三、总结
1.synchronized
优点:实现简单,语义清晰,便于JVM堆栈跟踪,加锁解锁过程由JVM自动控制,提供了多种优化方案,使用更广泛
缺点:悲观的排他锁,不能进行高级功能
2.lock
优点:可定时的、可轮询的与可中断的锁获取操作,提供了读写锁、公平锁和非公平锁
缺点:需手动释放锁unlock,不适合JVM进行堆栈跟踪
3.相同点
都是可重入锁- 多进程操作文件,怎么解决同步?
- CAS同步方法。
- binder的实现。
- 插件化的原理。如果自己去设计插件化的框架,怎么设计?注意哪些地方?
- 第三方框架及其原理。在用第三方框架时有没有做比较,了解项目之外的第三方框架吗?
- 如何分析ANR问题?腾讯Bugly这样的框架是什么原理。
- JNI开发,及原理。
- Java反射的原理。
- dex, odex是什么。
- Android系统开机过程。
- 面向切面编程。
- AutomicInteger的原理。
- 调用System.gc()会怎样,系统什么时候会触发GC。
- 线程的sleep和wait的区别。
- Google最新动态,Android最新变化等。
- 队列有哪几种。
- 从点击桌面图标,到App启动中间发生了什么。
上面这些都是技术的问题,还有一些频繁被问到的体现技术能力的问题
- 在工作中做过的最有难度的需求是什么,是怎么实现的。
- 工作中遇到的最难的Bug是怎样的,及解决过程。
- 工作中代码优化、性能优化等,是怎么做的,代码规范性如何保证。
- 在团队中担任怎样的角色,团队的开发流程是怎样的。
- 有开源项目吗?
- 平时怎么获取新知识。
另外,作为Android开发,只会Android和java的知识是不够的,虽然可以应付一般工作,但是如果想进阶的话,下面这些技能起码要掌握一二
- Java虚拟机。推荐看《深入理解Java虚拟机》
- 设计模式。经典书籍很多,比如《大话设计模式》
- 数据库操作。
- Http,TCP/IP相关
- Android源码,底层实现。
- 跨平台开发技术,比如ReativeNative,flutter,week等。
- Android和其他方向的行业新技术,新动态
- Kotlin,python,Linux等。