Android系统四大组件分别是活动、服务、广播接受器、内容提供器。其中活动是所有Abdroid应用程序的门面凡是在应用中你看得到的都是放在活动中的,服务是无法看到的但是它会一直在后台默默的运行即使用户退出了应用服务仍然可以继续运行,广播接受器允许你的应用接受来自各处的广播消息,当然你的应用同样也可以向外发出广播消息,内容提供器则为应用程序之间共享数据提供了可能。
经过了上面的简单了解,现在对每一个进行单独的认识。
Activity:
首先什么是活动,活动是一种包含用户界面的组件,主要用于和用户进行交互,一个应用程序可以包含零个或多个,Activity是一个可以让屏幕提供给用户交互的组件,一个应用程序里会有多个活动但是会有一个特殊的Activity,那就是main Activity 它代表了应用程序启动看到的首个界面,而且每个Activity可以通过不同的动作启动其它的Activity。为了创建一个Activity就必须要继承Activity的子类,同时重载两个方法onCreate(),onPause()。
其中的onCreate()方法在Activity创建的时候就调用了,可以在这个方法中初始化一些常量、资源的链接,其中最重要的是setContentView()去加载活动界面的布局。而onPause()方法是当活动交互暂停的时候,一些在界面上填过的交互数据可以在这里保存以免造成丢失用户体验不好。
除了要定义的和继承Activity之外,还需要在manifest中进行注册,表示声明(如果不声明这个Activity在运行时会报ANR错误,就是应用程序没有响应),每新建一个项目都会有一个AndroidMainifest.xml文件中我们的Activity进行注册,每一个应用程序都必须有一个AndroidManifest.xml。
Activity返回栈的概念
在Android中多个Activity定义在一个Task(这个Task就是任务)就是说一个Task是一组Activity的集合,然后Activity又被安排在back stack 即返回栈,当手机启动了一个应用程序图标时,应用程序的Task成为了一个前台进程,如果首次启动发现不存在Task实例就会创建一个实例,然后把main Activity放入到stack(默认是栈顶)这样一个Task就管理了一组栈,当Activity1启动Activity2,Activity1被推到栈底,Activity2被推到栈顶,同理Activity3也是这样的。当按到back键时则在栈顶的Activity就会被弹出,当所有的Activity都被弹出那么Task就不存在了。
同时Task有两个状态:Foreground和Background即前景和背景,当处于Background时所有的Activity都是静止的,当处于Foreground时就表示当前用户交互的应用程序。
当启动了A程序此时Task A是Foreground 用于和用户交互,当点击其它程序B时Task A变成了Background,里面所有的Activity都是停止的,Task B被实例化变成Foreground而Task A一只等待着被恢复。所以Android是一个多任务系统,不同的任务被互相切换。
Activity的生命周期
每个活动在其生命周期都只有四个形态,运行状态、暂停状态、停止状态、销毁状态。Activity的生命周期就是它所在进程的生命周期,每一个活动都处于一个状态,但是对于开发者来说是无法控制其应用程序处于某一个状态,均由系统来完成。
(1)运行状态(Running):当一个活动位于返回栈的栈顶时,这时活动就处于运行状态,系统最不愿意回收的就是处于运行状态的活动。在这个状态下是可视的可接受用户输入的尽最大可能能够保持它的活动状态,杀死其他的活动来保证当前活动
(2)暂停状态(Paused):当一个活动不在处于栈顶的位置但还是可见的时候并且没有焦点,有可能原因是一个透明或者非全屏的Activity被激活,这时活动就进入了暂停状态,处于暂停状态的活动是完全存活的仍被当成活动状态,只不过不能接受用户的输入,系统也不会回收这种活动,只有在内存极低的情况下才会考虑回收这种活动。
(3)停止状态(Stopped):当一个活动不在处于栈顶的位置,并且完全不可见的时候,就进入了停止状态,系统仍然会为这种活动保存相应的状态和成员变量,但是这并不是完全可靠的,当其他地方需要内存时,处于停止状态的活动有可能被系统回收。
(4)销毁状态(Destroyed):当一个活动从返回栈中移除后就变成了销毁状态,系统会回收这种状态的活动,以保证内存的充足。
还分为三种生存期:完整生存期、可见生存期、交互生存期。
完整生存期:活动在onCreate()和onDestroy()方法之间所经历的
可见生存期:活动在onStart()和onStop()方法之间所经历的
交互活动期:活动在onResum()和onPause()方法之间所经历的
Activity的异常生命周期
(1)资源相关的系统配置发生导致Activity被杀死重建。
View源码的每个View都有onSaveInstancState和onRestoreInstanceState这两个方法,但是onSavedInstanceState()和onRestoreInstanceState()并不是Activity生命周期的方法。
Activity中保存数据有两种方式onPause(),onSaveInstance(Bundle), 恢复数据也有两种途径onCreate(Bundle), onRestoreInstanceState(Budle),默认情况下onSaveInstanceSate()和onRestoreInstanceState()会对UI状态进行保存和恢复,如果需要保存其他持久化的数据得通过onPause()保存(google推荐)。
接受位置可以是onRestoreInstanceState和onCreate方法,onSaveInstanceState()会在onPause()或onStop()之前执行,onRestoreInstanceState()会在onStart()和onResume()之间执行。区别是onRestoreInstanceState()被调用Bundle一定是有值的而onCreate中需要判断参数是否为null,onSaveInstanceState只有在Activity有机会重新显示的时候才会调用,正常销毁的Activity周期不会调用,系统调用这个回调方法可以保证一定在活动被回收之前被调用,可以通过这个方法来解决被回收时临时数据得不到保存的情况。通常情况下开发者不需要重写覆盖该方法,默认提供了自动保存活动状态所涉及的用户界面组件的所有信息。
那么onRestoreInstanceState()会跟onSaveInstanceState()成对出现吗? 答案是不会成对出现,onSaveInstanceState()需要调用的时,activity可能销毁,也可能没有销毁,只有在activity销毁重建的时候onRestoreInstanceState()才会调用。
调用onSaveInstanceState()方法后数据保存了,会通过onCreate()里的Bundle类型的参数,这个参数在一般情况下是null但是当活动被回收之前来通过onSavelnstanceState()来保存数据的话,这个参数就会保存之前的数据,再通过相应的方法取出来。默认情况下默认会自动保存Activity中的某些状态,比如activity中各种UI的状态,因此在activity被“系统”销毁和重建的时候,这些Ui的状态会默认保存,但是前提条件是Ui控件必须制定id,如果没有指定id的话,UI的状态是无法保存 的。
protected void onSaveInstance (Bundle outState){
super.onSaveInstanceState(outState);
String tempData = "Something you just tyed";
outState.putString("data_key",tempData);
}
这个代码可以保存数据
if(savedInstanceState != null) {
String tempData = savedInstanceState.getString("data__key");
Log.d(TAG,tempData);
}
相应的取值方法来将数据取出
(2)资源内存不足导致低优先级的Activity被销毁
Activity的优先级:前台Activity-->可见但非前台Activity(可见但无法交互)-->后台Activity(已被暂停的Activity),系统内存不足时会按照这个顺序杀死Activity并通过onSaveInstancState和onRestoreInstanceState这两个方法来保存并恢复数据
当应用遇到意外情况(内存不足,用户直接按home键)由系统直接销毁一个Activity时,onSaveInstanceState()就会调用,但是当用户主动销毁activity,如按back键,或直接执行finish(),这种情况下onSaveInstanceState()就不会执行,因为这种情况下,用户的行为决定了不需要保存Activity的状态。
Activity的方法
这就说到了Activity的方法,一个Activity类里有七个回调方法,包含了一个活动里的所有环节。可以通过覆盖(override)这些方法即可在你需要处理的时候来调用。我们不能调用什么时候调用是由Activity决定的。
1.onCreate():每个活动中都会重写这个方法,它会在活动的第一次被创建的时候调用,比如初始化操作,加载布局、绑定事件、从Intent中获取数据、声明UI变量,声明全局变量,设置UI控件的具体点击事件,以及一些访问网络获取数据等等。
2.onStart():这个方法在活动的不可见变为可见的时候进行调用,即当一个活动不需要展示给用户,即该方法的触发表示所属活动将被展现给用户。当系统调用activity的onStop()之后,此app就会处在后台一段时间,之后走onRestart()-->onStart()-->onResume(),此时在onStart()中可以做的事情:在onPause(),onStop()中清除的资源重新实例化。
3.onResume():这个方法在活动准备好和用户进行交流的时候进行调用,此时的活动一定在栈的顶部并且处于运行状态。
4.onPause():这个方法在系统准备去启动或者去恢复另一个活动时调用,即当一个正在前台运行的活动因为其他的活动需要前台运行而转入后台运行的时候,会在这个方法中将一些消耗CPU的资源释放掉,以及保存一些关键数据,但这个方法的执行速递一定要快否则会影响到新的栈顶活动的使用。这个方法应该要保存状态信息,因为活动没有在前台时都有可能被停止或者被进程管理为了给新的活动预留足够内存而随时结束这些活动,可能onPause()对于一个活动来说是最后一个触发的方法。
可以通过一些操作来释放activity的资源,但是不做耗时的事情,耗时的事情放在onstop方法去做,如果在onpause()方法中做耗时操作的话,会使页面跳转变的很慢,不顺畅。1.停止动画活着其他正在运行导致cpu消费的操作。2.保存没有保存的改变(例如邮件草稿)。 3.释放系统资源,例如broadcast,sensors(比如gps),活着其他任何影响到电量的资源。4.如果程序正在使用camera,在这个地方去做释放资源的操作。5. 视频的暂停以及播放进度。
5.onStop():这个方法在活动完全不可见的时候调用,和onPause()的区别在于如果新启动的活动是一个对话框形式的活动那么onPause()会启动但是onStop()不会执行。如果内存紧张系统会直接结束该活动,而不会触发onStop()方法。在该方法中可以做一些耗时操作,比如写数据到database等等。
6.onRestar():这个方法在活动由停止状态变为运行状态,也就是这个活动被重新启动了。一般性不做,特殊情况下会做:开始动画,或者只有在用户获取到焦点时才需要的组件,视频的开始播放等
7.onDestory():这个方法在活动被销毁之前调用,之后活动的状态将变为销毁状态。有可能导致内存泄漏的地方,做彻底的清除工作,释放资源(置空)。
当一个程序首先被启动它会依次执行onCreate(),onStart(),onResume()方法,如果启动了另外一个Activity把MainActivity完全覆盖就会执行onPause()和onStop()方法,当按下返回键回到刚才的Activity就会执行onRestart()方法,因为之前已经进入了停止状态,之后会依次执行onStrat()和onResume()方法,但是没有执行onCreat()因为mainActivity没有重新创建,最后退出程序时活动被注销执行onDestroy()方法
对此进行一个总结:
(1)一个Activity启动的顺序:onCreate() -> onStart() -> onResume()
(2)当另一个Activity启动时,第一个Activity onPause() -> 第二个Activity onCrete() -> onStart() -> onResume() -->第一个Activity onStop()
(3)当返回到第一个Activity时 :第二个Activity onPause() -> 第一个Activity onRestart() -> onStart() -> onResume() -->第二个Activity onStop() -> onDestory()
(4)一个Activity销毁的顺序:
onPause() -> <Process Killed>
onPause() -> onStop() -> <Process Killed>
onPause() -> onStop() -> onDestory()
在finish()执行了的方法: 在onStop()中调用system.exit(0) 方法用来退出app,此时app就不会再走onDestroy(),但是在onPause()调用时app会瞬间启动。当在onCreate()中调用finish()的时候,此时activity走的生命周期就是直接onCreate()-->onDestroy(), 当调用finish()的时候,一般性的会走onPause()-->onStop()--onDestroy()
Activity和Context
Context描述的是一个应用程序的环境信息,即上下文,这个类是一个抽象类,Android提供了该类的具体实现类Contextlml,通过它可以获取应用程序的各种资源和类,也包括一些应用级别的操作。
Context和Activity、Application有什么异同:Activity和Application都是Context的子类但是Context维护的是当前的Activiry的生命周期,Applacation维护的是整个项目的生命周期。
使用Context的注意事项:
(1)不要让生命周期过长的对象引用activity和context,既保证引用Activity的对象要与activity本身的生命周期一样长
(2)避免非静态的内部类,尽量使用静态类,避免生命周期的问题,注意内部类对外部对象引用导致的生命周期的变化
Activity 启动模式
在多Activity开发中,其之间希望跳转到某个Activity实例而不是生成大量重复的实例,启动模式就是决定以哪种方式启动一个跳转到原来某个Activity的实例,每个活动都有相应的启动模式,启动模式一共有四种,分别是standard、singleTop、singleTask、singleInstance,可以在AndroidManifest.xml中通过给<activitt>标签指定android:launchMode属性来选择启动模式。
standard:是活动默认的启动模式,在不进行显示指定的情况下所有活动都会默认使用这种启动模式。在standard模式下,每当启动一个新的活动,它就会在返回栈中入栈并处于栈顶的位置。对于standard模式的活动,系统不会在乎这个活动是否已经在返回栈中存在,每次启动都会创建该活动的一个新的实例。
singleTop:当活动的启动模式指定为该模式,在启动活动时如果发现返回栈的栈顶已经是该活动,则认为可以直接使用它,不会再创建新的活动实,使用该模式很好地解决了重复创建栈活动的问题。但是当该活动并没有处于栈顶的位置,还是可能会创建多个活动实例。
singleTask:如果想要让某个活动在整个应用程序的上下文中只存在一个实例呢就可以使用singleTask模式来启动。每次启动该活动时系统首先会在返回栈中检查是否存在该活动的实例,如果发现已经存在则直接使用该实例,并把在这个活动之上的所有活动统统出栈,如果没有发现就会创建一个新的活动实例。
singleInstanc:指定为singleInstance模式的活动会启用一个新的返回栈来管理这个活动。通常应用以下场景当程序中有一个活动是允许其他程序调用的,想实现其他程序和我们的程序可以共享这个活动的实例,那么该如何实现呢?使用前面三种启动模式肯定是做不到的,因为每个应用程序都会有自己的返回栈,同一个活动在不同的返回栈中入栈时必然是创建了新的实例。而使用了singleInstance模式就可以解决这个问题,在这种模式下会有一个单独的返回栈来管理这个活动,不管是哪个应用程序来访问这个活动,都共用的同一个返回栈,也就解决了共享活动实例的问题。它独占一个task会每次都被重用 ,其他Activity不能存在那个Task里,如果它启动了一个新Activity那么新的Activity就会到其他的task里运行。
那四个启动模式在哪个所属Task呢,一般情况下standard和 single Top的activity 的目标Task和收到的Intent的发送者在同一个task内,谁调用它它就跟谁在同一个Task中,除非Intent包括参数FLAG_ACTIVITY_NEW_TASK,如果具备了该参数会启动到其他task中。singleTask和singleInstance总是把要启动的activity作为一个task的根元素不会被启动其他的task里。
standard和singleTop可以被多次实例化,并且可以存在于不同的task中。singleTask和singleInstance则限制只生成一个实例
Activity的启动模式受启动Activity的Intent对象设置的Flag和manifest文件中Activity的元素的特性值交互控制。
核心的Intent Flag有:
1.FLAG_ACTIVITY_NEW_TASK:使用一个新的task来启动Activity,一般用在service中启动Activity的场景,因为service不存在Activity栈
2.FLAG_ACTIVITY_CLEAR_TOP:类似“singleTop”
3.FLAG_ACTIVITY_RESET_TASK_NO_HISTORY:使用这种模式启动Activity,当该Activity启动其他的Activity该Activity就会消失不会保留在Task中
4.FLAG_ACTIVITY_SINGLE_TOP:类似“singleTask”
核心的特性有:
clearTaskOnLaunch:每次返回该Activity时都将该Activity之上的所有Activity清除,通过这个属性可以让Task每次在初始化时都只有这一个Activity。
alwaysRetainTaskState:如果将Activity的这个属性设置成true,那么这个Task就不接受任何清除命令,一只保持当前Task的状态。
finishOnTaskLaunch:作用在自己身上通过这个属性,当离开的这个Activity所在的task,当用户在返回时该Activity就会被finish掉。
Activity的隐式启动和显示启动
(1)Activity的显示启动:有两种方式,第一种方式setClass(上下文,类名.class),第二种方式setClassName(应用报名,类名的全路径名)
(2)Activity的隐式启动:当隐式意图启动activity时,此时系统会遍历所有应用的清单文件,寻找是否存在与隐式intent匹配的intentFilter,如果找到就会把intentFilter对应的Activity启动,如果找不到就会抛出异常ActivityNotFoundException异常,此时在启动这个activity就要调用setActivity(),setData()进行匹配。
启动同一应用的activity使用显示启动方式效率高,启动不同应用的activity使用隐式的效率高。
Activity的相关
Activity之间通过Intent进行通信,在Intent的中有最重要的两个部分:动作和动作对应的数据,常用的动作类型:MAIN、VIEW、PICK、EDIT等,而动作对应的数据则是以URI的形式进行表示,与之有关系的一个类叫做IntentFiler,相对于Intent是一个有效的做某事的请求,一个IntentFilter则用于描述一个Activity能够操纵哪些Intent,一个Activity要显示一个数据的时候需要声明一个IntentFliter,这个IntentFilter要知道怎么去处理VIEW动作和表示一个人的URI。IntentFilter需要在AndroidManifest.xml中定义。
通过解析各种Intent从一个屏幕导航到另一个屏幕是很简单的,当向前导航的时候Activity会调用startActivity(Intent myIntent),然后系统会按照IntentFiler中查找,找到最匹配myIntent的Intent对应的Activity,新的Activity接收到myIntent的通知后开始运行。startActivity(Intent myintent)这个机制的好处在于能够使Activity重复利用从其他组件中以Intent的形式产生的请求,可以在任何时候被一个具有相同IntentFilter的新的Activity取代。
而且Activity的构成不是一个Activity对象加上一个布局文件那么简单,在Activity和开发人员之间还隔了两层,实际上视图会被设置一个window类,window类里有一个DecorView,这个就是整个窗口的顶级视图,开发人员设置的布局会被设置到这个DecorView的mContentParent布局中,这样用户界面就被添加到了系统布局中。
IntentFilter的匹配规则
它的过滤信息有action、category、data,为了匹配过滤列表,需要同时匹配过滤列表里的action、category、data信息,一个过滤列表里的action、category、data可以有多个,所有的action、category、data分别构成不同的类别,同一类别的信息共同约束当前类别的匹配过程,只有完全匹配才能成功启动目标Activity,一个Actovity可以有多个IntentFilter,一个Intent只能匹配任何一组IntentFilter即可成功启动对应的Activity
(1)action匹配规则:只要Intent中的action能够和过滤规则中的任何一个action相同即可匹配成功,action匹配区分大小写。
(2)category匹配规则:Intent中如果有category那么所有的category都必须和过滤规则中的其中一个category相同,如果没有category的话那么就是默认的category,即android.intent.category.DEFAULT,所以为了Activity能够接收隐式调用,配置多个category的时候必须加上默认的category。
(3)data匹配规则:Intent中必须含有data数据,如果要指定完整的data,必须要调用setDataAndType方法,不能先调用setData然后调用setType,因为这两个方法会彼此清除对方的值,并且data数据能够完全匹配过滤规则中的某一个data。主要由mimeType和URI组成,其中mimeType代表媒体类型,而URI的结构也复杂,大致如下:://:/[]|[]|[pathPattern],scheme、host、port分别表示URI的模式、主机名和端口号,其中如果scheme或者host未指定那么URI就无效。path、pathPattern、pathPrefix都是表示路径信息,其中path表示完整的路径信息,pathPrefix表示路径的前缀信息;pathPattern表示完整的路径,但是它里面包含了通配符(*)。
如何判断是否有Activity能够匹配我们的隐式Intent?
(1)PackageManager的resolveActivity方法或者Intent的resolveActivity方法:如果找不到就会返回null
(2)PackageManager的queryIntentActivities方法:它返回所有成功匹配的Activity信息
针对Service和BroadcastReceiver等组件,PackageManager同样提供了类似的方法去获取成功匹配的组件信息,例如queryIntentServices、queryBroadcastReceivers等方法
Activity间的数据传递
在Activity之间可以进行数据传递不管是正向传递还是回传,都是通过intent来进行传递的。可以传递八种基本数据类型、数组、字符串、实现的序列化接口的类的对象以及实现了Parcelable接口的对象数组、Bundle对象、四种类型的ArraryList()<>(四种类型分别是String、Integer、charsequence和继承parcelable的对象)
(1)使用Intent正向传递数据:即从ActivityA跳到ActivityB,此时通过intent将数据从A带到B此时就叫做数据传递。
(2)使用Intent数据回传:当从actiityA跳到activityB时,然后从activityB返回到actiityA,而且需要actiivtyB向activityA回传一些数据,此时就要进行数据回传 在进行数据会传递的时候,还是通过intent的五种携带数据的方式进行携带数据,总体来说是这样的:
从A跳转到B时,要使用startActivityForResult(int requestCode,Intent intent)方法--其中requestCode是用于区分将来会传递的数据来自于哪个activity中,然后在A的onActivityResult(int ResultCode,int resultCodeIntent intent)方法中进行接受回传的数据,在B中的如果要回传数据的话,直接调用setResult(int resultCode,Intent intent),将数据直接封装在intent中,然后设置resultCode来区分是该activity中哪种条件下的数据,因为一个activity有可能根据条件的不同要回传的数也不同。
Activity的横竖屏之间的转换
在设置方向时有两种方式:
第一种是在文件中配置activity的属性android:screenOrientation=“portrait”
<activity android:name=".ThirdActivity"
android:screenOrientation="portrait"/>
第二种是在代码中设置横屏竖屏:
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);//这是设置竖屏
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);//这是设置横屏
但是在横竖屏切换的时候,会触发生命周期的各种方法onPause()、onStop()、onDestroy()、onCreate()、onStart()、onResume()等方法,所以想要在横竖屏之间进行数据的保存与传递的时候应该在onStop()方法中进行保存数据,在onDestroy()方法中进行释放资源。
但是有种情况在理论上可以通过在清单文件中配置一些activity属性,从而让Actiivty在进行横竖屏转换的时候不走该activity的生命周期方法(包括onSaveInstanceState()和onRestoreInstanceState()),而是走一个特定的方法onConfigurationChanged(Configuration newConfig)在清单文件中的配置如下:
<activity android:name=".MainActivity"
android:configChanges="orientation|screenSize"/>
横竖屏之间的屏幕适配:当一个actiity在进行横竖屏之间进行切换的时候,此时该activity会使用不同的xml布局文件,当我们的项目中包含layout_land的时候,此时当横屏的时候,就会走该文件夹下的相应的布局文件,如果项目文件中不包含这个layout_land的时候,此时就是使用原来的xml布局文件。而且在layout和layout_land中,相应的xml布局文件的名字必须是一样的。