一:Activity:
Activity 是Android 四大组件之一,用于展示界面。Activity 中所有操作都与用户密切相关,是一个负责与用户交互的组件,它上面可以显示一些控件也可以监听并处理用户的事件。一个Activity 通常就是一个单独的屏幕,Activity 之间通过Intent 进行通信。
一、关闭当前Activity
在Activity 中调用finish()方法即可关闭当前Activity。
二、启动一个Activity 时获取该Activity 销毁时的返回值
如果想让我们的MainActivity 打开ContactListActivity,同时能够获取到ContactListActivity销毁时返回的数据, 那么就不能通过startActivity(Intent) 方法去启动ContactListActivity了。必须使用startActivityForResult(Intent intent, int requestCode)方法,该方法必须跟onActivityResult(int requestCode,int resultCode, Intent data)方法结合着使用才行,当MainActivity 启动的ContactListActivity销毁前,如果ContactListActivity调用了setResult(int resultCode, Intent data)方法,那么系统就会调用MainActivity 的onActivityResult()方法,同时将数据Intent 的形式传递过来。
Activity 的生命周期
学到这里我们有必要对Activity 有一个全新的认识。Activity 已经不能再简单的认为就是一个普通的类,其生命周期就是类的加载,对象的的创建和对象的卸载那么简单。Activity 从创建到销毁,整个生命周期是一个非常复杂的过程,该过程由Android 系统负责维护。
Activity 的3 种状态
我们人类有婴幼儿时期、青少年时期、中老年时期,Activity 一样也有3 种状态:Resumed、Paused、Stopped,这三种状态是Android 官方给出的,我们翻译过来可以理解为:激活或运行状态、暂停状态、停止状态。这3 种状态的特点如下:
Resumed 状态:Activity 位于前端位置,并且获取到了用户的焦点。也就是当前Activity 完全可见也可用。
注意:在Android 中目前只允许同时只能有一个Activity 位于前端位置。
Paused 状态:如果另外一个Activity 位于前端位置并且获取了焦点,但是该Activity 还依然可见,那么该Activity 就处于了Paused 状态。比如如果另外一个Activity 虽然位于前端,但是是透明的或者没有占满整个屏幕,那么就会出现上面的这种情况。位于Paused 状态的Activity 依然是“存活”着的,但是如果系统内存极端的不足,那么就有可能被系统“杀死”以便释放内存。
Stopped 状态:当另外一个Activity 完全将该Activity 遮盖住的情况下,那么该Activity 就处于停止状态了。位于停止状态的Activity 依然“活着”,但是它已经对用户完全不可见了,因此只要系统需要释放内存就会将该Activity“杀死”。
我们编写的代码要根据Activity 的不同状态让其做不同的工作,比如如果我们的Activity 是用于播放视频的,那么当其位于Resumed 状态时我们可以让视频正常的播放,当Activity 位于Paused 状态的时候,我们也应该让我们的视频暂停播放,当Activity 位于Stopped 状态时我们应该停止播放视频并释放资源。
那么如何让我们的程序员能够感知到Activity 的状态变化呢?Android 系统为了将Activity 状态的变化通知给我们在Activity 中提供了7 个回调方法。我们只需要在不同的回调方法中完成不同的工作即可。
Activity 生命周期的7 个回调方法
方法名
特点
OnCreate
当Activity 第一次创建时被调用,在该方法中我们应该执行创建视图、初始化数据等工作。该方法被调用之后紧接着就是调用onStart 方法。
onStart
当Activity 对用户可见前被系统调用。
onResume
当Activity 可以跟用户交互前被调用,该方法被调用后Activity 就处于Resumed 状态。
onPause
当另外一个Activity 即将成为Resumed 状态前调用,在该方法中应该保存临时数据、停止动画、暂停视频播放器等。必须要注意的就是该方法执行必须要快,因为只有当该方法执行过之后,另外一个Activity 才会成为Resumed 状态,不然会造成Activity 直接切换的“卡顿”现象。
onStop
当Activity 对用户已经完全不可见的时候就会调用该方法。当另外一个Activity 已经成为Resumed 状态或者当前Activity 被销毁的情况下会导致当前Activity 不可见。
onDestory
当Activity 被销毁前调用。当前Activity 调用finish()方法或者系统为了释放内存而将其销毁都会调用该方法。
onRestart
当Activity 处于onStop 状态之后,如果重新启动则会调用该方法。比如如果当前Activity位于Resumed 状态,此时我们按了Home 键,那么Activity 就完全不可见了,onStop 方法就会被执行,然后再长按Home 键再将该Activity 重新启动起来就会调用onRestart 方法。
Activity 生命周期图
下图是Android官方给出的Activity 生命周期图:
横竖屏切换时Activity 的生命周期
Android 手机在横竖屏切换时,默认情况下会把Activity 先销毁再创建,模拟器横竖屏切换的快捷键是Ctrl+F11。
在类似手机游戏、手机影音这一类的应用中,这个体验是非常差的。不让Activity 在横竖屏切换时销毁,只需要在清单文件声明Activity 时配置节点的几个属性即可,其方式如下:
一. 4.0 以下版本:
android:configChanges="orientation|keyboardHidden"
二. 4.0 以上版本:
android:configChanges="orientation|screenSize"
三. 4.0 以上版本:
android:configChanges="orientation|keyboardHidden|screenSize"
将该参数配置到Activity 中后再切换屏幕方向,那么Activity 就不会再销毁和重新创建了。但是配置了该参数仅仅是不让Activity 销毁和重建,Activity 界面依然会跟着屏幕方向重新调整,那么如何固定Activity 的方向呢?
固定Activity 的方向
想固定Activity 的方向其实比较简单,有两种方法:
1、通过配置文件
在AndroidManifest.xml 中的activity 节点中添加如下属性。
android:screenOrientation="portrait"
该属性通常有两个常量值,portrait:垂直方向,landscape:水平方向。
2、通过代码
在Activity 的onCreate 方法中执行如下方法。
//垂直方向
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
//水平方向
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
Activity 的启动模式
Activity 的任务栈
Task Stack(任务栈)是一个具有栈结构的容器,可以放置多个Activity 实例。启动一个应用,系统就会为之创建一个task,来放置根Activity;默认情况下,一个Activity 启动另一个Activity 时,两个Activity是放置在同一个task 中的,后者被压入前者所在的task 栈,当用户按下back 键,后者从task 中被弹出,
前者又显示在屏幕前,特别是启动其他应用中的Activity 时,两个Activity 对用户来说就好像是属于同一个应用;系统task 和应用task 之间是互相独立的,当我们运行一个应用时,按下Home 键回到主屏,启动另一个应用,这个过程中,之前的task 被转移到后台,新的task 被转移到前台,其根Activity 也会显示到
幕前,过了一会之后,再次按下Home 键回到主屏,再选择之前的应用,之前的task 会被转移到前台,系统仍然保留着task 内的所有Activity 实例,而那个新的task 会被转移到后台,如果这时用户再做后退等动作,就是针对该task 内部进行操作了。
任务栈的设计是为了提高用户体验,但是也有其不足的地方。任务栈的缺点如下:
1、每开启一次页面都会在任务栈中添加一个Activity,而只有任务栈中的Activity 全部清除出栈时,任务栈被销毁,程序才会退出,这样的设计在某种程度上可能造成了用户体验差,需要点击多次返回才可以把程序退出了。
2、每开启一次页面都会在任务栈中添加一个Activity 还会造成数据冗余, 重复数据太多, 会导致内存溢出的问题(OOM)。
Andriod给出的官方图:
为了解决任务栈产生的问题,Android 为Activity 设计了启动模式,那么下面的内容将介绍Android 中Activity 的启动模式,这也是最重要的内容之一。
Activity 的启动模式
启动模式(launchMode)在多个Activity 跳转的过程中扮演着重要的角色,它可以决定是否生成新的Activity 实例,是否重用已存在的Activity 实例,是否和其他Activity 实例共用一个task。
Activity 一共有以下四种launchMode:standard、singleTop、singleTask、singleInstance。我们可以在AndroidManifest.xml 配置的android:launchMode 属性为以上四种之一即可。
standard: 标准启动模式
特点:默认启动模式, 每次激活Activity时(startActivity),都创建Activity实例,并放入任务栈.
singleTop:单一顶部模式
特点:如果activity已经被开启,而且是在栈顶,就不会在创建当前这个activity的实例,而是复用这个已经开启的activity,但是如果不是在栈顶,就会初始化一个新的实例,在整个栈里允许有多个实例
singleTask:单一任务栈
特点:当前栈里只允许有一个当前activity的实例,如果要开启的activity在栈里存在,并且在底部,就会移除这个activity上面所有的activity
应用场景:如果这个activity非常消耗cpu和内存,建议把这个activity的启动模式设置为singleTask,浏览器的browserActivity 设置的就是
singleinstance:单一实例
特点:整个手机操作系统只有一个实例,并且是单独运行在自己的任务栈里
应用场景:通话界面的activity
Note:
Activity的启动模式是面试几乎都会问到的一个知识点,也是很重要的一个知识点,在项目开发中经常会用到启动模式来帮助我们解决一些比较难解决的bug,也可以帮助我们去优化一个项目的任务栈,防止界面太多导致卡顿和OOM。
二:Broadcast:
在Android 中,Broadcast 是一种广泛运用的在应用程序之间传输信息的机制。而BroadcastReceiver 是对发送出来的Broadcast 进行过滤接受并响应的一类组件,是Android 四大组件之一。
广播接收者(BroadcastReceiver)用于接收广播的,广播的发送是通过调用sendBroadcast(Intent)/sendOrderedBroadcast(Intent)来实现的。通常一个广播可以被多个广播接收者所接收。
广播被分为两种不同的类型:“普通广播(Normal Broadcasts)”也叫无序广播和“有序广播(OrderedBroadcasts)”。
注意:如果我们的应用退出了,那么就无法监听到该广播,可能有人说是因为退出的时候调用了MainActivity 的onDestory()方法,而onDestory()方法中调用了unregisterReceiver(BroadCastReceiver)方法,确实如此的。那么如果我们将onDestory()方法中的unregisterReceiver()方法给去掉会是什么样的情况呢?那我们就一起试一下吧!
将MainActivity.java 中的unregisterReceiver()去掉之后重新运行上面的项目,然后点击back 键,发现logcat 输入了如下错误信息:
上面的信息说我们的MainActivity 导致了IntentReceiver 的泄露。ScreenReceiver 在MainActivity 中被注册了但是当MainActivity 销毁的时候没有被反注册。
可见要想让我们的代码更加完善,必须在onDestory 中或者其他地方(必须保证Activity 销毁前)将接收者给反注册掉。
三:Serivce:
Serivce的基本概念
Android 官方文档对Service 的解释如下:
翻译:
Service 是应用组件之一,用来执行需要长时间运行的操作,Service 运行在后台并且跟用户没有交互界面。第三方应用的组件(指四大组件的任意一种)可以启动一个Service,一旦启动起来,该Service 就一直在后台运行,就算用户已经将该应用置为后台进程。另外,组件也可以通过绑定的形式跟一个Service
进行交互,甚至完成进程间通信。比如:Service 可以在后台处理网络传输、播放音乐、进行I/O 流的读写或者跟内容提供者进行交互。
Service 的特点如下:
Service 在Android 中是一种长生命周期的组件,它不实现任何用户界面,是一个没有界面的Activity
Service 长期在后台运行,执行不关乎界面的一些操作比如:网易新闻服务,每隔1 分钟去服务查看是否有最新新闻
Service 和Thread 有点相似,但是使用Thread 不安全,不严谨
Service 和其他组件一样,都是运行在主线程中,因此不能用它来做耗时的操作
Service 的生命周期回调函数
和Activity 一样,service 也有一系列的生命周期回调函数,你可以实现它们来监测service 状态的变化,并且在适当的时候执行适当的工作。
下面的service 展示了每一个生命周期的方法,也是官网给出的demo:
注意:不同于Activity 的生命周期回调方法,Service 不须调用父类的生命周期回调方法。
Service的生命周期图
官方给出的Service 生命周期图如下所示。该图左侧展示的是用startService()方法启动的Service的生命周期,右侧展示的是用bindService()方法启动的Service 的生命周期。
这个图说明了Service 典型的回调方法,尽管这个图中将开启的Service 和绑定的Service 分开,但是我们需要记住,任何Service 都潜在地允许被绑定。所以,一个被开启的Service 仍然可能被绑定。实现这些方法,可以看到两层嵌套的Service 的生命周期。
整体生命周期(The entire lifetime)
Service 整体的生命时间是从onCreate()被调用开始,到onDestroy()方法返回为止。和Activity 一样,Service 在onCreate()中进行它的初始化工作,在onDestroy()中释放残留的资源。比如,一个音乐播放service 可以在onCreate()中创建播放音乐的线程,在onDestory()中停止这个线程。
onCreate() 和onDestroy()会被所有的Service 调用,不论Service 是通过startService()还是bindService()建立的。
积极活动的生命周期(The active lifetime)
Service 积极活动的生命周期(active lifetime)是从onStartCommand() 或onBind()被调用开始,它们各自处理由startService()或bindService()方法传过来的Intent 对象。如果service 是被开启的,那么它的活动生命周期和整个生命周期一同结束。如果service 是被绑定的,它们它的活动生命周期是在onUnbind()方法返回后结束。
注意:
尽管一个被开启的service 是通过调用stopSelf() 或stopService()来停止的,没有一个对应的回调函数与之对应,即没有onStop()回调方法。所以,当调用了停止的方法,除非这个service 和客户组件绑定,否则系统将会直接销毁它,onDestory()方法会被调用,并且是这个时候唯一会被调用的回调方法。
四:ContentProvider:
内容提供者ContentProvider,是Android 的四大组件之一。内容提供者是应用程序之间共享数据的接口。
Android 系统将这种机制应用到方方面面,比如:联系人(通讯录应用程序)Provider 专为不同应用程序提供联系人数据;短信(短信应用程序)Provider 专为不同应用程序提供系统短信信息。当应用继承ContentProvider 类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据。虽然使用其他方法也可以对外共享数据,但数据访问方式会因数据存储的方式而不同,如:采用文件方式对外共享数据,需要进行文件操作读写数据;采用SharedPreferences 共享数据,需要使用SharedPreferences API 读写数据。而使用ContentProvider 共享数据的好处是统一了数据访问方式。
网友评论