Ⅰ android采用什么方式管理activity实例
Android采用任务线(Task)的方式来管理Activity的实例搭模。
在开发Android应用时,经常会涉及一些消耗大量系统内存的情况,例如视频播放、大量图片或者程序中开启多个Activity没有及时关闭等,会导致程序出现错误。为了避免这种问题,Google提供了一套完整的机制让开发人员控制 Android中的任务线。
Android系统中的任务线,类似于一个容器,用于管理所有的Activity实例。在存放Activity时,满足“先进后出 (First-In/Last-Out )"的原则。
但是使用任务线有以下缺点:粗枝链
每开启一次页面都会在任务栈中添加一个岩孙Activity,而只有任务栈中的Activity全部清除出线时,任务线被销毁,程序才会退出。这样就造成了用户体验差, 需要点击多次返回才可以把程序退出。
每开启一次页面都会在任务栈中添加一个Activity还会造成数据冗余, 重复数据太多, 会导致内存溢出的问题(OOM)。为了解决任务栈产生的问题,Android为Activity设计了启动模式。
在实际开发中,应根据特定的需求为每个Activity指定恰当的启动模式。Activity的启动模式有4种,分别是standard、singleTop、singleTask和singlelnstance。在AndroidManifest.xml中,通过<activity>标签的android:launchMode属性可以设置启动模式。
Ⅱ 全面解析Activity: Activity的工作过程
本文将对Activity的工作过程进行分析。
主要学习以下内容:
(1)系统内部是如何启动一个Activity的?
(2)新Activity的对象是何时创建的?
(3)Activity的各个生命周日是被系统何时回调的?
Activity启动流程分两种,一种是启动正在运行的app的Activity,即启动子Activity。如无特殊声明默认和启动该activity的activity处于同一进程。如果有声明在一个新的进程中,则处于两个进程。另一种是打开新的app,即为Launcher启动新的Activity。后边启动Activity的流程是一样的,区别是前边判断进程是否存在的那部分。
Activity的启动流程整体如下:
一.Activity启动阶段
(一)涉及到的概念
进程:Android系统为每个APP分配至少一个进程
IPC:跨进程通信,Android中采用Binder机制。
(二)涉及到的类
ActivityStack:Activity在AMS的栈管理,用来记录已经启动的Activity的先后关系,状态信息等。通过ActivityStack决定是否需要启动新的进程。
ActivitySupervisor:管理 activity 任务栈
ActivityThread:ActivityThread 运行在UI线程(主线程),App的真正入口。
ApplicationThread:用来实现AMS和ActivityThread之间的交互。
ApplicationThreadProxy:ApplicationThread 在服务端的代理。AMS就是通过该代理与ActivityThread进行通信的。
IActivityManager:继承与IInterface接口,抽象出跨进程通信需要实现的功能
AMN:运行在server端(SystemServer进程)。实现了Binder类,具体功能由子类AMS实现。
AMS:AMN的子类,负责管理四大组件和进程,包括生命周期和状态切换。AMS因为要和ui交互,所以极其复杂,涉及window。
AMP:AMS的client端代理(app进程)。了解Binder知识可以比较容易理解server端的stub和client端的proxy。AMP和AMS通过Binder通信。
Instrumentation:仪表盘,负责调用Activity和Application生命周期。测试用到这个类比较多。
(三)涉及到的进程
(1)Launcher所在的进程
(2)AMS所在的SystemServer进程
(3)要启动的Activity所在的app进程
如果是启动根Activity,就涉及上述三个进程。
如果是启动子Activity,那么就只涉及AMS进程和app所在进程。
(四)具体流程
Launcher:Launcher通知AMS要启动activity。
startActivitySafely->startActivity->Instrumentation.execStartActivity()(AMP.startActivity)->AMS.startActivity
AMS:PMS的resoveIntent验证要启动activity是否匹配。如果匹配,通过ApplicationThread发消息给Launcher所在的主线程,暂停当前Activity(即Launcher)。
暂停完,在该activity还不可见时,通知AMS,根据要启动的Activity配置ActivityStack。然后判断要启动的Activity进程是否存在?
存在:发送消息LAUNCH_ACTIVITY给需要启动的Activity主线程,执行handleLaunchActivity
不存在:通过socket向zygote请求创建进程。进程启动后,ActivityThread.attach
判断Application是否存在,若不存在,通过LoadApk.makeApplication创建一个。在主线程中通过thread.attach方法来关联ApplicationThread。
在通过ActivityStackSupervisor来获取当前需要显示的ActivityStack。
继续通过ApplicationThread来发送消息给主线程的Handler来启动Activity (handleLaunchActivity)。
handleLauchActivity:调用了performLauchActivity,里边Instrumentation生成了新的activity对象,继续调用activity生命周期。
IPC过程:
双方都是通过对方的代理对象来进行通信。
1.app和AMS通信:app通过本进程的AMP和AMS进行Binder通信
2.AMS和新app通信:通过ApplicationThreadProxy来通信,并不直接和ActivityThread通信
(五)参考函数流程
Activity启动流程(从Launcher开始):
第一阶段: Launcher通知AMS要启动新的Activity(在Launcher所在的进程执行)
第二阶段:AMS先校验一下Activity的正确性,如果正确的话,会暂存一下Activity的信息。然后,AMS会通知Launcher程序pause Activity(在AMS所在进程执行)
第三阶段:pause Launcher的Activity,并通知AMS已经paused(在Launcher所在进程执行)
第四阶段:检查activity所在进程是否存在,如果存在,就直接通知这个进程,在该进程中启动Activity;不存在的话,会调用Process.start创建一个新进程(执行在AMS进程)
第五阶段: 创建ActivityThread实例,执行一些初始化操作,并绑定Application。如果Application不存在,会调用LoadedApk.makeApplication创建一个新的Application对象。之后进入Loop循环。(执行在新创建的app进程)
第六阶段:处理新的应用进程发出的创建进程完成的通信请求,并通知新应用程序进程启动目标Activity组件(执行在AMS进程)
第七阶段: 加载MainActivity类,调用onCreate声明周期方法(执行在新启动的app进程)
从另一个角度下图来概括:
下面简要介绍一下启动的过程:
Step 1. 无论是通过Launcher来启动Activity,还是通过Activity内部调用startActivity接口来启动新的Activity,都通过Binder进程间通信进入到ActivityManagerService进程中,并且调用ActivityManagerService.startActivity接口;
Step 2. ActivityManagerService调用ActivityStack.startActivityMayWait来做准备要启动的Activity的相关信息;
Step 3. ActivityStack通知ApplicationThread要进行Activity启动调度了,这里的ApplicationThread代表的是调用ActivityManagerService.startActivity接口的进程,对于通过点击应用程序图标的情景来说,这个进程就是Launcher了,而对于通过在Activity内部调用startActivity的情景来说,这个进程就是这个Activity所在的进程了;
Step 4. ApplicationThread不执行真正的启动操作,它通过调用ActivityManagerService.activityPaused接口进入到ActivityManagerService进程中,看看是否需要创建新的进程来启动Activity;
Step 5. 对于通过点击应用程序图标来启动Activity的情景来说,ActivityManagerService在这一步中,会调用startProcessLocked来创建一个新的进程,而对于通过在Activity内部调用startActivity来启动新的Activity来说,这一步是不需要执行的,因为新的Activity就在原来的Activity所在的进程中进行启动;
Step 6. ActivityManagerServic调用ApplicationThread.scheleLaunchActivity接口,通知相应的进程执行启动Activity的操作;
Step 7. ApplicationThread把这个启动Activity的操作转发给ActivityThread,ActivityThread通过ClassLoader导入相应的Activity类,然后把它启动起来。
Ⅲ activity(Android组件中最重要的四大组件之一)详细资料大全
activity是Android组件中最基本也是最为常见用的四大组件之一。Android四大组件有Activity,Service服务,Content Provider内容提供,BroadcastReceiver广播接收器。
Ⅳ Android中的Activity详解--启动模式与任务栈
目录
activity的简单介绍就不写了,作为最常用的四大组件之一,肯定都很熟悉其基本用法了。
首先,是都很熟悉的一张图,即官方介绍的Activity生命周期图.
情景:打开某个应用的的FirstActivity调用方法如下:
由于之前已经很熟悉了,这里就简单贴一些图。
按下返回键:
重新打开并按下home键:
再重新打开:
在其中打开一个DialogActivity(SecondActivity)
按下返回:
修改SecondAcitvity为普通Activity,依旧是上述操作:
这里强调一下 onSaveInstanceState(Bundle outState) 方法的调用时机:
当Activity有可能被系统杀掉时调用,注意,一定是被系统杀掉,自己调用finish是不行的。
测试如下:FirstActivity启动SecondActivity:
一个App会包含很多个Activity,多个Activity之间通过intent进行跳转,那么原始的Activity就是使用栈这个数据结构来保存的。
Task
A task is a collection of activities that users interact with when performing a certain job. The activities are arranged in a stack (the back stack ), in the order in which each activity is opened.
即若干个Activity的集合的栈表示一个Task。
当App启动时如果不存在当前App的任务栈就会自动创建一个,默认情况下一个App中的所有Activity都是放在一个Task中的,但是如果指定了特殊的启动模式,那么就会出现同一个App的Activity出现在不同的任务栈中的情况,即会有任务栈中包含来自于不同App的Activity。
标准模式,在不指定启动模式的情况下都是以此种方式启动的。每次启动都会创建一个新的Activity实例,覆盖在原有的Activity上,原有的Activity入栈。
测试如下:在FirstActivity中启动FirstActivity:
当只有一个FirstActivity时堆栈情况:
此种模式下,Activity在启动时会进行判断,如果当前的App的栈顶的Activity即正在活动的Activity就是将要启动的Activity,那么就不会创建新的实例,直接使用栈顶的实例。
测试,设置FirstActivity为此启动模式,多次点击FirstActivity中的启动FirstActivity的按钮查看堆栈情况:
(其实点击按钮没有启动新Activity的动画就可以看出并没有启动新Activity)
大意就是:
对于使用singleTop启动或Intent.FLAG_ACTIVITY_SINGLE_TOP启动的Activity,当该Activity被重复启动(注意一定是re-launched,第一次启动时不会调用)时就会调用此方法。
且调用此方法之前会先暂停Activity也就是先调用onPause方法。
而且,即使是在新的调用产生后此方法被调用,但是通过getIntent方法获取到的依旧是以前的Intent,可以通过setIntent方法设置新的Intent。
方法参数就是新传递的Intent.
1.如果是同一个App中启动某个设置了此模式的Activity的话,如果栈中已经存在该Activity的实例,那么就会将该Activity上面的Activity清空,并将此实例放在栈顶。
测试:SecondActivity启动模式设为singleTask,启动三个Activity:
这个模式就很好记,以此模式启动的Activity会存放在一个单独的任务栈中,且只会有一个实例。
测试:SecondActivity启动模式设为singleInstance
结果:
显然,启动了两次ThirdActivity任务栈中就有两个实例,而SecondActivity在另外一个任务栈中,且只有一个。
在使用Intent启动一个Activity时可以设置启动该Activity的启动模式:
这个属性有很多,大致列出几个:
每个启动的Activity都在一个新的任务栈中
singleTop
singleTask
用此种方式启动的Activity,在它启动了其他Activity后,会自动finish.
官方文档介绍如下:
这样看来的话,通俗易懂的讲,就是给每一个任务栈起个名,给每个Activity也起个名,在Activity以singleTask模式启动时,就检查有没有跟此Activity的名相同的任务栈,有的话就将其加入其中。没有的话就按照这个Activity的名创建一个任务栈。
测试:在App1中设置SecondActivity的taskAffinity为“gsq.test”,App2中的ActivityX的taskAffinity也设为“gsq.test”
任务栈信息如下:
结果很显然了。
测试:在上述基础上,在ActivityX中进行跳转到ActivityY,ActivityY不指定启动模式和taskAffinity。结果如下:
这样就没问题了,ActivityY在一个新的任务栈中,名称为包名。
这时从ActivityY跳转到SecondActivity,那应该是gsq.test任务栈只有SecondActivity,ActivityX已经没有了。因为其启动模式是singleTask,在启动它时发现已经有一个实例存在,就把它所在的任务栈上面的Activity都清空了并将其置于栈顶。
还有一点需要提一下,在上面,FirstActivity是App1的lunch Activity,但是由于SecondActivity并没有指定MAIN和LAUNCHER过滤器,故在FirstActivity跳转到SecondActivity时,按下home键,再点开App1,回到的是FirstActivity。
大致就先写这么多吧,好像有点长,废话有点多,估计也有错别字,不要太在意~~~
Ⅳ Android Activity生命周期解析
Activity 用户可以做单一的、集中的事情。几乎所有的Activity都与用户进行交互,所以Activity类负责创建一个窗口,你可以通过调用setContentView(View)把你的UI布局放置在Activity的窗口中。作为四大组件之一,使用频率非常高。深入了解Activity,对于我们高质量开发是很有帮助的,下面我们就来看看Activity的生命周期。
Android系统中是通过Activity栈的方式来管理Activity的,而Activity自身则是通过生命周期的方法来管理的自己的创建与销毁。那么我们就来看看Activity生命周期是怎样运作的。
周期即活动从开始到结束所经历的各种状态。生命周期即活动从开始到结束所经历的各个状态。从一个状态到另一个状态的转变,从无到有再到无,这样一个过程中所经历的状态就叫做生命周期。
Acitivity本质上有四种状态:
在上面的四中常有的状态之间,还有着其他的生命周期来作为不同状态之间的过度,用于在不同的状态之间进行转换。
我们先来看看下面这张经典的生命周期流程图:
1)启动Activity
onCreate() —> onStart() —> onResume()
2)按Home键回到桌面 / 锁屏
onPause() —> onStop()
3)从桌面回到Activity / 解锁
onRestart() —> onStart() —> onResume()
4)跳转新Activity
A: onPause() —> onStop()
B: onCreate() —> onStart() —> onResume()
A —> B: onPause()_A —> onCreate()_B —> onStart()_B —> onResume()_B —> onStop()_A
5)返回上一个Activity
B: onPause() —> onStop() —> onDestroy()
A: onRestart() —> onStart() —> onResume()
B —> A: onPause()_B —> onRestart()_A —> onStart()_A —> onResume()_A —> onStop()_B —> onDestroy()_B
6)退出Activity
onPause() —> onStop() —> onDestroy()
Activity官方文档,开启传送门
至此Activity的整个生命周期都介绍完了,现在我们再看之前的生命周期流程图,是不是清晰许多。搞清楚Activity活动原理,这样理解起来就会容易许多,工作中也能如鱼得水。
Ⅵ Android Activity启动模式与状态保存及恢复详解
Activity是 Android组件 中最基本也是最为常见用的四大组件(Activity,Service服务,Content Provider内容提供者,BroadcastReceiver广播接收器)之一 。
Activity是一个应用程序 组件 ,提供一个 屏幕 ,用户可以用来交互为了完成某项任务。
Activity中所有操作都与用户密切相关,是一个负责与 用户交互 的组件,可以通过setContentView(View)来 显示指定控件 。
在一个android应用中,一个Activity通常就是一个单独的屏幕,它上面可以显示一些控件也可以监听并处理用户的事件做出响应。Activity之间通过Intent进行通信。
关于Activity启动流程请参考之前的文章 Android activity启动流程分析
activity有四种启动模式,分别为standard,singleTop,singleTask,singleInstance。如果要使用这四种启动模式,必须在manifest文件中<activity>标签中的launchMode属性中配置。
标准的默认启动模式,这种模式下activity可以被多次实例化,即在一个task中可以存在多个activity,每一个activity会处理一个intent对象,(在A中再次启动A,会存在后面的A在前面的A上面,当前task会存在两个activity的实例对象)
如果一个singleTop模式启动的activity实例已经存在于栈顶,那么再次启动这个activity的时候,不会重新创建实例,而是重用位于栈顶的那个实例,并且会调用实例的onNewIntent()方法将Intent对象传递到这个实例中,如果实例不位于栈顶,会创建新的实例。
启动模式设置为singleTask,framework在启动该activity时只会把它标示为可在一个新任务中启动,至于是否在一个新任务中启动,还要受其他条件的限制,即taskAffinity属性。
taskAffinity :默认情况下,一个应用中的所有activity具有相同的taskAffinity,即应用程序的包名。我们可以通过设置不同的taskAffinity属性给应用中的activity分组,也可以把不同的应用中的activity的taskAffinity设置成相同的值,当两个不同应用中的activity设置成相同的taskAffinity时,则两个activity会属于同一个TaskRecord。
在启动一个singleTask的Activity实例时,如果系统中已经存在这样一个实例,就会将这个实例调度到任务栈的栈顶,并清除它当前所在任务中位于它上面的所有的activity;如果这个已存在的任务中不存在一个要启动的Activity的实例,则在这个任务的顶端启动一个实例;若这个任务不存在,则会启动一个新的任务,在这个新的任务中启动这个singleTask模式的Activity的一个实例。
以singleInstance模式启动的Activity具有全局唯一性,即整个系统中只会存在一个这样的实例,如果在启动这样的Activiyt时,已经存在了一个实例,那么会把它所在的任务调度到前台,重用这个实例。
以singleInstance模式启动的Activity具有独占性,即它会独自占用一个任务,被他开启的任何activity都会运行在其他任务中(官方文档上的描述为,singleInstance模式的Activity不允许其他Activity和它共存在一个任务中)。
被singleInstance模式的Activity开启的其他activity,能够开启一个新任务,但不一定开启新的任务,也可能在已有的一个任务中开启,受条件的限制,这个条件是:当前系统中是不是已经有了一个activity B的taskAffinity属性指定的任务。
涉及到Activity启动,就不得不说一下Activity的管理,Activity是以什么方式及被什么类来进行管理的,涉及的类主要如下:
历史栈中的一个条目,代表一个activity。ActivityRecord中的成员变量task表示其所在的TaskRecord,ActivityRecord与TaskRecord建立了联系。
内部维护一个 ArrayList<ActivityRecord> 用来保存ActivityRecord,TaskRecord中的mStack表示其所在的ActivityStack,TaskRecord与ActivityStack建立了联系。
内部维护了一个 ArrayList<TaskRecord> ,用来管理TaskRecord,ActivityStack中持有ActivityStackSupervisor对象,由ActivityStackSupervisor创建。
负责所有ActivityStack的管理。内部管理了mHomeStack、mFocusedStack和mLastFocusedStack三个Activity栈。其中,mHomeStack管理的是Launcher相关的Activity栈;mFocusedStack管理的是当前显示在前台Activity的Activity栈;mLastFocusedStack管理的是上一次显示在前台Activity的Activity栈。
ActivityThread 运行在UI线程(主线程),App的真正入口。
用来实现AMS和ActivityThread之间的交互。
负责调用Activity和Application生命周期。
当一个Activity未被主动关闭,即“被动关闭”时,可能需要系统给用户提供保持一些状态的入口。
前面说的入口就是:Activity提供了onSaveInstanceState()方法,该方法是Activity在关闭前保存状态的核心方法。
前面提到“被动关闭”,如果是主动关闭那么就不会调用,比如:按back键、调用finish()等,那么"被动关闭"的场景有哪些呢?下面给列举一下:
肯定在调用onStop()前被调用,但不保证在onPause()前 / 后,一般是在onPause()后调用。
当需要保持状态时,在onSaveInstanceState()内执行以下逻辑:
当需要恢复时,在onCreate()内部执行以下逻辑:
布局每个View默认实现:onSaveInstanceState(),即UI的任何改变都会自动的存储和在activity重新创建的时候自动的恢复(只有在为该UI提供了唯一ID后才起作用);
若需复写该方法从而存储额外的状态信息时,应先调用父类的onSaveInstanceState()(因为默认的onSaveInstanceState()帮助UI存储它的状态);
只使用该方法记录Activity的瞬间状态(UI的状态),而不是去存储持久化数据,因为onSaveInstanceState()调用时机不确定性;可使用 onPause()[一定会执行]存储持久化数据;
Activity提供了onRestoreInstanceState()方法,该方法是Activity在重新创建后恢复之前保存状态的核心方法。
若被动关闭了Activity,即调用了onSaveInstanceState(),那么下次启动时会调用onRestoreInstanceState()。
onCreate()--->onStart()--->onRestoreInstanceState()--->onResume()
注意: onSaveInstanceState()、onRestoreInstanceState()不一定 成对被调用
如:当正在显示Activity A时,用户按下HOME键回到主界面,然后用户紧接着又返回到Activity A,此时Activity A一般不会因为内存的原因被系统销毁,故Activity A的onRestoreInstanceState()不会被执行;
针对以上情况,onSaveInstanceState保持的参数可以选择在onCreate()内部进行解析使用,因为onSaveInstanceState的bundle参数会传递到onCreate方法中,可选择在onCreate()中做数据还原。
至此:Activity的启动模式及Activity的状态保持及恢复介绍完毕。
Ⅶ Activity的基础知识(下)
上篇总结了Activity的一些知识,现在继续对Activity的知识进行梳理,包括Activity直接传递数据,Activity的生命周期,Activity的启动模式等.
1.intent传递数据:
使用startActivity方法,intent的putExtra()方法,以键值对的形式传递数据,该方法有很多重载方法,可以根据传递数据的不同类型选择合适的方法.除了有putExtra()方法外,还有putExtras()方法,传递的参数是Bundle.
如果传递的是对象,这个对象要实现序列化,也就是实现Parcelable或者Serializable接口.
如果希望被启动的页面返回数据,需要使用startActivityForResult()方法,这个方法中需要设置访问号,用来区分不同的访问者.并且在启动页重写onActivityResult方法用来接收返回的数据,
2.两种情况下的Activity的生命周期.
正常情况下的生命周州春或期,正常情况是指用户的正常操作下的Activity的生命周期.后面会分析异常情况下的生命周期.
onCreate: Activity第一次创建时候的回调,主要是在这个方法进行初始化工作,比如初始化控件和事件绑定工作.
onStart:Activity从不可见状态变成可见状态.
onResume:Activity变成前台,可以和用户交互.
onPause:Activity可见但是不能和用户交互.
onStop:Activity从可见变得不可见,成为后台Activity.
onDestroy:Activity销毁时调用.
onRestart:Activity从后台册伍变成前台Activity.
在启动Activity和两个Activity之间跳转时,可以知道Activity的生命周期变化过程,有两个说明:(1)在两个Activity跳转时,第一个Activty的onPause,onStop方法和第二个Activity的生命周期方法调用时机.
第一个Activity先执行onPause方法,第二个Activity才能创建.这也就意味着在onPause方法中不能执行太耗时的操作,否则会影响第二个Activity的创建.在源码(ActivityStack)中有这样的注释:
(2)onStart和onResume,onPause和onStop这两对方法的实质不同处:onStart和onStop这两个方法是从Activity的可见性来区分的,onResume和onPause是从Activity是否处于前台,是否可以和用户交互来区分的,注意在onPause调用时Activity还是可见的,调用时机比如弹出dialog时,下面的Activity是可见的.这个时候调用的是onPause方法.
异常情况下Activity的生命周期:异常情况下是指资源相关配置发生变化或后台Activity被系统回收时Activity的生命周森茄期.后台Activity被系统回收的情况比较难复现,在资源相关配置发生变化时和后台Activity被回收时的生命周期执行过程是一样的,比较容易复现的就是横竖屏切换时的生命周期执行过程.在 AndroidManifest的Activity组件下配置android:screenOrientation标签,当设置可以横竖方向随着方向感应器来调节时,在切换时会出现先销毁Activity再创建的过程.
过程:
在这种情况下有可能会有数据的丢失,系统提供用来保存数据和还原数据的方法:onSaveInstanceState和onRestoreInstanceState.用方法参数Bundle可以保存和还原数据.
可以根据需要设置android:screenOrientation标签,设定activity的方向,如果activity的方向是需要横竖屏切换,但是不容许销毁Activity,可以设置如下标签,当这些情况(常用的)发生变化是不会重新走Activity的生命周期方法,只会调用onConfigurationChanged,可以根据情况在这个方法里更新操作.
切换时的log输出
3.Activity的四种启动模式
标准:是Activity的默认启动模式,对于AndroidManifest的Activity节点下的android:launchMode="standard"标签.
特定:每次启动都会重新创建新的Activity.
singleTop:对应的AndroidManifes的Activity节点下的android:launchMode="singleTop"标签
特点:当此模式的Activity处于栈顶时,不会重新创建新的Activity,会调用onNewIntent方法,如果更新Activity的intent,需要调用 setIntent()方法,具体的生命周期过程
singleTask:在activity栈中已经有需要再启动的activity时,会先清除位于需要启动activity之上的activity,例如:启动顺序mainActivity-activityA -activityB-activityA,其中activityA是singleTask的启动模式:
singleInstance:在一个栈中单独存在的activity.
关于activity栈:是指用来管理activity一种"先进先出"的队列结构,查看activity对应栈的方法:Activity的getTaskId()方法,同一个栈的id值是相同的.adb shell mpsys activity在终端查看栈结构,比如还是上面的activity启动顺序,不同是ActivityA这是设置成singleInstance,这是的栈结构:有两个TaskRecord,其中ActivityB和MainActivity位于同一个栈中.
4.Activity开发中使用技巧:
<1>定义一个父Activity,在创建新的Activity时继承这个activity即可,将一些activity的公共设置可以设置在父activity中,比如获取每个Activity的名字,设置activity的窗体属性,同一管理activity的生命周期等,
<2>在启动的activity中定义静态方法,启动条件会显而易见:
<3>管理activity类,用来一键退出app.在父类Activity的创建和销毁时用来添加和移除Activity,在需要一键退出的地方调用静态finishAllActivity方法.
Ⅷ Android之Activity全面解析,有些知识点容易忘记
Activity作为安卓四大组件之一,是最重要也是用得最多的组件,涉及的知识点非常多,有些知识点平时开发很少用到,但在某些场景下需要特别注意,本文详细整理了Activity涉及的知识点,供开发参考。
针对Activity可以提出很多问题,如:
Activity 的生命周期?
Activity 之间的通信方式?
Activity 各种情况下的生命周期?
横竖屏切换时 Activity 的生命周期?
前台切换到后台,然后再回到前台时 Activity 的生命周期?
弹出 Dialog 的时候按 Home 键时 Activity 的生命周期?
两个Activity之间跳转时的生命周期?
下拉状态栏时 Activity 的生命周期?
Activity 与 Fragment 之间生命周期比较?
Activity 的四种 LaunchMode(启动模式)的区别?
Activity 状态保存与恢复?
Activity的转场动画有哪些实现方式?
Activity的生命周期中怎么获取控件宽高?
onNewIntent的执行时机?
如何连续退出多个Activity?
如何把Acitivty设置成Dialog样式 ,android:theme="@android:style/Theme.Dialog"
关于横竖屏切换的生命周期,对应不同的手机,由于厂商定制的原因,会有不同的效果,如设置了configChanges="orientation”在有些手机会执行各个生命周期,但有些手机却不会执行。
网上常见的结论如下:
但实际的测试如下:
可以看出,不同厂商的手机切屏生命周期会有差异。
从API 13以上,当设备在横竖切屏时,“屏幕尺寸”也会发生变化,因此为了杜绝切屏导致页面销毁重建,需要加上screenSize,使用设置4,即 android:configChanges="orientation|keyboardHidden|screenSize" .
Activity的四种状态如下:
在activity处于paused或者stoped状态下,如果系统内存紧张,可能会被销毁,当重回该activity时会重建,正常返回和被回收后返回的生命周期如下:
如果是回收后返回,onCreate的参数savedInstanceState不为空。
有哪些场景会触发onNewIntent回调呢?跟启动模式有关,首先该Activity实例已经存在,再次启动才可能触发。一种情况是启动模式是singleTask或者singleInstance,无论该activity在栈中哪个位置,都会触发onNewIntent回调,并且把上面其他acitivity移除,另一种情况是启动模式是singleTop或者以FLAG_ACTIVITY_SINGLE_TOP启动,并且该activity实例在栈顶,会触发onNewIntent,如果不在栈顶是重新创建的,不会触发。
在实际业务开发中,往往碰到需要连续退出多个activity实例,下面整理了几种常见方法:
● 发送特定广播
1、在需要处理连续退出的activity注册该特定广播;
2、发起退出的activity发送该特定广播;
3、接收到该广播的activity 调用finish结束页面。
● 递归退出
1、用startActivityForResult启动新的activity;
2、前一个页面finish时,触发onActvityResult回调,再根据requestCode和resultCode处理是否finish,达到递归退出的效果。
● FLAG_ACTIVITY_CLEAR_TOP
通过intent.setFlag(Intent.FLAG_ACTIVITY_CLEAR_TOP)启动新activity,如果栈中已经有该实例,则会把该activity之上的所有activity关闭,达到singleTop启动模式的效果。
● 自定义activity栈
1、自定义activity列表,新打开activity则加入栈中,关闭则移除栈;
2、需要退出多个activity时,则循环从栈中移除activity实例,并调用finish。
在讨论Activity启动模式经常提到任务栈,那到底什么是任务栈?
任务是一个Activity的集合,它使用栈的方式来管理其中的Activity,这个栈又被称为返回栈(back stack),栈中Activity的顺序就是按照它们被打开的顺序依次存放的。返回栈是一个典型的后进先出(last in, first out)的数据结构。下图通过时间线的方式非常清晰地向我们展示了多个Activity在返回栈当中的状态变化:
taskAffinity 任务相关性,可以用于指定一个Activity更加愿意依附于哪一个任务,在默认情况下,同一个应用程序中的所有Activity都具有相同的affinity, 名字为应用的包名。当然了,我们可以为每个 Activity 都单独指定 taskAffinity 属性(不与包名相同)。taskAffinity 属性主要和 singleTask 启动模式和 allowTaskReparenting 属性配对使用,在其他情况下没有意义。
taskAffinity 有下面两种应用场景:
分为显示启动和隐式启动。
(1)显示启动
直接指定待调整的Activity类名。
(2)隐式启动
Intent 能够匹配目标组件的 IntentFilter 中所设置的过滤信息,如果不匹配将无法启动目标 Activity。IntentFilter 的过滤信息有 action、category、data。
IntentFilter 需要注意的地方有以下:
● 一个 Activity 中可以有多个 intent-filter
● 一个 intent-filter 同时可以有多个 action、category、data
● 一个 Intent 只要能匹配任何一组 intent-filter 即可启动对应 Activity
● 新建的 Activity 必须加上以下这句,代表能够接收隐式调用
<category android:name="android.intent.category.DEFAULT" />
只要匹配一个action即可跳转,注意的是action要区分大小写。
规则:如果intent中有category,则所有的都能匹配到intent-filter中的category,intent中的category数量可用少于intent-filter中的。另外,单独设置category是无法匹配activity的,因为category属性是一个执行Action的附加信息。
intent不添加category会匹配默认的,即 “android:intent.category.DEFAULT”
如果上面例子,如果去掉intent.setAction("action_name"),则会抛出异常:
规则:类似action,但data有复杂的结构,只要匹配一个data并且与data中所有属性都一致就能匹配到Activity,只要有1个属性不匹配,都无法找到activity。
data的结构:
data 主要是由 URI 和 mimeType 组成的。
URI 可配置很多信息,的结构如下:
与url类似,例如:
mineType:指资源类型包括文本、图片、音视频等等,例如:text/plain、 image/jpeg、video/* 等
下面看下data匹配的例子:
只匹配scheme
只匹配scheme也是能匹配到activity的。
匹配scheme、host、port
将上面的data改为
匹配mineType
如果有mineType,则不能仅设置setData或setMineType了,因为setData会把mineType置为null,而setMineType会把data置为null,导致永远无法匹配到activity,要使用setDataAndType。
使用scheme的默认值contentfile
注意该方法需要在startAtivity方法或者是finish方法调用之后立即执行,不能延迟,但可以在子线程执行。
而在windowAnimationStyle中存在四种动画:
activityOpenEnterAnimation // 打开新的Activity并进入新的Activity展示的动画
activityOpenExitAnimation // 打开新的Activity并销毁之前的Activity展示的动画
activityCloseEnterAnimation //关闭当前Activity进入上一个Activity展示的动画
activityCloseExitAnimation // 关闭当前Activity时展示的动画
overridePendingTransition的方式比较生硬,方法也比较老旧了,不适用于MD风格,google提供了新的转场动画ActivityOptions,并提供了兼容包ActivityOptionsCompat。
我们知道在onCreate和onResume里面直接获取到控件宽高为0,那有什么办法获取到控件的实际宽高?只要有onWindowFocusChanged、view.post、ViewTreeObserver三种方式获取。
当用户点击桌面图标启动APP时,背后的流程如下:
我们看到的手机桌面是Launch程序的界面,点击应用图标会触发点击事件,调用startActivity(intent),然后通过Binder IPC机制,与ActivityManagerService(AMS)通讯,AMS执行一系列操作,最终启动目前应用,大概流程如下:
通过PackageManager的resolveIntent()收集跳转intent对象的指向信息,然后通过grantUriPermissionLocked()方法来验证用户是否有足够的权限去调用该intent对象指向的Activity。如果有权限,则在新的task中启动目标activity,如果发现没有进程,则先创建进程。
如果进程不存在,AMS会调用startProcessLocked创建新的进程,在该方法中,会通过socket的通讯方式通知zygote进程孵化新的进程并返回pid,在新的进程中会初始化ActivityThread,并依次调用Looper.prepareLoop()和Looper.loop()来开启消息循环。
创建好进程后下一步要将Application和进程绑定起来,AMS会调用上一节创建的ActivityThread对象的bindAppliction方法完成绑定工作,该方法会发送一条BIND_APPLICATION的消息,最终会调用handleBindApplication方法处理消息,并调用makeApplication方法处理消息,加载APP的classes到内存中。
通过前面的步骤,系统已经拥有了该Application的进程,后续的启动则是从已存在其他进程中启动Acitivity,即调用realStartAcitvityLocked,该方法会调用Application的主线程对象ActivityThread的sheleLaunchActivity方法,在方法中会发送LAUNCH_ACTIVITY到消息队列,最终通过handleLaunchActivity处理消息,完成Acitivty的启动。
Activity
Activity 的 36 大难点,你会几个?“建议收藏”
[译]Android Application启动流程分析