Android中launcherMode="singleTask"详解【android源码解析六】

2023-05-16

            android中launcherMode有4中属性:standard(默认), singleTop,singleTask和 singleInstance;网上有好多例子讲解这四种关系的:下面我列举几个链接:

       大明原创---->转载请标明出处:http://blog.csdn.net/wdaming1986/article/details/7304191

        http://www.cnblogs.com/xiaoQLu/archive/2011/09/29/2195742.html

        http://marshal.easymorse.com/archives/2950

        http://blog.csdn.net/infsafe/article/details/5666964

        http://www.j2megame.com/html/xwzx/ty/2027.html

        等等。我不一一列举;

       下面是我对singleTask的一点拙见,希望分享一下给大家:

        最近研究android浏览器browser,这个BrowserActivity的launcherMode="singleTask",因为browser不断地启动自己,所以要求这个栈中保持只能有一个自己的实例,就像别人总结的这样“注意singleTask模式的Activity不管是位于栈顶还是栈底,再次运行这个Activity时,都会destory掉它上面的Activity来保证整个栈中只有一个自己,切记切记”这点是毋庸置疑的。

       问题:browser的launcherMode=“singleTask”,browser上网的时候,遇到播放视频的链接,就会通过隐式intent方式跳转找Gallery3D中的MovieView这个类来播放视频,这时候如果你点击home键,再点击browser,你会发现MovieView这个类已经销毁不存在了,而不会像保存这个MovieView的类对象,给客户带来的用户体验特别的不好。用户还得从新下载刚才的视频。不支持断点播放。为了验证这一问题,我专门写了个小例子来说明activity之间的生命周期:

       (1)先建一个App1的工程。包名为:com.cn.daming.app1,包里面有一个类:App1Activity。里面的代码如下:

package com.cn.daming.app1;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class App1Activity extends Activity {

	private Button mButton = null;
	
	@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        Log.v("daming", "App1Activity--11-->onCreate()...."+" /n getTaskId() == "+
        		getTaskId()+"   isTaskRoot() == "+isTaskRoot());
        mButton = (Button)findViewById(R.id.button1);
        mButton.setOnClickListener(new OnClickListener(){

			public void onClick(View arg0) {
				Intent intent = new Intent();
				intent.setClassName("com.cn.daing.app2", "com.cn.daing.app2.App2Activity");
				startActivity(intent);
			}
        });
    }

	@Override
	protected void onDestroy() {
		super.onDestroy();
		Log.v("daming", "App1Activity--11-->onDestroy()...."+" /n getTaskId() == "+
        		getTaskId()+"   isTaskRoot() == "+isTaskRoot());
	}

	@Override
	protected void onPause() {
		super.onPause();
		Log.v("daming", "App1Activity--11-->onPause()...."+" /n getTaskId() == "+
        		getTaskId()+"   isTaskRoot() == "+isTaskRoot());
	}

	@Override
	protected void onRestart() {
		super.onRestart();
		Log.v("daming", "App1Activity--11-->onRestart()...."+" /n getTaskId() == "+
        		getTaskId()+"   isTaskRoot() == "+isTaskRoot());
	}

	@Override
	protected void onResume() {
		super.onResume();
		Log.v("daming", "App1Activity--11-->onResume()...."+" /n getTaskId() == "+
        		getTaskId()+"   isTaskRoot() == "+isTaskRoot());
	}

	@Override
	protected void onStart() {
		super.onStart();
		Log.v("daming", "App1Activity--11-->onStart()...."+" /n getTaskId() == "+
        		getTaskId()+"   isTaskRoot() == "+isTaskRoot());
	}

	@Override
	protected void onStop() {
		super.onStop();
		Log.v("daming", "App1Activity--11-->onStop()...."+" /n getTaskId() == "+
        		getTaskId()+"   isTaskRoot() == "+isTaskRoot());
	}

	@Override
	protected void onNewIntent(Intent intent) {
		super.onNewIntent(intent);
		Log.v("daming", "App1Activity--11-->onNewIntent()...."+" /n getTaskId() == "+
        		getTaskId()+"   isTaskRoot() == "+isTaskRoot());
	}

	@Override
	protected void onSaveInstanceState(Bundle outState) {
		super.onSaveInstanceState(outState);
		Log.v("daming", "App1Activity--11-->onSaveInstanceState()...."+" /n getTaskId() == "+
        		getTaskId()+"   isTaskRoot() == "+isTaskRoot());
	}
}

       manifest的属性为:android:launchMode="singleTask"     android:alwaysRetainTaskState="true"

 

      (2)在建一个App2的工程,包名:com.cn.daing.app2  ;类名为:App2Activity;代码为:

package com.cn.daing.app2;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class App2Activity extends Activity {
	
	private Button mButton = null;
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        Log.v("daming", "App2Activity--22-->onCreate()...."+" /n getTaskId() == "+
        		getTaskId()+"   isTaskRoot() == "+isTaskRoot());
        mButton = (Button)findViewById(R.id.button1);
        mButton.setOnClickListener(new OnClickListener(){

			public void onClick(View arg0) {
				Intent intent = new Intent();
				intent.setClassName("com.cn.daming.app1", "com.cn.daming.app1.App1Activity");
				startActivity(intent);
			}
        });
    }

	@Override
	protected void onDestroy() {
		super.onDestroy();
		Log.v("daming", "App2Activity--22-->onDestroy()...."+" /n getTaskId() == "+
        		getTaskId()+"   isTaskRoot() == "+isTaskRoot());
	}

	@Override
	protected void onNewIntent(Intent intent) {
		super.onNewIntent(intent);
		Log.v("daming", "App2Activity--22-->onNewIntent()...."+" /n getTaskId() == "+
        		getTaskId()+"   isTaskRoot() == "+isTaskRoot());
	}

	@Override
	protected void onPause() {
		super.onPause();
		Log.v("daming", "App2Activity--22-->onPause()...."+" /n getTaskId() == "+
        		getTaskId()+"   isTaskRoot() == "+isTaskRoot());
	}

	@Override
	protected void onRestart() {
		super.onRestart();
		Log.v("daming", "App2Activity--22-->onRestart()...."+" /n getTaskId() == "+
        		getTaskId()+"   isTaskRoot() == "+isTaskRoot());
	}

	@Override
	protected void onResume() {
		super.onResume();
		Log.v("daming", "App2Activity--22-->onResume()...."+" /n getTaskId() == "+
        		getTaskId()+"   isTaskRoot() == "+isTaskRoot());
	}

	@Override
	protected void onSaveInstanceState(Bundle outState) {
		super.onSaveInstanceState(outState);
		Log.v("daming", "App2Activity--22-->onSaveInstanceState()...."+" /n getTaskId() == "+
        		getTaskId()+"   isTaskRoot() == "+isTaskRoot());
	}

	@Override
	protected void onStart() {
		super.onStart();
		Log.v("daming", "App2Activity--22-->onStart()...."+" /n getTaskId() == "+
        		getTaskId()+"   isTaskRoot() == "+isTaskRoot());
	}

	@Override
	protected void onStop() {
		super.onStop();
		Log.v("daming", "App2Activity--22-->onStop()...."+" /n getTaskId() == "+
        		getTaskId()+"   isTaskRoot() == "+isTaskRoot());
	}
    
}

     manifest的属性为默认值;


     (3)然后启动App1---->点击调用第二个App2button按钮的log: 

                       

      从log中可以看出:从App1到App2是在一个task栈中,值都是33,并且App1Activity是位于栈底。根节点的。点击按钮的时候,调用App1Activity的onSaveInstanceState()----->onPause()----->App2Activity的onCreate()----->onStart()---->onResume()---->App1Activity的onStop()方法。

 

     (4)点击调用第一个App1---->button按钮;

               

   

        看一下log:

 还在一个栈中,数字变了因为我退出后从新操作的,把以前的log清空了,这时候发现了一个奇怪的现象,启动第一个App1的时候,App2从onStop()---->onDestroy()了,彻底销毁了;这个对象就不存在了。这也就验证了browser点击视频播放,点击home键,再点击browser,视频播放停止的现象的,android的singleTask就是这个设计的。挺独特的。

      (5) 要想解决这一个问题,也是有办法的,我经过两天的钻研,终于找到了方法,现分享给大家:就是在App1中启动一个activity,这个activity的launcherMode=“standard”,然后由它来启动App1Activity,这样就避免了这一现象;

      启动一个默认的activity:LoginApp1Activity代码:

package com.cn.daming.app1;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;

public class LoginApp1Activity extends Activity{

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		Intent intent = (Intent)getIntent().clone();
		intent.setClass(LoginApp1Activity.this, App1Activity.class);
		startActivity(intent);
		finish();
	}
}

      

         manifest.xml中的代码:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.cn.daming.app1"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="8" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:launchMode="standard"
            android:alwaysRetainTaskState="true"
            android:name=".LoginApp1Activity" 
            >
            <intent-filter >
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:label="@string/app_name"
            android:launchMode="singleTask"
            android:alwaysRetainTaskState="true"
            android:name=".App1Activity" >
            <intent-filter >
                <action android:name="android.intent.action.MAIN" />
                <action android:name="android.intent.action.DELETE"/>
            </intent-filter>
        </activity>
    </application>

</manifest>

     

 

      (6)从启动App1---->点击按钮启动App2---->点击home键---->在点击App1---->进入App2界面的log如下:


       说明:从mainmenu见面点击App1的时候,调用的是App2Activiy的onRestart()方法---->onStart()---->onResume()方法,而不是调用的onDestroy()方法。

 

        原因梳理:

 在Task范围内只产生一个实例。且这个实例启动完成后是在栈顶。

如果在一个apk应用里,singleTask的A启动了一个B,B中启动了A,这样的情况怎么办呢?如果A是在原Task中启  动,系统会将B结束,然后保持原A在栈顶。

设置了"singleTask"启动模式的Activity的特点:

1. 设置了"singleTask"启动模式的Activity,它在启动的时候,会先在系统中查找属性值affinity等于它的属性值taskAffinity的任务存在;如果存在这样的任务,它就会在这个任务中启动(此后在根据第2条检查),否则就会在新任务中启动。因此,如果我们想要设置了"singleTask"启动模式的Activity在新的任务中启动,就要为它设置一个独立的taskAffinity属性值。

2. 如果设置了"singleTask"启动模式的Activity不是在新的任务中启动时,它会在已有的任务中查看是否已经存在相应的Activity实例,如果存在,就会把位于这个Activity实例上面的Activity全部结束掉,即最终这个Activity实例会位于任务的Stack顶端中(官方文档说同时也在root of Stack,Stack底端,存疑1)。

   真正的原因是singleTask的模式有个FLAG_ACTIVITY_BROUGHT_TO_FRONT,它才是最后的罪魁祸首!

   官方文档上的解释如下:

    If, when starting the activity, there is already a task running that starts with this activity, then instead of starting a new instance the current task is brought to the front. The existing instance will receive a call to Activity.onNewIntent() with the new Intent that is being started, and with the Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT flag set. This is a superset of the singleTop mode, where if there is already an instance of the activity being started at the top of the stack, it will receive the Intent as described there (without the FLAG_ACTIVITY_BROUGHT_TO_FRONT flag set). See the Tasks and Back Stack document for more details about tasks.

    大致意思为:当启动一个activity的时候,如果这个task中有这个activity的实例,这个实例就会被放到当前的task的前面,这个存在的实例是通过onNewIntent()调用实现的。伴随着这个activity的flag---->(FLAG_ACTIVITY_BROUGHT_TO_FRONT )会被设置,这是singleTop模式的超集,如果当这儿已经有个实例的activity在这个栈的顶部,这时候它不会设置这个flag(FLAG_ACTIVITY_BROUGHT_TO_FRONT )的值,详解看tasks和栈说明,更清楚的了解这方面的知识!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android中launcherMode="singleTask"详解【android源码解析六】 的相关文章

  • Android Studio 无法正确构建 flutter 应用程序

    我正在开发一个应用程序 当我打开它时build gradle文件 出现了几个错误 def localProperties new Properties def localPropertiesFile rootProject file loc
  • android:应用内计费:错误响应:7:项目已拥有

    我正在学习为我的应用程序实现应用程序内计费 以便人们可以在按下捐赠按钮时捐赠美元 用户可以多次捐赠 即购买的是消耗品 下面的代码来自 TrivalDrive 示例和网上的一些教程 Code IabHelper mHelper static
  • 从具有 Holo 父主题的 Theme.Light 继承 editText

    我想继承editText from android Theme而我的父主题是android Theme Holo Light 有没有什么 干净 的方法可以将资源从 android sdk 文件夹复制到我的项目中 所以我的想法是有一个自定义主
  • 如何使用 Android 加速计?

    我正在尝试构建一个应用程序来读取手机上加速度计的值 该应用程序仅支持 Android 2 1 如何使用 2 1 兼容代码读取加速度计 从这个开始 public class yourActivity extends Activity impl
  • 如何减少 MediaCodec H264 编码器延迟

    我正在尝试使用 Android6 0 的 MediaCodec 将 h264 实时低延迟编码为流 编码器大约有 6 帧延迟 我想知道如何减少 代码来自屏幕记录 cpp https android googlesource com platf
  • “赠送”应用内购买 Android

    有没有办法将 Google Billing 中的应用内购买 赠送 给特定帐户 我把这个问题放在这里是因为如果有一种方法可以以编程方式完成它 那很好 但不是必须的 在官方文档中找不到任何相关内容 我想要这个的原因是因为我的一个应用程序目前处于
  • 如何在android中的网格视图中进行分页? [关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 如何在 android 中对 gri
  • Nexus 10 (Android 4.4.2) 中未设置 FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY

    我有一个主要活动 A 它将调用另一个活动 B 其 oncreate 如下所述 当我在活动 B 中按主页按钮并按最近使用的应用程序时 从未设置标志 FLAG ACTIVITY LAUNCCHED FROM HISTORY 我使用的是三星 Ne
  • 如何将数据一次性插入sqlite数据库

    我需要将数据添加到 sqlite 数据库一次 也就是说 我希望我的应用程序的用户看到该数据已加载 如何做到这一点 我使用查询执行了它 INSERT INTO TABLE NAME VALUES 值1 值2 值3 值N 但是每次应用程序打开该
  • 设备收到 GCM Android 通知但未显示

    尽管通知已在应用程序本身中注册 但我的 Ionic Android 应用程序的 GCM Cloud 消息通知未出现在我的设备的主屏幕中 我正在使用 npm 模块node gcm https www npmjs com package nod
  • 从alertDialog构建器获取editText值

    我是新来的android 我需要得到editText s搜索某些东西的价值 但是当我运行程序时 出现错误null pointer exception EditText etSearch 没有获取它的文本 请帮我 谢谢 public bool
  • Android - 向 Android 的内置应用程序添加菜单项

    我想在 联系人 菜单中添加按钮或菜单项 是否可以 我使用 Android 2 2 和 Eclipse 3 6 1 不可以 内置 Android 应用程序不支持菜单中的意图选项 即您无法向其中添加任何内容
  • 如何打开相机然后切换到图像模式(反之亦然)

    就我而言 我想拍照或捕捉视频 实际上 如果我创建单独的意图 我可以做到这些 我的意思是我可以将相机打开为图像模式或视频模式 但无法在它们之间切换 这与我使用的意图过滤器有关吗 我应该怎么办 我如何在它们之间切换 我有同样的问题 在我想放置一
  • Android studio - 如何使用 gradle 中的可执行 jar 文件

    所以我有一个 custom rules xml 文件 我试图在 gradle 中重建 到目前为止 我对其他所有事情都没有问题 但我试图完成的最后一部分是在我使用 gradle 构建过程生成的未签名 apk 上运行一个特殊的 apk 签名工具
  • 如何在android中格式化长整型以始终显示两位数

    我有一个倒计时器 显示从 60 到 0 的秒数 1 分钟倒计时器 当它达到 1 位数字 例如 9 8 7 时 它显示 9 而不是 09 我尝试使用String format B 02d B x 我将 x 从 long 转换为字符串 它不起作
  • 如何使用Android平板模拟器?

    我想开始 Android 平板电脑应用程序开发 我已经使用 1024X600 模拟器创建了一个新的 avd 并启动了 但是当我单击任何应用程序时如何从模拟器返回 是否有任何特定的 api 可用于开发 android 平板电脑应用程序 或者我
  • Android ProGuard 混淆库:让类无法工作

    Intro 我在 AS 1 项目中有 2 个模型 带有一些 公共 API 类 的 Android 库项目 Android APP依赖上述库 库模块在依赖列表中 Task 我想混淆我的库项目 因为我想将其公开为公共 SDK 但又要保护我的代码
  • Android相对布局放置问题

    我在 XML 中为列表项创建相对布局以用于 ListView 中的一系列项目时遇到问题 我已经尝试了几个小时 并且正在撕扯我的头发 试图让它看起来像我想要的那样 但无法让所有东西都出现在正确的位置 而不是重叠或错位 我可以获取第一张图像和接
  • Android 数组列表如何使用

    所以我有一个标记列表 我需要弄清楚如何处理它们 我已经搜索了几个小时 但没有明确说明如何使用这些信息或如何从列表中获取信息 请解释或分解我的代码并告诉我它到底是如何工作的 这样我就可以在地图上设置标记 这就是我将标记添加到列表中的方法 现在
  • Android 并获取 id 转换为字符串的视图

    在 Android 项目的 Java 代码中 如果您想要视图资源的引用 您可以执行以下操作 View addButton findViewById R id button 0 在上面的 R id button 0 不是一个字符串 是否可以通

随机推荐

  • git 切换分支

    1 查看所有分支 git branch a 2 查看当前分支 号表示当前分支 git branch 3 切换分支 git checkout 39 分支名 39 4 修改代码仓库 git remote set url origin 39 仓库
  • 嵌入式安卓开发:使用Camera2获取相机

    文章目录 Camera2介绍Camera2的主要API类介绍CameraManager通过CameraManage获取Cameracharacteristics通过CameraManage获取CameraDevice从CameraDevic
  • ESP32的VSPI和HSPI

    说明 SPI共有4根线 xff0c MOSI MISO CS CLK xff0c 在ESP32中对应规则如下表 xff1a ESP32共有4个SPI xff0c 但是用户能够使用的只有2个SPI xff0c 分为VSPI和HSPI 引脚接口
  • Android Studio添加EasyPemissions

    问题描述 按照EasyPermissions主页描述的那样添加完依赖后 xff0c 在程序中使用还是报错 xff1a Failed to resolve pub devrel easypermissions 0 3 0 解决方法 首先 xf
  • ROS:话题编程 订阅者Subscriber的简单实现

    1 xff08 1 xff09 编写一个C 43 43 话题订阅者 该例程将订阅 turtle1 pose话题 xff0c 消息类型turtlesim Pose include lt ros ros h gt include 34 turt
  • 浅谈操作系统-启动过程

    前言 时光匆碌 xff0c 不知不觉都大三了 xff0c 在众多的专业课的学习中也算是找到了一些乐趣 xff0c 纸上得来终觉浅 xff0c 所以决定完整的回顾一下整个操作系统的知识 xff0c 为了理论与实践相结合 xff0c 以学校实验
  • 串口调试常见问题和排查方法

    串口UART作为嵌入式应用和通讯领域中最常用的接口之一 xff0c 接口协议虽然简单 xff0c 但在实际应用中不同设备之间的通讯也会存在各种小问题 xff0c 下面对使用中各种常见的问题做下总结和梳理 xff0c 可作为调试参考 串口可分
  • 3 POSIX 多任务及同步机制-拓展实验 条件变量与生产者-消费者问题

    3 POSIX 多任务及同步机制 拓展实验 条件变量与生产者 消费者问题 一 xff0e 实验目的 理解进程 线程同步问题 掌握POSIX条件变量机制的使用方法 深入理解在动态并发环境下 xff0c 进程 线程在运行过程中的资源竞争应发的问
  • 深度优先搜索DFS和广度优先搜索BFS

    相关博客链接 xff1a https www cnblogs com rjgcs p 5198467 html https blog csdn net xiaobo Clanguage article details 88085074 ht
  • 埋头努力之前得先看清方向

    埋头努力之前得先看清方向 写在工作半年 xff0c 第一次拿到绩效结果后 工作和学生时代最大的不同在于 xff0c 工作之后很多事情是消耗型的 xff0c 领导和组织看重的是输出 xff0c 而个人的成长和提升需要自己全权负责 这两者之间是
  • 周末写点轻松的吧

    一直觉得 有一段跟自己独处的时光是很幸福的事情 难得的双休日 xff0c 中午太阳很好 xff0c 照在绿萝上 xff0c 嫩嫩绿绿的叶子很漂亮 前几周去花市买了盆栀子花 xff0c 整整一大盆 xff0c 上面布满了花苞 以前见过开了花的
  • 为什么大部分的C/C++码农都成不了高级工程师?真实原因是缺少核心能力!

    一般来说技术团队的金字塔顶尖往往是技术最牛的人做底层架构师 xff08 或高级工程师 xff09 所以底层架构师在广大码农中的占比大概平均不到 20 然而80 码农干上许多年都是重复以下内容 xff0c 所以做不了架构师 xff0c 正在辛
  • kaggle邮箱不能验证+安装python的Speedml库

    注册kaggle账号遇到一些问题 下面是具体问题和解决方案 希望遇到同样问题的小伙伴不要再踩到坑啦 1kaggle邮箱不能验证You did not enter the correct captcha response Please try
  • 用程序验证生日“悖论”

    生日 悖论 其实并不是悖论 xff0c 它是说在一个人数超过23人的集体中 xff0c 至少有两个人生日在同一天的概率约为0 5 因为这个理论上的概率与人们的直觉不符 xff0c 才会被称为 悖论 我们可以用一个简单的小程序验证它哦 xff
  • python 中 defaultdict 的用法

    场景 xff1a 统计一个字符串列表中每个字符串的频数 一个明显的方法是建立一个键是字符串 xff0c 值是频数的字典 方法1 xff1a word count 61 for word in document if word in word
  • K近邻算法所面临的维数灾难问题

    K近邻算法的基本思想 K近邻算法是一种常用的监督学习方法 xff0c 其原理非常简单 xff1a 给定测试样本 xff0c 基于某种距离找出训练集中与其最靠近的K个训练样本 xff0c 然后基于这K个邻居的信息来进行预测 两个基本要素 xf
  • Android广播发送机制剖析【android广播系列二】

    上篇博客大致说了说广播的注册机制 xff0c 动态注册和静态注册广播的原理还不一样 xff0c 动态广播最后HashMap中了 xff0c 最后放到mReceiverResolver中 xff0c 以后当ActivityManagerSer
  • 观察者模式--Java设计模式

    观察者模式定义 xff1a 定义了对象之间的一对多的依赖 xff0c 这样一来 xff0c 当一个对象发生改变状态的时候 xff0c 它的所有依赖者都会收到通知并自动更新 参考如下图 xff1a 观察者设计模式也叫发布 订阅模式 也可以称作
  • Android——RuntimePermission介绍

    1 介绍 androidM版本上 xff0c 对permission的管理做了部分改动 xff0c 针对dangerous permission xff0c 不在安装的时候给予权限 xff0c 而是在运行过程中咨询用户是否给予app响应的权
  • Android中launcherMode="singleTask"详解【android源码解析六】

    android中launcherMode有4中属性 xff1a standard 默认 xff0c singleTop xff0c singleTask和 singleInstance xff1b 网上有好多例子讲解这四种关系的 xff1a