Android近期任务列表 Recent Applicatoins 分析 + Android(安卓)PackageManager
转自 : http://www.cnblogs.com/coding-way/archive/2013/06/05/3118732.html
这里的近期任务列表就是长按Home键出来的那个Dialog,里面放着近期打开过的应用,当然3.0以上系统的多任务切换键也是。
这个Dialog的实现在Android源码的/frameworks/base/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java中。
接下来就对这个源码分析一下。
[java] view plain copy- publicclassRecentApplicationsDialogextendsDialogimplementsOnClickListener{
- //Elementsfordebuggingsupport
- //privatestaticfinalStringLOG_TAG="RecentApplicationsDialog";
- privatestaticfinalbooleanDBG_FORCE_EMPTY_LIST=false;
- staticprivateStatusBarManagersStatusBar;
- privatestaticfinalintNUM_BUTTONS=8;
- privatestaticfinalintMAX_RECENT_TASKS=NUM_BUTTONS*2;//allowforsomediscards
- finalTextView[]mIcons=newTextView[NUM_BUTTONS];
- ViewmNoAppsText;
- IntentFiltermBroadcastIntentFilter=newIntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
- classRecentTag{
- ActivityManager.RecentTaskInfoinfo;
- Intentintent;
- }
- HandlermHandler=newHandler();
- RunnablemCleanup=newRunnable(){
- publicvoidrun(){
- //dumpextramemorywe'rehangingonto
- for(TextViewicon:mIcons){
- icon.setCompoundDrawables(null,null,null,null);
- icon.setTag(null);
- }
- }
- };
- publicRecentApplicationsDialog(Contextcontext){
- super(context,com.android.internal.R.style.Theme_Dialog_RecentApplications);
- }
- /**
- *Wecreatetherecentapplicationsdialogjustonce,anditstaysaround(hidden)
- *untilactivatedbytheuser.
- *
- *@seePhoneWindowManager#showRecentAppsDialog
- */
- @Override
- protectedvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
- Contextcontext=getContext();
- if(sStatusBar==null){
- sStatusBar=(StatusBarManager)context.getSystemService(Context.STATUS_BAR_SERVICE);
- }
- Windowwindow=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);
- finalWindowManager.LayoutParamsparams=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);
- //默认显示8个
- 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);
- //关键在哪,你懂得...
- for(TextViewb:mIcons){
- b.setOnClickListener(this);
- }
- }
- @Override
- publicbooleanonKeyDown(intkeyCode,KeyEventevent){
- if(keyCode==KeyEvent.KEYCODE_TAB){
- //IgnoreallmetakeysotherthanSHIFT.Theappswitchkeycouldbea
- //fallbackactionchordedwithALT,METAorevenCTRLdependingonthekeymap.
- //DPadnavigationishandledbytheViewRootelsewhere.
- finalbooleanbackward=event.isShiftPressed();
- finalintnumIcons=mIcons.length;
- intnumButtons=0;
- while(numButtons<numIcons&&mIcons[numButtons].getVisibility()==View.VISIBLE){
- numButtons+=1;
- }
- if(numButtons!=0){
- intnextFocus=backward?numButtons-1:0;
- for(inti=0;i<numButtons;i++){
- if(mIcons[i].hasFocus()){
- if(backward){
- nextFocus=(i+numButtons-1)%numButtons;
- }else{
- nextFocus=(i+1)%numButtons;
- }
- break;
- }
- }
- finalintdirection=backward?View.FOCUS_BACKWARD:View.FOCUS_FORWARD;
- if(mIcons[nextFocus].requestFocus(direction)){
- mIcons[nextFocus].playSoundEffect(
- SoundEffectConstants.getContantForFocusDirection(direction));
- }
- }
- //ThedialogalwayshandlesthekeytopreventtheViewRootfrom
- //performingthedefaultnavigationitself.
- returntrue;
- }
- returnsuper.onKeyDown(keyCode,event);
- }
- /**
- *Dismissthedialogandswitchtotheselectedapplication.
- */
- publicvoiddismissAndSwitch(){
- finalintnumIcons=mIcons.length;
- RecentTagtag=null;
- for(inti=0;i<numIcons;i++){
- if(mIcons[i].getVisibility()!=View.VISIBLE){
- break;
- }
- if(i==0||mIcons[i].hasFocus()){
- tag=(RecentTag)mIcons[i].getTag();
- if(mIcons[i].hasFocus()){
- break;
- }
- }
- }
- if(tag!=null){
- switchTo(tag);
- }
- dismiss();
- }
- /**
- *Handlerforuserclicks.Ifabuttonwasclicked,launchthecorrespondingactivity.
- */
- publicvoidonClick(Viewv){
- for(TextViewb:mIcons){
- if(b==v){
- RecentTagtag=(RecentTag)b.getTag();
- switchTo(tag);
- break;
- }
- }
- dismiss();
- }
- //
- privatevoidswitchTo(RecentTagtag){
- if(tag.info.id>=0){
- //Thisisanactivetask;itshouldjustgototheforeground.
- finalActivityManageram=(ActivityManager)
- getContext().getSystemService(Context.ACTIVITY_SERVICE);
- am.moveTaskToFront(tag.info.id,ActivityManager.MOVE_TASK_WITH_HOME);
- }elseif(tag.intent!=null){
- tag.intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
- |Intent.FLAG_ACTIVITY_TASK_ON_HOME);
- try{
- getContext().startActivity(tag.intent);
- }catch(ActivityNotFoundExceptione){
- Log.w("Recent","Unabletolaunchrecenttask",e);
- }
- }
- }
- /**
- *Setupandshowtherecentactivitiesdialog.
- */
- @Override
- publicvoidonStart(){
- super.onStart();
- reloadButtons();
- if(sStatusBar!=null){
- sStatusBar.disable(StatusBarManager.DISABLE_EXPAND);
- }
- //receivebroadcasts
- getContext().registerReceiver(mBroadcastReceiver,mBroadcastIntentFilter);
- mHandler.removeCallbacks(mCleanup);
- }
- /**
- *Dismisstherecentactivitiesdialog.
- */
- @Override
- publicvoidonStop(){
- super.onStop();
- if(sStatusBar!=null){
- sStatusBar.disable(StatusBarManager.DISABLE_NONE);
- }
- //stopreceivingbroadcasts
- getContext().unregisterReceiver(mBroadcastReceiver);
- mHandler.postDelayed(mCleanup,100);
- }
- /**
- *Reloadthe6buttonswithrecentactivities
- */
- privatevoidreloadButtons(){
- finalContextcontext=getContext();
- finalPackageManagerpm=context.getPackageManager();
- finalActivityManageram=(ActivityManager)
- context.getSystemService(Context.ACTIVITY_SERVICE);
- finalList<ActivityManager.RecentTaskInfo>recentTasks=
- am.getRecentTasks(MAX_RECENT_TASKS,ActivityManager.RECENT_IGNORE_UNAVAILABLE);
- ActivityInfohomeInfo=
- newIntent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
- .resolveActivityInfo(pm,0);
- IconUtilitiesiconUtilities=newIconUtilities(getContext());
- //Performancenote:OurandroidperformanceguidesaystopreferIteratorwhen
- //usingaListclass,butbecauseweknowthatgetRecentTasks()alwaysreturns
- //anArrayList<>,we'lluseasimpleindexinstead.
- intindex=0;
- intnumTasks=recentTasks.size();
- for(inti=0;i<numTasks&&(index<NUM_BUTTONS);++i){
- finalActivityManager.RecentTaskInfoinfo=recentTasks.get(i);
- //fordebugpurposesonly,disallowfirstresulttocreateemptylists
- if(DBG_FORCE_EMPTY_LIST&&(i==0))continue;
- Intentintent=newIntent(info.baseIntent);
- if(info.origActivity!=null){
- intent.setComponent(info.origActivity);
- }
- //Skipthecurrenthomeactivity.
- if(homeInfo!=null){
- if(homeInfo.packageName.equals(
- intent.getComponent().getPackageName())
- &&homeInfo.name.equals(
- intent.getComponent().getClassName())){
- continue;
- }
- }
- intent.setFlags((intent.getFlags()&~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
- |Intent.FLAG_ACTIVITY_NEW_TASK);
- finalResolveInforesolveInfo=pm.resolveActivity(intent,0);
- if(resolveInfo!=null){
- finalActivityInfoactivityInfo=resolveInfo.activityInfo;
- finalStringtitle=activityInfo.loadLabel(pm).toString();
- Drawableicon=activityInfo.loadIcon(pm);
- if(title!=null&&title.length()>0&&icon!=null){
- finalTextViewtv=mIcons[index];
- tv.setText(title);
- icon=iconUtilities.createIconDrawable(icon);
- tv.setCompoundDrawables(null,icon,null,null);
- RecentTagtag=newRecentTag();
- tag.info=info;
- tag.intent=intent;
- tv.setTag(tag);
- tv.setVisibility(View.VISIBLE);
- tv.setPressed(false);
- tv.clearFocus();
- ++index;
- }
- }
- }
- //handlethecaseof"noiconstoshow"
- mNoAppsText.setVisibility((index==0)?View.VISIBLE:View.GONE);
- //hidetherest
- for(;index<NUM_BUTTONS;++index){
- mIcons[index].setVisibility(View.GONE);
- }
- }
- /**
- *ThisisthelistenerfortheACTION_CLOSE_SYSTEM_DIALOGSintent.It'sanindicationthat
- *weshouldcloseourselvesimmediately,inordertoallowahigher-priorityUItotakeover
- *(e.g.phonecallreceived).
- */
- privateBroadcastReceivermBroadcastReceiver=newBroadcastReceiver(){
- @Override
- publicvoidonReceive(Contextcontext,Intentintent){
- Stringaction=intent.getAction();
- if(Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)){
- Stringreason=intent.getStringExtra(PhoneWindowManager.SYSTEM_DIALOG_REASON_KEY);
- if(!PhoneWindowManager.SYSTEM_DIALOG_REASON_RECENT_APPS.equals(reason)){
- dismiss();
- }
- }
- }
- };
- }
- RecentApplicationsDialog.java完整源码
从源码可以看出,关键部分有三处。
一个很关键的内部类:
// 每个任务都包含一个Tag,这个Tag保存着这个App的一些非常有用的信息 class RecentTag { ActivityManager.RecentTaskInfo info; Intent intent; }
这个RecentTag保存在每个近期任务的图标里,并且保存着这个任务的原始信息。
刚启动Dialog时对每个任务的初始化:
[java] view plain copy- privatevoidreloadButtons(){
- finalContextcontext=getContext();
- finalPackageManagerpm=context.getPackageManager();
- finalActivityManageram=(ActivityManager)
- context.getSystemService(Context.ACTIVITY_SERVICE);
- //拿到最近使用的应用的信息列表
- finalList<ActivityManager.RecentTaskInfo>recentTasks=
- am.getRecentTasks(MAX_RECENT_TASKS,ActivityManager.RECENT_IGNORE_UNAVAILABLE);
- //自制一个homeactivityinfo,用来区分
- ActivityInfohomeInfo=
- newIntent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
- .resolveActivityInfo(pm,0);
- IconUtilitiesiconUtilities=newIconUtilities(getContext());
- intindex=0;
- intnumTasks=recentTasks.size();
- //开始初始化每个任务的信息
- for(inti=0;i<numTasks&&(index<NUM_BUTTONS);++i){
- finalActivityManager.RecentTaskInfoinfo=recentTasks.get(i);
- //复制一个任务的原始Intent
- Intentintent=newIntent(info.baseIntent);
- if(info.origActivity!=null){
- intent.setComponent(info.origActivity);
- }
- //跳过homeactivity
- if(homeInfo!=null){
- if(homeInfo.packageName.equals(
- intent.getComponent().getPackageName())
- &&homeInfo.name.equals(
- intent.getComponent().getClassName())){
- continue;
- }
- }
- intent.setFlags((intent.getFlags()&~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
- |Intent.FLAG_ACTIVITY_NEW_TASK);
- finalResolveInforesolveInfo=pm.resolveActivity(intent,0);
- if(resolveInfo!=null){
- finalActivityInfoactivityInfo=resolveInfo.activityInfo;
- finalStringtitle=activityInfo.loadLabel(pm).toString();
- Drawableicon=activityInfo.loadIcon(pm);
- if(title!=null&&title.length()>0&&icon!=null){
- finalTextViewtv=mIcons[index];
- tv.setText(title);
- icon=iconUtilities.createIconDrawable(icon);
- tv.setCompoundDrawables(null,icon,null,null);
- //new一个Tag,保存这个任务的RecentTaskInfo和Intent
- RecentTagtag=newRecentTag();
- tag.info=info;
- tag.intent=intent;
- tv.setTag(tag);
- tv.setVisibility(View.VISIBLE);
- tv.setPressed(false);
- tv.clearFocus();
- ++index;
- }
- }
- }
- ...//无关紧要的代码
- }
这里的过程是:先用ActivityManager获取RecentTasks并生成一个List,然后利用这个List为Dialog中的每个任务初始化,并生成对应的信息RecentTag。
需要注意的是,RecentTag中的Intent是从对应任务的原始Intent复制过来的,这意味着那个原始Intent的一些Extra参数将会一并复制过来,
我来举个例子:比如我的App支持从第三方启动,并且第三方要提供一个token,当然这个token就以Extra参数的形式放进Intent里,然后通过startActivity()启动我的App;然后我的App根据这个token来处理,注意这里,当我的App退出后,再从近期任务里启动这个App,之前的那个token还会传递给我的App,这里就会出现错误了,原因就是上面分析的。这就是为什么从第三方跳转的应用不会出现在近期任务的列表里(比如点击短信里的url启动一个浏览器,之后近期任务里只有短信app,不会出现浏览器app)。要想不出现在近期任务里,可以给Intent设置FLAG_ACTIVITY_NO_HISTORY标志。
响应每个任务的点击事件:
[java] view plain copy- privatevoidswitchTo(RecentTagtag){
- if(tag.info.id>=0){
- //这个Task没有退出,直接移动到前台
- finalActivityManageram=(ActivityManager)
- getContext().getSystemService(Context.ACTIVITY_SERVICE);
- am.moveTaskToFront(tag.info.id,ActivityManager.MOVE_TASK_WITH_HOME);
- }elseif(tag.intent!=null){
- //task退出了的话,id为-1,则使用RecentTag中的Intent重新启动
- tag.intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
- |Intent.FLAG_ACTIVITY_TASK_ON_HOME);
- try{
- getContext().startActivity(tag.intent);
- }catch(ActivityNotFoundExceptione){
- Log.w("Recent","Unabletolaunchrecenttask",e);
- }
- }
- }
如果该Task没有退出,只是切到后台,则切换到前台;如果已经退出,就要重新启动了。
这里的Intent就是之前说的,重复使用的旧Intent了,这里注意,系统添加了FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY和FLAG_ACTIVITY_TASK_ON_HOME标志,所以我们可以在App中通过判断Intent的flag是否包含这两个来判断是否是从近期任务里启动的。注意FLAG_ACTIVITY_TASK_ON_HOME标志是Api 11添加的,所以11一下的之判断FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY就行了。
============================================================================================================================
本节内容是如何获取Android系统中应用程序的信息,主要包括packagename、label、icon、占用大小等。具体分为两个 部分,计划如下:
第一部分: 获取应用程序的packagename、label、icon等 ;
第二部分: 获取应用程序的占用大小,包括:缓存大小(cachsize)、数据大小(datasize)。
每部分都为您准备了简单丰富的实例,您一定不会错过。
Android系统为我们提供了很多服务管理的类,包括ActivityManager、PowerManager(电源管理)、AudioManager(音频管理)
等。除此之外,还提供了一个PackageManger管理类,它的主要职责是管理应用程序包。 通过它,我们就可以获取应用程序信息。
引入: AnroidManifest.xml文件节点说明:
相关类的介绍
PackageItemInfo类 说明: AndroidManifest.xml文件中所有节点的基类,提供了这些节点的基本信息:a label、icon、 meta-data。它并不 直接使用,而是由子类继承然后调用相应方法。 常用字段: public int icon 获得该资源图片在R文件中的值 (对应于android:icon属性) public int labelRes 获得该label在R文件中的值(对应于android:label属性) public String name 获得该节点的name值 (对应于android:name属性) public String packagename 获得该应用程序的包名 (对应于android:packagename属性) 常用方法: Drawable loadIcon(PackageManager pm) 获得当前应用程序的图像 CharSequence loadLabel(PackageManager pm) 获得当前应用程序的label ActivityInfo类 继承自 PackageItemInfo 说明: 获得应用程序中<activity/>或者 <receiver />节点的信息 。我们可以通过它来获取我们设置的任何属性,包括 theme 、launchMode、launchmode等 常用方法继承至PackageItemInfo类中的loadIcon()和loadLabel() ServiceInfo 类 说明: 同ActivityInfo类似 ,同样继承自 PackageItemInfo,只不过它表示的是<service>节点信息。 ApplicationInfo类 继承自 PackageItemInfo 说明:获取一个特定引用程序中<application>节点的信息。 字段说明: flags字段: FLAG_SYSTEM 系统应用程序 FLAG_EXTERNAL_STORAGE 表示该应用安装在sdcard中 常用方法继承至PackageItemInfo类中的loadIcon()和loadLabel() ResolveInfo类 说明:根据<intent>节点来获取其上一层目录的信息,通常是<activity>、<receiver>、<service>节点信息。 常用字段: public ActivityInfo activityInfo 获取 ActivityInfo对象,即<activity>或<receiver >节点信息 public ServiceInfo serviceInfo 获取 ServiceInfo对象,即<activity>节点信息 常用方法: Drawable loadIcon(PackageManager pm) 获得当前应用程序的图像 CharSequence loadLabel(PackageManager pm) 获得当前应用程序的label PackageInfo类 说明:手动获取AndroidManifest.xml文件的信息 。 常用字段: public String packageName 包名 public ActivityInfo[] activities 所有<activity>节点信息 public ApplicationInfo applicationInfo <application>节点信息,只有一个 public ActivityInfo[] receivers 所有<receiver>节点信息,多个 public ServiceInfo[] services 所有<service>节点信息 ,多个 PackageManger 类 说明: 获得已安装的应用程序信息 。可以通过getPackageManager()方法获得。 常用方法: public abstract PackageManager getPackageManager() 功能:获得一个PackageManger对象 public abstrac tDrawable getApplicationIcon(StringpackageName) 参数: packageName 包名 功能:返回给定包名的图标,否则返回null public abstract ApplicationInfo getApplicationInfo(String packageName, int flags) 参数:packagename 包名 flags 该ApplicationInfo是此flags标记,通常可以直接赋予常数0即可 功能:返回该ApplicationInfo对象 public abstract List<ApplicationInfo> getInstalledApplications(int flags) 参数:flag为一般为GET_UNINSTALLED_PACKAGES,那么此时会返回所有ApplicationInfo。我们可以对ApplicationInfo 的flags过滤,得到我们需要的。 功能:返回给定条件的所有PackageInfo public abstract List<PackageInfo> getInstalledPackages(int flags) 参数如上 功能:返回给定条件的所有PackageInfo public abstractResolveInfo resolveActivity(Intent intent, int flags) 参数: intent 查寻条件,Activity所配置的action和category flags: MATCH_DEFAULT_ONLY :Category必须带有CATEGORY_DEFAULT的Activity,才匹配 GET_INTENT_FILTERS :匹配Intent条件即可 GET_RESOLVED_FILTER :匹配Intent条件即可 功能 :返回给定条件的ResolveInfo对象(本质上是Activity) public abstract List<ResolveInfo> queryIntentActivities(Intent intent, int flags) 参数同上 功能 :返回给定条件的所有ResolveInfo对象(本质上是Activity),集合对象 public abstract ResolveInfo resolveService(Intent intent, int flags) 参数同上 功能 :返回给定条件的ResolveInfo对象(本质上是Service) public abstract List<ResolveInfo> queryIntentServices(Intent intent, int flags) 参数同上 功能 :返回给定条件的所有ResolveInfo对象(本质上是Service),集合对象
更多相关文章
- Android(安卓)JNI打印c\c++日志信息
- 学了一年的Android,还不会调试?你白学了!
- Android(安卓)开发艺术探索笔记(十五) 之 Android(安卓)的线程和线
- Android的异步任务AsyncTask
- Android处理异步耗时任务,AsyncTask使用教程
- Android(安卓)学习记录-网络请求
- Android输入系统解析及Native层模拟按键方案
- Android中读取电话本Contacts联系人的所有电话号信息
- android UI进阶之布局的优化