转自 : 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
  1. publicclassRecentApplicationsDialogextendsDialogimplementsOnClickListener{
  2. //Elementsfordebuggingsupport
  3. //privatestaticfinalStringLOG_TAG="RecentApplicationsDialog";
  4. privatestaticfinalbooleanDBG_FORCE_EMPTY_LIST=false;
  5. staticprivateStatusBarManagersStatusBar;
  6. privatestaticfinalintNUM_BUTTONS=8;
  7. privatestaticfinalintMAX_RECENT_TASKS=NUM_BUTTONS*2;//allowforsomediscards
  8. finalTextView[]mIcons=newTextView[NUM_BUTTONS];
  9. ViewmNoAppsText;
  10. IntentFiltermBroadcastIntentFilter=newIntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
  11. classRecentTag{
  12. ActivityManager.RecentTaskInfoinfo;
  13. Intentintent;
  14. }
  15. HandlermHandler=newHandler();
  16. RunnablemCleanup=newRunnable(){
  17. publicvoidrun(){
  18. //dumpextramemorywe'rehangingonto
  19. for(TextViewicon:mIcons){
  20. icon.setCompoundDrawables(null,null,null,null);
  21. icon.setTag(null);
  22. }
  23. }
  24. };
  25. publicRecentApplicationsDialog(Contextcontext){
  26. super(context,com.android.internal.R.style.Theme_Dialog_RecentApplications);
  27. }
  28. /**
  29. *Wecreatetherecentapplicationsdialogjustonce,anditstaysaround(hidden)
  30. *untilactivatedbytheuser.
  31. *
  32. *@seePhoneWindowManager#showRecentAppsDialog
  33. */
  34. @Override
  35. protectedvoidonCreate(BundlesavedInstanceState){
  36. super.onCreate(savedInstanceState);
  37. Contextcontext=getContext();
  38. if(sStatusBar==null){
  39. sStatusBar=(StatusBarManager)context.getSystemService(Context.STATUS_BAR_SERVICE);
  40. }
  41. Windowwindow=getWindow();
  42. window.requestFeature(Window.FEATURE_NO_TITLE);
  43. window.setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
  44. window.setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
  45. WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
  46. window.setTitle("Recents");
  47. setContentView(com.android.internal.R.layout.recent_apps_dialog);
  48. finalWindowManager.LayoutParamsparams=window.getAttributes();
  49. params.width=WindowManager.LayoutParams.MATCH_PARENT;
  50. params.height=WindowManager.LayoutParams.MATCH_PARENT;
  51. window.setAttributes(params);
  52. window.setFlags(0,WindowManager.LayoutParams.FLAG_DIM_BEHIND);
  53. //默认显示8个
  54. mIcons[0]=(TextView)findViewById(com.android.internal.R.id.button0);
  55. mIcons[1]=(TextView)findViewById(com.android.internal.R.id.button1);
  56. mIcons[2]=(TextView)findViewById(com.android.internal.R.id.button2);
  57. mIcons[3]=(TextView)findViewById(com.android.internal.R.id.button3);
  58. mIcons[4]=(TextView)findViewById(com.android.internal.R.id.button4);
  59. mIcons[5]=(TextView)findViewById(com.android.internal.R.id.button5);
  60. mIcons[6]=(TextView)findViewById(com.android.internal.R.id.button6);
  61. mIcons[7]=(TextView)findViewById(com.android.internal.R.id.button7);
  62. mNoAppsText=findViewById(com.android.internal.R.id.no_applications_message);
  63. //关键在哪,你懂得...
  64. for(TextViewb:mIcons){
  65. b.setOnClickListener(this);
  66. }
  67. }
  68. @Override
  69. publicbooleanonKeyDown(intkeyCode,KeyEventevent){
  70. if(keyCode==KeyEvent.KEYCODE_TAB){
  71. //IgnoreallmetakeysotherthanSHIFT.Theappswitchkeycouldbea
  72. //fallbackactionchordedwithALT,METAorevenCTRLdependingonthekeymap.
  73. //DPadnavigationishandledbytheViewRootelsewhere.
  74. finalbooleanbackward=event.isShiftPressed();
  75. finalintnumIcons=mIcons.length;
  76. intnumButtons=0;
  77. while(numButtons<numIcons&&mIcons[numButtons].getVisibility()==View.VISIBLE){
  78. numButtons+=1;
  79. }
  80. if(numButtons!=0){
  81. intnextFocus=backward?numButtons-1:0;
  82. for(inti=0;i<numButtons;i++){
  83. if(mIcons[i].hasFocus()){
  84. if(backward){
  85. nextFocus=(i+numButtons-1)%numButtons;
  86. }else{
  87. nextFocus=(i+1)%numButtons;
  88. }
  89. break;
  90. }
  91. }
  92. finalintdirection=backward?View.FOCUS_BACKWARD:View.FOCUS_FORWARD;
  93. if(mIcons[nextFocus].requestFocus(direction)){
  94. mIcons[nextFocus].playSoundEffect(
  95. SoundEffectConstants.getContantForFocusDirection(direction));
  96. }
  97. }
  98. //ThedialogalwayshandlesthekeytopreventtheViewRootfrom
  99. //performingthedefaultnavigationitself.
  100. returntrue;
  101. }
  102. returnsuper.onKeyDown(keyCode,event);
  103. }
  104. /**
  105. *Dismissthedialogandswitchtotheselectedapplication.
  106. */
  107. publicvoiddismissAndSwitch(){
  108. finalintnumIcons=mIcons.length;
  109. RecentTagtag=null;
  110. for(inti=0;i<numIcons;i++){
  111. if(mIcons[i].getVisibility()!=View.VISIBLE){
  112. break;
  113. }
  114. if(i==0||mIcons[i].hasFocus()){
  115. tag=(RecentTag)mIcons[i].getTag();
  116. if(mIcons[i].hasFocus()){
  117. break;
  118. }
  119. }
  120. }
  121. if(tag!=null){
  122. switchTo(tag);
  123. }
  124. dismiss();
  125. }
  126. /**
  127. *Handlerforuserclicks.Ifabuttonwasclicked,launchthecorrespondingactivity.
  128. */
  129. publicvoidonClick(Viewv){
  130. for(TextViewb:mIcons){
  131. if(b==v){
  132. RecentTagtag=(RecentTag)b.getTag();
  133. switchTo(tag);
  134. break;
  135. }
  136. }
  137. dismiss();
  138. }
  139. //
  140. privatevoidswitchTo(RecentTagtag){
  141. if(tag.info.id>=0){
  142. //Thisisanactivetask;itshouldjustgototheforeground.
  143. finalActivityManageram=(ActivityManager)
  144. getContext().getSystemService(Context.ACTIVITY_SERVICE);
  145. am.moveTaskToFront(tag.info.id,ActivityManager.MOVE_TASK_WITH_HOME);
  146. }elseif(tag.intent!=null){
  147. tag.intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
  148. |Intent.FLAG_ACTIVITY_TASK_ON_HOME);
  149. try{
  150. getContext().startActivity(tag.intent);
  151. }catch(ActivityNotFoundExceptione){
  152. Log.w("Recent","Unabletolaunchrecenttask",e);
  153. }
  154. }
  155. }
  156. /**
  157. *Setupandshowtherecentactivitiesdialog.
  158. */
  159. @Override
  160. publicvoidonStart(){
  161. super.onStart();
  162. reloadButtons();
  163. if(sStatusBar!=null){
  164. sStatusBar.disable(StatusBarManager.DISABLE_EXPAND);
  165. }
  166. //receivebroadcasts
  167. getContext().registerReceiver(mBroadcastReceiver,mBroadcastIntentFilter);
  168. mHandler.removeCallbacks(mCleanup);
  169. }
  170. /**
  171. *Dismisstherecentactivitiesdialog.
  172. */
  173. @Override
  174. publicvoidonStop(){
  175. super.onStop();
  176. if(sStatusBar!=null){
  177. sStatusBar.disable(StatusBarManager.DISABLE_NONE);
  178. }
  179. //stopreceivingbroadcasts
  180. getContext().unregisterReceiver(mBroadcastReceiver);
  181. mHandler.postDelayed(mCleanup,100);
  182. }
  183. /**
  184. *Reloadthe6buttonswithrecentactivities
  185. */
  186. privatevoidreloadButtons(){
  187. finalContextcontext=getContext();
  188. finalPackageManagerpm=context.getPackageManager();
  189. finalActivityManageram=(ActivityManager)
  190. context.getSystemService(Context.ACTIVITY_SERVICE);
  191. finalList<ActivityManager.RecentTaskInfo>recentTasks=
  192. am.getRecentTasks(MAX_RECENT_TASKS,ActivityManager.RECENT_IGNORE_UNAVAILABLE);
  193. ActivityInfohomeInfo=
  194. newIntent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
  195. .resolveActivityInfo(pm,0);
  196. IconUtilitiesiconUtilities=newIconUtilities(getContext());
  197. //Performancenote:OurandroidperformanceguidesaystopreferIteratorwhen
  198. //usingaListclass,butbecauseweknowthatgetRecentTasks()alwaysreturns
  199. //anArrayList<>,we'lluseasimpleindexinstead.
  200. intindex=0;
  201. intnumTasks=recentTasks.size();
  202. for(inti=0;i<numTasks&&(index<NUM_BUTTONS);++i){
  203. finalActivityManager.RecentTaskInfoinfo=recentTasks.get(i);
  204. //fordebugpurposesonly,disallowfirstresulttocreateemptylists
  205. if(DBG_FORCE_EMPTY_LIST&&(i==0))continue;
  206. Intentintent=newIntent(info.baseIntent);
  207. if(info.origActivity!=null){
  208. intent.setComponent(info.origActivity);
  209. }
  210. //Skipthecurrenthomeactivity.
  211. if(homeInfo!=null){
  212. if(homeInfo.packageName.equals(
  213. intent.getComponent().getPackageName())
  214. &&homeInfo.name.equals(
  215. intent.getComponent().getClassName())){
  216. continue;
  217. }
  218. }
  219. intent.setFlags((intent.getFlags()&~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
  220. |Intent.FLAG_ACTIVITY_NEW_TASK);
  221. finalResolveInforesolveInfo=pm.resolveActivity(intent,0);
  222. if(resolveInfo!=null){
  223. finalActivityInfoactivityInfo=resolveInfo.activityInfo;
  224. finalStringtitle=activityInfo.loadLabel(pm).toString();
  225. Drawableicon=activityInfo.loadIcon(pm);
  226. if(title!=null&&title.length()>0&&icon!=null){
  227. finalTextViewtv=mIcons[index];
  228. tv.setText(title);
  229. icon=iconUtilities.createIconDrawable(icon);
  230. tv.setCompoundDrawables(null,icon,null,null);
  231. RecentTagtag=newRecentTag();
  232. tag.info=info;
  233. tag.intent=intent;
  234. tv.setTag(tag);
  235. tv.setVisibility(View.VISIBLE);
  236. tv.setPressed(false);
  237. tv.clearFocus();
  238. ++index;
  239. }
  240. }
  241. }
  242. //handlethecaseof"noiconstoshow"
  243. mNoAppsText.setVisibility((index==0)?View.VISIBLE:View.GONE);
  244. //hidetherest
  245. for(;index<NUM_BUTTONS;++index){
  246. mIcons[index].setVisibility(View.GONE);
  247. }
  248. }
  249. /**
  250. *ThisisthelistenerfortheACTION_CLOSE_SYSTEM_DIALOGSintent.It'sanindicationthat
  251. *weshouldcloseourselvesimmediately,inordertoallowahigher-priorityUItotakeover
  252. *(e.g.phonecallreceived).
  253. */
  254. privateBroadcastReceivermBroadcastReceiver=newBroadcastReceiver(){
  255. @Override
  256. publicvoidonReceive(Contextcontext,Intentintent){
  257. Stringaction=intent.getAction();
  258. if(Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)){
  259. Stringreason=intent.getStringExtra(PhoneWindowManager.SYSTEM_DIALOG_REASON_KEY);
  260. if(!PhoneWindowManager.SYSTEM_DIALOG_REASON_RECENT_APPS.equals(reason)){
  261. dismiss();
  262. }
  263. }
  264. }
  265. };
  266. }
  267. RecentApplicationsDialog.java完整源码

从源码可以看出,关键部分有三处。

一个很关键的内部类:

// 每个任务都包含一个Tag,这个Tag保存着这个App的一些非常有用的信息    class RecentTag {        ActivityManager.RecentTaskInfo info;        Intent intent;    }

这个RecentTag保存在每个近期任务的图标里,并且保存着这个任务的原始信息。

刚启动Dialog时对每个任务的初始化:

[java] view plain copy
  1. privatevoidreloadButtons(){
  2. finalContextcontext=getContext();
  3. finalPackageManagerpm=context.getPackageManager();
  4. finalActivityManageram=(ActivityManager)
  5. context.getSystemService(Context.ACTIVITY_SERVICE);
  6. //拿到最近使用的应用的信息列表
  7. finalList<ActivityManager.RecentTaskInfo>recentTasks=
  8. am.getRecentTasks(MAX_RECENT_TASKS,ActivityManager.RECENT_IGNORE_UNAVAILABLE);
  9. //自制一个homeactivityinfo,用来区分
  10. ActivityInfohomeInfo=
  11. newIntent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
  12. .resolveActivityInfo(pm,0);
  13. IconUtilitiesiconUtilities=newIconUtilities(getContext());
  14. intindex=0;
  15. intnumTasks=recentTasks.size();
  16. //开始初始化每个任务的信息
  17. for(inti=0;i<numTasks&&(index<NUM_BUTTONS);++i){
  18. finalActivityManager.RecentTaskInfoinfo=recentTasks.get(i);
  19. //复制一个任务的原始Intent
  20. Intentintent=newIntent(info.baseIntent);
  21. if(info.origActivity!=null){
  22. intent.setComponent(info.origActivity);
  23. }
  24. //跳过homeactivity
  25. if(homeInfo!=null){
  26. if(homeInfo.packageName.equals(
  27. intent.getComponent().getPackageName())
  28. &&homeInfo.name.equals(
  29. intent.getComponent().getClassName())){
  30. continue;
  31. }
  32. }
  33. intent.setFlags((intent.getFlags()&~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
  34. |Intent.FLAG_ACTIVITY_NEW_TASK);
  35. finalResolveInforesolveInfo=pm.resolveActivity(intent,0);
  36. if(resolveInfo!=null){
  37. finalActivityInfoactivityInfo=resolveInfo.activityInfo;
  38. finalStringtitle=activityInfo.loadLabel(pm).toString();
  39. Drawableicon=activityInfo.loadIcon(pm);
  40. if(title!=null&&title.length()>0&&icon!=null){
  41. finalTextViewtv=mIcons[index];
  42. tv.setText(title);
  43. icon=iconUtilities.createIconDrawable(icon);
  44. tv.setCompoundDrawables(null,icon,null,null);
  45. //new一个Tag,保存这个任务的RecentTaskInfo和Intent
  46. RecentTagtag=newRecentTag();
  47. tag.info=info;
  48. tag.intent=intent;
  49. tv.setTag(tag);
  50. tv.setVisibility(View.VISIBLE);
  51. tv.setPressed(false);
  52. tv.clearFocus();
  53. ++index;
  54. }
  55. }
  56. }
  57. ...//无关紧要的代码
  58. }

这里的过程是:先用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
  1. privatevoidswitchTo(RecentTagtag){
  2. if(tag.info.id>=0){
  3. //这个Task没有退出,直接移动到前台
  4. finalActivityManageram=(ActivityManager)
  5. getContext().getSystemService(Context.ACTIVITY_SERVICE);
  6. am.moveTaskToFront(tag.info.id,ActivityManager.MOVE_TASK_WITH_HOME);
  7. }elseif(tag.intent!=null){
  8. //task退出了的话,id为-1,则使用RecentTag中的Intent重新启动
  9. tag.intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
  10. |Intent.FLAG_ACTIVITY_TASK_ON_HOME);
  11. try{
  12. getContext().startActivity(tag.intent);
  13. }catch(ActivityNotFoundExceptione){
  14. Log.w("Recent","Unabletolaunchrecenttask",e);
  15. }
  16. }
  17. }

如果该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文件节点说明:Android近期任务列表 Recent Applicatoins 分析 + Android PackageManager_第1张图片

相关类的介绍

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),集合对象




更多相关文章

  1. Android应用程序编译过程简述
  2. Android 应用程序四大组件
  3. Android应用程序的六个组成部分
  4. Android 判断应用程序安装位置

随机推荐

  1. android selector(zhuan)
  2. Android输入法之如何自定义每个key的属性
  3. android学习记录
  4. 40.Android(安卓)资源Id工具类ResourcesU
  5. android studio 全局变量和变量保存到xml
  6. Android中常见MIME类型
  7. android pinch:双指缩放图片和单指拖拽
  8. Android(安卓)自定义WheelView滚动控件
  9. Android(安卓)TabHost 选项卡 滑动activi
  10. Android自定义属性,attr format取值类型