android 四大组件之一Activity

一. Activity生命周期

activity生命周期图
简单说明一下Activity每个生命周期函数:
onCreate():

表示Activity正在被创建,这是Activity生命周期的第一个方法

onStart():

表示Activity正在被启动,这时候的Activity已经被创建好了,完全过了准备阶段,但是没有出现在前台,需要执行onResume()函数才可以进入到前台与用户进行交互。

onResume():

表示Activitiy已经可见了,并且Activity处于运行状态,也就是Activity不止出现在了前台,而且还可以让用户点击,滑动等等操作与它进行交互。

onPause(): (注意)

onPause被触发,并不是Activity的主界面处于被其它东西不完全覆盖状态,而是被某个其它Activity不完全覆盖。

onStop():

表示Activity即将停止,我们程序员应该在此函数中做一些不那么耗时的轻量级回收操作。

onRestart():

表示Activity正在重新启动。一般情况下,一个存在于后台不可见的Activity变为可见状态,都会去执行onRestart()函数,然后会继续执行onStart()函数,onResume()函数出现在前台并且处于运行状态。

onDestory():

表示Activity要被销毁了。这是Activity生命中的最后一个阶段,我们可以在onDestory()函数中做一些回收工作和资源释放等,比如:广播接收器的注销等。

Activity常见几种状态的生命周期分析

Activity启动–>onCreate()–>onStart()–>onResume()

点击home键回到桌面–>onPause()–>onStop()

再次回到原Activity时–>onRestart()–>onStart()–>onResume()

退出当前Activity时–>onPause()–>onStop()–>onDestroy()

二 android 任务栈

参考地址:http://blog.csdn.net/javazejian/article/details/52071885

1. android任务栈特点

(1) android任务栈又称为Task,它是一个栈结构,具有后进先出的特性,用于存放我们的Activity组件。

(2) 我们每次打开一个新的Activity或者退出当前Activity都会在一个称为任务栈的结构中添加或者减少一个Activity组件,因此一个任务栈包含了一个activity的集合, android系统可以通过Task有序地管理每个activity,并决定哪个Activity与用户进行交互:只有在任务栈栈顶的activity才可以跟用户进行交互。

(3) 在我们退出应用程序时,必须把所有的任务栈中所有的activity清除出栈时,任务栈才会被销毁。当然任务栈也可以移动到后台, 并且保留了每一个activity的状态. 可以有序的给用户列出它们的任务, 同时也不会丢失Activity的状态信息。

(4) 需要注意的是,一个App中可能不止一个任务栈,某些特殊情况下,单独一个Actvity可以独享一个任务栈。还有一点就是一个Task中的Actvity可以来自不同的App,同一个App的Activity也可能不在一个Task中。

2. taskAffinity属性,一个APP引用多个任务栈(下面singTask特殊场景用到)

(1). TaskAffinity 参数标识着Activity所需要的任务栈的名称,默认情况下,一个应用中所有Activity所需要的任务, 栈名称都为该应用的包名。

(1). TaskAffinity 属性一般跟singleTask模式或者跟allowTaskReparenting属性结合使用,在其他情况下没有实际意义。

(3). TaskAffinity属性的值不能与当前应用包名相同,否则其值跟作废没两样。

使用方法:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
              package="comzejian.myapplication">
<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <activity android:name=".ActivityA"
                android:launchMode="singleTask"
                android:taskAffinity="com.zejian.singleTask.affinity"/>
</application>
</manifest>

我们可以通过singleTask与android:taskAffinity属性相结合的方式来指定我们Activity所需要的栈名称,使相应的Activity存在于不同的栈中,图解如下:


任务栈.png

3. allowTaskReparenting属性
(1) 主要作用是activity的迁移,即从一个task迁移到另一个task,这个迁移跟activity的taskAffinity有关。

(2) 使用方法:

<activity android:name=".ActivityC"
      android:allowTaskReparenting="true">
</activity>

(3) 使用过程图解:


allowTaskReparenting.png

说明:如果allowTaskReparenting值为false时,ActivityC并不会直接从A应用的任务栈迁移到B应用的任务栈,而是B应用直接重新创建了ActivityC的实例

注意:allowTaskReparenting仅限于singleTop和standard模式

例:TaskAffinity与singleTask应用场景
  假如现在有这么一个需求,我们的客户端app正处于后台运行,此时我们因为某些需要,让微信调用自己客户端app的某个页面,用户完成相关操作后,我们不做任何处理,按下回退或者当前Activity.finish(),页面都会停留在自己的客户端(此时我们的app回退栈不为空),这显然不符合逻辑的,用户体验也是相当出问题的。我们要求是,回退必须回到微信客户端,而且要保证不杀死自己的app.这时候我们的处理方案就是,设置当前被调起Activity的属性为:

LaunchMode=""SingleTask" taskAffinity="com.tencent.mm"

4.清空任务栈
清空任务栈的方法,在一般情况下我们只需要在<activity>标签中指明相应的属性值即可。
(1) android:clearTaskOnLaunch .
这个属性用来标记是否从task清除除根Activity之外的所有的Activity,“true”表示清除,“false”表示不清除,默认为“false”。
注意的,这个属性只对任务栈内的root Activity起作用,任务栈内其他的Activity都会被忽略。
例如:一个应用的Activity A,B,C。 A跳转B,B跳转C,点击HOME,再在桌面点击图标,结果只能看到A,如下图所示:

clearTaskOnLaunch.png

情况1 当:ActivityA的clearTaskOnLaunch = true的时候,只要ActivityA没有finish(),其它的Activity设置clearTaskOnLaunch属性全部没用。假如ActivityA.finish(),任务栈最底部Activity设置clearTaskOnLaunch有效。

情况2 当:ActivityA的clearTaskOnLaunch = false的时候,无论ActivityA有没有被finish(),其它的Activity设置clearTaskOnLaunch属性全部没用。

如下图: clearTaskOnLaunch2.png

clearTaskOnLaunch2.png

(2)android:finishOnTaskLaunch
这个属性是作用在自己身上(把自己移除任务栈,不影响别的Activity).
把ActivityB的android:finishOnTaskLaunch = true时,点击HOME,再在桌面点击图标进入APP,此时任务栈里已经没有ActivityB了。如下图:

finishOnTaskLaunch.png

(3)android:alwaysRetainTaskState
alwaysRetainTaskState实际上是给了当前Activity所在的任务栈一个“免死金牌”,如果当前Activity的android:alwaysRetainTaskState设置为true时,那么该Activity所在的任务栈将不会受到任何清理命令的影响,一直保持当前任务栈的状态。

默认任务栈的缺点:
(1)每开启一次页面都会在任务栈中添加一个Activity,而只有任务栈中的Activity全部清除出栈时,任务栈被销毁,程序才会退出,这样就造成了用,户体验差, 需要点击多次返回才可以把程序退出了。
(2)每开启一次页面都会在任务栈中添加一个Activity还会造成数据冗余, 重复数据太多, 会导致内存溢出的问题(OOM)。

为了解决任务栈的缺点,我们引入了启动模式。

三 Activity启动模式

参考地址:http://blog.csdn.net/javazejian/article/details/52071885

Activity的4种启动模式(standard,singleTop,singTask和singleInstance)

1. Standard 模式
又称为标准模式,也是系统的默认模式(可以不指定),在这样模式下,每启动一个Activity都会重新创建一个Activity的新实例,并且将其加入任务栈中,而且完全不会去考虑这个实例是否已存在。我们通过图解来更清晰地了解Standard模式:

Standard.png

通过上图,我们可以发现,这个过程中,在standard模式下启动了三次MainActivity后,都生成了不同的新实例,并添加到同一个任务栈中。这个时候Activity的onCreate、onStart、onResume方法都会被调用。

2. singleTop 模式
又称栈顶复用模式,顾名思义,在这种模式下,如果有新的Activity已经存在任务栈的栈顶,那么此Activity就不会被重新创建新实例,而是复用已存在任务栈栈顶的Activity。这里重点是位于栈顶,才会被复用,如果新的Activity的实例已存在但没有位于栈顶,那么新的Activity仍然会被重建。需要注意的是,Activity的onNewIntent方法会被调用,方法原型如下:

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
}

通过此方法的参数,我们可以获取当前请求的相关信息,此时Activity的onCreate、onStart方法不会被调用,因为Activity并没有被重建。同理,我们通过图解来协助我们更清晰的理解singleTop模式:


singleTop1.png

从上图我们可以看出,当需要新创建的MainActivity位于栈顶时,MainActivity并没有重新创建。下面我们再来看看新创建的MainActivity没有位于栈顶的情况。

singleTop2.png

从图中可以看出,当我们再次启动MainActivity时,由于MainActivity位于栈中,所以系统直接将其置于栈顶,并移除其上方的所有Activity。当然如果所需要的MainActivity不存在栈中,则会创建新的Activity并添加到栈中。singleTask 模式比较适合应用的主界面activity(频繁使用的主架构),可以用于主架构的activity,(如新闻,侧滑,应用主界面等)里面有好多fragment,一般不会被销毁,它可以跳转其它的activity 界面再回主架构界面,此时其他Activity就销毁了。当然singTask还有一些比较特殊的场景这个我们后面会一一通过情景代码分析。

3. singleTask 模式
又称为栈内复用模式。这是一种单例模式,与singTop点类似,只不过singTop是检测栈顶元素是否有需要启动的Activity,而singTask则是检测整个栈中是否存在当前需要启动的Activity,如果存在就直接将该Activity置于栈顶,并将该Activity以上的Activity都从任务栈中移出销毁,同时也会回调onNewIntent方法。情况如下图:

singleTask.png

从图中可以看出,当我们再次启动MainActivity时,由于MainActivity位于栈中,所以系统直接将其置于栈顶,并移除其上方的所有Activity。当然如果所需要的MainActivity不存在栈中,则会创建新的Activity并添加到栈中。singleTask 模式比较适合应用的主界面activity(频繁使用的主架构),可以用于主架构的activity,(如新闻,侧滑,应用主界面等)里面有好多fragment,一般不会被销毁,它可以跳转其它的activity 界面再回主架构界面,此时其他Activity就销毁了。

singleTask 特殊场景
现在我们假设有如下两个Task栈,分别为前台任务栈和后台任务栈,如下图:

singleTask特殊场景.png

情况一:
此时我们先启动CD,然后再启动AB,再有B启动D,此时后台任务栈便会被切换到前台,而且这个时候整个后退列表就变成了ABCD,请注意我们这里强调的是后退列表,而非栈合并。因此当用户点击back键时,列表中的Activity会依次按DCBA顺序出栈,如下图所示:

singleTask特殊场景1.png

情况二:
如果上面B请求启动C,那么又会是什么情况呢?其实这个时候任务栈退出列表变成C->B->A,其实原因很简单,singleTask模式的ActivityC切换到栈顶时会导致在他之上的栈内的Activity出栈,如下图:

singleTask特殊场景2.png

4. singleInstance 模式
在singleInstance模式下,该Activity在整个android系统内存中有且只有一个实例,而且该实例单独尊享一个Task。换句话说,A应用需要启动的MainActivity 是singleInstance模式,当A启动后,系统会为它创建一个新的任务栈,然后A单独在这个新的任务栈中,如果此时B应用也要激活MainActivity,由于栈内复用的特性,则不会重新创建,而是两个应用共享一个Activity的实例。如下图所示:

singleInstance.png

从图中我们可以看到最终AB应用都共享一个singleInstance模式的MainActivity,也没有去重新创建。到此Activity的四种启动模式我们都介绍完了,下面我们接着来聊聊怎么使用启动模式。

Activity启动模式的使用方式

1.通过AndroidMenifest.xml文件为Activity指定启动模式,代码如下:

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

2.通过在Intent中设置标志位(addFlags方法)来为Activity指定启动模式,示例代码如下:

Intent intent = new Intent();
intent.setClass(ActivityB.this,ActivityA.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

Flags对应的值
Intent.FLAG_ACTIVITY_SINGLE_TOP
  该标志位表示使用singleTop模式来启动一个Activity,与在清单文件指定android:launchMode="singleTop"效果相同。

Intent.FLAG_ACTIVITY_CLEAR_TOP
该标志位表示使用singleTask模式来启动一个Activity,与在清单文件指定android: launchMode="singleTask"效果相同。

Intent.FLAG_ACTIVITY_NO_HISTORY
  使用该模式来启动Activity,当该Activity启动其他Activity后,该Activity就被销毁了,不会保留在任务栈中。如A-B,B中以这种模式启动C,C再启动D,则任务栈只有ABD。

Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
  使用该标识位启动的Activity不添加到最近应用列表,也即我们从最近应用里面查看不到我们启动的这个activity。与属性android:excludeFromRecents="true"效果相同。

四. scheme跳转协议(简单的应用)

Android中的scheme是一种页面内跳转协议,通过自定义scheme协议,可以非常方便的跳转到app中的各个页面,通过scheme协议,服务器可以定制化告诉app跳转到哪个页面,可以通过通知栏消息定制化跳转页面,可以通过H5页面跳转到相应页面等等。

目前主要用于以下几种场景:

(1) 服务器下发跳转路径,客户端根据服务器下发跳转路径跳转相应的页面;

(2) H5页面点击锚点,根据锚点具体跳转路径App端跳转具体的页面;

(3) App端收到服务器端下发的PUSH通知栏消息,根据消息的点击跳转路径跳转相关页面

scheme协议跳转的一个完整实例

第一步:在Mainefest配置文件中配置需要用scheme协议跳转的Activity

        <!-- scheme协议 -->
        <activity
            android:name=".SchemeActivity"
            android:label="@string/app_name">
            <!-- 要想在别的App上能成功调起App,必须添加intent过滤器 -->
                <!-- 协议部分,随便设置 -->
                <intent-filter>
                    <!--协议部分,随便设置-->
                    <data android:scheme="scheme" android:host="mtime" android:path="/goodsDetail" />
                    <!--下面这几行也必须得设置-->
                    <category android:name="android.intent.category.DEFAULT"/>
                    <action android:name="android.intent.action.VIEW"/>
                    <category android:name="android.intent.category.BROWSABLE"/>
                </intent-filter>
        </activity>

第二步:模拟从网络中获取scheme协议的url,跳转到指定Activity

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
                 // (1)在manifest配置文件中配置了scheme参数
                 // (2)网络端获取url
                 // (3)跳转
                String url = "scheme://mtime/goodsDetail?goodsId=10011002";
                Intent intent = new Intent(Intent.ACTION_VIEW,Uri.parse(url));
                startActivity(intent);
    }
}

第三步:获取scheme协议参数

public class SchemeActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_scheme);
       //获取参数
        Uri data = getIntent().getData();
        Log.i(TAG, "host = " + data.getHost() + " path = " + data.getPath() + " query = " + data.getQuery());
        String param = data.getQueryParameter("goodsId");
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容