Android四大组件(一):Activity

Activity是Android应用中负责与用户交互的组件,给Android应用提供可视化用户界面。Activity是Window的容器,Activity包含一个getWindow()方法,返回该Activity所包含的窗口。

创建Activity

  • 需要在清单文件中为其配置一个activity标签

  • 标签中如果带有这个子节点,则会在系统中多创建一个快捷图标

       <intent-filter>
           <action android:name="android.intent.action.MAIN" />
           <category android:name="android.intent.category.LAUNCHER" />
       </intent-filter>
    
  • 一个应用程序可以在桌面创建多个快捷图标。

  • activity的名称、图标可以和应用程序的名称、图标不相同

      android:icon="@drawable/ic_launcher"
      android:label="@string/app_name"
    

Activity的生命周期

Activity的四个状态

Activity的四个状态
  • 活动状态:当前Activity位于前台,用户可见,可以获得焦点进行交互。
  • 暂停状态:其他Activity位于前台,该Activity依然可见,只是不能获得焦点进行交互,常见如弹出PopupWindow。
  • 停止状态:该Activity不可见,失去焦点。
  • 销毁状态:该Activity结束,或Activity所在的Dalvik进程结束。

Activity生命周期及回调方法

Activity生命周期及相关的回调方法

  • onCreate(Bundle savedStatus):创建Activity时被回调;
  • onStart():启动Activity时被回调,Activity已经显示在屏幕,但没有得到焦;
  • onRestart():重新启动Activity时被回调,Activity从不可见变成可见时会执行此方法;
  • onResume():恢复Activity时被回调, Activity得到焦点,可以与用户交互;
  • onPause():暂停Activity时被回调,Activity失去焦点,无法再与用户交互,但依然可见;(可用于保护界面当前状态)
  • onStop():停止Activity时被回调,Activity不可见,进入后台;
  • onDestroy():销毁Activity时被回调。

Activity实践中的生命周期方法回调过程

  • 启动Activity:onCreate->onStart->onResume;
  • 点击Home键返回系统桌面:onPause->onStop;
  • 点击应用列表的图标重新进入应用:onRestart->onStart->onResume;
  • 点击返回键(或程序调用finish()方法):onPause->onStop->onDestory;

Activity跳转时的生命周期方法回调过程

Activity跳转时生命周期

横竖屏切换的生命周期
默认情况下 ,横竖屏切换, 销毁当前的activity,重新创建一个新的activity。在一些特殊的应用程序常见下,比如游戏,不希望横竖屏切换activity被销毁重新创建。

  • 默认情况(manifest清单文件中不对Activity的configChanges属性做任何设置):
    1、android3.2之前的版本:onSaveInstanceState->onPause->onStop->onDestroy->onCreate->onStart->onResume;
    2、android3.2以后的版本:onPause->onSaveInstanceState->onStop->onDestroy->onCreate->onStart->onResume;

  • 设置让Activity对横竖屏切换不敏感(清单文件中设置android:configChanges="orientation"):
    1、android3.2之前的版本:调用onConfigurationChanged(不会重建Activity,也不会调用任何生命周期方法)
    2、android3.2之后的版本,又分为两种情况:
    ①targetSdkVersion<=12:调用onConfigurationChanged(不会重建Activity,也不会调用任何生命周期方法);
    ②targetSdkVersion>12:onPause->onSaveInstanceState->onStop->onDestroy->onCreate->onStart->onResume(配置的android:configChanges="orientation"没起作用);
    ③如果targetSdkVersion>12时,想让横竖屏切换时不重建Activity,还得配置screenSize,也就是android:configChanges="orientation|screenSize"(因为google在android3.2中添加了screensize改变的通知,在转屏的时候,不仅是orientation发生了改变,screensize同样也发生了改变),这是最保险的做法。

  • 开发实践中禁用掉横竖屏重建Activity:
    1、直接将Activity的横竖屏写死(简单、但是用户体验不友好,具体根据产品需求):

//①通过清单文件配置Activity
android:screenOrientation="landscape"//始终横屏
android:screenOrientation="portrait"//始终竖屏

//②在代码中实现:
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);//始终横屏
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); //始终竖屏

2、通过清单文件配置Activity不再敏感横竖屏切换(相对更友好安全的做法,参见上文这也是兼容性更好的做法,如果需要在横竖屏切换时做业务逻辑处理,可以在onConfigurationChanged方法中实现):
在manifest.xml中配置activity:

android:configChanges="orientation|screenSize"

Activity的启动模式

所有的Activity都将存放在任务栈中,通过设置Activity的启动模式,可以修改任务栈的排列模式。我们可以在可以通过adb指令来查看系统的任务栈情况:adb shell dumpsys activity。 adb shell dumpsys activity activities | findstr "ResumedActivity"(这是用来查看当前活跃的栈顶Activity)。

Activity的四种启动模式

  • Standard 标准启动模式:Android创建Activity时的默认模式,假设没有为Activity设置启动模式的话,默觉得标准模式。每次启动一个Activity都会又一次创建一个新的实例入栈,无论这个实例是否存在。
Standard启动模式
  • SingleTop 栈顶复用模式:假设你在当前的Activity中又要启动同类型的Activity,此时建议将此类型Activity的启动模式指定为SingleTop,能够降低Activity的创建,节省内存。
    ①如果需要创建的Activity已经不处于栈顶,将会创建一个新的Activity入栈,同Standard模式一样。
SingleTop模式Activity位于栈顶

②如果须要创建的Activity已经处于栈顶时,此时不会再创建新的Activity,而是直接复用栈顶的Activity,保证栈顶如果存在,不会重复创建。此时Activity的onCreate、onStart不会被系统调用,由于它并没有发生改变。但是它的的 onNewIntent会被回调。

SingleTop模式Activity不在栈顶
  • SingleTask 栈内复用模式: 当开启activity的时候,就去检查在任务栈里面是否有实例已经存在,如果有实例存在就复用这个已经存在的activity,并且把这个activity上面的所有的别的activity都清空让此Activity位于栈顶,并且回调Activity的onNewIntent方法。保证整个任务栈里面只有一个实例存在。如果不存在同Standard模式一样。(如果一个activity的创建需要占用大量的系统资源一般配置这个activity为singletask的启动模式,一般应用的主界面也会配置为SingleTask模式) 。
SingleTask模式复用
  • SingleInstance启动模式非常特殊,是全局单例模式,是一种加强的SingleTask模式。它除了具有SingleTask的所有特性外,还有一个特点,SingleInstance启动的activity会运行在自己的任务栈里面,并且这个任务栈里面只有一个实例存在。一般应用开发很少用到,如果你要保证一个activity在整个手机操作系统里面只有一个实例存在,使用singleInstance。比如Launch、锁屏键、电话拨打界面等。
    ①我们可以试着做一个SingleInstance启动模式的小猜想:假设我们从MainActivity ->(跳转到)SingleInstanceActivity ->(跳转到) ->SecondActivity。 然后我们点击返回按钮,页面的返回顺序会是怎样?如果你试了之后不理解其原理,下文有关于任务栈的详细介绍。

任务栈的相关理解
在上文的启动模式中我们多次提到任务栈这个概念,任务是一个activity的集合,它用栈(后进先出)的方式来管理activity;这个栈被称为返回栈(back stack),栈里的activity顺序是按打开顺序放置。

①当用户在home界面点击应用图标时候,这个应用的任务就会被转移到前台,如果这个应用的任务是空的,说明最近这个应用没有被启动过,系统就会去创建一个新的任务,将该应用的主activity放入到返回栈中。

②当一个activity启动了一个新activity的时候,新的activity会被放置到返回栈的栈顶并获取焦点(如果是SingleInstance启动模式,新的Activity会新建一个栈并独享);前一个activity仍然保留在任务栈,但处于停止状态。

③当用户按下返回键的时候,处于栈顶的activity会被移除掉,前一个activity就会重新回到栈顶的位置。我们只能向栈顶添加activity或者将栈顶的activity移除掉。

④如果一直按返回键,返回栈中的activity会一个一个的被移除,最终返回到主屏幕,这时候返回栈中activity全被清空,对应的任务也就不存在了。

  • 当打开一个应用,对应的任务处于前台;这时候点击home键回到主屏幕,任务就被转移到后台;当任务处于后台状态的时候,返回栈中的activity都进入停止状态,但在返回栈中的顺序不会变,每个activity的信息和数据都在;当处于内存不足的情况下有可能会被销毁回收(涉及到进程优先级问题)。
通过SingleInstance启动模式理解任务栈

启动模式的使用方式
1、在Manifest.xml清单文件中静态指定启动模式:

        <activity
            android:name=".MainActivity"
            android:launchMode="singleTask"/>

2、在Intent中动态指定启动模式去创建Activity:

        Intent intent = new Intent();
        intent.setClass(context, MainActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(intent);
  • 两种方式的区别:在Intent中动态指定的方式,优先级更高,若两者同时存在,以动态指定方式为准;

Activity异常结束时保存状态

Activity异常结束
Activity的异常结束是指,非人为主动结束和退出Activity的行为,一般有以下两种情况:
1、资源系统设置变更:如横竖屏切换,可能会导致Activity销毁重建,或者设备语言发生变化,或者键盘发生变化时。我们可以在清单文件中配置Activity的configChanges属性来避免(具体参照上文)。
2、系统资源不足时:由于虚拟机的垃圾回收机制,在系统内存不足时,会自动回收掉低优先级的进程,释放内存保证优先级高的进程能正常运行(Android进程优先级相关知识)。

在Activity异常结束时保存和恢复数据

  • 保存数据:onSaveInstanceState(),只会在异常退出时才会回调。如果用户显式关闭Activity时(点击返回,或者触发Activity的finish方法),则系统不会回调此方法。调用的时机android3.2之前的版本:onSaveInstanceState在onPause之前回调,Android3.2之后在onPause之后回调。
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putString("name", "rave");
    }
  • 恢复数据:onRestoreInstanceState(),会在页面异常关闭之后,重新进入时回调。调用时机是在onStart之后,onResume之前(没有做过低版本测试)。
    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        if(savedInstanceState != null){
            String name = savedInstanceState.getString("name");
            System.out.println(name);
        }
    }
  • 对于控件来说,在Activity异常结束时,部分系统控件会自动保存和恢复它的状态(如EditView)。因为View类里面也有onSaveInstanceState()和onRestoreInstanceState(Parcelable state),如果要自定义控件实现该功能可以参考系统控件的做法。

Activity的之间的数据传递

当一个Activity启动另一个Activity时,或者当一个Activity关闭并返回时,常常会有一些数据需要传递。Android的做法是将数据以键值对的方式存放在Bundle中,让Intent携带Bundle在Activity之间完成数据传递。我们通过intent的putExtras()来添加数据,里面可以存放所以可序列化的类型(基本数据类型及其数组:byte、boolean、char、short、int、long、float、double、byte[]...;实现Parcelable接口的类;实现Serializable接口的类;还有这些类型的ArrayList集合类:ArrayList<? extends Parcelable>、ArrayList<Integer>、ArrayList<String>、ArrayList<CharSequence>)。???疑问这里Parcelable和Serializable有什么区别

  • Activity启动另一个Activity传递数据:
Bundle bundle = new Bundle();
bundle.putBoolean("booleanValue", true);

Intent intent = new Intent();
intent.putExtras(bundle);

startActivity(intent);
//startActivityForResult(intent, 0);
  • 从Intent里面提取数据:
Bundle bundle = getIntent().getExtras();
boolean value = bundle.getBoolean("booleanValue");
  • Activity关闭并返回数据:
Bundle bundle = new Bundle();
bundle.putSerializable("booleanValue", true);

Intent intent = new Intent();
intent.putExtras(bundle);

setResult(1, intent);
finish();
  • 从返回的Activity里提取数据:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    Bundle bundle = data.getExtras();
}

Activity转场动画


使用Scheme方式唤起Activity

Android中启动Activity主要有两种方式,一种是通过显示意图启动,一种是通过隐式意图启动。这里介绍的使用Scheme方式唤起,其实是隐式意图启动的一种,因为这种方式在hybird开发中很常用所以提出来单独介绍。

android中的scheme是一种页面内跳转协议,是一种非常好的实现机制,通过定义自己的scheme协议,可以非常方便跳转app中的各个页面。客户端应用可以通过清单文件向操作系统注册一个 URL scheme,该 scheme 用于从浏览器或其他应用中启动本应用。通过指定的 URL 字段,可以让应用在被调起后直接打开某些特定页面,比如商品详情页、活动详情页等等。也可以执行某些指定动作,如完成支付等。也可以在应用内通过 html 页来直接调用显示 app 内的某个页面。

URL Scheme使用场景大致分以下几种

  • 服务器下发跳转路径,客户端根据服务器下发跳转路径跳转相应的页面
  • H5页面点击锚点,根据锚点具体跳转路径APP端跳转具体的页面
  • APP端收到服务器端下发的PUSH通知栏消息,根据消息的点击跳转路径跳转相关页面
  • APP根据URL跳转到另外一个APP指定页面

URL scheme协议格式
例如:xl://goods:8888/goodsDetail?goodsId=10011002

  • xl代表该Scheme 协议名称
  • goods代表Scheme作用于哪个地址域
  • goodsDetail代表Scheme指定的页面
  • goodsId代表传递的参数
  • 8888代表该路径的端口号

使用方式

  • 唤起外部应用的Activity:

1、必须在目标Activity的Manifest.xml中配置如下过滤器(关于Intent和Intent-filter会单独说明):

<intent-filter>
    <action android:name="android.intent.action.VIEW"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <data android:scheme="artist"
        android:host="first"
        android:path="/enter"/>
</intent-filter>

2、启动方式:

Intent intent = new Intent();
intent.setData(Uri.parse("artist://first/enter"));
startActivity(intent);
  • 唤起应用内部Activity(也可以用上面的方式唤起内部Activity),可以用下面的方式:

1、在目标Activity的Manifest.xml中配置如下过滤器

<intent-filter>
    <action android:name="android.intent.action.VIEW"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <category android:name="android.intent.category.BROWSABLE"/>
    <data android:scheme="artist"
        android:host="first"
        android:path="/enter"/>
</intent-filter>

2、启动方式:

WebView.loadUrl("artist://first/enter");

相关源码分析

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,470评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,393评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,577评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,176评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,189评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,155评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,041评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,903评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,319评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,539评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,703评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,417评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,013评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,664评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,818评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,711评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,601评论 2 353

推荐阅读更多精彩内容