解开Android应用程序组件Activity的"singleTask"之谜(1)

简介:

  在Android应用程序中,可以配置Activity以四种方式来启动,其中最令人迷惑的就是"singleTask"这种方式了,官方文档称以这种方式启动的Activity总是属于一个任务的根Activity。果真如此吗?本文将为你解开Activity的"singleTask"之谜。

        在解开这个谜之前,我们先来简单了解一下在Android应用程序中,任务(Task)是个什么样的概念。我们知道,Activity是Android应用程序的基础组件之一,在应用程序运行时,每一个Activity代表一个用户操作。用户为了完成某个功能而执行的一系列操作就形成了一个Activity序列,这个序列在Android应用程序中就称之为任务,它是从用户体验的角度出发,把一组相关的Activity组织在一起而抽象出来的概念。

        对初学者来说,在开发Android应用程序时,对任务的概念可能不是那么的直观,一般我们只关注如何实现应用程序中的每一个Activity。事实上,Android系统中的任务更多的是体现是应用程序运行的时候,因此,它相对于Activity来说是动态存在的,这就是为什么我们在开发时对任务这个概念不是那么直观的原因。不过,我们在开发Android应用程序时,还是可以配置Activity的任务属性的,即告诉系统,它是要在新的任务中启动呢,还是在已有的任务中启动,亦或是其它的Activity能不能与它共享同一个任务,具体配置请参考官方文档:

       http://developer.android.com/guide/topics/fundamentals/tasks-and-back-stack.html

       它是这样介绍以"singleTask"方式启动的Activity的:

       The system creates a new task and instantiates the activity at the root of the new task. However, if an instance of the activity already exists in a separate task, the system routes the intent to the existing instance through a call to its onNewIntent() method, rather than creating a new instance. Only one instance of the activity can exist at a time.

       它明确说明,以"singleTask"方式启动的Activity,全局只有唯一个实例存在,因此,当我们第一次启动这个Activity时,系统便会创建一个新的任务,并且初始化一个这样的Activity的实例,放在新任务的底部,如果下次再启动这个Activity时,系统发现已经存在这样的Activity实例,就会调用这个Activity实例的onNewIntent成员函数,从而把它激活起来。从这句话就可以推断出,以"singleTask"方式启动的Activity总是属于一个任务的根Activity。

       但是文档接着举例子说明,当用户按下键盘上的Back键时,如果此时在前台中运行的任务堆栈顶端是一个"singleTask"的Activity,系统会回到当前任务的下一个Activity中去,而不是回到前一个Activity中去,如下图所示:

 

        真是坑爹啊!有木有!前面刚说"singleTask"会在新的任务中运行,并且位于任务堆栈的底部,这里在Task B中,一个赤裸裸的带着"singleTask"标签的箭头无情地指向Task B堆栈顶端的Activity Y,刚转身就翻脸不认人了呢!

        狮屎胜于熊便,我们来做一个实验吧,看看到底在启动这个"singleTask"的Activity的时候,它是位于新任务堆栈的底部呢,还是在已有任务的顶部。

        首先在Android源代码工程中创建一个Android应用程序工程,名字就称为Task吧。关于如何获得Android源代码工程,请参考在Ubuntu上下载、编译和安装Android最新源代码一文;关于如何在Android源代码工程中创建应用程序工程,请参考在Ubuntu上为Android系统内置Java应用程序测试Application Frameworks层的硬件服务一文。这个应用程序工程定义了一个名为shy.luo.task的package,这个例子的源代码主要就是实现在这里了。下面,将会逐一介绍这个package里面的文件。

        应用程序的默认Activity定义在src/shy/luo/task/MainActivity.java文件中:

 
 
  1. package shy.luo.task;      
  2.      
  3. import android.app.Activity;     
  4. import android.content.Intent;     
  5. import android.os.Bundle;     
  6. import android.util.Log;     
  7. import android.view.View;     
  8. import android.view.View.OnClickListener;     
  9. import android.widget.Button;     
  10.      
  11. public class MainActivity extends Activity  implements OnClickListener {     
  12.     private final static String LOG_TAG = "shy.luo.task.MainActivity";     
  13.      
  14.     private Button startButton = null;     
  15.      
  16.     @Override     
  17.     public void onCreate(Bundle savedInstanceState) {     
  18.         super.onCreate(savedInstanceState);     
  19.         setContentView(R.layout.main);     
  20.      
  21.         startButton = (Button)findViewById(R.id.button_start);     
  22.         startButton.setOnClickListener(this);     
  23.      
  24.         Log.i(LOG_TAG, "Main Activity Created.");     
  25.     }     
  26.      
  27.     @Override     
  28.     public void onClick(View v) {     
  29.         if(v.equals(startButton)) {     
  30.             Intent intent = new Intent("shy.luo.task.subactivity");     
  31.             startActivity(intent);     
  32.         }     
  33.     }     
  34. }     

 

        它的实现很简单,当点击它上面的一个按钮的时候,就会启动另外一个名字为“shy.luo.task.subactivity”的Actvity。
        名字为“shy.luo.task.subactivity”的Actvity实现在src/shy/luo/task/SubActivity.java文件中:

 
 
  1. package shy.luo.task;     
  2.      
  3. import android.app.Activity;     
  4. import android.os.Bundle;     
  5. import android.util.Log;     
  6. import android.view.View;     
  7. import android.view.View.OnClickListener;     
  8. import android.widget.Button;     
  9.      
  10. public class SubActivity extends Activity implements OnClickListener {     
  11.     private final static String LOG_TAG = "shy.luo.task.SubActivity";     
  12.      
  13.     private Button finishButton = null;     
  14.      
  15.     @Override     
  16.     public void onCreate(Bundle savedInstanceState) {     
  17.         super.onCreate(savedInstanceState);     
  18.         setContentView(R.layout.sub);     
  19.      
  20.         finishButton = (Button)findViewById(R.id.button_finish);     
  21.         finishButton.setOnClickListener(this);     
  22.              
  23.         Log.i(LOG_TAG, "Sub Activity Created.");     
  24.     }     
  25.      
  26.     @Override     
  27.     public void onClick(View v) {     
  28.         if(v.equals(finishButton)) {     
  29.             finish();     
  30.         }     
  31.     }     
  32. }     

        它的实现也很简单,当点击上面的一个铵钮的时候,就结束自己,回到前面一个Activity中去。

        再来看一下应用程序的配置文件AndroidManifest.xml:

 
 
  1. <?xml version="1.0" encoding="utf-8"?>     
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"     
  3.     package="shy.luo.task"     
  4.     android:versionCode="1"     
  5.     android:versionName="1.0">     
  6.     <application android:icon="@drawable/icon" android:label="@string/app_name">     
  7.         <activity android:name=".MainActivity"     
  8.                   android:label="@string/app_name">     
  9.             <intent-filter>     
  10.                 <action android:name="android.intent.action.MAIN" />     
  11.                 <category android:name="android.intent.category.LAUNCHER" />     
  12.             </intent-filter>     
  13.         </activity>     
  14.         <activity android:name=".SubActivity"     
  15.                   android:label="@string/sub_activity"   
  16.                   android:launchMode="singleTask">     
  17.             <intent-filter>     
  18.                 <action android:name="shy.luo.task.subactivity"/>     
  19.                 <category android:name="android.intent.category.DEFAULT"/>     
  20.             </intent-filter>     
  21.         </activity>     
  22.     </application>     
  23. </manifest>     

 

      注意,这里的SubActivity的launchMode属性配置为"singleTask"。
       再来看界面配置文件,它们定义在res/layout目录中,main.xml文件对应MainActivity的界面: 

 
 
  1. <?xml version="1.0" encoding="utf-8"?>     
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"     
  3.     android:orientation="vertical"     
  4.     android:layout_width="fill_parent"     
  5.     android:layout_height="fill_parent"      
  6.     android:gravity="center">     
  7.         <Button      
  8.             android:id="@+id/button_start"     
  9.             android:layout_width="wrap_content"     
  10.             android:layout_height="wrap_content"     
  11.             android:gravity="center"     
  12.             android:text="@string/start" >     
  13.         </Button>     
  14. </LinearLayout>     

      而sub.xml对应SubActivity的界面: 

 
 
  1. <?xml version="1.0" encoding="utf-8"?>     
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"     
  3.     android:orientation="vertical"     
  4.     android:layout_width="fill_parent"     
  5.     android:layout_height="fill_parent"      
  6.     android:gravity="center">     
  7.         <Button      
  8.             android:id="@+id/button_finish"     
  9.             android:layout_width="wrap_content"     
  10.             android:layout_height="wrap_content"     
  11.             android:gravity="center"     
  12.             android:text="@string/finish" >     
  13.         </Button>     
  14. </LinearLayout>     

  字符串文件位于res/values/strings.xml文件中: 

 
 
  1. <?xml version="1.0" encoding="utf-8"?>     
  2. <resources>     
  3.     <string name="app_name">Task</string>     
  4.     <string name="sub_activity">Sub Activity</string>     
  5.     <string name="start">Start singleTask activity</string>     
  6.     <string name="finish">Finish activity</string>     
  7. </resources>     

    最后,我们还要在工程目录下放置一个编译脚本文件Android.mk: 

 
 
  1. LOCAL_PATH:= $(call my-dir)     
  2. include $(CLEAR_VARS)     
  3.      
  4. LOCAL_MODULE_TAGS :optional     
  5.      
  6. LOCAL_SRC_FILES := $(call all-subdir-java-files)     
  7.      
  8. LOCAL_PACKAGE_NAME :Task     
  9.      
  10. include $(BUILD_PACKAGE)     

        这样,原材料就准备好了,接下来就要编译了。有关如何单独编译Android源代码工程的模块,以及如何打包system.img,请参考如何单独编译Android源代码中的模块一文。
        执行以下命令进行编译和打包: 

 
 
  1. USER-NAME@MACHINE-NAME:~/Android$ mmm packages/experimental/Task       
  2. USER-NAME@MACHINE-NAME:~/Android$ make snod      

       这样,打包好的Android系统镜像文件system.img就包含我们前面创建的Task应用程序了。







本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966123,如需转载请自行联系原作者
目录
相关文章
|
16天前
|
移动开发 Java Android开发
构建高效Android应用:探究Kotlin与Java的性能差异
【4月更文挑战第3天】在移动开发领域,性能优化一直是开发者关注的焦点。随着Kotlin的兴起,其在Android开发中的地位逐渐上升,但关于其与Java在性能方面的对比,尚无明确共识。本文通过深入分析并结合实际测试数据,探讨了Kotlin与Java在Android平台上的性能表现,揭示了在不同场景下两者的差异及其对应用性能的潜在影响,为开发者在选择编程语言时提供参考依据。
|
17天前
|
数据库 Android开发 开发者
构建高效Android应用:Kotlin协程的实践指南
【4月更文挑战第2天】随着移动应用开发的不断进步,开发者们寻求更流畅、高效的用户体验。在Android平台上,Kotlin语言凭借其简洁性和功能性赢得了开发社区的广泛支持。特别是Kotlin协程,作为一种轻量级的并发处理方案,使得异步编程变得更加简单和直观。本文将深入探讨Kotlin协程的核心概念、使用场景以及如何将其应用于Android开发中,以提高应用性能和响应能力。通过实际案例分析,我们将展示协程如何简化复杂任务,优化资源管理,并为最终用户提供更加流畅的体验。
|
17天前
|
开发框架 安全 Android开发
探索安卓系统的新趋势:智能家居应用的蓬勃发展
随着智能家居概念的兴起,安卓系统在智能家居应用领域的应用日益广泛。本文将探讨安卓系统在智能家居应用开发方面的最新趋势和创新,以及其对用户生活的影响。
13 2
|
20天前
|
缓存 监控 Java
构建高效Android应用:从优化用户体验到提升性能
在竞争激烈的移动应用市场中,为用户提供流畅和高效的体验是至关重要的。本文深入探讨了如何通过多种技术手段来优化Android应用的性能,包括UI响应性、内存管理和多线程处理。同时,我们还将讨论如何利用最新的Android框架和工具来诊断和解决性能瓶颈。通过实例分析和最佳实践,读者将能够理解并实施必要的优化策略,以确保他们的应用在保持响应迅速的同时,还能够有效地利用系统资源。
|
21天前
|
Java Android开发
Android 开发获取通知栏权限时会出现两个应用图标
Android 开发获取通知栏权限时会出现两个应用图标
12 0
|
25天前
|
编解码 算法 Java
构建高效的Android应用:内存优化策略详解
随着智能手机在日常生活和工作中的普及,用户对移动应用的性能要求越来越高。特别是对于Android开发者来说,理解并实践内存优化是提升应用程序性能的关键步骤。本文将深入探讨针对Android平台的内存管理机制,并提供一系列实用的内存优化技巧,以帮助开发者减少内存消耗,避免常见的内存泄漏问题,并确保应用的流畅运行。
|
18天前
|
Java Android开发 开发者
构建高效Android应用:Kotlin协程的实践与优化
在响应式编程范式日益盛行的今天,Kotlin协程作为一种轻量级的线程管理解决方案,为Android开发带来了性能和效率的双重提升。本文旨在探讨Kotlin协程的核心概念、实践方法及其在Android应用中的优化策略,帮助开发者构建更加流畅和高效的应用程序。通过深入分析协程的原理与应用场景,结合实际案例,本文将指导读者如何优雅地解决异步任务处理,避免阻塞UI线程,从而优化用户体验。
|
23天前
|
Java 编译器 Android开发
构建高效Android应用:探究Kotlin与Java的性能差异
在开发高性能的Android应用时,选择合适的编程语言至关重要。近年来,Kotlin因其简洁性和功能性受到开发者的青睐,但其性能是否与传统的Java相比有所不足?本文通过对比分析Kotlin与Java在Android平台上的运行效率,揭示二者在编译速度、运行时性能及资源消耗方面的具体差异,并探讨在实际项目中如何做出最佳选择。
17 4
|
1天前
|
缓存 移动开发 Android开发
构建高效Android应用:从优化用户体验到提升性能表现
【4月更文挑战第18天】 在移动开发的世界中,打造一个既快速又流畅的Android应用并非易事。本文深入探讨了如何通过一系列创新的技术策略来提升应用性能和用户体验。我们将从用户界面(UI)设计的简约性原则出发,探索响应式布局和Material Design的实践,再深入剖析后台任务处理、内存管理和电池寿命优化的技巧。此外,文中还将讨论最新的Android Jetpack组件如何帮助开发者更高效地构建高质量的应用。此内容不仅适合经验丰富的开发者深化理解,也适合初学者构建起对Android高效开发的基础认识。
2 0
|
1天前
|
移动开发 Android开发 开发者
构建高效Android应用:采用Kotlin进行内存优化的策略
【4月更文挑战第18天】 在移动开发领域,性能优化一直是开发者关注的焦点。特别是对于Android应用而言,由于设备和版本的多样性,确保应用流畅运行且占用资源少是一大挑战。本文将探讨使用Kotlin语言开发Android应用时,如何通过内存优化来提升应用性能。我们将从减少不必要的对象创建、合理使用数据结构、避免内存泄漏等方面入手,提供实用的代码示例和最佳实践,帮助开发者构建更加高效的Android应用。
5 0