Android L 上,长按Home键可以显示最近使用的应用,以便用户可以快速回到之前使用的应用。

现在项目需要用程序把栈信息清空=_=

下面是实现过程,分析源码的实现过程:

@Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {

        final boolean handled = super.onKeyDown(keyCode, event);

        if (keyCode==KeyEvent.KEYCODE_ENTER && event.isLongPress()) {
                // index is reverse since most recent appears at the bottom...
                int taskSize = mRecentTaskDescriptions.size();

                for(int ii=0;ii<taskSize;ii++){
                        TaskDescription ad = mRecentTaskDescriptions.get(0);
                        if (ad == null) {
                                return true;
                        }

                        mRecentTaskDescriptions.remove(ad);
                        mRecentTasksLoader.remove(ad);

                        // Handled by widget containers to enable LayoutTransitions properly
                        // mListAdapter.notifyDataSetChanged();

                        if (mRecentTaskDescriptions.size() == 0) {
                            dismissAndGoBack();
                        }

                        // Currently, either direction means the same thing, so ignore direction and remove
                        // the task.
                        final ActivityManager am = (ActivityManager)
                                mContext.getSystemService(Context.ACTIVITY_SERVICE);
                        if (am != null) {
                            am.removeTask(ad.persistentTaskId, ActivityManager.REMOVE_TASK_KILL_PROCESS);

                            // Accessibility feedback
                            setContentDescription(
                                    mContext.getString(R.string.accessibility_recents_item_dismissed, ad.getLabel()));
                            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
                            setContentDescription(null);
                        }
                }
                return true;
        }
        return handled;
    }







源码应用到了ActivityManager的removeTask()方法来清除,获取是应用RecentTask类。

在网上找了下资料,资料不多,找到一篇很有用,谢谢作者。

他分析了系统的处理过程,并模拟系统给出了一个获取历史栈的方法。

// 得到包管理器和activity管理器
		final Context context = this;
		final PackageManager pm = context.getPackageManager();
		final ActivityManager am = (ActivityManager) context
				.getSystemService(Context.ACTIVITY_SERVICE);
		// 从ActivityManager中取出用户最近launch过的 MAX_RECENT_TASKS + 1 个,以从早到晚的时间排序,
		// 注意这个 0x0002,它的值在launcher中是用ActivityManager.RECENT_IGNORE_UNAVAILABLE
		// 但是这是一个隐藏域,因此我把它的值直接拷贝到这里
		final List<ActivityManager.RecentTaskInfo> recentTasks = am
				.getRecentTasks(MAX_RECENT_TASKS + 1, 0x0002);

		// 这个activity的信息是我们的launcher
		ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(
				Intent.CATEGORY_HOME).resolveActivityInfo(pm, 0);
		int numTasks = recentTasks.size();
		for (int i = 0; i < numTasks && (i < MAX_RECENT_TASKS); i++) {
			HashMap<String, Object> singleAppInfo = new HashMap<String, Object>();// 当个启动过的应用程序的信息
			final ActivityManager.RecentTaskInfo info = recentTasks.get(i);

			Intent intent = new Intent(info.baseIntent);
			Log.e("", "###" + info.persistentId);
			appIds.add(info.persistentId);
			if (info.origActivity != null) {
				intent.setComponent(info.origActivity);
			}
			// TODO 测试用,无意义代码
			String currentInfo = "PackageName=="
					+ intent.getComponent().getPackageName() + ",ClassName=="
					+ intent.getComponent().getClassName();
			/**
			 * 如果找到是launcher,直接continue,后面的appInfos.add操作就不会发生了
			 */
			if (homeInfo != null) {
				if (homeInfo.packageName.equals(intent.getComponent()
						.getPackageName())
						&& homeInfo.name.equals(intent.getComponent()
								.getClassName())) {
					MAX_RECENT_TASKS = MAX_RECENT_TASKS + 1;
					continue;
				}
			}

			// 设置intent的启动方式为 创建新task()【并不一定会创建】
			intent.setFlags((intent.getFlags() & ~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
					| Intent.FLAG_ACTIVITY_NEW_TASK);
			// 获取指定应用程序activity的信息(按我的理解是:某一个应用程序的最后一个在前台出现过的activity。)
			final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0);
			if (resolveInfo != null) {
				final ActivityInfo activityInfo = resolveInfo.activityInfo;
				final String title = activityInfo.loadLabel(pm).toString();
				Drawable icon = activityInfo.loadIcon(pm);
				if (title != null && title.length() > 0 && icon != null) {
					singleAppInfo.put("title", title);
					singleAppInfo.put("icon", icon);
					singleAppInfo.put("tag", intent);
				}
			}
			appInfos.add(singleAppInfo);
		}
		MAX_RECENT_TASKS = repeatCount;
		for (HashMap<String, Object> map : appInfos) {
			Log.e("", "@" + map.get("title"));
		}



(1)通过Activity的getRecentTasks方法获取近来历史栈的信息。

(2)根据ActivityManager.RecentTaskInfo 获取对应Activity以及对应的应用信息。



接下来是如何清除的问题。

阅读ActivityManager源码,am.removeTask(ad.persistentTaskId, ActivityManager.REMOVE_TASK_KILL_PROCESS);

是一个系统隐藏方法,所以我们通过反射调用这个方法。

需要传入两个字段。

第一个字段:可以通过getRecentTasks获取

第二个字段:ActivityManager.REMOVE_TASK_KILL_PROCESS是个隐藏字段,直接把值从源码中读出传入。

private void removeTasks() {
		
		ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
		
		Class<ActivityManager> clazz = ActivityManager.class;
		Method method;
		for (int id : appIds) {
			try {
				method = clazz.getDeclaredMethod("removeTask", int.class,
						int.class);
				method.setAccessible(true);
				try {
					method.invoke(am, id, 0x0001);
				} catch (IllegalAccessException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IllegalArgumentException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (InvocationTargetException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			} catch (NoSuchMethodException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

	}



perfect!测试会出现异常:没有remove_Tasks权限。

这个权限需要系统级别的权限才能实现。

因而manifest.xml里配置如下:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.gi.test"
    android:versionCode="1"
    android:versionName="1.0"
    android:sharedUserId="android.uid.system" >

    <uses-permission android:name="android.permission.GET_TASKS" />
    <uses-permission android:name="android.permission.REMOVE_TASKS" />



设置sharedUserId=Android.uid.system

并设置权限需要的权限

然后将它和目标系统共同编译,并放到系统分区,这样就有了系统权限,就可以运行了。本方法只适用于定制。如果想要做一个app在普通的设备上实现这个功能,恐怕就不行了,主要是remove_Tasks这个系统级别权限的问题。