精华内容
下载资源
问答
  • 答案:任务进程可以说没什么关系,不同进程中的Activity可以在同一个任务中,任务可以看作是系统级别的东西,跟具体某个app没关系 A启动B的Activity1时,Activity1是运行在A进程还是B进程中? 答案:默认...

    基于Android11设备测试结果
    疑问:

    • 任务栈和进程关系?
      答案:任务栈和进程可以说没什么关系,不同进程中的Activity可以在同一个任务栈中,任务栈可以看作是系统级别的东西,跟具体某个app没关系

    • A启动B的Activity1时,Activity1是运行在A进程还是B进程中?
      答案:默认情况下是B的进程,除非Activity1有指定其他进程,即使Activity1指定的进程名称与A的一样,它也不是跟A运行在同一个进程中,而是同名但是pid不同的另一个进程中

    • "standard"启动模式下,A启动B的Activity1时,将Activity1的taskAffinity属性改成A的包名,再从B启动Activity1,启动的这两个Activity1会在同一个任务栈中吗?
      答案:不会,它们会存在AB各自的任务栈中,因为taskAffinity属性只对SingleTask启动模式(FLAG_ACTIVITY_NEW_TASK)或者设置了allowTaskReparenting为true时(默认false)才有效

    • 如果只是将Activity1启动模式改成SingleTask,会出现什么情况?
      答案:不管allowTaskReparenting属性是true还是false,当启动B时,Activity1总是会回到B界面

    • 如果Activity1启动模式改成SingleTask,同时设置taskAffinity属性值为A的包名,启动A时Activity1会自动回到A的界面吗?
      答案:不管allowTaskReparenting属性是true还是false,启动A时Activity1都不会回到A界面,同时会发现再打开B时,Activity1也不在B界面了

    • 标准启动模式下,设置taskAffinity属性值为A的包名,allowTaskReparenting改为true,会怎么样?
      答案:B启动Activity1后,再打开A,Activity1不会回到A,再次回到B发现Activity1也不见了

    结论:

    • taskAffinity只有在SingleTask(FLAG_ACTIVITY_NEW_TASK)或者allowTaskReparenting为true时才有效
      • SingleTask模式下:
        • 如果taskAffinity的值与原本所属APP包名不一致:则该Activity完全在独立的一个任务栈中,不会自动回到A或者B的任务栈,回到Home后重新打开A后者B都看不到打开的那个Activity,只能通过最近任务那里打开
        • 如果taskAffinity的值与原本所属APP包名一样(默认是一样的):则activity1刚启动时判断B的任务栈是否存在,存在则入B栈,不存在则创建, 当打开B时会自动会看到Activity1出现在B
        • allowTaskReparenting不管是true还是false都对上面结果不影响
        • process指定其他进程也对上面结果不影响
      • SingleInstance模式:
        • 不管设置什么,都在独立的任务栈中,不会自动回到与它taskAffinity相同的任务栈,最近任务里可以看到activity1
      • standard/SingleTop,并且allowTaskReparenting为true时:
        • 如果taskAffinity的值与原本所属APP包名不一致:则该Activity完全在独立的一个任务栈中,不会自动回到A或者B的任务栈,回到Home后重新打开A后者B都看不到打开的那个Activity,只能通过最近任务那里打开
        • 如果taskAffinity的值与原本所属APP包名一样(默认是一样的):则activity1刚启动时是存在于A的任务栈中的,当A回到Home或者打开B时会自动回到B所在任务栈
        • 如果process指定了其他进程,则无法自动回到B,并且Home后再回到A,也是看不到activity1的
        • 如果taskAffinity的值改成A的包名,从B启动后,再打开A,并不会进入A的任务栈,再回来B也看不到Activity1
    • allowTaskReparenting属性为true生效的前提是:taskAffinity属性只能是它所在app的包名,一旦改成其它的了,AB两边都回不去了,会跑到最近任务那里去
    • 两个不同app的Activity即使设置了SingleTask和相同taskAffinity属性也不能进入同一个任务栈中

    查看任务栈命令:
    adb shell dumpsys activity activities

    Android11以下搜索关键字:Running activities

    Android11以及以上搜索关键字:Task display areas in top down Z order

    展开全文
  • android进程 清理及activity栈管理

    千次阅读 2014-06-16 14:08:39
    MainActivity如下: [java] view plaincopy ... import android.app.Activity; import android.content.Context; import android.content.Intent; import android.os.Bundle; import a
    MainActivity如下:
    
    1. package come.on;  
    2.   
    3. import android.app.Activity;  
    4. import android.content.Context;  
    5. import android.content.Intent;  
    6. import android.os.Bundle;  
    7. import android.view.View;  
    8. import android.view.View.OnClickListener;  
    9. import android.widget.Button;  
    10. import android.widget.EditText;  
    11. /** 
    12.  * Demo描述: 
    13.  * 内存清理及内存工具类 
    14.  * 具体代码参见工具类MemoryUtils 
    15.  *  
    16.  */  
    17. public class MainActivity extends Activity {  
    18.     private Button mButton;  
    19.     private Context mContext;  
    20.     private EditText mEditText;  
    21.     @Override  
    22.     public void onCreate(Bundle savedInstanceState) {  
    23.         super.onCreate(savedInstanceState);  
    24.         setContentView(R.layout.main);  
    25.         init();  
    26.     }  
    27.       
    28.     private void init(){  
    29.         mContext=this;  
    30.         Intent intent=new Intent("come.on.service");  
    31.         //该Service无用,可去掉  
    32.         startService(intent);  
    33.           
    34.         mEditText=(EditText) findViewById(R.id.editText);  
    35.         mButton=(Button) findViewById(R.id.button);  
    36.         mButton.setOnClickListener(new OnClickListener() {  
    37.             @Override  
    38.             public void onClick(View v) {  
    39.                 MemoryUtils.cleanMemory(mContext,mEditText);  
    40.             }  
    41.         });  
    42.     }  
    43.      
    44. }  

    MemoryUtils如下:
    1. package come.on;  
    2.   
    3. import java.util.List;  
    4. import java.io.FileReader;  
    5. import android.os.Process;  
    6. import java.io.IOException;  
    7. import java.io.BufferedReader;  
    8. import android.widget.EditText;  
    9. import android.content.Context;  
    10. import java.io.InputStreamReader;  
    11. import android.app.ActivityManager;  
    12. import android.content.ComponentName;  
    13. import android.app.ActivityManager.MemoryInfo;  
    14. import android.app.ActivityManager.RunningTaskInfo;  
    15. import android.app.ActivityManager.RunningAppProcessInfo;  
    16.   
    17. /** 
    18.  * 文档描述: 
    19.  * 内存工具类 
    20.  *  
    21.  * 包括内容: 
    22.  * 1 内存清理即杀掉进程的几种方式 
    23.  * 2 获取内存总大小及其可用大小 
    24.  * 3 判断栈顶Activity名及其所属进程名 
    25.  *  
    26.  * 注意权限: 
    27.  * <uses-permission android:name="android.permission.GET_TASKS"/>   
    28.  * <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" /> 
    29.  *  
    30.  * 文档日期: 
    31.  * 2014年5月30日10:01:55 
    32.  * 
    33.  */  
    34. public class MemoryUtils {  
    35.       
    36.     //----------> 以下为杀掉进程的几种方式  
    37.       
    38.     public static void cleanMemory(Context context,EditText editText) {  
    39.         long beforeCleanMemory=getAvailMemory(context);  
    40.         System.out.println("---> 清理前可用内存大小:"+beforeCleanMemory+"M");  
    41.         ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);  
    42.         RunningAppProcessInfo runningAppProcessInfo = null;  
    43.         List<RunningAppProcessInfo> runningAppProcessInfoList = activityManager.getRunningAppProcesses();  
    44.         for (int i = 0; i < runningAppProcessInfoList.size(); ++i) {  
    45.             runningAppProcessInfo = runningAppProcessInfoList.get(i);  
    46.             String processName = runningAppProcessInfo.processName;  
    47.             //调用杀掉进程的方法  
    48.             System.out.println("---> 开始清理:"+processName);  
    49.             killProcessByRestartPackage(context, processName);  
    50.         }  
    51.         long afterCleanMemory=getAvailMemory(context);  
    52.         System.out.println("---> 清理后可用内存大小:"+afterCleanMemory+"M");  
    53.         System.out.println("---> 节约内存大小:"+(afterCleanMemory-beforeCleanMemory)+"M");  
    54.         editText.setText("共清理:"+(afterCleanMemory-beforeCleanMemory)+"M");  
    55.     }  
    56.       
    57.       
    58.       
    59.     //利用activityManager.restartPackage()方法杀死进程  
    60.     //该方法实际调用了activityManager.killBackgroundProcesses()方法  
    61.     public static void killProcessByRestartPackage(Context context,String packageName) {  
    62.         ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);  
    63.         activityManager.restartPackage(packageName);  
    64.         System.gc();  
    65.     }  
    66.       
    67.       
    68.     //利用Process.killProcess(pid)杀死进程  
    69.     //注意事项:  
    70.     //1 该方式可自杀,即杀掉本进程  
    71.     //2 该方式可杀掉其他普通应用进程  
    72.     //3 该方式不可杀掉系统级应用即system/app应用  
    73.     public static void killProcessBykillProcess(int pid){  
    74.         Process.killProcess(pid);  
    75.     }  
    76.       
    77.       
    78.     //利用adb shell命令杀死进程  
    79.     public static void killProcessByAdbShell(int pid) {  
    80.         String cmd = "adb shell kill -9 " + pid;  
    81.         System.out.println("-------> cmd=" + cmd);  
    82.         try {  
    83.             java.lang.Process process = Runtime.getRuntime().exec(cmd);  
    84.             InputStreamReader inputStreamReader = new InputStreamReader(process.getInputStream());  
    85.             BufferedReader bufferedReader = new BufferedReader(inputStreamReader);  
    86.             String line = null;  
    87.             while ((line = bufferedReader.readLine()) != null) {  
    88.                 System.out.println("----> exec shell:" + line);  
    89.             }  
    90.             bufferedReader.close();  
    91.         } catch (IOException e) {  
    92.             e.printStackTrace();  
    93.         }  
    94.     }  
    95.        
    96.      //利用su进程的命令方式杀死进程  
    97.      //1 得到su进程(super进程)  
    98.      //  Runtime.getRuntime().exec("su");  
    99.      //2 利用su进程执行命令  
    100.      //  process.getOutputStream().write(cmd.getBytes());  
    101.     public static void killProcessBySu(int pid) {  
    102.         try {  
    103.             java.lang.Process process = Runtime.getRuntime().exec("su");  
    104.             String cmd = "kill -9 " + pid;  
    105.             System.out.println("-------> cmd = " + cmd);  
    106.             process.getOutputStream().write(cmd.getBytes());  
    107.             if ((process.waitFor() != 0)) {  
    108.                 System.out.println("-------> su.waitFor()!= 0");  
    109.             } else {  
    110.                 System.out.println("------->  su.waitFor()==0 ");  
    111.             }  
    112.         } catch (Exception e) {  
    113.             e.printStackTrace();  
    114.         }  
    115.   
    116.     }  
    117.       
    118.     //----------> 以上为杀掉进程的几种方式  
    119.       
    120.   
    121.       
    122.       
    123.       
    124.     //获取当前进程名  
    125.     public static String getCurrentProcessName(Context context) {  
    126.         int pid = android.os.Process.myPid();  
    127.         ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);  
    128.         for (RunningAppProcessInfo runningAppProcessInfo : activityManager.getRunningAppProcesses()) {  
    129.             if (runningAppProcessInfo.pid == pid) {  
    130.                 String processName=runningAppProcessInfo.processName;  
    131.                 return processName;  
    132.             }  
    133.         }  
    134.         return null;  
    135.     }  
    136.       
    137.       
    138.     //获取栈顶Activity名称  
    139.     public static String getTopActivityName(Context context) {  
    140.         String topActivityName = null;  
    141.         ActivityManager activityManager = (ActivityManager) (context.getSystemService(android.content.Context.ACTIVITY_SERVICE));  
    142.         List<RunningTaskInfo> runningTaskInfos = activityManager.getRunningTasks(1);  
    143.         if (runningTaskInfos != null) {  
    144.             ComponentName f = runningTaskInfos.get(0).topActivity;  
    145.             String topActivityClassName = f.getClassName();  
    146.             String temp[] = topActivityClassName.split("\\.");  
    147.             topActivityName = temp[temp.length - 1];  
    148.         }  
    149.         return topActivityName;  
    150.     }  
    151.       
    152.       
    153.       
    154.     //获取栈顶Activity所属进程的名称  
    155.     public static String getTopActivityProcessName(Context context) {  
    156.         String processName = null;  
    157.         ActivityManager activityManager = (ActivityManager) (context.getSystemService(android.content.Context.ACTIVITY_SERVICE));  
    158.         List<RunningTaskInfo> runningTaskInfos = activityManager.getRunningTasks(1);  
    159.         if (runningTaskInfos != null) {  
    160.             ComponentName componentName = runningTaskInfos.get(0).topActivity;  
    161.             String topActivityClassName = componentName.getClassName();  
    162.             int index = topActivityClassName.lastIndexOf(".");  
    163.             processName = topActivityClassName.substring(0, index);  
    164.         }  
    165.         return processName;  
    166.     }  
    167.       
    168.       
    169.       
    170.     //获取内存总大小  
    171.     public static long getTotalMemory() {  
    172.         // 系统的内存信息文件  
    173.         String filePath = "/proc/meminfo";  
    174.         String lineString;  
    175.         String[] stringArray;  
    176.         long totalMemory = 0;  
    177.         try {  
    178.             FileReader fileReader = new FileReader(filePath);  
    179.             BufferedReader bufferedReader = new BufferedReader(fileReader,1024 * 8);  
    180.             // 读取meminfo第一行,获取系统总内存大小  
    181.             lineString = bufferedReader.readLine();  
    182.             // 按照空格拆分  
    183.             stringArray = lineString.split("\\s+");  
    184.             // 获得系统总内存,单位KB  
    185.             totalMemory = Integer.valueOf(stringArray[1]).intValue();  
    186.             bufferedReader.close();  
    187.         } catch (IOException e) {  
    188.         }  
    189.         return totalMemory / 1024;  
    190.     }  
    191.       
    192.       
    193.       
    194.     //获取可用内存大小  
    195.     public static long getAvailMemory(Context context) {  
    196.         ActivityManager activityManager=(ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);  
    197.         MemoryInfo memoryInfo = new MemoryInfo();  
    198.         activityManager.getMemoryInfo(memoryInfo);  
    199.         return memoryInfo.availMem / (1024 * 1024);  
    200.     }  
    201.       
    202.   
    203. }  

    ServiceSubclass如下:
    1. package come.on;  
    2.   
    3. import android.app.Service;  
    4. import android.content.Intent;  
    5. import android.os.IBinder;  
    6.   
    7. public class ServiceSubclass extends Service {  
    8.   
    9.     @Override  
    10.     public IBinder onBind(Intent intent) {  
    11.         return null;  
    12.     }  
    13.       
    14.     @Override  
    15.     public void onCreate() {  
    16.         super.onCreate();  
    17.         System.out.println("---> Service的 onCreate()");  
    18.     }  
    19.       
    20.     @Override  
    21.     public void onStart(Intent intent, int startId) {  
    22.         super.onStart(intent, startId);  
    23.         System.out.println("---> Service的 onStart()");  
    24.     }  
    25.   
    26. }  

    main.xml如下:
    1. <?xml version="1.0" encoding="utf-8"?>  
    2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    3.     android:layout_width="fill_parent"  
    4.     android:layout_height="fill_parent"  
    5.    >  
    6.   
    7.     <TextView  
    8.         android:layout_width="wrap_content"  
    9.         android:layout_height="wrap_content"  
    10.         android:text="测试内存清理"   
    11.         android:layout_centerHorizontal="true"  
    12.         android:layout_marginTop="25dip"  
    13.         android:textSize="20sp"  
    14.      />  
    15.       
    16.     <EditText   
    17.         android:id="@+id/editText"  
    18.         android:layout_width="160dip"  
    19.         android:layout_height="50dip"  
    20.         android:layout_centerHorizontal="true"  
    21.         android:layout_marginTop="100dip"  
    22.         android:gravity="center"  
    23.         android:textSize="20sp"  
    24.         />  
    25.       
    26.     <Button   
    27.         android:id="@+id/button"  
    28.         android:layout_width="wrap_content"  
    29.         android:layout_height="wrap_content"  
    30.         android:text="清理内存"   
    31.         android:layout_centerInParent="true"  
    32.          android:textSize="20sp"  
    33.         />  
    34.       
    35.   
    36. </RelativeLayout>  

    AndroidManifest.xml如下:
    1. <?xml version="1.0" encoding="utf-8"?>  
    2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
    3.     package="come.on"  
    4.     android:versionCode="1"  
    5.     android:versionName="1.0" >  
    6.   
    7.     <uses-sdk android:minSdkVersion="10" />  
    8.       
    9.       
    10.       
    11.       
    12.     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />  
    13.     <uses-permission android:name="android.permission.ACCESS_CHECKIN_PROPERTIES" />  
    14.     <uses-permission android:name="android.permission.RECORD_AUDIO" />  
    15.     <uses-permission android:name="android.permission.INTERNET" />  
    16.     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />  
    17.     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />  
    18.     <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />  
    19.     <uses-permission android:name="android.permission.READ_PHONE_STATE" />  
    20.     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />  
    21.     <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />  
    22.     <uses-permission android:name="android.permission.GET_TASKS"/>    
    23.       
    24.       
    25.       
    26.   
    27.     <application  
    28.         android:icon="@drawable/a"  
    29.         android:label="@string/app_name" >  
    30.         <activity  
    31.             android:label="@string/app_name"  
    32.             android:name=".MainActivity" >  
    33.             <intent-filter >  
    34.                 <action android:name="android.intent.action.MAIN" />  
    35.   
    36.                 <category android:name="android.intent.category.LAUNCHER" />  
    37.             </intent-filter>  
    38.         </activity>  
    39.         <service android:name=".ServiceSubclass">  
    40.             <intent-filter>  
    41.                 <action android:name="come.on.service"/>  
    42.             </intent-filter>  
    43.         </service>  
    44.     </application>  
    45.   
    46. </manifest> 
    展开全文
  • Android Activity管理之Activity栈

    千次阅读 2015-10-20 23:34:19
     若新的被激活的activity组件属于另一个应用程序,则那个activity组件会运行在那个应用程序的进程中,但是从用户的角度来看,好像就是属于本应用程序一样。Android是通过将之前的activity组件和新被激活的activity...

    android中,一个activity组件可以激活另一个activity组件(可能属于另一个应用程序)。

        若新的被激活的activity组件属于另一个应用程序,则那个activity组件会运行在那个应用程序的进程中,但是从用户的角度来看,好像就是属于本应用程序一样。Android是通过将之前的activity组件和新被激活的activity组件放入同一个任务栈来实现这个功能的。从用户的角度看,一个任务栈就代表了一个应用程序。它实际上是一个栈,里面放着一组被排列好的相关的activity组件。位于栈底的activity(根activity)就是开启这个任务栈的activity组件,一般情况下,就是应用程序的主界面。而位于栈顶的activity组件即代表当前被激活的activity组件(可接收用户行为的activity)。

        任务栈中包含了activity组件的对象,且任务栈中可以包含有某一个activity组件类型的多个实例对象。在任务栈中的activity组件不能被重排序,只能被压栈和弹栈。

        任务栈不是某个类型,也不是某一个元素,它是一组activity组件的组织形式。所以没有办法在不影响任务栈中的activity组件的情况下,单独设置任务栈的参数。activity的参数既是整个任务栈的参数,它会影响任务栈中的所有activity组件。

        当某个应用程序在前后台切换的时候,实际上就是代表这个应用程序的一个任务栈在前后台切换。

        刚刚描述的行为是activity和任务栈的默认行为,但也有办法在很多方面对它进行修改:

        方法1:在发送的请求(即Intent对象)中设置一些标记。

        方法2:在manifest文件中,对接收请求(即Intent对象)的activity组件设置一些属性。

        所以在请求者和接收者中都可以进行控制。

    Intent对象中主要的标志有:

        FLAG_ACTIVITY_NEW_TASK

        FLAG_ACTIVITY_CLEAR_TOP

        FLAG_ACTIVITY_RESET_TASK_IF_NEEDED

        FLAG_ACTIVITY_SINGLE_TOP

    <activity>标签中,主要的属性有:

        taskAffinity   android:taskAffinity="com.cardroid.sdhc"  表示两个应用里面的亲属关系,如果一个应用的某个ACTIVITY和另一个应用的ACTIVITY设置这个属性
        然后,这两个ACTIVITY显示后点击HOME键盘,从一个应用启动就会显示点击HOME的那个ACTIVITY

        launchMode 

        allowTaskReparenting 

        clearTaskOnLaunch 

        alwaysRetainTaskState 

        finishOnTaskLaunch

        接下来的内容就会讲解一些Intent标志和<activity>标签属性的作用和用法。

    1.亲属关系和新的任务

        默认情况下,一个应用程序中的activity组件彼此之间是亲属关系――也就是说它们属于同一个任务栈。但是我们可以通过设置某个<activity>标签的taskAffinity属性来为这个activity组件设置亲属关系。在不同的应用程序中定义的activity组件可以共用同一个亲属关系,或者在同一个的应用程序中定义的activity组件可以使用不同的亲属关系。亲属关系会在两种情况下发挥作用:

        1)负责激活activity组件的Intent对象中包含了FLAG_ACTIVITY_NEW_TASK标志。

        2)被激活的activity组件的allowTaskReparenting属性被设置为“true”

    问题:

    1.     在同一个程序中,不同的Activity设置了相同的(默认)或不同的taskAffinity属性,那么在默认标志和FLAG_ACTIVITY_NEW_TASK时,会如何跳转?共有4中组合。

    2.      在跨程序启动Activity时,不同的Activity设置了相同的或不同的(默认)taskAffinity属性,那么在默认标志和FLAG_ACTIVITY_NEW_TASK时,会如何跳转?共有4中组合。


    关于FLAG_ACTIVITY_NEW_TASK标志量

        默认情况下,一个被激活的新activity会和负责激活它的那个activity组件存在于同一个任务栈中。但是若负责激活的Intent对象包含了FLAG_ACTIVITY_NEW_TASK标志,则系统会为存放那个即被激活的新activity寻找一个新的任务栈。此时,若已经存在了相同亲属关系的任务栈,则系统会直接将这个即被激活的新activity放入到这个任务栈中;否则系统会开始一个新的任务栈。

    关于allowTaskReparenting属性

        若一个activity组件的allowTaskReparenting被置为“true”,则当与这个activity有相同的亲属关系的任务栈被切换到前台的时候,这个activity会从当前存在的任务栈中移动到与其有相同的亲属关系的任务栈中。

        若从用户的角度来看,一个.apk文件包含了一个以上的应用程序,那你可能要为那些activity组件指定不同的亲属关系。

    2.启动模式

    <activity>标签的launchMode属性可以设置为四种不同的模式:

        “standard”(默认模式)

        “singleTop”
     android:launchMode="singleTop" 相当于每次都从这个ACITIVITY启动
        “singleTask”
     android:launchMode="singleTop" singleTop类似,不同于,每次启动后都在最上面
        “singleInstance”

        这几种模式的区别体现以下四点上:

        1)当这个activity被激活的时候,会放入哪个任务栈。

        对于“standard”“singleTop”模式,这个新被激活的activity会放入和之前的activity相同的任务栈中――除非如前所述,Intent对象包含FLAG_ACTIVITY_NEW_TASK标志。

        “singleTask”“singleInstance”模式则表示这个新被激活的activity不会放入已经存在的任务栈中,它会重新开启一个任务栈,并作为这个新的任务栈的根activity

        2)是否可以存在这个activity类型的多个实例。

        对于“standard”“singleTop”模式,可以有多个实例,并且这些实例可以属于不同的任务栈,每个任务栈也可以包含有这个activity类型的多个实例。

        “singleTask”“singleInstance”模式则表示至多只可以存在这个activity类型的一个实例。又因为有第一点必须是根activity的限制,所以这意味着在同一时间,在手机上绝不会存在多于一个的由这个activity启动的任务栈。

        3)包含此activity的任务栈是否可以包含其它的activity

        “singleInstance”模式表示包含此activity的任务栈不可以包含其它的activity。若此activity启动了另一个activity组件,那么无论那个activity组件的启动模式是什么或是Intent对象中是否包含了FLAG_ACTIVITY_NEW_TASK标志,它都会被放入另外的任务栈。在其它方面“singleInstance”模式和“singleTask”模式是一样的。

        其余三种启动模式则允许包含此activity的任务栈包含其它的activity

        4Whether a new instance of the class will be launched to handle a new intent.

        对于默认的“standard”模式,每当响应一个Intent对象,都会创建一个这种activity类型的新的实例。即每一个activity实例处理一个intent

        对于“singleTop”模式,只有当这个activity的实例当前处于任务栈的栈顶位置,则它会被重复利用来处理新到达的intent对象。否则就和“standard”模式的行为一样。

        正如第二点所说的,“singleTask”“singleInstance”模式表示只能有一个实例,所以这个唯一的实例需要处理所有新到达的intent对象。又由于“singleInstance”模式的activity实例总是位于任务栈的栈顶,所以这样做很正常。但对于“singleTask”模式的acitvity,在其上面可能存在其它的activity组件,所以它的位置并不是栈顶,在这种情况下,intent对象会被丢弃。(虽然会被丢弃,但是这个intent对象会使这个任务栈切换到前台)

        如果一个新到达的intent对象是被一个已经存在的activity组件来处理的,那么这个activityonNewIntent(android.content.Intent)方法会被系统调用。

        注意:若为了处理一个新到达的intent对象而创建了一个activity实例,则用户按下“BACK”键就会退到之前的那个activity。但若这个新到达的intent对象是由一个已经存在的activity组件来处理的,那么用户按下“BACK” 键就不会回退到处理这个新intent对象之前的状态了。

    3.清理任务栈

        如果一个任务栈在很长的一段时间都被用户保持在后台的,那么系统就会将这个任务栈中除了根activity以外的其它所有activity全部清除掉。从这之后,当用户再将任务栈切换到前台,则只能显示根activity了。

    以上说的是默认模式,可以通过<activity>标签的一些属性来更改:

        1alwaysRetainTaskState属性

        如果将根activityalwaysRetainTaskState属性设置为“true”,则即便一个任务栈在很长的一段时间都被用户保持在后台的,系统也不会对这个任务栈进行清理。

        2clearTaskOnLaunch属性

        如果将根activityclearTaskOnLaunch属性设置为“true”,那么只有这个任务栈切换到了后台,那么系统就会将这个任务栈中除了根activity以外的其它所有activity全部清除掉。即和alwaysRetainTaskState的行为完全相反。

        3) finishOnTaskLaunch属性

        这个属性的行为类似于clearTaskOnLaunch,但是此属性作用于单个的activity对象,而不是整个任务栈。当这个任务栈切换到了后台,这个属性可以使任务栈清理包括根activity在内的任何activity对象。

        这里也有另一种方法来使activity对象从任务栈中被移除。若Intent对象包含FLAG_ACTIVITY_CLEAR_TOP标志,并且在目标任务栈中已经存在了用于处理这个Intent对象的activity类型的一个实例,那么在任务栈中这个实例之上的所有activity实例会被移除。从而用于处理这个Intent对象的activity类型的那个实例会位于任务栈的栈顶,并用来处理那个Intent对象。若那个匹合的activity类型的启动模式是“standard”,则这个已经存在于任务栈中的匹合的activity类型的实例也会被移除,并且一个新的此类型activity的实例被创建并压栈来处理这个Intent对象。

        FLAG_ACTIVITY_CLEAR_TOP这个标志经常和FLAG_ACTIVITY_NEW_TASK标志结合使用,这样结合使用的意思是在另一个任务栈中定位已经存在的匹合的activity类型的实例,并且让此实例位于栈顶。

    4.启动任务栈

        通过将一个activity类型的intent-filter的动作设置为“android.intent.action.MAIN”,类别设置为“android.intent.category.LAUNCHER”可以使这个activity实例称为一个任务栈的入口。拥有这种类型的intent-filteractivity类型的图表和名字也会显示在application launcher中。

        第二个能力是很重要的:用户必须能够使一个任务栈切换到后台,也可以随时将其切换到前台。出于这个原因,使activity在启动时新开任务栈的启动模式(即“singleTask”“singleInstance”模式)只应该被利用在拥有拥有“android.intent.action.MAIN”动作和“android.intent.category.LAUNCHER”类别的intent-filteractivity类型上。

        类似的限制同样体现在FLAG_ACTIVITY_NEW_TASK标志上。如果这个标志使一个activity开始了一个新的任务栈,并且用户点击“HOME”键将其切换到了后台,则必须有某种方式使用户可以重新将那个任务栈切换到前台。一些实例(比如通知管理器),总是在外部的任务栈中开启一个activity,而不是其自身的任务栈,所以它们总是将FLAG_ACTIVITY_NEW_TASK标志放入Intent对象中,并将Intent对象传入startActivity()方法中。

        对于在某些情况下,你不希望用户能够返回到某一个activity,那么可以通过设置<activity>标签的“finishOnTaskLaunch”属性为“true”来实现。

    展开全文
  • 关于AMS,原计划是只写篇文章来介绍,但是AMS功能繁多,篇文章的篇幅远远不够。这篇我们接着来学习与AMS相关的ActivityTask和Activity栈管理。

    本文首发于微信公众号「刘望舒」

    关联系列
    Android系统启动流程系列
    Android应用进程系列
    Android深入四大组件系列
    Android深入解析AMS系列

    前言

    关于AMS,原计划是只写一篇文章来介绍,但是AMS功能繁多,一篇文章的篇幅远远不够。这一篇我们接着来学习与AMS相关的ActivityTask和Activity栈管理。

    1.ActivityStack

    ActivityStack是一个管理类,用来管理系统所有Activity的各种状态,其内部维护了TaskRecord的列表,因此从Activity任务栈这一角度来说,ActivityStack也可以理解为Activity堆栈。它由ActivityStackSupervisor来进行管理的,而ActivityStackSupervisor在AMS中的构造方法中被创建。
    frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

       public ActivityManagerService(Context systemContext) {
       ...
        mStackSupervisor = new ActivityStackSupervisor(this);
       ... 
       }
    

    1.1 ActivityStack的实例类型

    ActivityStackSupervisor中有多种ActivityStack实例,如下所示。
    frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java

    public final class ActivityStackSupervisor implements DisplayListener {
       ...
        ActivityStack mHomeStack;
        ActivityStack mFocusedStack;
        private ActivityStack mLastFocusedStack;
        ...
    }
    

    mHomeStack用来存储Launcher App的所有Activity,mFocusedStack表示当前正在接收输入或启动下一个Activity的所有Activity。mLastFocusedStack表示此前接收输入的所有Activity。

    通过ActivityStackSupervisor提供了获取上述ActivityStack的方法,比如要获取mFocusedStack,只需要调用ActivityStackSupervisor的getFocusedStack方法就可以了:
    frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java

       ActivityStack getFocusedStack() {
            return mFocusedStack;
        }
    

    1.2 ActivityState

    ActivityStack中通过枚举存储了Activity的所有的状态,如下所示。
    frameworks/base/services/core/java/com/android/server/am/ActivityStack.java

     enum ActivityState {
            INITIALIZING,
            RESUMED,
            PAUSING,
            PAUSED,
            STOPPING,
            STOPPED,
            FINISHING,
            DESTROYING,
            DESTROYED
        }
    

    通过名称我们可以很轻易知道这些状态所代表的意义。应用ActivityState的场景会有很多,比如下面的代码:
    frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

       @Override
        public void overridePendingTransition(IBinder token, String packageName,
                int enterAnim, int exitAnim) {
         ...
                if (self.state == ActivityState.RESUMED
                        || self.state == ActivityState.PAUSING) {//1
                    mWindowManager.overridePendingAppTransition(packageName,
                            enterAnim, exitAnim, null);
                }
                Binder.restoreCallingIdentity(origId);
            }
        }
    

    overridePendingTransition方法用于设置Activity的切换动画,注释1处可以看到只有ActivityState为RESUMED状态或者PAUSING状态时才会调用WMS类型的mWindowManager对象的overridePendingAppTransition方法来进行切换动画。

    1.3 特殊状态的Activity

    在ActivityStack中定义了一些特殊状态的Activity,如下所示。

    ActivityRecord mPausingActivity = null;//正在暂停的Activity
    ActivityRecord mLastPausedActivity = null;//上一个已经暂停的Activity
    ActivityRecord mLastNoHistoryActivity = null;//最近一次没有历史记录的Activity
    ActivityRecord mResumedActivity = null;//已经Resume的Activity
    ActivityRecord mLastStartedActivity = null;//最近一次启动的Activity
    ActivityRecord mTranslucentActivityWaiting = null;//传递给convertToTranslucent方法的最上层的Activity
    

    这些特殊的状态都是ActivityRecord类型的,ActivityRecord用来记录一个Activity的所有信息。从Activity任务栈的角度来说,一个或多个ActivityRecord会组成一个TaskRecord,TaskRecord用来记录Activity的栈,而ActivityStack包含了一个或多个TaskRecord。
    VeivrR.png

    1.4 维护的ArrayList

    ActivityStack中维护了很多ArrayList,这些ArrayList中的元素类型主要有ActivityRecord和TaskRecord,其中TaskRecord用来记录Activity的Task。

    | ArrayList| 元素类型| 说明|
    | :-------- ? :--------? :–: |
    | mTaskHistory| TaskRecord | 所有没有被销毁的Task |
    | mLRUActivities| ActivityRecord| 正在运行的Activity,列表中的第一个条目是最近最少使用的元素 |
    | mNoAnimActivities| ActivityRecord| 不考虑转换动画的Activity |
    | mValidateAppTokens| TaskGroup| 用于与窗口管理器验证应用令牌 |

    2.Activity栈管理

    我们知道Activity是由任务栈来进行管理的,有了栈管理,我们可以对应用程序进行操作,应用可以复用自身应用中以及其他应用的Activity,节省了资源。比如我们使用一款社交应用,这个应用的联系人详情界面提供了联系人的邮箱,当我们点击邮箱时会跳到发送邮件的界面。
    VeiL24.png

    社交应用和系统Email中的Activity是处于不同应用程序进程的,而有了栈管理,就可以把发送邮件界面放到社交应用中详情界面所在栈的栈顶,来做到跨进程操作。
    为了更灵活的进行栈管理,Android系统提供了很多配置,下面分别对它们进行介绍。

    2.1 Launch Mode

    Launch Mode都不会陌生,用于设定Activity的启动方式,无论是哪种启动方式,所启动的Activity都会位于Activity栈的栈顶。有以下四种:

    • standerd:默认模式,每次启动Activity都会创建一个新的Activity实例。
    • singleTop:如果要启动的Activity已经在栈顶,则不会重新创建Activity,同时该Activity的onNewIntent方法会被调用。如果要启动的Activity不在栈顶,则会重新创建该Activity的实例。
    • singleTask:如果要启动的Activity已经存在于它想要归属的栈中,那么不会创建该Activity实例,将栈中位于该Activity上的所有的Activity出栈,同时该Activity的onNewIntent方法会被调用。如果要启动的Activity不存在于它想要归属的栈中,并且该栈存在,则会重新创建该Activity的实例。如果要启动的Activity想要归属的栈不存在,则首先要创建一个新栈,然后创建该Activity实例并压入到新栈中。
    • singleInstance:和singleTask基本类似,不同的是启动Activity时,首先要创建在一个新栈,然后创建该Activity实例并压入新栈中,新栈中只会存在这一个Activity实例。

    2.2 Intent的FLAG

    Intent中定义了很多了FLAG,其中有几个FLAG也可以设定Activity的启动方式,如果Launch Mode设定和FLAG设定的Activity的启动方式有冲突,则以FLAG设定的为准。

    • FLAG_ACTIVITY_SINGLE_TOP:和Launch Mode中的singleTop效果是一样的。
    • FLAG_ACTIVITY_NEW_TASK:和Launch Mode中的singleTask效果是一样的。
    • FLAG_ACTIVITY_CLEAR_TOP:Launch Mode中没有与此对应的模式,如果要启动的Activity已经存在于栈中,则将所有位于它上面的Activity出栈。singleTask默认具有此标记位的效果。

    除了这三个FLAG,还有一些FLAG对我们分析栈管理有些帮助。

    • FLAG_ACTIVITY_NO_HISTORY:Activity一旦退出,就不会存在于栈中。同样的,也可以在AndroidManifest.xml中设置“android:noHistory”。
    • FLAG_ACTIVITY_MULTIPLE_TASK:需要和FLAG_ACTIVITY_NEW_TASK一同使用才有效果,系统会启动一个新的栈来容纳新启动的Activity.
    • FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:Activity不会被放入到“最近启动的Activity”列表中。
    • FLAG_ACTIVITY_BROUGHT_TO_FRONT:这个标志位通常不是由应用程序中的代码设置的,而是Launch Mode为singleTask时,由系统自动加上的。
    • FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY:这个标志位通常不是由应用程序中的代码设置的,而是从历史记录中启动的(长按Home键调出)。
    • FLAG_ACTIVITY_CLEAR_TASK:需要和FLAG_ACTIVITY_NEW_TASK一同使用才有效果,用于清除与启动的Activity相关栈的所有其他Activity。

    接下来通过系统源码来查看FLAG的应用,在Android深入四大组件(一)应用程序启动过程(后篇)中讲过,根Activity启动时会调用AMS的startActivity方法,经过层层调用会调用ActivityStarter的startActivityUnchecked方法,如下面的时序图所示。
    VeiOxJ.png

    frameworks/base/services/core/java/com/android/server/am/ActivityStarter.java

     private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
                IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
                int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask) {
            setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
                    voiceInteractor);//1
            computeLaunchingTaskFlags();//2
            computeSourceStack();
            mIntent.setFlags(mLaunchFlags);//3
     ...       
    }
    

    注释1处用于初始化启动Activity的各种配置,在初始化前会重置各种配置再进行配置,这些配置包括:ActivityRecord、Intent、TaskRecord和LaunchFlags(启动的FLAG)等等。注释2处的computeLaunchingTaskFlags方法用于计算出启动的FLAG,并将计算的值赋值给mLaunchFlags。在注释3处将mLaunchFlags设置给Intent,达到设定Activity的启动方式的目的。接着来查看computeLaunchingTaskFlags方法。

    frameworks/base/services/core/java/com/android/server/am/ActivityStarter.java

    private void computeLaunchingTaskFlags() {
    ...
          if (mInTask == null) {//1
                if (mSourceRecord == null) {//2
                    if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 && mInTask == null) {//3
                        Slog.w(TAG, "startActivity called from non-Activity context; forcing " +
                                "Intent.FLAG_ACTIVITY_NEW_TASK for: " + mIntent);
                        mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
                    }
                } else if (mSourceRecord.launchMode == LAUNCH_SINGLE_INSTANCE) {//4
                    mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
                } else if (mLaunchSingleInstance || mLaunchSingleTask) {//5
                    mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
                }
            }
    }
    

    计算启动的FLAG的逻辑比较复杂,这里只截取了一小部分,注释1处的TaskRecord类型的mInTask为null时,说明Activity要加入的栈不存在。因此,这一小段代码主要解决的问题就是Activity要加入的栈不存在时如何计算出启动的FLAG。注释2处,ActivityRecord类型的mSourceRecord用于描述“初始Activity”,什么是“初始Activity”呢?比如ActivityA启动了ActivityB,ActivityA就是初始Activity。同时满足注释2和注释3的条件则需要创建一个新栈。注释4处,如果“初始Activity”所在的栈只允许有一个Activity实例,则也需要创建一个新栈。注释5处,如果Launch Mode设置了singleTask或singleInstance,则也要创建一个新栈。

    2.3 taskAffinity

    我们可以在AndroidManifest.xml设置android:taskAffinity,用来指定Activity希望归属的栈, 默认情况下,同一个应用程序的所有的Activity都有着相同的taskAffinity。
    taskAffinity在下面两种情况时会产生效果。

    1. taskAffinity与FLAG_ACTIVITY_NEW_TASK或者singleTask配合。如果新启动Activity的taskAffinity和栈的taskAffinity相同(栈的taskAffinity取决于根Activity的taskAffinity)则加入到该栈中。如果不同,就会创建新栈。
    2. taskAffinity与allowTaskReparenting配合。如果allowTaskReparenting为true,说明Activity具有转移的能力。拿此前的邮件为例,当社交应用启动了发送邮件的Activity,此时发送邮件的Activity是和社交应用处于同一个栈中。如果发送邮件的Activity的allowTaskReparenting设置为true,此后邮件程序所在的栈位于前台,这个时候发送邮件的Activity就会由社交应用的栈中转移到与它更亲近的邮件程序(taskAffinity相同)所在的栈中。

    接着通过系统源码来查看taskAffinity的应用。ActivityStackSupervisor的findTaskLocked方法用于找到Activity最匹配的栈,最终会调用ActivityStack的findTaskLocked方法。
    frameworks/base/services/core/java/com/android/server/am/ActivityStack.java

     void findTaskLocked(ActivityRecord target, FindTaskResult result) {
    ...
       for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {//1
         final TaskRecord task = mTaskHistory.get(taskNdx);//2
       ...
         else if (!isDocument && !taskIsDocument
                        && result.r == null && task.canMatchRootAffinity()) {
                    if (task.rootAffinity.equals(target.taskAffinity)) {//3
                        if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Found matching affinity candidate!");
                        result.r = r;
                        result.matchedByRootAffinity = true;
                    }
                } else if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Not a match: " + task);
            }
        }
    

    这个方法的逻辑比较复杂,这里截取了和taskAffinity相关的部分。注释1处遍历mTaskHistory列表,列表的元素为TaskRecord,
    用于存储没有被销毁的Task。注释2处得到某一个Task的信息。注释3处将Task的rootAffinity(初始的taskAffinity)和目标Activity的taskAffinity做对比,如果相同,则将FindTaskResult的matchedByRootAffinity 属性设置为true,说明找到了匹配的Task。

    参考资料
    《深入理解Android卷二》
    《深入理解Android内核设计思想》第二版
    《Android开发艺术探索》
    ActivityRecord、TaskRecord、ActivityStack


    这里不仅分享大前端、Android、Java等技术,还有程序员成长类文章。
    展开全文
  • Activity状态转换和Activity栈

    千次阅读 2014-03-22 10:19:32
    Activity状态转换和Activity栈 一个Activity的启动顺序: onCreate()——>onStart()——>onResume()  当另一个Activity启动时:  第一个ActivityonPause()——>第二个ActivityonCreate()——>onStart()——>...
  • android N进程启动流程(二)(上一个activity的暂停、进程启动、绑定进程与创建application)第二部分将分为:上一个activity的暂停、进程启动、绑定进程与创建application5. 上一个activity的暂停 图5.1 上一个...
  • 获取activity栈的最顶端的一个

    千次阅读 2016-01-20 14:08:51
    用过ActivityManager的童鞋估计都知道,可以从ActivityManager里面可以获取到当前运行的所有任务,所有进程和所有服务,这是任务管理器的核心。  那么,从里面我们可以发掘点什么出来吗?  仔细看...
  • Android开发的四大组件在开发中应用中是必不可少的,下面就来详解下四大组件之一Activity,总结自网络。Activty的生命周期的也就是它所在进程的生命周期。 ... 第一个Activity onPause()—
  • 查看Activity栈

    千次阅读 2015-06-11 10:21:47
    谈起Android程序开发,就需要了解其四主要的部件:Activity、Service、ContentProvider、 BroadcastReceiver。而其中Activity是唯一直接控制程序界面呈现,直面用户操作的部件(当然BrowadCastReceiver也能通过...
  • Task(Activity栈) 详解

    千次阅读 2014-03-07 11:34:51
     Android管理task和back stack的默认行为:activity 在同一个任务中创建并置于先进后出的中。如果这种默认的行为不能满足我们的app设计,如:为一个activity创建一个新任务(而不是在相同的任务中),或者启动...
  • activity管理

    万次阅读 2014-10-03 21:00:29
     若新的被激活的activity组件属于另一个应用程序,则那个activity组件会运行在那个应用程序的进程中,但是从用户的角度来看,好像就是属于本应用程序一样。Android是通过将之前的activity组件和新被激活的activity...
  • Activity任务

    2016-04-19 22:56:42
    3.一个应用程序一被开启系统就给他分配一个任务,当所有的activity都退出的时候,任务就清空了。 4.任务的id是一个integer的数据类型 自增长的。 5.在android操作系统里面会存在多个任务一个应用程
  • 前言:Activity和Activity栈的关系在android中,一个activity组件可以激活另一个activity组件(可能属于另一个应用程序)。若新的被激活的activity组件属于另一个应用程序,则那个activity组件会运行在那个应用程序...
  • 1、子Activity组件在进程内的启动过程  在Android Activity组件的启动过程http://blog.csdn.net/jltxgcy/article/details/35984557一文中,我们已经详细分析了Activity的启动过程,对于子Activity组件在进程内的...
  • 1、Activity栈  从上篇文章Android组件之Activityshengmingzhouqi
  • 任务Task,是种用来放置Activity实例的容器,他是以的形式进行盛放, 也就是所谓的先进后出,主要有2基本操作:压栈和出栈,其所存放的Activity是不支持重新排序的, 只能根据压栈和出栈操作更改Activity的...
  • activity栈一直以来总有点不清晰的感觉,今天就彻底的梳理一下。  在android中,一个activity组件可以激活另一个activity组件(可能属于另一个应用程序)。 若新的被激活的activity组件属于另一个应用程序,则...
  • 就像前面提到的,一个activity可以启动另一个,包括那些定义在不同应用程序中的。假设,例如,你想让用户显示一些地方的街道地图。已经有一个activity可以做这个事,所以你的activity所要做的就是将行为对象和需要的...
  • Android下的任务和Activity栈 文章分类:移动开发 就像前面提到的,一个activity可以启动另一个,包括那些定义在不同应用程序中的。假设,例如,你想让用户显示一些地方的街道地图。已经有一个activity可以做这个事...
  • Activity是由任务管理的,一般情况下一个应用程序只有一个任务。什么是?是一种常用的数据结构,只允许访问栈顶的元素,就像一个杯子,每次都只能取杯子顶上的东西 的特点就是先进后出,与截然相反的...
  • Activity启动流程()发起端进程请求启动目标Activity

    千次阅读 热门讨论 2020-09-22 17:30:05
       Android四大组件之Activity启动流程源码实现详解() Activity启动流程源码实现详解系列博客目录: Activity启动流程源码实现详解概要 前言   在正式开始Android四大组件之Activity启动流程源码实现详解...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 23,913
精华内容 9,565
关键字:

activity栈一个进程