草庐IT

Android的四种启动模式

xiaoshiquan1206 2023-04-18 原文

一、Activity的四种启动模式:

1、standard(标准模式):一个定义Activity在mainfest中不设置android:launchMode=“standard”,也会默认为standard,standard就是新建一个Activity就在栈中新建一个Activity实例。
2、singleTop(栈顶复用模式):在mainfest中设置singleTop模式时,当前栈顶如果有一个相同的Activity,就不创建而复用栈顶的那个,只要创建新的和栈顶相同的Activity才会复用,复用的Activity就回调onNewIntent方法。
3、singleTask(栈内单例模式):当前栈内只有一个Activity实例,栈内已存activity实例,在其他Activity中开启这个Activity,Android直接把这个实例栈上面的其他Activity实例踢出栈GC掉。
4、singleInstace(堆内单例):设置该模式的Activity实例存在一个单独的任务栈中,整个系统独立的。

设置了singleTop、singleTask、singleInstance这三种模式的Activity,如果开启一个新的Activity页面,栈顶存在相同的实例就复用,都不会重新创建一个新实例,Activity复用后都会调用onNewIntent(Intent intent)方法。

测试demo例子:
在mainfest清单文件中定义这些Activity

        <activity android:name=".lauchmode.LaunchModeActivity"
            android:launchMode="singleTask"/>
        <activity
            android:name=".lauchmode.StandardActivity"
            android:launchMode="standard" />
        <activity
            android:name=".lauchmode.SingleTopActivity"
            android:launchMode="singleTop" />
        <activity
            android:name=".lauchmode.SingleTaskActivity"
            android:launchMode="singleTask"/>
        <activity android:name=".lauchmode.SingleInstanceActivity"
            android:launchMode="singleInstance"/>
class LaunchModeActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivityLaunchModeBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.btStandard.setOnClickListener {
            startActivity(Intent(this, StandardActivity::class.java))
        }
        binding.btSingleTop.setOnClickListener {
            startActivity(Intent(this, SingleTopActivity::class.java))
        }
        binding.btSingleTask.setOnClickListener {
            startActivity(Intent(this, SingleTaskActivity::class.java))
        }
    }
}

以下就是LaunchModeActivity页面,下面按钮分别跳转到StandardActivity 、SingleTopActivity、SingleInstanceActivity、SingleTaskActivity页面。

class StandardActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivityStandardBinding.inflate(layoutInflater)
        setContentView(binding.root)
        
        binding.btStandard.setOnClickListener {
            startActivity(Intent(this, StandardActivity::class.java))
        }
        binding.btSingleTop.setOnClickListener {
            startActivity(Intent(this, SingleTopActivity::class.java))
        }
        binding.btSingleTask.setOnClickListener {
            startActivity(Intent(this, SingleTaskActivity::class.java))
        }
        binding.btSingleInstance.setOnClickListener {
            startActivity(Intent(this, SingleInstanceActivity::class.java))
        }
    }
}
class SingleTopActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivitySingleTopBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.btStandard.setOnClickListener {
            startActivity(Intent(this, StandardActivity::class.java))
        }
        binding.btSingleTop.setOnClickListener {
            startActivity(Intent(this, SingleTopActivity::class.java))
        }
        binding.btSingleTask.setOnClickListener {
            startActivity(Intent(this, SingleTaskActivity::class.java))
        }
        binding.btSingleInstance.setOnClickListener {
            startActivity(Intent(this, SingleInstanceActivity::class.java))
        }
    }
}
class SingleTaskActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivitySingleTaskBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.btStandard.setOnClickListener {
            startActivity(Intent(this, StandardActivity::class.java))
        }
        binding.btSingleTop.setOnClickListener {
            startActivity(Intent(this, SingleTopActivity::class.java))
        }
        binding.btSingleTask.setOnClickListener {
            startActivity(Intent(this, SingleTaskActivity::class.java))
        }
        binding.btSingleInstance.setOnClickListener {
            startActivity(Intent(this, SingleInstanceActivity::class.java))
        }
    }
}
class SingleInstanceActivity : AppCompatActivity() {
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivitySingleInstanceBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.btStandard.setOnClickListener {
            startActivity(Intent(this, StandardActivity::class.java))
        }
        binding.btSingleTop.setOnClickListener {
            startActivity(Intent(this, SingleTopActivity::class.java))
        }
        binding.btSingleTask.setOnClickListener {
            startActivity(Intent(this, SingleTaskActivity::class.java))
        }
        binding.btSingleInstance.setOnClickListener {
            startActivity(Intent(this, SingleInstanceActivity::class.java))
        }
    }
}

StandardActivity 、SingleTopActivity、SingleInstanceActivity、SingleTaskActivity这些Activity的页面都是下图,方便测试

查看task栈情况指令: adb shell dumpsys activity
1、singleTop模式测试:
开启Activity的顺序是StandardActivity---->SingleTopActivity---->SingleTopActivity,查看任务栈可发现,本来是开启两个SingleTopActivity页面的,但是栈顶只有一个实例。

2、singleTask模式测试:
开启Activity的顺序是SingleTaskActivity---->StandardActivity---->SingleTopActivity---->SingleTaskActivity,如下图任务栈可发现,本来一共开启了四个Activity,最后只剩下一个SingleTaskActivity,所以设置了singleTask模式的Activity,在一个任务栈中只能有一个实例,栈顶不管开多少个Activity,只要打开设置了singleTask的Activity后,该Activity上面的Activity都会销毁回收掉。

3、singleInstance模式测试:
开启Activity的顺序是StandardActivity---->SingleInstanceActivity---->StandardActivity,然后关闭页面顺序是这样的StandardActivity—>StandardActivity---->SingleInstanceActivity,最后关闭的页面即然是SingleInstanceActivity,如果再关闭SingleInstanceActivity页面就回到手机的桌面了。所有这个模式有点特别,设置了该模式Activity存在在一个单独的任务栈中。如下图:

4、taskAffinity属性:
taskAffinity属性和Activity的启动模式息息相关,而且taskAffinity属性比较特殊,在普通的开发中也是鲜有遇到,但是在有些特定场景下却有着出其不意的效果。
taskAffinity是Activity在mainfest中配置的一个属性,暂时可以理解为:taskAffinity为宿主Activity指定了存放的任务栈[不同于App中其他的Activity的栈],为activity设置taskAffinity属性时不能和包名相同,因为Android团队为taskAffinity默认设置为包名任务栈。
taskAffinity只有和SingleTask启动模式匹配使用时,启动的Activity才会运行在名字和taskAffinity相同的任务栈中。

5、Intent中标志位设置启动模式:
四种模式可以使用代码中设置,通过Intent.setFlags(int flags)设置启动模式。
FLAG_ACTIVITY_CLEAR_TOP : 等同于mainfest中配置的singleTask。
FLAG_ACTIVITY_SINGLE_TOP: 同样等同于mainfest中配置的singleTop。
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS: 其对应在AndroidManifest中的属性为android:excludeFromRecents=“true”,当用户按了“最近任务列表”时候,该Task不会出现在最近任务列表中,可达到隐藏应用的目的。
FLAG_ACTIVITY_NO_HISTORY: 对应在AndroidManifest中的属性为:android:noHistory=“true”,这个FLAG启动的Activity,一旦退出,它不会存在于栈中。

6、设置FLAG_ACTIVITY_NEW_TASK属性:
这个属性需要在被开启的目标Activity在AndroidManifest.xml文件配置taskAffinity的值(必须和startActivity发其者Activity的包名不一样,如果是跳转另一个App的话可以taskAffinity可以省略),则会在新标记的Affinity所存在的taskAffinity中压入这个Activity。

如下面跳转到微信页面的代码中,设置FLAG_ACTIVITY_NEW_TASK后,跳转到微信的时候会开启一个新的任务栈存放微信的页面,如果不设置就回加入到当前的任务栈中

try {
                val intent = Intent(Intent.ACTION_MAIN)
                val cmp = ComponentName("com.tencent.mm","com.tencent.mm.ui.LauncherUI")
                intent.addCategory(Intent.CATEGORY_LAUNCHER)
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                intent.component = cmp
                startActivity(intent)
            } catch (e: ActivityNotFoundException) {
                Toast.makeText(this,"检查到您手机没有安装微信,请安装后使用该功能", Toast.LENGTH_SHORT).show()
            }

如果去掉上面intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)这条代码,微信的页面就回加入到自己app的任务栈中。

7、Intent属性:
在Android中Intent是在四大组件之间进行交互与通讯,也可以在应用之间通讯。其底层的通信是以Binder机制实现的,在物理层则是通过共享内存的方式实现的。Intent的属性有:component(组件)、action、category、data、type、extras、flags;所有的属性也是各显神通,满足开发者的各种需要满足不同场景。
component: 显然就是设置四大组件的,将直接使用它指定的组件,借助这一属性可以实现不同应用组件之间通讯。
action: 是一个可以指定目标组件行为的字符串,开发人员可以自定义action通过匹配action实现组件之间的隐士跳转,当然Android系统也已经预定部分String作为系统应用Action,例如打开系统设置页面等等。
data: 通常是URI类型或者MIME类型格式定义的操作数据;表示与动作要操纵的数据。
Category: 属性用于指定当前动作(Action)被执行的环境。
type: 对于data范例的描写。
extras:extras和flags 这两个太熟悉了就不在重复。

8、Activity的onSaveInstanceState和onRestoreInstanceState方法:
在Android系统内存不足时,同时Activity失去焦点后被系统给回收后,Activity 再次被创建时,通过onSaveInstanceState 和onRestoreInstanceState使用Bundle来存储恢复数据,例如屏幕的横竖屏切换时,就会先调用onSaveInstanceState方法,切换屏幕后的页面就会调用onRestoreInstanceState方法。

有关Android的四种启动模式的更多相关文章

  1. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  4. ruby-on-rails - 启动 Rails 服务器时 ImageMagick 的警告 - 2

    最近,当我启动我的Rails服务器时,我收到了一长串警告。虽然它不影响我的应用程序,但我想知道如何解决这些警告。我的估计是imagemagick以某种方式被调用了两次?当我在警告前后检查我的git日志时。我想知道如何解决这个问题。-bcrypt-ruby(3.1.2)-better_errors(1.0.1)+bcrypt(3.1.7)+bcrypt-ruby(3.1.5)-bcrypt(>=3.1.3)+better_errors(1.1.0)bcrypt和imagemagick有关系吗?/Users/rbchris/.rbenv/versions/2.0.0-p247/lib/ru

  5. ruby - 是否有用于序列化和反序列化各种格式的对象层次结构的模式? - 2

    给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最

  6. UE4 源码阅读:从引擎启动到Receive Begin Play - 2

    一、引擎主循环UE版本:4.27一、引擎主循环的位置:Launch.cpp:GuardedMain函数二、、GuardedMain函数执行逻辑:1、EnginePreInit:加载大多数模块int32ErrorLevel=EnginePreInit(CmdLine);PreInit模块加载顺序:模块加载过程:(1)注册模块中定义的UObject,同时为每个类构造一个类默认对象(CDO,记录类的默认状态,作为模板用于子类实例创建)(2)调用模块的StartUpModule方法2、FEngineLoop::Init()1、检查Engine的配置文件找出使用了哪一个GameEngine类(UGame

  7. 安卓apk修改(Android反编译apk) - 2

    最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路

  8. ruby-on-rails - environment.rb 中设置的常量在开发模式中消失 - 2

    了解Rails缓存如何工作的人可以真正帮助我。这是嵌套在Rails::Initializer.runblock中的代码:config.after_initializedoSomeClass.const_set'SOME_CONST','SOME_VAL'end现在,如果我运行script/server并发出请求,一切都很好。然而,在我的Rails应用程序的第二个请求中,一切都因单元化常量错误而变得糟糕。在生产模式下,我可以成功发出第二个请求,这意味着常量仍然存在。我已通过将以上内容更改为以下内容来解决问题:config.after_initializedorequire'some_cl

  9. Ruby:标准递归模式 - 2

    我经常迷上ruby​​的一件事是递归模式。例如,假设我有一个数组,它可能包含无限深度的数组作为元素。所以,例如:my_array=[1,[2,3,[4,5,[6,7]]]]我想创建一个方法,可以将数组展平为[1,2,3,4,5,6,7]。我知道.flatten可以完成这项工作,但这个问题是作为我经常遇到的递归问题的一个例子-因此我试图找到一个更可重用的解决方案。简而言之-我猜这种事情有一个标准模式,但我想不出任何特别优雅的东西。任何想法表示赞赏 最佳答案 递归是一种方法,它不依赖于语言。您在编写算法时要考虑两种情况:再次调用函数的情

  10. ruby - 在 Ruby 中查找多个正则表达式匹配的模式和位置 - 2

    这应该是一个简单的问题,但我找不到任何相关信息。给定一个Ruby中的正则表达式,对于每个匹配项,我需要检索匹配的模式$1、$2,但我还需要匹配位置。我知道=~运算符为我提供了第一个匹配项的位置,而string.scan(/regex/)为我提供了所有匹配模式。如果可能,我需要在同一步骤中获得两个结果。 最佳答案 MatchDatastring.scan(regex)do$1#Patternatfirstposition$2#Patternatsecondposition$~.offset(1)#Startingandendingpo

随机推荐