Android Framework架构浅析之【近期任务】

近期任务框(就是近期打开过的应用)其实也就是一个系统级别的对话框,就是长按手机的HOME键弹出的视图。

源码中的路径为:D:\tools\android4.0.1\frameworks\base\policy\src\com\android\internal\policy\impl\RecentApplicationsDialog.java

1,显示方式,该对话框在PhoneWindowManager (路径和RecentApplicationsDialog.java同样)类中的showRecentAppsDialog方法

  /**
     * Create (if necessary) and launch the recent apps dialog, or hide it if it is
     * already shown.
     */
    void showOrHideRecentAppsDialog(final int heldModifiers, final boolean dismissIfShown) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                if (mRecentAppsDialog == null) {
                    mRecentAppsDialog = new RecentApplicationsDialog(mContext);
                }
                if (mRecentAppsDialog.isShowing()) {
                    if (dismissIfShown) {
                        mRecentAppsDialog.dismiss();
                    }
                } else {
                    mRecentAppsDialog.setHeldModifiers(heldModifiers);
                    mRecentAppsDialog.show();
                }
            }
        });
    }

2,创建对话框时配置一些参数:比如填充父窗体,没有标题,更重要的一点就是设成系统级别的对话框。

/**
	 * 我们创建最近的应用程序对话框只是一次,它会留下(隐藏),直到用户激活。
	 * 
	 * @see 调用显示是在这个类里(PhoneWindowManager#showRecentAppsDialog)
	 */
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		Context context = getContext();

		if (sStatusBar == null) {
			sStatusBar = (StatusBarManager) context
					.getSystemService(Context.STATUS_BAR_SERVICE);
		}
		
		//获取窗体,注意:window是整个手机屏幕的窗体,而不仅仅但是对话框的窗体
		Window window = getWindow();
		//设置无标题
		window.requestFeature(Window.FEATURE_NO_TITLE);
		//将该对话框设置成系统级别对话框
		window.setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
		window.setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
				WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
		window.setTitle("Recents");

		setContentView(com.android.internal.R.layout.recent_apps_dialog);
		
		//设置对话框的参数:宽、高为填充父窗体
		final WindowManager.LayoutParams params = window.getAttributes();
		params.width = WindowManager.LayoutParams.MATCH_PARENT;
		params.height = WindowManager.LayoutParams.MATCH_PARENT;
		window.setAttributes(params);
		window.setFlags(0, WindowManager.LayoutParams.FLAG_DIM_BEHIND);
		
		mIcons[0] = (TextView) findViewById(com.android.internal.R.id.button0);
		mIcons[1] = (TextView) findViewById(com.android.internal.R.id.button1);
		mIcons[2] = (TextView) findViewById(com.android.internal.R.id.button2);
		mIcons[3] = (TextView) findViewById(com.android.internal.R.id.button3);
		mIcons[4] = (TextView) findViewById(com.android.internal.R.id.button4);
		mIcons[5] = (TextView) findViewById(com.android.internal.R.id.button5);
		mIcons[6] = (TextView) findViewById(com.android.internal.R.id.button6);
		mIcons[7] = (TextView) findViewById(com.android.internal.R.id.button7);
		mNoAppsText = findViewById(com.android.internal.R.id.no_applications_message);
		
		//为每个TextView设置点击事件
		for (TextView b : mIcons) {
			b.setOnClickListener(this);
		}
	}


3,为八个按钮载入最近的活动,如果最近打过的应用任务栈中超过8个也只能取八个

/**
	 * 为八个按钮载入最近的活动,如果最近打过的应用任务栈中超过8个也只能取八个,
	 */
	private void reloadButtons() {

		final Context context = getContext();
		final PackageManager pm = context.getPackageManager();
		
		final ActivityManager am = (ActivityManager) context
				.getSystemService(Context.ACTIVITY_SERVICE);
		//这里是获取近期打开过的应用的RecentTaskInfo
		final List<ActivityManager.RecentTaskInfo> recentTasks = am
				.getRecentTasks(MAX_RECENT_TASKS,
						ActivityManager.RECENT_IGNORE_UNAVAILABLE);

		ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(
				Intent.CATEGORY_HOME).resolveActivityInfo(pm, 0);

		IconUtilities iconUtilities = new IconUtilities(getContext());
		
		// 性能注意:android性能指南推荐迭代器来遍历集合,因为知道getRecentTasks()总是返回一个ArrayList
		// < >,我们将使用一个简单的指数相反。
		int index = 0;
		int numTasks = recentTasks.size();
		for (int i = 0; i < numTasks && (index < NUM_BUTTONS); ++i) {
			final ActivityManager.RecentTaskInfo info = recentTasks.get(i);

			// 目的用于调试,但不允许第一个结果创建一个空的列表
			if (DBG_FORCE_EMPTY_LIST && (i == 0))
				continue;

			Intent intent = new Intent(info.baseIntent);
			if (info.origActivity != null) {
				intent.setComponent(info.origActivity);
			}

			// 跳过当前Launcher数据,就是不把当前launcher作为近期打开过的任务。
			if (homeInfo != null) {
				if (homeInfo.packageName.equals(intent.getComponent()
						.getPackageName())
						&& homeInfo.name.equals(intent.getComponent()
								.getClassName())) {
					continue;
				}
			}
			//下面做的其实就是封装一些数据给每一个对应的TextView
			intent.setFlags((intent.getFlags() & ~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
					| Intent.FLAG_ACTIVITY_NEW_TASK);
			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) {
					final TextView tv = mIcons[index];
					tv.setText(title);
					icon = iconUtilities.createIconDrawable(icon);
					tv.setCompoundDrawables(null, icon, null, null);
					RecentTag tag = new RecentTag();
					tag.info = info;
					tag.intent = intent;
					//关于setTag这个是view中集成的一个神奇的东西,后面我会专门表述一下我的见解
					tv.setTag(tag);
					tv.setVisibility(View.VISIBLE);
					tv.setPressed(false);
					tv.clearFocus();
					++index;
				}
			}
		}

		// 处理没有图标的
		mNoAppsText.setVisibility((index == 0) ? View.VISIBLE : View.GONE);

		// 隐藏其他的图标
		for (; index < NUM_BUTTONS; ++index) {
			mIcons[index].setVisibility(View.GONE);
		}
	}


4,设置和显示最近的活动对话框。

/**
	 * 设置和显示最近的活动对话框。
	 */
	@Override
	public void onStart() {
		super.onStart();
		reloadButtons();
		//禁止状态栏
		if (sStatusBar != null) {
			sStatusBar.disable(StatusBarManager.DISABLE_EXPAND);
		}

		// 注册接收广播
		getContext().registerReceiver(mBroadcastReceiver,
				mBroadcastIntentFilter);

		mHandler.removeCallbacks(mCleanup);
	}


5,处理手指按下的动作

/**
	 * 处理手指按下时的程序
	 */
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) 


6,处理手指离开的动作

/**
	 * 处理手指抬起时的程序
	 */
	@Override
	public boolean onKeyUp(int keyCode, KeyEvent event)


7,处理用户单击事件

/**
	 * 用户单击处理程序。如果一个按钮被单击时,启动相应的活动。
	 */
	@Override
	public void onClick(View v) {
		for (TextView b : mIcons) {
			if (b == v) {
				RecentTag tag = (RecentTag) b.getTag();
				switchTo(tag);
				break;
			}
		}
		dismiss();
	}


8,切换到所点击的应用

/**
	 * 切换到所点击的应用
	 * 
	 * @param tag
	 */
	private void switchTo(RecentTag tag) {
		if (tag.info.id >= 0) {
			// 这是一个活跃的任务,所以把它移动到最近任务的前面
			final ActivityManager am = (ActivityManager) getContext()
					.getSystemService(Context.ACTIVITY_SERVICE);
			am.moveTaskToFront(tag.info.id, ActivityManager.MOVE_TASK_WITH_HOME);
		} else if (tag.intent != null) {
			tag.intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
					| Intent.FLAG_ACTIVITY_TASK_ON_HOME);
			try {
				getContext().startActivity(tag.intent);
			} catch (ActivityNotFoundException e) {
				Log.w("Recent", "Unable to launch recent task", e);
			}
		}
	}


9,处理接收关闭对话框的广播

/**
	 * 这是监听关闭系统对话框意图的广播。收到广播后立即关闭自己,为了允许高优先级的UI来接管(如电话收到了)。
	 */
	private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			String action = intent.getAction();
			if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
				String reason = intent
						.getStringExtra(PhoneWindowManager.SYSTEM_DIALOG_REASON_KEY);
				if (!PhoneWindowManager.SYSTEM_DIALOG_REASON_RECENT_APPS
						.equals(reason)) {
					dismiss();
				}
			}
		}
	};


如果有需要从别的应用中关闭这个对话框,那么向系统发送一个广播即可:

Intent i = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
		this.sendBroadcast(i);


你可能感兴趣的:(framework)