四大组件概论
如果把四大组件比作MP4中的电影播放系统,那么要保证这个系统有基础功能则:
Android四大组件,除了BroadcastReceiver,其他三种必学在AndroidManifest.xml 文件中注册,BroadcastReceiver既可以在AndroidManifest.xml文件中注册也可以在java代码中注册。在调用方式上Activity、Service、和BroadcastReceiver必须通过Intent调用,而ContentProvider不必。
Activity和View搭配显示布局:
Activity是我们最常见的一个组件,他与View搭配完成了界面的显示。我们初始创建helloworld应用的时候有setContentView(R.layout.activity_main)与一个activity_main.xml搭配来显示初始界面。其实我们还可以这样写:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout layout = new LinearLayout(this);
setContentView(layout);
}
就这样写就可以显示出一个空白界面的布局,不用要activity_main.xml 文件。其实我们分析一下,activity_main.xml里边也是一个LinearLayout布局,其实他们加载的是一样的。
Activity继承关系
java.lang.Object
↳ android.content.Context
↳ android.content.ContextWrapper
↳ android.view.ContextThemeWrapper
↳ android.app.Activity
//直接或间接继承Context、ContextWrapper、ContextThemeWrapper类,可直接调用其方法
Known Direct Subclasses
- AccountAuthenticatorActivity(账户管理界面的Activity),
- ActivityGroup,
- AliasActivity(启动其他Activity结束自己),
- FragmentActivity,
- ListActivity,
- NativeActivity(用c++写android的activity)
Known Indirect Subclasses
- ActionBarActivity,
- AppCompatActivity,
- LauncherActivity(继承ListActivity,类列表界面),
- PreferenceActivity(继承后Activity托管系统读写参数,一般用于系统设置界面),
- TabActivity
AS2.3新建项目我们发现默认继承AppcompatActivity,相比以往,Android5.0之后,supportV7更新包,使用了AppCompatActivity来代替ActionBarActivity,继承Activity会发现没有ActionBar的导航栏,继承ActionBarActivity会发现提示他已经过时了,所以AppCompatActivity是更新后Google推荐使用的,继承Activity和AppCompatActivity区别是Activity没有给你加载ActionBar支持,所以项目创建后AppCompatActivity的包要比Activity的大近10M左右。
Activity的显式配置
在AndroidManifest.xml文件中标签下声明我们的Activity,我们新建一个Activity,AS自动帮我们在下创建标签并声明好命名空间。
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity"
android:theme="@style/AppTheme"
android:launchMode="singleInstance"
android:exported="true"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
intent-filter>
activity>
application>
Activity的生命周期
官方开发文档给的很经典的流程图。要学会查阅官方文档。其中的介绍也很详细标准。
如下代码我们覆写这七个函数加入log标志:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.v("test","onCreate");
}
@Override
protected void onStart() {
super.onStart();
Log.v("test","onStart");
}
@Override
protected void onResume() {
super.onResume();
Log.v("test","onResume");
}
@Override
protected void onPause() {
super.onPause();
Log.v("test","onPause");
}
@Override
protected void onStop() {
super.onStop();
Log.v("test","onStop");
}
@Override
protected void onRestart() {
super.onRestart();
Log.v("test","onRestart");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.v("test","onDestroy");
}
}
启动测试Application:
04-23 16:44:40.513 9241-9241/com.alphathink.myactivity V/test: onCreate
04-23 16:44:40.514 9241-9241/com.alphathink.myactivity V/test: onStart
04-23 16:44:40.514 9241-9241/com.alphathink.myactivity V/test: onResume
点击home键:
04-23 16:46:38.906 9241-9241/com.alphathink.myactivity V/test: onPause
04-23 16:46:39.177 9241-9241/com.alphathink.myactivity V/test: onStop
重新进入Application:
04-23 16:47:32.843 9241-9241/com.alphathink.myactivity V/test: onRestart
04-23 16:47:32.843 9241-9241/com.alphathink.myactivity V/test: onStart
04-23 16:47:32.843 9241-9241/com.alphathink.myactivity V/test: onResume
点击返回键:
04-23 16:47:56.401 9241-9241/com.alphathink.myactivity V/test: onPause
04-23 16:47:56.533 9241-9241/com.alphathink.myactivity V/test: onStop
04-23 16:47:56.533 9241-9241/com.alphathink.myactivity V/test: onDestroy
关于onPause()我们新建一SecondActivity设置Theme样式为:
<activity android:name=".SecondActivity"
android:theme="@style/Theme.AppCompat.Dialog"/>
在MainActivity中设置按钮监听Intent跳转到第二个Activity:
startActivity(new Intent(MainActivity.this,SecondActivity.class));
后台log信息:
04-23 17:14:43.269 4947-4947/com.alphathink.myactivity V/test: onPause
我们看到第二个Activity挡住了第一个,第一个部分可见,所以就有了onPause()暂停状态,其实也是失去了焦点。(注意不要用Dialog测试,这是Activity的生命周期,和Dialog无关,Dialog也是Activity的部分组件)
结合以上文字说明,其实也很好理解的。
onCreate()–>onStart()–>onResume():一气呵成,显示到屏幕
onPause():部分遮挡,但是还是可以看到的
onStop():看不到了,但是有机会重生
onDestroy():看不到了,没有机会重生了
onRestart():刚有机会重生的借此重生了
我们可以看到,当销毁或者后台一个Activity的时候他都会先调用onPause()方法,而不是直接调用onStop()或者onDestory()方法去为之,这也体现了设计的严整性,因为你做这些操作的时候屏幕显示先要经历部分可见的状态再到全部不可见,这是一个过渡操作,只是速度快我们可能不太容易察觉到,并不是直接就销毁掉了。
Activity的存储结构分析
Activity被设计成堆栈的方式来使用,什么意思呢?比如我们使用APP的时候,进入的功能复杂了,会跳来跳去的,其实我们是新开了多个Activity,我们点击返回按钮的时候,又倒序一个一个返回了。这是不是后进先出的栈结构。
结合例子来分析吧:
A、B、C、D是四个Activity,每个Activity中都有个按钮作用是:A–>B–>C–>D–>A(箭头代表Intent跳转的意思)栈就好比一个柱形空水杯:
第一个Activity为启动Activity,后依次点击每个按钮Intent跳转启动下一Activity:
当你点击回退键时,出栈的顺序刚好相反(后进先出),A–>D–>C–>B–>A
其实这是标准下的模式,实际开发中我们肯定有各种各样的需求,比如上边我们的A启动了两次,我们需要如果他有了就不实例化A,保证整个进程里只有一个A的Activity,那么下边我们来了解下其他模式。
Activity的启动模式
standard:标准模式
上边所讲的就是标准模式,也是系统默认的模式,如果我们没有特殊需求就不用生命Activity的启动模式,默认为standard。每启动一个Activity就会新建Activity的实例,不管此Activity是否在栈顶有实例,新建按钮跳转到本身,你点击多少次,他就实例化多少次,当然回退的时候也是一个一个回退出栈,尽管他们都相同。standard模式下的Activity启动会默认加载到启动它的Activity栈下,比如A所在的栈启动B,B就归属为A栈。
singleTop:栈顶复用模式
如果新Activity在栈顶的话就不创建,直接用栈顶的,否则新建。
比如A–>B–>B–>A
在此模式下,B在栈顶,复用之,他的onCreate(),onStart()方法并不会被调用。
点击按钮下看log输出:
04-23 18:16:52.939 5984-5984/com.alphathink.myactivity V/test: onPause
04-23 18:16:52.939 5984-5984/com.alphathink.myactivity V/test: onResume
会调用onPause()和onResume()方法,因为Intent的确执行了跳转但是他发现实例存在,不用创建,但我们点击按钮跳转的瞬间activity还是不察觉的隐藏了一下。
singleTask:栈内复用模式
单任务,就是他不回去创建新任务,就是新的栈。这其实类似于设计模式中的单例模式,如果存在就用,如果不存在就新建,不同的是这里Activity如果在栈中的位置不是栈顶,那么调用他会把他上边的所有Activity都清理了。
比如A–>B–>C–>A
我们可以看到A–>B–>C因为栈中没有相应实例,依次入栈,而C–>A的时候,监测到有实例A就调用它,可他在最底层,所以只好把上边的都清理掉才能用到A。
singleInstance:单实例模式
这个模式和singleTask有点像,区别就是这个模式下创建的Activity独自享有这个栈,别人都不能进。
A–>B
任务栈
每个Activity都有个任务栈,默认的是应用的包名。如果我们有需求,可以单独为Activity指定特定的任务栈
(taskAffinity主要和singleTask或allowReparenting配对使用才有效,其他情况下无意义)
:
".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:launchMode="singleTop"
android:taskAffinity="com.zdh"
android:allowTaskReparenting="true"
android:theme="@style/AppTheme"/>
应用A启动应用B的某个Activity C,C的属性如上图所示,这时候由于是A启动了C,所以C在A所在的任务栈中,但是这时候我们返回桌面,启动B应用的时候,是重新显示onPause()、onResume()了C,然后B可以正常使用,但是C所在的任务栈已经变成B的应用了,上边allowTaskReparenting就是允许改变栈的意思,因为B启动了会有自己的任务栈,C原本想要的任务栈也就是包名,有了,所以C就直接跳栈了。
设置Activity启动模式的两种方式
AndroidManifest.xml中在Activity标签下设置lanuchMode
<activity
android:name=".SecondActivity"
android:launchMode="singleTop"
/>
在Intent后添加Flag
Intent intent = new Intent();
intent.setClass(MainActivity.this,SecondActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
activity中指定的启动模式就不多说了。FLag标记为有很多,一般情况下,我们用不到,有些也是与系统息息相关,不能随便调用,所以介绍下常用的到的:
IntentFilter的匹配规则
Activity启动方式为两种,显式调用和隐式调用。显式调用需要明确指定包名类名,隐式调用则不必指明。但需匹配所有的过滤信息。过滤信息有action、category、data。
我们经常在AndroidManiFest.xml文件中看到主Activity中有个intent-filter:
<activity android:name=".SecondActivity"/>
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:allowTaskReparenting="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
intent-filter>
activity>
显式调用就不说了,很简单。隐式调用需要匹配所有的规则。一个过滤列表中action、category、data可以有多个,一个Activity可以有多组intent-filter只需匹配任何一组就算成功。一个Intent同时匹配action类别、category类别、data类别,才能匹配成功,启动目标Activity。
1.action匹配:Intent中只能有一个action,且必须和过滤规则中某个action匹配成功。
2. category匹配:Intent中可没有,一旦有不管多少都要匹配过滤规则中任何一个category,不写的话是系统默认加上的“android.intent.category.DEFAULT”,但在规则中须加上此条规则。
3. data匹配:和action类似
data语法如下所示:
<data android:scheme="string"
android:host="string"
android:port="string"
android:path="string"
android:pathPattern="string"
android:pathPrefix="string"
android:mimeType="string"/>
data实际由两部分组成:mimeType(媒体类型)和URL
//URL结构
<scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]
//类似于:http://www.baidu.com:80/search/info
如果过滤规则中这样写:
<intent-filter>
<action android:name="com.zdh.alphathink.action"/>
<category android:name="com.zdh.alphathink.category"/>
<data android:mimeType="image/*"
android:scheme="http"
android:host="www.baidu.com"
android:port="80"/>
....
intent-filter>
则intent设置中这样匹配:
Intent intent = new Intent("com.alphathink.action");
intent.addCategory("com.zdh.alphathink.category");
intent.setDataAndType(Uri.parse("http://www.baidu.com:80"),"image/*");
一般我们使用显式调用即可,有特殊要求了再自定义intent-filter。