-
Notifications
You must be signed in to change notification settings - Fork 116
Android基础知识汇总
横竖屏切换的生命周期:
onPause() --> onSaveInstanceState() --> onStop() --> onDestory() --> onCreate() --> onStart() --> onRestoreInstanceState() --> onResume()
在Activity由于异常情况下终止时,系统会调用 onSaveInstanceState 来保存当前 Activity 的状态。这个方法的调用是在onStop之前,它和onPause没有既定的时序关系,该方法只有在Activity被异常终止的情况下调用。当异常终止的Activity被重建之后,系统会调用onRestoreInstanceState,并且把Activity销毁时onSaveInstanceState方法所保存的Bundle对象参数同时传递给onRestoreInstanceState和onCreate方法。因为,可以通过onRestoreInstanceState方法来恢复Activity的状态,该方法的调用时机是在onStart之后。其中,onCreate和onRestoreInstanceState方法来恢复Activity状态的区别:onRestoreInstanceState回调则表明其中Bundle对象非空,不用加非空判断,而onCreate需要非空判断,建议使用onRestoreInstanceState。
可以通过在AndroidManifest文件的Activity中指定如下属性来避免横竖屏切换:
android:configChanges = "orientation| screenSize"
Activity的Flags很多,这里介绍集中常用的,用于设定Activity的启动模式,可以在启动Activity时,通过Intent.addFlags()方法设置。
- FLAG_ACTIVITY_NEW_TASK 即 singleTask
- FLAG_ACTIVITY_SINGLE_TOP 即 singleTop
- FLAG_ACTIVITY_CLEAR_TOP 当他启动时,在同一个任务栈中所有位于它之上的Activity都要出栈。如果和singleTask模式一起出现,若被启动的Activity已经存在栈中,则清除其之上的Activity,并调用该Activity的onNewIntent方法。如果被启动的Activity采用standard模式,那么该Activity连同之上的所有Activity出栈,然后创建新的Activity实例并压入栈中。
- Launcher通过Binder进程间通信机制通知AMS,它要启动一个Activity
- AMS通过Binder进程间通信机制通知Launcher进入Paused状态
- Launcher通过Binder进程间通信机制通知AMS,它已经准备就绪进入Paused状态,于是AMS就创建一个新的线程,用来启动一个ActivityThread实例,即将要启动的Activity就是在这个ActivityThread实例中运行
- ActivityThread通过Binder进程间通信机制将一个ApplicationThread类型的Binder对象传递给AMS,以便以后AMS能够通过这个Binder对象和它进行通信
- AMS通过Binde进程间通信机制通知ActivityThread,现在一切准备就绪,它可以真正执行Activity的启动操作了
Android应用模型是基于组件的应用设计模式,组件的运行要有一个完整的Android工程环境。在这个工程环境下,Activity、Service等系统组件才能够正常工作,而这些组件并不能采用普通的Java对象创建方式,new一下就能创建实例了,而是要有它们各自的上下文环境,也就是Context,Context是维持Android程序中各组件能够正常工作的一个核心功能类。
如何生动形象的理解Context?
一个Android程序可以理解为一部电影,Activity、Service、BroadcastReceiver和ContentProvider这四大组件就好比戏了的四个主角,它们是剧组(系统)一开始定好的,主角并不是大街上随便拉个人(new 一个对象)都能演的。有了演员当然也得有摄像机拍摄啊,它们必须通过镜头(Context)才能将戏传给观众,这也就正对应说四大组件必须工作在Context环境下。那么Button、TextView等等控件就相当于群演,显然没那么重用,随便一个路人甲都能演(可以new一个对象),但是它们也必须在面对镜头(工作在Context环境下),所以Button mButtom = new Button(context) 是可以的。
源码中的Context
public abstract class Context {
}
它是一个纯抽象类,那就看看它的实现类。
它有两个具体实现类:ContextImpl和ContextWrapper。
其中ContextWrapper类,是一个包装类而已,ContextWrapper构造函数中必须包含一个真正的Context引用,同时ContextWrapper中提供了attachBaseContext()用于给ContextWrapper对象指定真正的Context对象,调用ContextWrapper的方法都会被转向其包含的真正的Context对象。ContextThemeWrapper类,其内部包含了与主题Theme相关的接口,这里所说的主题就是指在AndroidManifest,xml中通过android:theme为Application元素或者Activity元素指定的主题。当然,只有Activity才需要主题,Service是不需要主题的,所以Service直接继承与ContextWrapper,Application同理。而ContextImpl类则真正实现了Context中的所有函数,应用程序中所调用的各种Context类的方法,其实现均来源于该类。Context得两个子类分工明确,其中ContextImpl是Context的具体实现类,ContextWrapper是Context的包装类。 Activity、Application、Service虽都继承自ContextWrapper(Activity继承自ContextWrapper的子类ContextThemeWrapper),但它们初始化的过程中都会创建ContextImpl对象,由ContextImpl实现Context中的方法。
在应用程序中Context的具体实现子类就是:Activity、Service和Application。那么Context数量=Activity数量+Service数量+1。那么为什么四大组件中只有Activity和Service持有Context呢?BroadcastReceiver和ContextPrivider并不是Context的子类,它们所持有的Context都是其他地方传过去的,所以并不计入Context总数。
TextView tv = new TextView(getContext());
ListAdapter adapter = new SimpleCursorAdapter(getApplicationContext(), ...);
AudioManager am = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);getApplicationContext().getSharedPreferences(name, mode);
getApplicationContext().getContentResolver().query(uri, ...);
getContext().getResources().getDisplayMetrics().widthPixels * 5 / 8;
getContext().startActivity(intent);
getContext().startService(intent);
getContext().sendBroadcast(intent);
虽然Context神通广大,但并不是随便拿到一个Context实例就可以为所欲为,它的使用还是有一些规则限制的。由于Context的具体实例是由ContextImpl类去实现的,因此在绝大多数场景下,Activity、Service和Application这三种类型的Context都是可以通用的。不过有几种场景比较特殊,比如启动Activity,还有弹出Dialog。出于安全原因的考虑,Android是不允许Activity或Dialog凭空出现的,一个Activity的启动必须要建立在另一个Activity的基础之上,也就是以此形成返回栈。而Dialog则必须在一个Activity上面弹出(除非是System Alert类型的Dialog),因此在这种场景下,我们只能使用Activity类型的Context,否则将会报错。
从上图我们可以发现Activity所持有的Context的作用域最广,无所不能,因此Activity继承至ContextThemeWrapper,而Application和Service继承至ContextWrapper,很显然ContextThemeWrapper在ContextWrapper的基础上又做了一些操作使得Activity变得更强大。着重讲一下不推荐使用的两种情况:
- 如果我们用ApplicationContext去启动一个LaunchMode为standard的Activity的时候会报错:
android.util.AndroidRuntimeException: Calling startActivity from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
这是因为非Activity类型的Context并没有所谓的任务栈,所以待启动的Activity就找不到栈了。解决这个问题的方法就是为待启动的Activity指定FLAG_ACTIVITY_NEW_TASK标记位,这样启动的时候就为它创建一个新的任务栈,而此时Activity是以singleTask模式启动的。所有这种用Application启动Activity的方式都不推荐,Service同Application。
- 在Application和Service中去LayoutInflate也是合法的,但是会使用系统默认的主题样式,如果你自定义了某些样式可能不会被使用,这种方式也不推荐使用。
一句话总结:凡是跟UI相关的,都应该使用Activity作为Context来处理;其他的一些操作,Service、Activity、Application等实例都可以,当然了注意Context引用的持有,防止内存泄露。
其内存地址是一样的。Application本身就是一个Context,这里获取getApplicationContext得到的结果就是Application本身的实例。getApplication方法的语义性很强,就是用来获取Application实例的,但是这个方法只有在Activity和Service中才能调用的到。那么也许在绝大多数情况下我们都是在Activity或者Service中使用Application,但是如果在一些其他的场景,比如BroadcastReceiver中也想获取Application实例,这时就可以借助getApplicationContext方法了。
public class MyReceiver extends BroadcastReceiver{
@Override
public void onReceive(Contextcontext,Intentintent){
Application myApp= (Application)context.getApplicationContext();
}
}
Activity
Activity并不负者视图控制,它只是控制生命周期和处理事件。真正控制视图的是Window。一个Activity包含了一个Window,Window才是真正代表一个窗口。Activity就像一个控制器,统筹视图的添加与显示,以及通过其他回调方法,来与Window以及View进行交互。
Window
Window是视图的承载器,内部持有一个DecorView,而这个DecorView才是view的跟布局。Window是一个抽象类,实际在Activity中持有的是其子类PhoneWindow。PhoneWindow中有个内部类DecorView,通过创建DecorView来加载Activity中设置的布局。Window通过WindowManager将DecorView加载其中,并将DecorView交给ViewRoot,进行视图绘制以及其他交互。
DecorView
DecorView是FrameLayout的子类,它可以被认为是Android视图树的根节点视图。
DecorView作为顶级View,一般情况下它内部包含一个竖直方向的LinearLayout,**在这个LinearLayout里面有上下三个部分,上面是个ViewStub,延迟加载的视图(应该是设置ActionBar,根据Theme设置),中间的是标题栏(根据Theme设置,有的布局没有),下面是内容栏。**在Activity中通过setContentView所设置的布局文件其实就是被加到内容栏之中的,成为其唯一子View。
ViewRoot
ViewRoot可能比较陌生,但是其作用非常重大。所有View的绘制以及事件分发等交互都是通过它来执行或传递的。
ViewRoot对应ViewRootImpl类,它是连接WindowManagerService和DecorView的纽带,View的三大流程(测量、布局、绘制)均通过ViewRoot来完成。
ViewRoot并不属于View树的一份子。从源码实现上来看,它既是非View的子类,也是非View的父类,但是,它实现了ViewParent接口,这让它可以作为View的名义上的父视图。RootView继承了Handler类,可以接收事件并分发,Android的所有触屏事件,按键事件、界面刷新等事件都是通过ViewRoot来进行分发的。
总结
Activity就像个控制器,不负责视图部分。Window像个承载器,装着内部视图。DecorView就是个顶级视图,是所有View的最外层布局。ViewRoot像个连接器,负者沟通,通过硬件感知来通知视图,进行用户之间的交互。
来源:https://github.com/Omooo/Android_QA/blob/master/Answer.md#android_base_1
- JMM与volatile关键字
- synchronized的实现原理
- synchronized等待与唤醒机制
- AQS的实现原理
- ReentrantLock的实现原理
- ReentrantLock等待与唤醒机制
- CAS、Unsafe类以及Automic并发包
- ThreadLocal的实现原理
- 线程池的实现原理
- Java线程中断机制
- 多线程与并发常见面试题
- Android基础知识汇总
- MVC、MVP与MVVM
- SparseArray实现原理
- ArrayMap的实现原理
- SharedPreferences
- Bitmap
- Activity的启动模式
- Fragment核心原理
- 组件化项目架构搭建
- 组件化WebView架构搭建
- 为什么 Activity.finish() 之后 10s 才 onDestroy ?
- Binder与AIDL
- Binder实现原理
- Android系统启动流程
- InputManagerService
- WindowManagerService
- Choreographer详解
- SurfaceFlinger
- ViewRootImpl
- ActivityManagerService
- APP启动流程
- PMS安装与签名校验
- Dalvik与ART
- 内存优化策略
- UI界面及卡顿优化
- App启动优化
- ANR问题
- 包体积优化
- APK打包流程
- 电池电量优化
- Android屏幕适配
- 线上性能监控1--线上监控切入点
- 线上性能监控2--Matrix实现原理
- Glide实现原理
- OkHttp实现原理
- Retrofit实现原理
- RxJava实现原理
- RxJava中的线程切换与线程池
- LeakCanary实现原理
- ButterKnife实现原理
- ARouter实现原理
- Tinker实现原理
- 2. 两数相加
- 19.删除链表的倒数第 N 个结点
- 21. 合并两个有序链表
- 24. 两两交换链表中的节点
- 61. 旋转链表
- 86. 分隔链表
- 92. 反转链表 II
- 141. 环形链表
- 160. 相交链表
- 206. 反转链表
- 206 反转链表 扩展
- 234. 回文链表
- 237. 删除链表中的节点
- 445. 两数相加 II
- 面试题 02.02. 返回倒数第 k 个节点
- 面试题 02.08. 环路检测
- 剑指 Offer 06. 从尾到头打印链表
- 剑指 Offer 18. 删除链表的节点
- 剑指 Offer 22. 链表中倒数第k个节点
- 剑指 Offer 35. 复杂链表的复制
- 1. 两数之和
- 11. 盛最多水的容器
- 53. 最大子序和
- 75. 颜色分类
- 124.验证回文串
- 167. 两数之和 II - 输入有序数组 -169. 多数元素
- 189.旋转数组
- 209. 长度最小的子数组
- 283.移动0
- 303.区域和检索 - 数组不可变
- 338. 比特位计数
- 448. 找到所有数组中消失的数字
- 643.有序数组的平方
- 977. 有序数组的平方