Android基础新手教程——4.1.3 Activity登堂入室

标签(空格分隔): Android基础新手教程


本节引言:

好的,在学习了两节的Activity后相信大家已经知道怎样去使用Activity了。好的,本节讲深一点。我们走下

底层,但不是考究源代码,还没到那个水平..我们先看下Activity与Window,View之间的关系,即载入Actvitiy

经历的流程,然后我们走下文档,来学习Activity管理机制:Task和Back Stack,接着看下Android给我们提供

的同意管理Task的相关属性,最后重点解说下Activity的四种启动方式模式。好的,開始本节内容!


1.Activity,Window与View的关系

好吧。本来就想了解下他们几个的关系,然后手多多。然后就開始看起他们的调用过程来了…结果扣了两个小时,仅仅理解了非常小非常小的一部分。果然,究竟层撸源代码的都是大神,比方老罗,还没到那个等级。以下是自己查阅资料。看了下一点源代码的归纳所得,假设哪写错了欢迎指出!

以下贴下小结图:

流程解析:

Activity调用startActivity后最后会调用attach方法,然后在PolicyManager实现一个Ipolicy接口。接着实现一个Policy对象。接着调用makenewwindow(Context)方法。该方法会返回一个PhoneWindow对象,而PhoneWindow

是Window的子类,在这个PhoneWindow中有一个DecorView的内部类,是全部应用窗体的根View,即View的老大,

直接控制Activity是否显示(引用老司机原话..),好吧,接着里面有一个LinearLayout。里面又有两个FrameLayout他们分别拿来装ActionBar和CustomView。而我们setContentView()载入的布局就放到这个CustomView中!

总结下这三者的关系:

打个牵强的比喻:

我们能够把这三个类分别堪称:画家,画布,画笔画出的东西。

画家通过画笔( LayoutInflater.infalte)画出图案,再绘制在画布(addView)上!

最后显示出来(setContentView)


2.Activity,Task和Back Stack的一些概念

接着我们来了解Android中Activity的管理机制,这就涉及到了两个名词:Task和Back Stack了!

概念解析:

我们的APP一般都是由多个Activity构成的。而在Android中给我们提供了一个Task(任务)的概念,

就是将多个相关的Activity收集起来,然后进行Activity的跳转与返回!当然。这个Task仅仅是一个

frameworker层的概念。而在Android中实现了Task的数据结构就是Back Stack(回退堆栈)

相信大家对于栈这样的数据结构并不陌生,Java中也有个Stack的集合类!栈具有例如以下特点:

先进后出。后进先出。经常使用操作入栈(push)。出栈(pop),处于最顶部的叫栈顶。最底部叫栈底

而Android中的Stack也具有上述特点,他是这样来管理Activity的:

当切换到新的Activity,那么该Activity会被压入栈中,成为栈顶。

而当用户点击Back键,栈顶的Activity出栈,紧随其后的Activity来到栈顶!

我们来看下官方文档给出的一个流程图:

流程解析:

应用程序中存在A1,A2,A3三个activity,当用户在Launcher或Home Screen点击应用程序图标时,

启动主A1,接着A1开启A2,A2开启A3,这时栈中有三个Activity。而且这三个Activity默认在

同一个任务(Task)中。当用户按返回时。弹出A3,栈中仅仅剩A1和A2,再按返回键,

弹出A2,栈中仅仅剩A1。再继续按返回键。弹出A1,任务被移除。即程序退出。

接着在官方文档中又看到了另外两个图,处于好奇,我又看了下解释,然后跟群里的人讨论了下:

然后还有这段解释:

然后总结下了结论:

Task是Activity的集合。是一个概念,实际使用的Back Stack来存储Activity,能够有多个Task。可是

同一时刻仅仅有一个栈在最前面,其它的都在后台!那栈是怎样产生的呢?

答:当我们通过主屏幕,点击图标打开一个新的App。此时会创建一个新的Task!举个样例:

我们通过点击通信录APP的图标打开APP,这个时候会新建一个栈1,然后開始把新产生的Activity加入进来,可能我们在通讯录的APP中打开了短信APP的页面,可是此时不会新建一个栈,而是继续加入到栈1中。这是

Android推崇一种用户体验方式。即不同应用程序之间的切换能使用户感觉就像是同一个应用程序,

非常连贯的用户体验。官方称其为seamless (无缝衔接)!

——————这个时候假如我们点击Home键,回到主屏幕,此时栈1进入后台。我们可能有下述两种操作:

1)点击菜单键(正方形那个button),点击打开刚刚的程序,然后栈1又回到前台了!

又或者我们点击主屏幕上通信录的图标,打开APP,此时也不会创建新的栈,栈1回到前台。

2)假设此时我们点击还有一个图标打开一个新的APP。那么此时则会创建一个新的栈2。栈2就会到前台,

而栈1继续呆在后台;

3) 后面也是这样…以此类推!


3.Task的管理

1)文档翻译:

好的,继续走文档。从文档中的ManagingTasks開始。大概的翻译例如以下:


1)文档翻译

继续走文档,从文档中的ManagingTasks開始,翻译例如以下:

如上面所述,Android会将新成功启动的Activity加入到同一个Task中而且依照以“先进先出”方式管理多个Task

和Back Stack。用户就无需去操心Activites怎样与Task任务进行交互又或者它们是怎样存在于Back Stack中!

也许,你想改变这样的正常的管理方式。比方,你希望你的某个Activity能够在一个新的Task中进行管理;

或者你仅仅想对某个Activity进行实例化。又或者你想在用户离开任务时清理Task中除了根Activity全部Activities。你能够做这些事或者很多其它,仅仅须要通过改动AndroidManifest.xml中

< activity >的相关属性值或者在代码中通过传递特殊标识的Intent给startActivity( )就能够轻松的实现

对Actvitiy的管理了。

< activity >中我们能够使用的属性例如以下:

  • taskAffinity
  • launchMode
  • allowTaskReparenting
  • clearTaskOnLaunch
  • alwaysRetainTaskState
  • finishOnTaskLaunch

你能用的基本的Intent标志有:

  • FLAG_ACTIVITY_NEW_TASK
  • FLAG_ACTIVITY_CLEAR_TOP
  • FLAG_ACTIVITY_SINGLE_TOP

好的,接下来逐个介绍这些怎么用:


2)taskAffinity和allowTaskReparenting

默认情况下,一个应用程序中的全部activity都有一个Affinity,这让它们属于同一个Task。

你能够理解为是否处于同一个Task的标志。然而,每一个Activity能够通过

< activity>中的taskAffinity属性设置单独的Affinity。

不同应用程序中的Activity能够共享同一个Affinity,同一个应用程序中的不同Activity

也能够设置成不同的Affinity。

Affinity属性在2种情况下起作用:

1)当启动 activity的Intent对象包括FLAG_ACTIVITY_NEW_TASK标记:

当传递给startActivity()的Intent对象包括 FLAG_ACTIVITY_NEW_TASK标记时,系统会为须要启动的Activity寻找与当前Activity不同Task。

假设要启动的 Activity的Affinity属性与当前全部的Task的Affinity属性都不同样,系统会新建一个带那个Affinity属性的Task。并将要启动的Activity压到新建的Task栈中。否则将Activity压入那个Affinity属性同样的栈中。

2)allowTaskReparenting属性设置为true

假设一个activity的allowTaskReparenting属性为true, 那么它能够从一个Task(Task1)移到另外一个有同样Affinity的Task(Task2)中(Task2带到前台时)。

假设一个.apk文件从用户角度来看包括了多个“应用程序”,你可能须要对那些 Activity赋不同的Affinity值。


3)launchMode:

四个可选值,启动模式我们研究的核心。以下再具体讲!

他们各自是:standard(默认),singleTopsingleTasksingleInstance


4)清空栈

当用户长时间离开Task(当前task被转移到后台)时。系统会清除task中栈底Activity外的全部Activity

。这样,当用户返回到Task时。仅仅留下那个task最初始的Activity了。

我们能够通过改动以下这些属性来

改变这样的行为!

alwaysRetainTaskState

假设栈底Activity的这个属性被设置为true。上述的情况就不会发生。 Task中的全部activity将被长时间保存。

clearTaskOnLaunch

假设栈底activity的这个属性被设置为true。一旦用户离开Task。

则 Task栈中的Activity将被清空到仅仅剩下栈底activity。

这样的情况刚好与

alwaysRetainTaskState相反。即使用户仅仅是短暂地离开。task也会返回到初始状态

(仅仅剩下栈底acitivty)。

finishOnTaskLaunch

与clearTaskOnLaunch类似,但它仅仅对单独的activity操

作,而不是整个Task。它能够结束不论什么Activity。包括栈底的Activity。

当它设置为true时。当前的Activity仅仅在当前会话期间作为Task的一部分存在。

当用户退出Activity再返回时,它将不存在。


4.Activity的四种载入模式具体解释:

接下来我们来具体地解说下四种载入模式:

他们各自是:standard(默认),singleTopsingleTasksingleInstance

在泡在网上的日子看到一篇图文并茂的解说启动模式的,非常赞。可能更easy理解吧,这里借鉴下:

原文链接:Activity启动模式图文具体解释:standard, singleTop, singleTask 以及 singleInstance

英文原文:Understand Android Activity’s launchMode: standard, singleTop, singleTask and singleInstance

另外还有一篇具体解说载入模式的:Android中Activity四种启动模式和taskAffinity属性具体解释

先来看看总结图:

模式具体解释:


standard模式:

标准启动模式。也是activity的默认启动模式。在这样的模式下启动的activity能够被多次实例化。即在同一个任务中能够存在多个activity的实例,每一个实例都会处理一个Intent对象。假设Activity A的启动模式为standard,而且A已经启动,在A中再次启动Activity A,即调用startActivity(new Intent(this。A.class))。会在A的上面再次启动一个A的实例。即当前的桟中的状态为A–>A。




singleTop模式:

假设一个以singleTop模式启动的Activity的实例已经存在于任务栈的栈顶。

那么再启动这个Activity时,不会创建新的实例,而是重用位于栈顶的那个实例,

而且会调用该实例的onNewIntent()方法将Intent对象传递到这个实例中。

举例来说,假设A的启动模式为singleTop,而且A的一个实例已经存在于栈顶中,

那么再调用startActivity(new Intent(this,A.class))启动A时,

不会再次创建A的实例。而是重用原来的实例。而且调用原来实例的onNewIntent()方法。

这时任务栈中还是这有一个A的实例。

假设以singleTop模式启动的activity的一个实例

已经存在与任务栈中。可是不在栈顶。那么它的行为和standard模式同样,也会创建多个实例。


singleTask模式:

仅仅同意在系统中有一个Activity实例。假设系统中已经有了一个实例。

持有这个实例的任务将移动到顶部,同一时候intent将被通过onNewIntent()发送。

假设没有,则会创建一个新的Activity并置放在合适的任务中。

官方文档中提到的一个问题:

系统会创建一个新的任务,并将这个Activity实例化为新任务的根部(root)

这个则须要我们对taskAffinity进行设置了。使用taskAffinity后的解雇:





singleInstance模式

保证系统不管从哪个Task启动Activity都仅仅会创建一个Activity实例,并将它加入新的Task栈顶

也就是说被该实例启动的其它activity会自己主动执行于还有一个Task中。

当再次启动该activity的实例时。会重用已存在的任务和实例。

而且会调用这个实例

的onNewIntent()方法,将Intent实例传递到该实例中。和singleTask同样,

同一时刻在系统中仅仅会存在一个这样的Activity实例。


5.Activity拾遗

对于Activity可能有些东西还没讲到,这里预留一个位置,漏掉的都会在这里补上!

首先是群友珠海-坤的建议,把开源中国的Activity管理类也贴上,嗯,这就贴上。大家能够直接用到

项目中~

1)开源中国clientActivity管理类:

package net.oschina.app;

import java.util.Stack;

import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context; public class AppManager { private static Stack<Activity> activityStack;
private static AppManager instance; private AppManager(){}
/**
* 单一实例
*/
public static AppManager getAppManager(){
if(instance==null){
instance=new AppManager();
}
return instance;
}
/**
* 加入Activity到堆栈
*/
public void addActivity(Activity activity){
if(activityStack==null){
activityStack=new Stack<Activity>();
}
activityStack.add(activity);
}
/**
* 获取当前Activity(堆栈中最后一个压入的)
*/
public Activity currentActivity(){
Activity activity=activityStack.lastElement();
return activity;
}
/**
* 结束当前Activity(堆栈中最后一个压入的)
*/
public void finishActivity(){
Activity activity=activityStack.lastElement();
finishActivity(activity);
}
/**
* 结束指定的Activity
*/
public void finishActivity(Activity activity){
if(activity!=null){
activityStack.remove(activity);
activity.finish();
activity=null;
}
}
/**
* 结束指定类名的Activity
*/
public void finishActivity(Class<? > cls){
for (Activity activity : activityStack) {
if(activity.getClass().equals(cls) ){
finishActivity(activity);
}
}
}
/**
* 结束全部Activity
*/
public void finishAllActivity(){
for (int i = 0, size = activityStack.size(); i < size; i++){
if (null != activityStack.get(i)){
activityStack.get(i).finish();
}
}
activityStack.clear();
}
/**
* 退出应用程序
*/
public void AppExit(Context context) {
try {
finishAllActivity();
ActivityManager activityMgr= (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
activityMgr.restartPackage(context.getPackageName());
System.exit(0);
} catch (Exception e) { }
}
}

本节小结:

好的,本节就到这里,东西都比較苦涩难懂,临时知道下就可以,总结下Task进行总体调度的

相关操作吧:

  • 按Home键。将之前的Task切换到后台
  • 长按Home键,会显示出近期执行过的Task列表
  • 在Launcher或HomeScreen点击app图标。开启一个新Task,或者是将已有的Task调度到前台
  • 启动singleTask模式的Activity时。会在系统中搜寻是否已经存在一个合适的Task。若存在,则会将这个Task调度到前台以重用这个Task。

    假设这个Task中已经存在一个要启动的Activity的实例,则清除这个实例之上的全部Activity。将这个实例显示给用户。假设这个已存在的Task中不存在一个要启动的Activity的实例,则在这个Task的顶端启动一个实例。若这个Task不存在,则会启动一个新的Task。在这个新的Task中启动这个singleTask模式的Activity的一个实例。

  • 启动singleInstance的Activity时,会在系统中搜寻是否已经存在一个这个Activity的实例,假设存在。会将这个实例所在的Task调度到前台。重用这个Activity的实例(该Task中仅仅有这一个Activity),假设不存在。会开启一个新任务,并在这个新Task中启动这个singleInstance模式的Activity的一个实例。

好的本节就到这里,关于Task与Activity载入模式的东西还是比較复杂的,以下给大家贴下编写该文的

时候的一些參考文献,能够自己看看~


參考文献:

1.Tasks and Back Stack

2.理解android中Activity和Task的关系

3.Activity启动模式图文具体解释:standard, singleTop, singleTask 以及 singleInstance

4.Understand Android Activity’s launchMode: standard, singleTop, singleTask and singleInstance

5.Android中Activity四种启动模式和taskAffinity属性具体解释

6.Android的Activity和Tasks具体解释

7.Activity的四种启动模式和onNewIntent()

8.译:Android任务和返回栈全然解析,细数那些你所不知道的细节

Android基础新手教程——4.1.3 Activity登堂入室的更多相关文章

  1. Android基础新手教程——4.1.2 Activity初窥门径

    Android基础新手教程--4.1.2 Activity初窥门径 标签(空格分隔): Android基础新手教程 本节引言: 上一节中我们对Activity一些主要的概念进行了了解,什么是Activ ...

  2. Android基础新手教程——4.1.1 Activity初学乍练

    Android基础新手教程--4.1.1 Activity初学乍练 标签(空格分隔): Android基础新手教程 本节引言: 本节開始解说Android的四大组件之中的一个的Activity(活动) ...

  3. Android基础新手教程——3.1 基于监听的事件处理机制

    Android基础新手教程--3.1.1 基于监听的事件处理机制 标签(空格分隔): Android基础新手教程 本节引言: 第二章我们学习的是Android的UI控件,我们能够利用这些控件构成一个精 ...

  4. Android基础新手教程——4.4.1 ContentProvider初探

    Android基础新手教程--4.4.1 ContentProvider初探 标签(空格分隔): Android基础新手教程 本节引言: 本节给大家带来的是Android四大组件中的最后一个--Con ...

  5. Android基础新手教程——3.7 AnsyncTask异步任务

    Android基础新手教程--3.7 AnsyncTask异步任务 标签(空格分隔): Android基础新手教程 本节引言: 本节给大家带来的是Android给我们提供的一个轻量级的用于处理异步任务 ...

  6. Android基础新手教程——4.3.1 BroadcastReceiver牛刀小试

    Android基础新手教程--4.3.1 BroadcastReceiver牛刀小试 标签(空格分隔): Android基础新手教程 本节引言 本节我们将来学习Android四大组件中的第三个:Bro ...

  7. Android基础新手教程——3.4 TouchListener PK OnTouchEvent + 多点触碰

    Android基础新手教程--3.4 TouchListener PK OnTouchEvent + 多点触碰 标签(空格分隔): Android基础新手教程 本节引言: 如题,本节给大家带来的是To ...

  8. Android基础新手教程——4.3.2 BroadcastReceiver庖丁解牛

    Android基础新手教程--4.3.2 BroadcastReceiver庖丁解牛 标签(空格分隔): Android基础新手教程 本节引言: 上节我们对BroadcastReceiver已经有了一 ...

  9. Android基础新手教程——3.8 Gestures(手势)

    Android基础新手教程--3.8 Gesture(手势) 标签(空格分隔): Android基础新手教程 本节引言: 周六不歇息,刚剪完了个大平头回来.继续码字~ 好的,本节给大家带来点的是第三章 ...

随机推荐

  1. ios iphone ipad上iframe的宽度会扩大的解决办法

    这个问题,我从网上查了下,好像是属于ios的bug,android,windows都没有问题. 解决办法,就是在iframe加载完成后,设置 iframe里面body的宽度为多少PX. $(" ...

  2. mybatis generator 生成javabean自定义类型转换

    因为默认mybatis generator自动生成的,带小数的都转成了bigdecimal了,而且长度不同的整数转成了不同的类型. 但是我想要带小数的转成double,整数转成integer. 所有自 ...

  3. redis.clients.jedis.HostAndPort - cant resolve localhost address

    阿里云ECS部署spring-boot访问redis出现redis.clients.jedis.HostAndPort - cant resolve localhost address 摘要: 阿里云 ...

  4. LISTVIEW 消息 结构 宏

    如果是要画的话,用CreateWindowEx创建 指定 WC_LISTVIEW window class 关于其消息如下: LVM_APPROXIMATEVIEWRECT  LVM_ARRANGE  ...

  5. DockerFile的编写和注意的一些知识点

    CMD,RUN,ENTRYPOINT之类的差别. VOLUMN和-V之间的差别. EXPOSE和-P的对应等. 今天上午写了一个脚本,可以传参数进IMAGE,让启动的CONTAINER具有不同的行为. ...

  6. HTTP 协议基本知识

    HTTP协议    7.1.什么是HTTP协议:        HTTP协议是用来规定浏览器客户端和服务器通信的方式 7.2.基本原则        基于请求响应模型        一次请求对应一次响 ...

  7. 【转】jenkins插件pipeline使用介绍

    摘要: pipeline字面意思就是流水线,将很多步骤按顺序排列好,做完一个执行下一个.下面简单介绍下如何使用该插件帮我们完成一些流水线型的任务 pipeline字面意思就是流水线,将很多步骤按顺序排 ...

  8. mysql:把DB1中A表a字段替换为DB2中B表b字段

    UPDATE DB1.A SET a = ( SELECT b FROM DB2.B WHERE B.Id = A.id) 实例: UPDATE wordpress.`wp_posts` SET po ...

  9. ubuntu fcitx google 输入法打不出中括号【】

    编辑/usr/share/fcitx/data/punc.mb.zh_CN, 将 [ · ] 「 」 这部分改成自己习惯的: [  [ ]  ] 保存后,重启一下fcitx就OK了.

  10. HDU 2044 一只小蜜蜂(递归)

    一只小蜜蜂... Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Su ...