Android里边有四大组件的概念:即Activity、Service、Broadcast Receiver、Content Provider,而这四大组件中相对于iOS来讲 类似于Activity 对应Controller、 Broadcast Receiver 类似于 NSNotificationCenter,另两个Service 和 Content Provider 在iOS并没有可以与之对应的组件,平台的差异性由此可以体现出来。
本文记录Activity的相关知识以及与iOS的某些方面的异同:
一 生命周期:
onCreate函数 ():函数是你进行初始化的地方,这个也是执行setContentView(View)函数的地方,setContentView(View)函数可以传入一个由XML编制的UI界面,可以使UI和具体实现完全分离。
onStart函数:该方法在 onCreate() 方法之后被调用, 当Activity被显示到屏幕上的时候调用此方法.
onResume()函数:在 Activity 从 Pause 状态转换到 Active 状态时被调用,,也就是能够获得用户的焦点之前调用此方法.
onRestart()函数:当Activity被停止后又被再次启动之前调用此方法.接着将调用onStart()方法.
onPause()函数:当第一个Activity通过Intent启动第二个Activity的时候,将调用第一个Activity的onPause()方法.然后调用第二个Activity的onCreate(),onStart(),onResume()方法,接着调用第一个Activity的onStop()方法.如果Activity重新获得焦点,则将调用onResume()方法;如果此Activity进入用户不可见状态,那么将调用onStop()方法.
onStop()函数:当第一个Activity被第二个Activity完全覆盖,或者被销毁的时候会调用此方法.如果此Activity还会与用户进行交互,将调用onRestart方法();如果此Activity将被销毁,那么将调用onDestroy()方法.
onDestroy()函数:Activity被销毁之前调用此方法.或者是调用finish()方法结束Activity的时候调用此方法.可以在此方法中进行收尾工作,比如释放资源等.
注意:重写某个Activity的这些回调方法的时候需要首先在第一行调用基类Activity的相应的回调方法.比如super.onCreate(),super.onStart()等等.
而iOS的生命周期如下,基本是一样的:
- alloc: 创建对象,分配空间
- init (initWithNibName): 初始化对象,初始化数据
- loadView: 从nib载入视图 ,通常这一步不需要去干涉。除非你没有使用xib文件创建视图
- viewDidLoad: 载入完成,可以进行自定义数据以及动态创建其他控件
- viewWillAppear: 视图将出现在屏幕之前,马上这个视图就会被展现在屏幕上了
- viewDidAppear 视图已在屏幕上渲染完成
当一个视图被移除屏幕并且销毁的时候的执行顺序,这个顺序差不多和上面的相反
- viewWillDisappear 视图将被从屏幕上移除之前执行
- viewDidDisappear 视图已经被从屏幕上移除,用户看不到这个视图了
- dealloc 视图被销毁,此处需要对你在init和viewDidLoad中创建的对象进行释放
可以看出 其实Android和iOS的生命周期可以说是一一对应的
二 通信
通信的方式有很多种,例如:Intent、静态变量、SharedPreference等,此处暂时只讲Intent的方式通信,类比iOS 的
- Android中的Activity依靠Intent的通信通信方式如下:
Intent是Android四大组件(Activity、Service、BroadcastReceiver、ContentProvider)之间通信的纽带,在Intent中携带数据也是四大组件之间数据通信最常用、最普通的方式。常规写法如下:
//创建用于封装数据的Bundle对象
Bundle bundle = new Bundle();
bundle.putString("name", "奥卡姆");
bundle.putInt("age", 25);
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
//将Bundle对象嵌入Intent中
intent.putExtras(bundle);
下边的为比较简洁的方式
//创建Intent对象
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
//程序自动创建Bundle,然后将对Intent添加的数据装载在Bundle中,对用户透明
intent.putExtra("name", "奥卡姆");
intent.putExtra("age", 25);
startActivity(intent);
然后再第二个Activity中获取数据:
//intent要用this的getIntent()获取
Intent intent = getIntent();
//用intent.getXXXExtra("key-name")或是intent.getXXXExtra("key-name", default-value)获取值
String name = intent.getStringExtra("key1");
int age = intent.getIntExtra("key2", 0);
-
iOS中的Controller(其实View也是类似)通信方式如下:
iOS的Controller中的通信方式就不依赖于另一个媒介了,可以实例化第二个Controller对象的时候,可以获取到属性直接赋值即可,非常方便。如下
// 实例化对象
let vc = SecondViewContoller()
// 给该对象的属性赋值
vc.str="second String"
// 跳转
self.navigationController?.pushViewController(vc, animated: true);
三 注册(Android特有)
所有的活动即activity必须要在AndroidManifest.xml中进行注册才能生效
在 application标签下声明添加
<activity
android:name=".MainActivity"
android:label="This is MainActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".SecondActivity"
android:label="This is SecondActivity" >
</activity>
name:指定注册的活动,为
com.exampleactivitytest.MainActivity
的缩写,由于<manifest>标签中已经通过package
属性指明了程序的包名是com.example.activitytest
,所以这里就简略写了.label: 指定活动中标题栏的内容
<intent-filter>
在<activity>
标签的内部的<intent-filter>
标签下的
<action android:name= "android.intent.action.MAIN" />
和<category android:name="android.intent.category.LAUNCHER" />
两句声明。
用于把该Activity
作为本程序的主活动,即点击应用图标后首先打开的activity
若不是主活动,不需配置<intent-filter>
标签里的内容
若应用程序中没有声明任何一个活动作为主活动,该仍可正常安装的,只是无法在启动器中看到或者打开这个程序。一般都是作为第三方服务供其他的应用在内部进行调用的,类似某些应用快捷支付服务。
Q: 为什么Activity都必须要在AndroidManifest文件中注册呢
A: 官方回答:每个应用的根目录中都必须包含一个 AndroidManifest.xml
文件(且文件名精确无误)。 清单文件向 Android 系统提供应用的必要信息,系统必须具有这些信息方可运行应用的任何代码。此外,清单文件还可执行以下操作:
- 为应用的 Java 软件包命名。软件包名称充当应用的唯一标识符。
- 描述应用的各个组件,包括构成应用的 Activity、服务、广播接收器和内容提供程序。它还为实现每个组件的类命名并发布其功能,例如它们可以处理的 Intent 消息。这些声明向 Android 系统告知有关组件以及可以启动这些组件的条件的信息。
- 确定托管应用组件的进程。
- 声明应用必须具备哪些权限才能访问 API 中受保护的部分并与其他应用交互。还声明其他应用与该应用组件交互所需具备的权限
- 列出 Instrumentation 类,这些类可在应用运行时提供分析和其他信息。这些声明只会在应用处于开发阶段时出现在清单中,在应用发布之前将移除。
- 声明应用所需的最低 Android API 级别
- 列出应用必须链接到的库
四 Activity的任务栈
应用内的Activity是被任务栈Task来管理的,一个Task中的Activity可以来自不同的应用,同一个应用的Activity也可能不在同一个Task中。默认情况下,任务栈依据栈的后进先出原则管理Activity,但是Activity可以设置一些“特权”打破默认的规则,主要是通过在AndroidManifest文件中的属性android:launchMode或者通过Intent的flag来设置。
standard:默认的启动模式,该模式下会生成一个新的Activity,同时将该Activity实例压入到栈中(不管该Activity是否已经存在在Task栈中,都是采用new操作)。例如: 栈中顺序是A B C D ,此时D通过Intent跳转到A,那么栈中结构就变成 A B C D A,点击返回按钮的 显示顺序是 D C B A,依次摧毁。
singleTop:在singleTop模式下,如果当前Activity D位于栈顶,此时通过Intent跳转到它本身的Activity(即D),那么不会重新创建一个新的D实例(走onNewIntent()),所以栈中的结构依旧为A B C D,如果跳转到B,那么由于B不处于栈顶,所以会新建一个B实例并压入到栈中,结构就变成了A B C D B。应用实例:三条推送,点进去都是一个activity。
singleTask: 在singleTask模式下,Task栈中只能有一个对应Activity的实例。例如:现在栈的结构为A B C D,此时D通过Intent跳转到B(走onNewIntent()),则栈的结构变成了:A B。其中的C和D被栈弹出销毁了,也就是说位于B之上的实例都被销毁了。如果系统已经存在一个实例,系统就会将请求发送到这个实例上,但这个时候,系统就不会再调用通常情况下我们处理请求数据的onCreate方法,而是调用onNewIntent方法。通常应用于首页,首页肯定得在栈底部,也只能在栈底部。
singleInstance: singleInstance模式下会将打开的Activity压入一个新建的任务栈中。例如:Task栈1中结构为:A B C,C通过Intent跳转到了D(D的启动模式为singleInstance),那么则会新建一个Task 栈2,栈1中结构依旧为A B C,栈2中结构为D,此时屏幕中显示D,之后D通过Intent跳转到D,栈2中不会压入新的D,所以2个栈中的情况没发生改变。如果D跳转到了C,那么就会根据C对应的启动模式在栈1中进行对应的操作,C如果为standard,那么D跳转到C,栈1的结构为A B C C,此时点击返回按钮,还是在C,栈1的结构变为A B C,而不会回到D。
Intent Flag 启动模式
- 1、Intent.FLAG_ACTIVITY_NEW_TASK: 使用一个新的task来启动Activity,一般用在service中启动Activity的场景,因为service中并不存在Activity栈。
- 2、Intent.FLAG_ACTIVITY_SINGLE_TOP : 类似
andoid:launchMode="singleTop"
- 3、Intent.FLAG_ACTIVITY_CLEAR_TOP :类似
andoid:launchMode="singleTask"
- 4、Intent.FLAG_ACTIVITY_NO_HISTORY :使用这种模式启动Activity,当该Activity启动其他Activity后,该Activity就消失了,不会保留在task栈中。例如A B,在B中以这种模式启动C,C再启动D,则当前的task栈变成A B D。
清空任务栈
- 1、clearTaskOnLaunch: 每次返回该Activity时,都将该Activity之上的所有Activity都清除。通过这个属性可以让task每次在初始化的时候都只有这一个Activity。
- 2、finishOnTaskLaunch: clearTaskOnLaunch作用在别的Activity身上,而finishOnTaskLaunch作用在自己身上。通过这个属性,当离开这个Activity所在的task,那么当用户再返回时,该Activity就会被finish掉。
- 3、alwaysRetainTaskState:如果将Activity的这个属性设置为true,那么该Activity所在的task将不接受任何清理命令,一直保持当前task状态,相当于给了task一道”免死金牌”。
五 IntentFilter的匹配模式
-
(1) IntentFilter中的过滤信息有action、category、data;
为了匹配过滤列表,需要同时匹配过滤列表中的action、category、data信息,否则匹配失败。一个过滤列表中的action、category、data可以有多个,所有的action、category、data分别构成不同类别,同一类别的信息共同约束当前类别的匹配过程。只有一个Intent同时匹配action类别、category类别和data类别才算完全匹配,只有完全匹配才能成功启动目标Activity。此外,一个Activity中可以有多个intent-filter,一个Intent只要能匹配任何一组intenf-filter即可成功启动对应的Activity。
<intent-filter>
<action android:name="com.aokamu.a" />
<action android:name="com.aokamu.b" />
<category android:name="com.ankamu.category.a" />
<category android:name="com.ankamu.category.b" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
(2) action匹配规则
只要Intent
中的action
能够和过滤规则中的任何一个action相同即可匹配成功,action
匹配区分大小写。(3) category匹配规则
Intent
中如果有category
那么所有的category
都必须和过滤规则中的其中一个category
相同,如果没有category的话那么就是默认的category
,即android.intent.category.DEFAULT
,所以为了Activity
能够接收隐式调用,配置多个category的时候必须加上默认的category
(4) data匹配规则
data匹配规则:Intent
中必须含有data
数据,并且data
数据能够完全匹配过滤规则中的某一个data。
如果要为Intent
指定完整的data
,必须要调用setDataAndType
方法!
六 一些关于Activity的技巧
- 1、锁定Activity屏幕的运行方向
<activity android:name=".EX01"
android:label="@string/app_name"
android:screenOrientation="portrait">// 竖屏 , 值为 landscape 时为横屏
</activity>
- 2、全屏的Activity
// 设置全屏模式
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
// 去除标题栏
requestWindowFeature(Window.FEATURE_NO_TITLE);
- 3、在Activity的title中加入进度条
// 不明确进度条
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setContentView(R.layout.main);
setProgressBarIndeterminateVisibility(true);
// 明确进度条
requestWindowFeature(Window.FEATURE_PROGRESS);
setContentView(R.layout.main);
setProgress(5000);
安卓相关文章阅读
参考:《Android开发艺术探索》