Activity使用详解

四大组件概论
如果把四大组件比作MP4中的电影播放系统,那么要保证这个系统有基础功能则:

  • Activity是显示到屏幕上的电影画面—-能看到,还能交互(必须,即使没有播放电影也需要基础界面,交互有快进、音量操作等)。
  • Service是影音文件读取器—-看不到,但是没有他看不了电影(非必须,但对看电影这一操作来说是必须的)。
  • BroadcastReceiver是通知服务—-看不到,如果电量不足立刻发送广播弹窗提醒你(非必须,除了电量监测还可有其他服务)。
  • ContentProvider是获取系统时间服务—-看不到,通过访问系统时钟组件获取时间信息,时间非本程序功能(非必须,跨进程通信)。

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>
  • application:代表整个应用其下的allowBackup是否允许后台运行
  • icon:应用图标
  • label:应用名称
  • roundIcon:圆形图标
  • exported:是否可被其他应用调用
  • theme:全局主题/如果在activity下设置则为当前activity的主题
  • launchMode:activity相关配置,可在此设计启动模式。(稍后分析)
  • intent-filter:过滤匹配规则。(稍后分析)

Activity的生命周期

Activity使用详解_第1张图片

官方开发文档给的很经典的流程图。要学会查阅官方文档。其中的介绍也很详细标准。

  1. onCreate(Bundle savedInstanceState):创建时调用,只调用一次
  2. onStart():onCreate(Bundle savedInstanceState)调用后立刻调用
  3. onResume():显示调用,这时已经可看到Activity
  4. onPause():Activity部分可见,比如Activity上显示个Dialog样式的Activity
  5. onStop():Activity不可见,但有机会重新显示,故没有销毁
  6. onDestory():Activity被销毁,没有机会再显示,只调用一次
  7. onRestart():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));

启动后截图:
Activity使用详解_第2张图片

后台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:

Activity使用详解_第3张图片
当你点击回退键时,出栈的顺序刚好相反(后进先出),A–>D–>C–>B–>A
其实这是标准下的模式,实际开发中我们肯定有各种各样的需求,比如上边我们的A启动了两次,我们需要如果他有了就不实例化A,保证整个进程里只有一个A的Activity,那么下边我们来了解下其他模式。


Activity的启动模式

  1. standard:标准模式,多实例创建,谁启动在谁栈中
  2. singleTop:栈顶复用模式,Activity在栈顶则复用,否则重创
  3. singleTask:栈内复用模式,不创新栈,栈顶自清
  4. singleInstance:单实例模式

standard:标准模式
上边所讲的就是标准模式,也是系统默认的模式,如果我们没有特殊需求就不用生命Activity的启动模式,默认为standard。每启动一个Activity就会新建Activity的实例,不管此Activity是否在栈顶有实例,新建按钮跳转到本身,你点击多少次,他就实例化多少次,当然回退的时候也是一个一个回退出栈,尽管他们都相同。standard模式下的Activity启动会默认加载到启动它的Activity栈下,比如A所在的栈启动B,B就归属为A栈。

singleTop:栈顶复用模式
如果新Activity在栈顶的话就不创建,直接用栈顶的,否则新建。
比如A–>B–>B–>A
Activity使用详解_第4张图片

在此模式下,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
Activity使用详解_第5张图片

我们可以看到A–>B–>C因为栈中没有相应实例,依次入栈,而C–>A的时候,监测到有实例A就调用它,可他在最底层,所以只好把上边的都清理掉才能用到A。

singleInstance:单实例模式
这个模式和singleTask有点像,区别就是这个模式下创建的Activity独自享有这个栈,别人都不能进。
A–>B
Activity使用详解_第6张图片

任务栈
每个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启动模式的两种方式

  1. AndroidManifest.xml中在Activity标签下设置lanuchMode
  2. 在Intent后添加Flag

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标记为有很多,一般情况下,我们用不到,有些也是与系统息息相关,不能随便调用,所以介绍下常用的到的:

  • Intent.FLAG_ACTIVITY_NEW_TASK:Task1中有A,B,C三个Activity,此时在C中启动D的话,如果在AndroidManifest.xml文件中给D添加了Affinity的值和Task中的不一样的话,则会在新标记的Affinity所存在的Task中压入这个Activity。如果是默认的或者指定的Affinity和Task一样的话,就和标准模式一样了启动一个新的Activity.
  • FLAG_ACTIVITY_SINGLE_TOP:相当于launchmode中的singletop
  • FLAG_ACTIVITY_CLEAR_TOP:相当于launchmode中的singleTask
  • FLAG_ACTIVITY_NO_HISTORY:意思就是说用这个FLAG启动的Activity,一旦退出,它不会存在于栈中,比方说!原来是A,B,C这个时候再C中以这个FLAG启动D的,D再启动E,这个时候栈中情况为A,B,C,E。
  • FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:具有这个标志的Activity启动后不会入栈,也就是用户不会通过历史返回到这个Activity等同于在XMl中指定android:excludeFromRecents=”true”

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。

你可能感兴趣的:(Android基础,activity解析)