- onCreate:创建时执行
- onTerminate:终止时执行
- onLowMemory:低内存时执行
Application的初始化流程
通过AMS协调,ActivityThread优先建立后,会新建一个ApplicationThread,用作和AMS通过Binder通信,之后AMS通知ActivityThread去bindApplication,将消息返送到messageQueue,进行初始化Application的任务,然后调用attachBaseContext
将Context绑定到Application,最后调用Application.onCreate()方法进行后续Activity的初始化
AMS 来通知 zygote 进程来 fork 一个新进程,来开启我们的目标 App 的
ActivityLifecycleCallbacks理解
ActivityLifecycleCallback是Application中的一个接口,可以监听应用中所有Activity的生命周期,可以通过该方法完成一些特殊的需求,比如监测当前App显示的Activity是那个?App是否存在前台
public class MyApplication extends Application {
private ActivityLifecycleCallbacks activityLifecycleCallbacks = new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
}
@Override
public void onActivityStarted(@NonNull Activity activity) {
}
@Override
public void onActivityResumed(@NonNull Activity activity) {
}
@Override
public void onActivityPaused(@NonNull Activity activity) {
}
@Override
public void onActivityStopped(@NonNull Activity activity) {
}
@Override
public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {
}
@Override
public void onActivityDestroyed(@NonNull Activity activity) {
}
};
@Override
public void onCreate() {
super.onCreate();
// 注册Callback
registerActivityLifecycleCallbacks(callbacks);
}
}
Activity
简述生命周期
Activity:
- onCreate() 正在被创建
- onRestart() 不可见到可见
- onStart() Activity正在启动,已经可见了
- onResume() Acitivty已经可见,并在前台显示
- onPause() Acitivty正在停止,之后会不可见
- onStop() Activity即将停止
- onDestory() Activity被销毁
onStart和onResume都为可见,onStart不在前台显示,onResume在前台显示
onPause后已经不可见,会进入onResume或者另一个onResume,所以不能做耗时操作,会影响界面显示
onPause和onStop中不能太耗时
如果新的Activity采用了透明主题,那么当前Activity不会回调onStop
onStart和onStop对应Activity是否可见
onResume和onPause对应Activity是否在前台显示
异常情况下会执行onSaveInstance方法进行数据保存
简述启动模式
- standard:标准模式,来一个添加一个
- singleTop:栈顶复用,如果在栈顶,就复用这个Activity,
onNewIntent
会被执行,替代onCreate() - singleTask:栈内复用,如果在栈内,就复用这个Activity,该Activity之上的全部出栈,onNewIntent会被执行
- singleInstance:单实例模式。加强版singleTask,会为自己新建一个栈,在该栈中栈内复用
LauncherMode和startActivityForResult
在5.0之前,当启动一个Activity时,系统将首先检查Activity的launchMode,如果为A页面设置为SingleInstance或者B页面设置为singleTask或者singleInstance,则会在LaunchFlags中加入FLAG_ACTIVITY_NEW_TASK标志,而如果含有FLAG_ACTIVITY_NEW_TASK标志的话,onActivityResult将会立即接收到一个cancle的信息,而5.0之后这个方法做了修改,修改之后即便启动的页面设置launchMode为singleTask或singleInstance,onActivityResult依旧可以正常工作,也就是说无论设置哪种启动方式,StartActivityForResult和onActivityResult()这一组合都是有效的
什么时候会启动一个新的Activity栈?
- allowTaskReparenting:
- singleInstance单独使用,会新建一个栈
- singleTask配合taskAffinity使用
- taskAffinity配合Intent.FLAG_ACTIVITY_NEW_TASK修饰Activity(AMS先处理LauncherMode,在处理FLAG_ACTIVITY_NEW_TASK)
- taskAffinity配合allowTaskReparenting属性,使Activity从启动栈移动到正在使用的栈中并显示出来
如何控制Activity的动画切换
- 通过overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left)方法控制
再startActivity方法后或者finish方法之后调用即可生效 - 使用style定义切换动画
如何控制Fragment的动画切换
FragmentTransaction fragmentTransaction = mFragmentManager
.beginTransaction();
fragmentTransaction.setCustomAnimations(
R.anim.in_from_right,
R.anim.out_to_left);
使用FragmentTransaction开启Fragment动画,设置自定义动画切换,进入动画和推出动画
ActivityA跳转到ActivityB,再按back键返回ActivityA,生命周期情况?
ActivityA跳转到ActivityB:onPauseA()----->onCreateB()----->onStartB()----->onResumeB()----->onStopA()
ActivityB按back键返回ActivityA:
onPauseB()----->onRestartA()----->onStartA()----->onResumeA()----->onStopB()----->onDestoryB()
如果ActivityB是窗口Activity呢?
ActivityA跳转到ActivityB:onPauseA()----->onCreateB()----->onStartB()----->onResumeB()
ActivityB按back键返回ActivityA:onPauseB()----->onResumeA()----->onStopB()----->onDestoryB()
Activity的生命周期会受Dialog影响吗?
不会,Activity生命周期不会随Dialog的显示而变化
Activity的生命周期受AMS调用,而dialog不是Activity,所以不受AMS控制,所以不会触发Activity的生命周期
Service
Service有几种创建方式?有什么区别?
- startService(),在不需要的时候stopService()
- bindService(),与生命周期相绑定,在销毁的时候进行回收unbind()
如何理解IntentService?生命周期是什么?HandlerThread 是什么?
intentService继承自Service,持有Service的功能,同时,他是一个处理异步操作的类,当异步执行结束后会自动关闭intentService,多次执行startService(),只是执行onStartCommand方法,将消息加入到消息队列中。
其本质就是启动了一个类似于主线程Handler的机制去维护异步操作。
生命周期:onStartCommand()中执行onStart()方法,在onstart()方法中添加handler.sendMessage()方法
HandlerThread:就是将Handler+looper进行封装,允许直接在子线程中使用handler的一套逻辑。
IntentService更像是一个后台线程,但是他又是一个服务,不容易被回收,这是他的优点
JobIntentService
是IntentService的子类,在android 8.0(26)以上,IntentService的所有后台执行任务都会受到限制约束,所以要使用JobIntentService。
service不能使用后台服务,需要使用ContextCompat.startForegroundService启动前台服务,这样就会启动一个notification,对用户来说体验不是很好,所以就要使用 JobIntentService启动一个后台服务
在使用JobIntentService的时候不需要startService,stopService,在需要的时候调用
DownLoadJobIntentService.enqueueWork(MainActivity.this,DownLoadJobIntentService.class,jobId(8),intent);
而后会执行onHandleWork方法中的逻辑,执行完毕后自动销毁
onStartCommand中三个回调分别是什么?
- START_NOT_STICKY:Service被回收后不做处理
- START_STICKY:Service在被回收后,重新创建Service,但是
不保存
intent - START_REDELIVER_INTENT:Service在被回收后,重新创建Service,
保存
intent - START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保证服务被kill后一定能重启。
Service保活
- 设置成前台服务,绑定Notification, startForeground(1, getNotification());
- 单独设置为服务进程
- onStartCommand返回START_STICKY,START_REDELIVER_INTENT,保证服务被干掉后重启
- 在onDestory发送一个广播,广播接收器接收广播后拉起服务
- 添加系统广播拉起
- 提升服务的优先级
Fragment
生命周期
FragmentPagerAdapter和FragmentStatePagerAdapter的区别?
FragmentPagerAdapter:切换页面只是将Fragment分离,适合Fragment较少的情况不影响内存
FragmentStatePagerAdapter:切换页面将Fragment回收,适合Fragment较多的情况,这样不会消耗太多内存资源
Fragment的3种切换方式
- add方法:只是重新回到fragment
- replace方法:每次都会重新构建fragment
为什么不能用Fragment的构造函数进行传参?有什么劣势,应该怎么办?为什么?
Fragment在异常崩溃后重建时,默认会调用Fragment无参构造,这样会导致Fragment中的有参构造的值不会被执行,这样数据就会异常
Fragment中调用setArguments来传递参数,在Activity构造Fragment时会通过反射午餐构造实例化一个新的Fragment,并且给mArguments初始化为原先的值
Fragment的重建在那个生命周期中?
在FragmentActivity的onSaveInstanceState中做存储,将Framgent通过序列化Parcelable进行存储,在Activity的onCreate中进行恢复
当配置发生变化时,Activity进入销毁过程,FragmentManager先销毁队列中Fragment的视图,然后检查每个Fragment的retainInstance属性。如果retainInstance为false,FragmentManager会销毁该Fragment实例;如果retainInstance为true,则不会销毁该Fragment实例,Activity重建后,新的FragmentManager会找到保留的Fragment并为其创建视图。
BroadCastReceiver
简述广播的启动方式和区别
- 静态注册:在AndroidManifest中注册,常驻型广播
- 动态注册:使用intentFilter过滤广播,registerReceiver注册广播,跟随生命周期
Android8.0以上部分广播不允许静态注册
无序广播和有序广播的区别
- 无序广播:所有广播接收器都可以获得,不可以拦截,不可以修改
- 有序广播:按照优先级向下传递,可拦截广播,修改广播
本地广播和全局广播
本地广播接收器只接收本地广播,减少应用外广播干扰,高效 androidx中1.1.0-alpha01中弃用本地广播,官方推荐该用LiveData或响应式流
IPC机制
简述android中的IPC机制
进程间通信
架构:Client/Server架构,Binder机制,之间通过代理接口通信
client,server,serverManager
AndroidManifest中指定Android:process属性
- 包名:remote为应用私有进程,其他应用不可访问
- 包名.remote为全局进程,其他应好通过ShareUID可以和他跑在同一个进程
多进程带来的问题:四大组件共享数据失败,每个进程会单独开辟内存空间存储信息
- 静态成员和单例模式完全失效
- 线程同步机制完全失效
- SharedPreferences可靠性下降,不支持多进程
- Application会多次创建
Serializable和parcelable区别
serializable:java自带,反射后产生大量临时变量,容易引起gc,主要用于持久化存储和网络传输的序列化
parcelable:android专用,性能强,将完整对象分解为部分对象,每一部分进行intent传输,可用于ipc,内部实现Bundle,主要用于内存的序列化
Android为什么引入Parcelable?
- serializable通过反射,性能不好,
- serializable反射产生大量临时变量,容易gc,导致内存抖动
- serializable使用了大量的IO操作,也影响了耗时
parcelable使用复杂,但高效,适用于内存序列化
Parcelable一定比Serializable快吗?
单论内存中的传输速度,Parcelable一定快于Serializable,但是Parcelable没有缓存的概念 Serializable存在缓存,会将解析过的内容放置在HandleTable,下次解析到同一类型的对象时就可以直接复用
为什么java使用Serializable序列化对象,而不是json或者xml?
因为历史遗留问题,在json和xml出来之前,java已经设计了Serializable,对于Java的庞大体系,并不容易修改这个问题。java官方文档也推荐使用json库,因为他简单、易读、高效
简析Binder机制
在Android通信中并不是所有的进程通信都使用Binder,当fork()进程时,使用的是Socket()通信,因为fork不允许多线程,Binder是多线程模式,所以不被允许
进程空间划分
一个进程空间分为用户空间和内核空间
用户空间:数据独享,其他进程无法访问
内核空间:数据共享,其他进程可以访问
所有的进程共用1个内核空间
如何看待ServiceManager?
ServiceManager管理系统中所有的服务,服务需要使用时都要在ServiceManager中进行注册,他的存在类似于DNS,提供client访问某一个服务的查询。
Binder原理
binder驱动属于进程中的内核空间,即共享空间,在client发起请求时,需要将数据从用户空间拷贝到内核空间,binder通过传输内核空间中数据存储的引用映射给服务端,供服务端调用,服务端处理后,将返回值放在内核空间,通过binder传递引用映射给客户端进行处理
简述通信流程
总体通信流程就是:
- 客户端通过代理对象向服务器发送请求。
- 代理对象通过Binder驱动发送到服务器进程
- 服务器进程处理请求,并通过Binder驱动返回处理结果给代理对象
- 代理对象将结果返回给客户端。
详细的通信过程
- 服务端跨进程的类都要继承Binder类,所以也就是服务端对应的Binder实体。这个类并不是实际真实的远程Binder对象,而是一个Binder引用(即服务端的类引用),会在Binder驱动里还要做一次映射。
- 客户端要调用远程对象函数时,只需把数据写入到Parcel,在调用所持有的Binder引用的transact()函数
- transact函数执行过程中会把参数、标识符(标记远程对象及其函数)等数据放入到Client的共享内存,Binder驱动从Client的共享内存中读取数据,根据这些数据找到对应的远程进程的共享内存。
- 然后把数据拷贝到远程进程的共享内存中,并通知远程进程执行onTransact()函数,这个函数也是属于Binder类。
- 远程进程Binder对象执行完成后,将得到的写入自己的共享内存中,Binder驱动再将远程进程的共享内存数据拷贝到客户端的共享内存,并唤醒客户端线程。
通过Binder将客户端,服务端的共享内存中的数据进行读写,放入对方的共享内存中,并通知。
Binder在Android中的应用?
- 系统服务及四大组件的启动调用工作:系统服务是通过getSystemService获取的服务,内部也就是通过ServiceManager。例如四大组件的启动调度等工作,就是通过Binder机制传递给ActivityManagerService,再反馈给Zygote。而我们自己平时应用中获取服务也是通过getSystemService(getApplication().WINDOW_SERVICE)代码获取。
- AIDL(Android Interface definition language)。例如我们定义一个IServer.aidl文件,aidl工具会自动生成一个IServer.java的java接口类(包含Stub,Proxy等内部类)。
- 前台进程通过bindService绑定后台服务进程时,onServiceConnected(ComponentName name, IBinder service)传回IBinder对象,并且可以通过IServer.Stub.asInterface(service)获取IServer的内部类Proxy的对象,其实现了IServer接口。
为什么选择Binder机制?他的优势是什么?
- 性能高,效率高:
传统的IPC(socket,管道,消息队列)需要拷贝两次内存,Binder只需要拷贝一次内存、共享内存不需要拷贝数据,只需要传递引用 - 安全性好:
C/S通信添加UID/PID,方便验证,安全机制完善。 - 利用C/S架构,通过多线程控制一个服务端多个客户端的情况
Android中IPC的几种方式详细分析与优缺点分析
- Bundle
- 文件共享
- Messenger:内部实现AIDL机制,c/s架构,通过handler接收message对象
- AIDL
- ContentProvider
- Binder连接池
Handler
其实并不是每一次添加消息时,都会唤醒线程。当该消息插入到队列头时,会唤醒该线程;如果有延迟消息,插入到头部,也会唤醒线程后在休眠
一句话概括Handler,并简述其原理
android中用于主线程和子线程之间通信的工具
主要包含Handler,Looper,MessageQueue,ThreadLocal.
Handler:封装了消息的发送和接收looper分发过来的Message
Looper:协调Handler和MessageQueue之间的桥梁,Looper的作用是循环从MessageQueue中取出message,并分发
给相应的Handler,Handler则存储在Message中的target中
message:单节点,存储handler传输的数据
MessageQueue:内部结构为单链表,由Looper创建,具体代码为Looper.prepare();先进先出原则(队列),根据 Message.when进行插入队列(队列中是按时间执行顺序排序)
ThreadLocal:负责存储和获取本线程的Looper
handler.sendMessage(message)将message发送到MessageQueue,MessageQueue执行enqueueMessage()方法入队,Looper执行looper.loop()方法从MessageQueue中取出message,执行message.target.dispatchMessage(message)方法将消息发送到Handler中,在handleMessage()方法中拿到回调
Looper.loop()是在主线程的死循环,为什么没有造成线程阻塞?
真正的ANR是在生命周期的回调中等待的时间过长导致的,深层次的讲,就是Looper.loop()没有及时取出消息进行分发导致的。一旦没有消息,Linux的epoll机制则会通过管道写文件描述符的方式来对主线程进行唤醒与沉睡,Android里调用了linux层的代码实现在适当时会睡眠主线程。
MessageQueue包含jni调用,无消息时,通知epoll休眠,来消息时,线程启动
looper.loop()中循环,判空退出怎么理解?
public static void loop() {
…
for (;😉 {
Message msg = queue.next(); // might block(可能会阻塞)
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
在queue.next中,会通过jni调用,通过Linux的epoll机制则会通过管道写文件描述符的方式来对主线程进行唤醒与沉睡,只有当应用程序退出时,才会执行if语句退出循环。
为什么不能在子线程更新UI?
因为如果要在子线程中更新UI,势必要考虑线程安全,加锁机制,这样很耗时,不加锁又很容易发生错误,这些错误是致命的,所以在设计时只允许UI线程更新UI,避免这些错误。
真的不能在子线程更新UI吗?
ViewRootImpl中会进行通过checkThread()进行线程检测
public ViewRootImpl(Context context, Display display) {
…
mThread = Thread.currentThread();
…
}
…
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
“Only the original thread that created a view hierarchy can touch its views.”);
}
}
由此得出:viewRootImpl在那个线程被初始化,就会在那个线程更新UI,大部分情况下,ViewRootImpl都是在UI线程中初始化的,所以只能在UI线程更新UI,部分情况下可以在子线程更新UI(比如Dialog是在addView中初始化ViewRootImpl)
SurfaceView可以在子线程中更新
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
总结
最后对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!
这里附上上述的技术体系图相关的几十套腾讯、头条、阿里、美团等公司20年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。
相信它会给大家带来很多收获:
上述【高清技术脑图】以及【配套的面试真题PDF】可以点击我的GitHub免费获取
本文在开源项目:[https://github.com]中已收录,里面包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…
当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。
- 诸多细节**,由于篇幅有限,这里以图片的形式给大家展示一部分。
相信它会给大家带来很多收获:
[外链图片转存中…(img-Q6yCcoXr-1711050255000)]
[外链图片转存中…(img-DHJjPPsW-1711050255001)]
上述【高清技术脑图】以及【配套的面试真题PDF】可以点击我的GitHub免费获取
本文在开源项目:[https://github.com]中已收录,里面包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…
当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。