转自 : 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文件节点说明:

相关类的介绍

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(安卓)JNI打印c\c++日志信息
  2. 学了一年的Android,还不会调试?你白学了!
  3. Android(安卓)开发艺术探索笔记(十五) 之 Android(安卓)的线程和线
  4. Android的异步任务AsyncTask
  5. Android处理异步耗时任务,AsyncTask使用教程
  6. Android(安卓)学习记录-网络请求
  7. Android输入系统解析及Native层模拟按键方案
  8. Android中读取电话本Contacts联系人的所有电话号信息
  9. android UI进阶之布局的优化

随机推荐

  1. android 设置字体
  2. MTK Log
  3. android短信获取小代码
  4. Android仿QQ好友列表实现列表收缩与展开
  5. Android(安卓)编程下 Touch 事件的分发和
  6. Android(安卓)开源 游戏引擎
  7. Ubuntu:带着Android的芯 走着微软的路
  8. Android(安卓)下GDB调试
  9. Android(安卓)- TabHost 选项卡功能用法
  10. Android(安卓)获取内部存储、内置SD卡、