记得2010年开始接触Android(Android 1.5),使用Eclipse开发,学完JDK5新特性之后,工作中其实用的是Java6,甚至身边都没有几个人用的是Android手机,当时HTC正火爆,一台便宜的G3都要三千左右的样子,对于刚毕业步入社会的童鞋来说绝对是奢侈品,开发全靠模拟器来做(苦逼脸)。

一、初步认识

学Android接触最早的就是Activity了,四大组件中相对来说也是使用最频繁的组件。毕竟绝大部分的App都是要和用户交互的(极少数特殊的App,例如窃听类的牛虻软件,不需要界面,甚至桌面图标也没有)。Activity简单理解就是和用户交互的、可以看得到摸得着的界面喽。其实Activity本身是没有界面的,所以Activity类创建了一个窗口,开发人员可以通过setContentView(View)接口把UI放到Activity创建的窗口上,当Activity指向全屏窗口时,也可以用其他方式实现:作为漂浮窗口(通过WindowIsFloating的主题集合),或者嵌入到其他的Activity(使用ActivityGroup)。Activity是单独的,用于处理用户操作。几乎所有的Activity都要和用户打交道,所以出去面试Android的职位必须会问到Activity相关的内容。

二、生命周期

去看Google官方的API文档,会看到提供的一张官方图,如下:

2.1 常用的生命周期方法

1.onCreate()

当活动第一次启动的时候,触发该方法,可以在此时完成活动的初始化工作。 onCreate 方法有一个参数,该参数可以为空( null ),也可以是之前调用 onSaveInstanceState ()方法保存的状态信息。

2.onStart()

该方法的触发表示所属活动将被展现给用户,“用户可见不可交互”的状态。

3.onResume()

当一个活动和用户发生交互的时候,触发该方法(在Activity栈系统通过栈的方式管理这些Activity,即当前Activity在栈的最上端,运行完弹出栈,则回到上一个Activity)。

4.onPause()

当一个正在前台运行的活动因为其他的活动需要前台运行而转入后台运行的时候,触发该方法。这时候需要将活动的状态持久化、保存起来,比如正在编辑的数据库记录等。

5.onStop()

当一个活动不再需要展示给用户的时候,触发该方法。如果内存紧张,系统会直接结束这个活动,而不会触发 onStop 方法。 所以保存状态信息是应该在onPause时做,而不是onStop时做。活动如果没有在前台运行,都将被停止或者Linux管理进程为了给新的活动预留足够的存储空间而随时结束这些活动。因此对于开发者来说,在设计应用程序的时候,必须时刻牢记这一原则。在一些情况下,onPause方法或许是活动触发的最后的方法,因此开发者需要在这个时候保存需要保存的信息。

6.onRestart()

当处于停止状态的活动需要再次展现给用户的时候,触发该方法。

7.onDestroy()

当活动销毁的时候,触发该方法。和 onStop 方法一样,如果内存紧张,系统会直接结束这个活动而不会触发该方法。

8.onSaveInstanceState()

系统调用该方法,允许活动保存之前的状态,比如说在一串字符串中的光标所处的位置等。 通常情况下,开发者不需要重写覆盖该方法,在默认的实现中,已经提供了自动保存活动所涉及到的用户界面组件的所有状态信息。

2.2一些操作对应的生命周期流程

一个Activity的启动顺序:

onCreate()——>onStart()——>onResume()

当另一个Activity启动时:

第一个Activity onPause()——>第二个Activity onCreate()——>onStart()——>onResume() ——>第一个Activity onStop()
当前Activity只有执行完onPause(),才会执行新Activity的onResume()。

当返回到第一个Activity时:

第二个Activity onPause() ——> 第一个Activity onRestart()——>onStart()——>onResume() ——>第二个Activity onStop()——>onDestroy()

一个Activity的销毁顺序:

(情况一)onPause()——>< Process Killed >
(情况二)onPause()——>onStop()——>< Process Killed >
(情况三)onPause()——>onStop()——>onDestroy()

用户打开新的Activity或者Home键切换到桌面:

onPause()——>onStop() ,如果新的Activity设置了透明主题,不会回调onStop()

用户按下Back键返回:

onPause()——>onStop()——>onDestroy()

Activity在屏幕旋转时的生命周期:

android:configChanges 切横屏 切竖屏 备注
不设置 1次 2次
orientation 1次 1次
orientation keyboardHidden 0次 0次 只执行onConfigurationChanged

生命周期小结

对于整个生命周期来说:

  • onCreate和onDestroy是一对,标志着Activity的创建和销毁,并且只可能调用一次。
  • 从Activity的是否可见来说,onStart和onStop是一对,随着用户的操作或者屏幕点亮熄灭,可能被调用多次。
  • 从Activity是否在前台来说,onResume和onPause是一对,随着用户的操作或者屏幕点亮熄灭,可能被调用多次。

【补充:题外话——android中进程的优先级?】
1 前台进程 > 2 可见进程 > 3 服务进程 > 4 后台进程 > 5 空进程

三、Activity之间的通信

3.1使用Intent

在 Android 中,不同的 Activity 实例可能运行在一个进程中,也可能运行在不同的进程中。因此我们需要一种特别的机制帮助我们在 Activity 之间传递消息。Android 中通过 Intent 对象来表示一条消息,一个 Intent 对象不仅包含有这个消息的目的地,还可以包含消息的内容,这好比一封 Email,其中不仅应该包含收件地址,还可以包含具体的内容。对于一个 Intent 对象,消息“目的地”是必须的,而内容则是可选项。

Intent负责对操作的动作、动作涉及数据、附加数据进行描述,Android则根据此Intent的描述,负责找到对应的组件,将 Intent传递给调用的组件,并完成组件的调用。因此,Intent在这里起着一个媒体中介的作用,专门提供组件互相调用的相关信息,实现调用者与被调用者之间的解耦。

在应用中,我们可以以两种形式来使用Intent:

  • 直接Intent:指定了component属性的Intent(调用setComponent(ComponentName)或者setClass(Context, Class)来指定)。通过指定具体的组件类,通知应用启动对应的组件。

  • 间接Intent:没有指定comonent属性的Intent。这些Intent需要包含足够的信息,这样系统才能根据这些信息,在在所有的可用组件中,确定满足此Intent的组件。

对于直接Intent,Android不需要去做解析,因为目标组件已经很明确。

Android需要解析的是那些间接Intent,通过解析,将 Intent映射给可以处理此Intent的Activity、IntentReceiver或Service。Intent解析机制主要是通过查找已注册在AndroidManifest.xml中的所有IntentFilter及其中定义的Intent,最终找到匹配的Intent。

这里给出一个易道用车的面试题(2016.09)

说说以下两种方式有何区别:
(1)intent.putExtra(“key”, “value”);
(2)Bundle b = new Bundle();
b.putString(“key”, “value”);
intent.putExtras(b);

也就是问:请说出intent.putExtra() 和 bundle.putExtra()的区别?

我们先看一下Intent对象相应函数的源代码:

public Intent putExtra(String name, String value) {      if (mExtras == null) {          mExtras = new Bundle();      }      mExtras.putString(name, value);      return this;  }  

可以知道,Intent的相关操作的实现是基于Bundle的,Bundle操作数据相对于Intent有哪些优势呢?
举个例子,如果我要从A界面跳转到B界面和C界面,那么我要写写两个Intent,如果传的数据相同,我两个Intent都要添加,但是如果我用Bundle,直接一次性包装就可以了。
再有,如果A界面要传值给B界面,再从B界面传值到C界面,你可以想象用Intent是多么的麻烦了,但是用Bundle就很简洁,而且还可以在B界面中添加新的信息。

当然如果传的数据非常之多而且很复杂,用这两种方式显然是不适合的,这时候我们可以使用可序列化的结构类,详情参考之前的文章 Android中的Serializable和Parcelable。

3.2使用SharedPreferences

3.3其他方式

Android 提供了包括 SharedPreferences 在内的很多种数据存贮方式,比如 SQLite,文件等,程序员可以通过这些 API 实现 Activity 之间的数据交换。如果必要,我们还可以使用 IPC 方式。

四、Activity 的 Intent Filter

  Intent Filter 描述了一个组件愿意接收什么样的 Intent 对象,Android 将其抽象为 android.content.IntentFilter 类。在 Android 的 AndroidManifest.xml 配置文件中可以通过 节点为一个 Activity 指定其 Intent Filter,以便告诉系统该 Activity 可以响应什么类型的 Intent。

  当使用 startActivity(intent) 来启动另外一个 Activity 时,如果直接指定 intent 对象的 Component 属性,那么 Activity Manager 将试图启动其 Component 属性指定的 Activity。否则 Android 将通过 Intent 的其它属性从安装在系统中的所有 Activity 中查找与之最匹配的一个启动,如果没有找到合适的 Activity,应用程序会得到一个系统抛出的异常。这个匹配的过程如下:

五、Activity的栈式管理

  Android针对Activity的管理使用的是栈,就是说某一个时刻只有一个Activity处在栈顶,当这个Activity被销毁后,下面的Activity才有可能浮到栈顶,或者有一个新的Activity被创建出来,则旧的Activity就被压栈沉下去了。Activity是Android程序的表现层。程序的每一个显示屏幕就是一个Activity。正在运行的Activity处在栈的最顶端,它是运行状态的。

当在程序中调用 Activity.finish()方法时,结果和用户按下 BACK 键一样:它告诉 Activity Manager该Activity实例可以被“回收”。随后 Activity Manager 激活处于栈第二层的 Activity ,把原 Activity 压入到栈的第二层,从 Running 状态转到 Paused 状态。

六、Activity的加载/启动模式

standard、singleTop、singleTask、singleInstance(其中前两个是一组、后两个是一组),默认为standard

  • standard:(默认|标准)
    就是intent将发送给新的实例,所以每次跳转都会生成新的activity。

    我们平时直接创建的Activity都是这种模式的Activity,这种模式的Activity的特点是:只要你创建了Activity实例,一旦激活该Activity,则会向任务栈中加入新创建的实例,退出Activity则会在任务栈中销毁该实例。谁启动了Activity,这个Activity就运行在启动他的任务栈中。

    注:不要以ApplicationContext启动,否则会报错:
    AndroidRuntime: android.util.AndroidRuntimeException: Calling startActivity from outside of
    an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag .Is this really what you want? 这是因为standard模式的Activity默认会进入启动它的Activity所属的任务栈中,但是由于非Activity类型的Context(如ApplicationContext)没有所谓的任务栈,所以出现了这个问题。解决方法就是为待启动Activity指定FLAG_ACTIVITY_NEW_TASK标记位,这样启动的时候就会创建一个新的任务栈,这个时候待启动的Activity实际是以singleTask模式启动的。

  • singleTop:(栈顶复用)
    也是发送新的实例,但不同standard的一点是,在请求的Activity正好位于栈顶时(配置成singleTop的Activity),不会构造新的实例。

    这种模式会考虑当前要激活的Activity实例在任务栈中是否正处于栈顶,如果处于栈顶则无需重新创建新的实例,同时它的onNewIntent()方法会被回调,会重用已存在的实例,需要注意的是,这个Activity的onCreate和onStart不会被系统调用;否则会在任务栈中创建新的实例。

    作用: 避免一个糟糕的用户体验,如果这个界面已经被打开且在任务栈的栈顶,就不会重复开启了

  • singleTask:(栈内复用)
    和后面的singleInstance都只创建一个实例,当intent到来,需要创建设置为singleTask的Activity的时候,系统会检查栈里面是否已经有该Activity的实例。如果有直接将intent发送给它。

    这是一种单实例模式,如果任务栈中存在该模式的Activity实例,则把栈中该实例以上的Activity实例全部移除,调用该实例的onNewIntent()方法重用该Activity,使该实例处於栈顶位置,否则就重新创建一个新的Activity实例。可以用来退出整个应用。将主Activity设为SingTask模式,然后在要退出的Activity中转到主Activity,然后重写主Activity的onNewIntent函数,并在函数中加上一句finish。相当于条用 onNewIntent()+ClearTop。

    • 设置了”singleTask”启动模式的Activity,它在启动的时候,会先在系统中查找属性值affinity等于它的属性值taskAffinity的任务存在;如果存在这样的任务,它就会在这个任务中启动,否则就会在新任务中启动。因此,如果我们想要设置了”singleTask”启动模式的Activity在新的任务中启动,就要为它设置一个独立的taskAffinity属性值。
    • 如果设置了”singleTask”启动模式的Activity不是在新的任务中启动时,它会在已有的任务中查看是否已经存在相应的Activity实例,如果存在,就会把位于这个Activity实例上面的Activity全部结束掉,即最终这个Activity实例会位于任务的堆栈顶端中。
  • singleInstance:(单实例)
    加强版的singleTask模式,除了具备singleTask的所有特性外,还加强了一点,具有此模式的Activity只能单独位于一个任务栈中。当该模式Activity实例在任务栈中创建后,只要该实例还在任务栈中,即只要激活的是该类型的Activity,都会通过调用实例的onNewIntent()方法重用该Activity,此时使用的都是同一个Activity实例,它都会处于任务栈的栈顶。此模式一般用于加载较慢的,比较耗性能且不需要每次都重新创建的Activity。

    onNewIntent()—->onResart()——>onStart()—–>onResume()

首先说明一下task这个概念,Task可以认为是一个栈,可放入多个Activity。比如启动一个应用,那么Android就创建了一个Task,然后启动这个应用的入口Activity,那在它的界面上调用其他的Activity也只是在这个task里面。那如果在多个task中共享一个Activity的话怎么办呢。举个例来说,如果开启一个导游服务类的应用程序,里面有个Activity是开启GOOGLE地图的,当按下home键退回到主菜单又启动GOOGLE地图的应用时,显示的就是刚才的地图,实际上是同一个Activity,实际上这就引入了singleInstance。singleInstance模式就是将该Activity单独放入一个栈中,这样这个栈中只有这一个Activity,不同应用的intent都由这个Activity接收和展示,这样就做到了共享。当然前提是这些应用都没有被销毁,所以刚才是按下的HOME键,如果按下了返回键,则无效。

七、Activity的跳转

  • Activity跳转,无返回结果
    这是最简单的Activity跳转方式。从一个Activity启动另一个Activity,直接startActivity(new Intent(当前Activity.this, 下一Activity.class))。
  • Activity跳转,返回数据/结果
    需要返回数据或结果的,则使用startActivityForResult (Intent intent, int requestCode),requestCode的值是自定义的,用于识别跳转的目标Activity。跳转的目标Activity所要做的就是返回数据/结果,setResult(int resultCode)只返回结果不带数据,或者setResult(int resultCode, Intent data)两者都返回!而接收返回的数据/结果的处理函数是onActivityResult(int requestCode, int resultCode, Intent data),这里的requestCode就是startActivityForResult的requestCode,resultCode就是setResult里面的resultCode,返回的数据在data里面。

    ** 注意,在setResult后,要调用finish()销毁当前的Activity,否则无法返回到原来的Activity,就无法执行原来Activity的onActivityResult函数,看到当前的Activity没反应。

八、Activity之启动方式

来自 浅谈Activity几种不同的启动方式

1.在“Home”下点击图标,启动应用程序的首个Activity。

我们称之为主Activity,这是最常见的启动方式,而且代表程序具备独立的运行条件。通常会在Manifest中指定某个Activity的android.intent.category属性为Launcher。 (实际上这也是隐式启动Activity的其中一个)

2.在程序中启动Activity。

应用(startActivity() 或者startActivityForResult())启动特定的Activity,或者当程序需要处理一些特定的功能,而其他应用程序中已经具备了这种功能的Activity时,我们可以通过intent-filter来启动Activity

3.强制程序仅以被其它程序调用的方式启动(没有直接的启动入口):

在系统中有相当一部分的应用程序需要有严格的运行条件(Context),其无法以Stand alone的方式独立运行于进程中。具备这种特性的Application大体上分为两种:其一,带有特定返回值的应用程序。parent出于当前操作需要,通过启动其它Application来获得某些特定的资源。例如:输入法或者文件资源选择器等。另外一种情况,经常被调用来修改系统设置或者提供单一有特定目的的操作。这样的Application无需在Home中提供快捷启动方式,其仅仅被其它某些程序在特定的需求下来满足需求。例如:更换系统铃音程序。

4.在一个Application中包含有多个Mian Activities,并且各自具备有独立的启动入口:

很多开发者都习惯于默认的开发配置,一个Application仅仅包含一个独立的应用。然后在特定的需求下可以打破这个限定,系统允许开发者将多个Main Activities应用捆绑在同一个Application中,而且这些Main Activities可以在Home中有独立的启动快捷方式。然而有非常重要的一点需要特别强调,这种方式并不提倡在任何情况下采用,一般当两个应用程序需要调用相同的系统资源或者包含有大部分相同功能的应用时,才可以考虑采用这种方式来简化用户的安装过程(另外一个角度来看,起到优化系统资源的目的)。技术上需要注意为不同的Main Activity定义不同的Task affinity。例如:Camera和Camcorder,它们共同使用摄像头,而且同样采用Gallery作为资源管理器,他们的Task Affinity分别设置为:”com.android.camera” 和 “com.android.videocamera”。

5.以Widget的方式体现Activity的应用价值:

严格意义上来看,这的确算得上是一种启动方式,某些Application将部分常用的功能以Widget的形式在Home或者其它Application中被引用。

隐式启动

(1) 根据Action和Category信息来进行匹配

在Manifest.xml中注册该Activity

<activity android:name=".TestActivity"  android:label="TestActivity">    <intent-filter >        <action android:name="cn.xiaoyao.test"/>        <category android:name="android.intent.category.DEFAULT"/>    intent-filter>activity>

在MainActivity.java里启动该Activity:

Intent intent = new Intent();intent.setAction( "cn.xiaoyao.test");//设置intent的Action属性值intent.addCategory(Intent.CATEGORY_DEFAULT);//不加这行也行,因为这个值默认就是Intent.CATEGORY_DEFAULTstartActivity( intent );

(2) 根据Action和Data信息进行匹配

在Manifest.xml中注册该Activity

<activity android:name=".TestActivity"    android:label="@string/testactivity">    <intent-filter>        <action android:name="cn.xiaoyao.test">action>        <category android:name="android.intent.category.DEFAULT">category>        <data android:scheme="xiaoyao">data>    intent-filter> activity>

如上注册信息,合法的Uri的写法如下:

 //Uri uri = Uri.parse("xiaoyao");//不行 //Uri uri = Uri.parse("xiaoyao://");//可以 //Uri uri = Uri.parse("xiaoyao:");//可以 //Uri uri = Uri.parse("xiaoyao://www.google.com/getDetails?id=123");//可以

在MainActivity.java里启动该Activity:

Intent intent = new Intent();intent.setAction("cn.xiaoyao.test");//仅有data不能匹配,所以要设置Action属性intent.addCategory(Intent.CATEGORY_DEFAULT);//可去除intent.setData(uri);//设置data属性startActivity(in);

(3) 根据action和data的mimeType属性匹配

在Manifest.xml中注册该Activity

<activity android:name=".TestActivity" android:label="@string/testactivity">    <intent-filter>        <action android:name="android.intent.action.VIEW">action>        <data android:mimeType="vnd.android.cursor.dir/vnd.google.note" />        <category android:name="android.intent.category.DEFAULT">category>    intent-filter>activity>

在MainActivity.java里启动该Activity:

Intent intent = new Intent();intent .setAction("android.intent.action.VIEW");intent .addCategory(Intent.CATEGORY_DEFAULT);//可去掉intent.setType("vnd.android.cursor.dir/vnd.google.note");//要设置Data的MIMEType属性startActivity(intent );

注意:一个Activity中可以包括多个intent-filter属性,intent-filter属性中也可以包含多个data标签对,只需要有一个满足便可以匹配成功

如果匹配不到合适的Activity启动时,会抛出异常给用户一个提示“应用程序意外停止”!

更多相关文章

  1. 带你从源代码详细分析View的绘制过程
  2. 【Android(安卓)学习记录】Fragment注入漏洞
  3. Android避免多次弹出Toast提示
  4. Android(安卓)JSBridge的原理与实现
  5. Android面试题精选,自己收藏下
  6. Android中的soundpool小结
  7. ubuntu下adb调试android找不到设备的解决方法
  8. Android(安卓)WebView 详解
  9. Android(安卓)之 数据存储方式

随机推荐

  1. Linux下mysql异地自动备份的方法
  2. mysql 使用存储过程实现树节点的获取方法
  3. MySQL横纵表相互转化操作实现方法
  4. centos7下安装mysql6初始化安装密码的方
  5. 关于MYSQL 你需要知道的数据类型和操作数
  6. Mysql索引类型与基本用法实例分析
  7. MySql数据库基础知识点总结
  8. MySQL自动停机的问题处理实战记录
  9. MySQL5.6.40在CentOS7 64下安装过程详解
  10. MySQL最佳实践之分区表基本类型