首页 文章 精选 留言 我的

精选列表

搜索[组件化],共10000篇文章
优秀的个人博客,低调大师

解开Android应用程序组件Activity的"singleTask"之谜(2)

再接下来,就是运行模拟器来运行我们的例子了。关于如何在Android源代码工程中运行模拟器,请参考 在Ubuntu上下载、编译和安装Android最新源代码 一文。 执行以下命令启动模拟器: USER-NAME@MACHINE-NAME:~/Android$emulator 模拟器启动起,就可以App Launcher中找到Task应用程序图标,接着把它启动起来: 点击中间的按钮,就会以"singleTask"的方式来启动SubActivity: 现在,我们如何来确认SubActivity是不是在新的任务中启动并且位于这个新任务的堆栈底部呢?Android源代码工程为我们准备了adb工具,可以查看模拟器上系统运行的状况,执行下面的命令查看; USER-NAME@MACHINE-NAME:~/Android$adbshelldumpsysactivity 这个命令输出的内容比较多,这里我们只关心TaskRecord部分: Runningactivities(mostrecentfirst): TaskRecord{4070d8f8#3Ashy.luo.task} Run#2:HistoryRecord{406a13f8shy.luo.task/.SubActivity} Run#1:HistoryRecord{406a0e00shy.luo.task/.MainActivity} TaskRecord{4067a510#2Acom.android.launcher} Run#0:HistoryRecord{40677518com.android.launcher/com.android.launcher2.Launcher} 果然,SubActivity和MainActivity都是运行在TaskRecord#3中,并且SubActivity在MainActivity的上面。这是怎么回事呢?碰到这种情况,Linus Torvalds告诫我们:Read the fucking source code;去年张麻子又说:枪在手,跟我走;我们没有枪,但是有source code,因此,我要说:跟着代码走。 前面我们在两篇文章Android应用程序启动过程源代码分析和Android应用程序内部启动Activity过程(startActivity)的源代码分析时,分别在Step 9和Step 8中分析了Activity在启动过程中与任务相关的函数ActivityStack.startActivityUncheckedLocked函数中,它定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中: publicclassActivityStack{ ...... finalintstartActivityUncheckedLocked(ActivityRecordr, ActivityRecordsourceRecord,Uri[]grantedUriPermissions, intgrantedMode,booleanonlyIfNeeded,booleandoResume){ finalIntentintent=r.intent; finalintcallingUid=r.launchedFromUid; intlaunchFlags=intent.getFlags(); ...... ActivityRecordnotTop=(launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP) !=0?r:null; ...... if(sourceRecord==null){ ...... }elseif(sourceRecord.launchMode==ActivityInfo.LAUNCH_SINGLE_INSTANCE){ ...... }elseif(r.launchMode==ActivityInfo.LAUNCH_SINGLE_INSTANCE ||r.launchMode==ActivityInfo.LAUNCH_SINGLE_TASK){ //Theactivitybeingstartedisasingleinstance...italways //getslaunchedintoitsowntask. launchFlags|=Intent.FLAG_ACTIVITY_NEW_TASK; } ...... booleanaddingToTask=false; if(((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK)!=0&& (launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK)==0) ||r.launchMode==ActivityInfo.LAUNCH_SINGLE_TASK ||r.launchMode==ActivityInfo.LAUNCH_SINGLE_INSTANCE){ //Ifbringtofrontisrequested,andnoresultisrequested,and //wecanfindataskthatwasstartedwiththissame //component,theninsteadoflaunchingbringthatonetothefront. if(r.resultTo==null){ //Seeifthereisatasktobringtothefront.Ifthisis //aSINGLE_INSTANCEactivity,therecanbeoneandonlyone //instanceofitinthehistory,anditisalwaysinitsown //uniquetask,sowedoaspecialsearch. ActivityRecordtaskTop=r.launchMode!=ActivityInfo.LAUNCH_SINGLE_INSTANCE ?findTaskLocked(intent,r.info) :findActivityLocked(intent,r.info); if(taskTop!=null){ ...... if((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP)!=0 ||r.launchMode==ActivityInfo.LAUNCH_SINGLE_TASK ||r.launchMode==ActivityInfo.LAUNCH_SINGLE_INSTANCE){ //Inthissituationwewanttoremoveallactivities //fromthetaskuptotheonebeingstarted.Inmost //casesthismeansweareresettingthetasktoits //initialstate. ActivityRecordtop=performClearTaskLocked( taskTop.task.taskId,r,launchFlags,true); if(top!=null){ ...... }else{ //Aspecialcase:weneedto //starttheactivitybecauseitisnotcurrently //running,andthecallerhasaskedtoclearthe //currenttasktohavethisactivityatthetop. addingToTask=true; //Nowpretendlikethisactivityisbeingstarted //bythetopofitstask,soitisputinthe //rightplace. sourceRecord=taskTop; } }elseif(r.realActivity.equals(taskTop.task.realActivity)){ ...... }elseif((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)==0){ ...... }elseif(!taskTop.task.rootWasReset){ ...... } ...... } } } ...... if(r.packageName!=null){ //Iftheactivitybeinglaunchedisthesameastheonecurrently //atthetop,thenweneedtocheckifitshouldonlybelaunched //once. ActivityRecordtop=topRunningNonDelayedActivityLocked(notTop); if(top!=null&&r.resultTo==null){ if(top.realActivity.equals(r.realActivity)){ if(top.app!=null&&top.app.thread!=null){ ...... } } } }else{ ...... } booleannewTask=false; //Shouldthisbeconsideredanewtask? if(r.resultTo==null&&!addingToTask &&(launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK)!=0){ //todo:shoulddobettermanagementofintegers. mService.mCurTask++; if(mService.mCurTask<=0){ mService.mCurTask=1; } r.task=newTaskRecord(mService.mCurTask,r.info,intent, (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH)!=0); if(DEBUG_TASKS)Slog.v(TAG,"Startingnewactivity"+r +"innewtask"+r.task); newTask=true; if(mMainStack){ mService.addRecentTaskLocked(r.task); } }elseif(sourceRecord!=null){ if(!addingToTask&& (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP)!=0){ ...... }elseif(!addingToTask&& (launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)!=0){ ...... } //Anexistingactivityisstartingthisnewactivity,sowewant //tokeepthenewoneinthesametaskastheonethatisstarting //it. r.task=sourceRecord.task; ...... }else{ ...... } ...... startActivityLocked(r,newTask,doResume); returnSTART_SUCCESS; } ...... } 首先是获得用来启动Activity的Intent的Flags,并且保存在launchFlags变量中,这里,launcFlags的Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP位没有置位,因此,notTop为null。 接下来的这个if语句: if(sourceRecord==null){ ...... }elseif(sourceRecord.launchMode==ActivityInfo.LAUNCH_SINGLE_INSTANCE){ ...... }elseif(r.launchMode==ActivityInfo.LAUNCH_SINGLE_INSTANCE ||r.launchMode==ActivityInfo.LAUNCH_SINGLE_TASK){ //Theactivitybeingstartedisasingleinstance...italways //getslaunchedintoitsowntask. launchFlags|=Intent.FLAG_ACTIVITY_NEW_TASK; } 这里变量r的类型为ActivityRecord,它表示即将在启动的Activity,在这个例子中,即为SubActivity,因此,这里的r.launchMode等于ActivityInfo.LAUNCH_SINGLE_TASK,于是,无条件将launchFlags的Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP位置为1,表示这个SubActivity要在新的任务中启动,但是别急,还要看看其它条件是否满足,如果条件都满足,才可以在新的任务中启动这个SubActivity。 接下将addingToTask变量初始化为false,这个变量也将决定是否要将SubActivity在新的任务中启动,从名字我们就可以看出,默认不增加到原有的任务中启动,即要在新的任务中启动。这里的r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK条成立,条件r.resultTo == null也成立,它表这个Activity不需要将结果返回给启动它的Activity。于是会进入接下来的if语句中,执行: ActivityRecordtaskTop=r.launchMode!=ActivityInfo.LAUNCH_SINGLE_INSTANCE ?findTaskLocked(intent,r.info) :findActivityLocked(intent,r.info) 这里的条件r.launchMode !=ActivityInfo.LAUNCH_SINGLE_INSTANCE成立,于是执行findTaskLocked函数,这个函数也是定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中: publicclassActivityStack{ ...... /** *Returnsthetopactivityinanyexistingtaskmatchingthegiven *Intent.Returnsnullifnosuchtaskisfound. */ privateActivityRecordfindTaskLocked(Intentintent,ActivityInfoinfo){ ComponentNamecls=intent.getComponent(); if(info.targetActivity!=null){ cls=newComponentName(info.packageName,info.targetActivity); } TaskRecordcp=null; finalintN=mHistory.size(); for(inti=(N-1);i>=0;i--){ ActivityRecordr=(ActivityRecord)mHistory.get(i); if(!r.finishing&&r.task!=cp &&r.launchMode!=ActivityInfo.LAUNCH_SINGLE_INSTANCE){ cp=r.task; //Slog.i(TAG,"Comparingexistingcls="+r.task.intent.getComponent().flattenToShortString() //+"/aff="+r.task.affinity+"tonewcls=" //+intent.getComponent().flattenToShortString()+"/aff="+taskAffinity); if(r.task.affinity!=null){ if(r.task.affinity.equals(info.taskAffinity)){ //Slog.i(TAG,"Foundmatchingaffinity!"); returnr; } }elseif(r.task.intent!=null &&r.task.intent.getComponent().equals(cls)){ //Slog.i(TAG,"Foundmatchingclass!"); //dump(); //Slog.i(TAG,"ForIntent"+intent+"bringingtotop:"+r.intent); returnr; }elseif(r.task.affinityIntent!=null &&r.task.affinityIntent.getComponent().equals(cls)){ //Slog.i(TAG,"Foundmatchingclass!"); //dump(); //Slog.i(TAG,"ForIntent"+intent+"bringingtotop:"+r.intent); returnr; } } } returnnull; } ...... } 这个函数无非就是根据即将要启动的SubActivity的taskAffinity属性值在系统中查找这样的一个Task:Task的affinity属性值与即将要启动的Activity的taskAffinity属性值一致。如果存在,就返回这个Task堆栈顶端的Activity回去。在上面的AndroidManifest.xml文件中,没有配置MainActivity和SubActivity的taskAffinity属性,于是它们的taskAffinity属性值就默认为父标签application的taskAffinity属性值,这里,标签application的taskAffinity也没有配置,于是它们就默认为包名,即"shy.luo.task"。由于在启动SubActivity之前,MainActivity已经启动,MainActivity启动的时候,会在一个新的任务里面启动,而这个新的任务的affinity属性就等于它的第一个Activity的taskAffinity属性值。于是,这个函数会动回表示MainActivity的ActivityRecord回去. 回到前面的startActivityUncheckedLocked函数中,这里的taskTop就表示MainActivity,它不为null,于是继续往前执行。由于条件r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK成立,于是执行下面语句: ActivityRecordtop=performClearTaskLocked( kTop.task.taskId,r,launchFlags,true); 函数performClearTaskLocked也是定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中: publicclassActivityStack{ ...... /** *Performclearoperationasrequestedby *{@linkIntent#FLAG_ACTIVITY_CLEAR_TOP}:searchfromthetopofthe *stacktothegiventask,thenlookfor *aninstanceofthatactivityinthestackand,iffound,finishall *activitiesontopofitandreturntheinstance. * *@paramnewRDescriptionofthenewactivitybeingstarted. *@returnReturnstheoldactivitythatshouldbecontinuetobeused, *ornullifnonewasfound. */ privatefinalActivityRecordperformClearTaskLocked(inttaskId, ActivityRecordnewR,intlaunchFlags,booleandoClear){ inti=mHistory.size(); //Firstfindtherequestedtask. while(i>0){ i--; ActivityRecordr=(ActivityRecord)mHistory.get(i); if(r.task.taskId==taskId){ i++; break; } } //Nowclearit. while(i>0){ i--; ActivityRecordr=(ActivityRecord)mHistory.get(i); if(r.finishing){ continue; } if(r.task.taskId!=taskId){ returnnull; } if(r.realActivity.equals(newR.realActivity)){ //Hereitis!Nowfinisheverythinginfront... ActivityRecordret=r; if(doClear){ while(i<(mHistory.size()-1)){ i++; r=(ActivityRecord)mHistory.get(i); if(r.finishing){ continue; } if(finishActivityLocked(r,i,Activity.RESULT_CANCELED, null,"clear")){ i--; } } } //Finally,ifthisisanormallaunchmode(thatis,not //expectingonNewIntent()),thenwewillfinishthecurrent //instanceoftheactivitysoanewfreshonecanbestarted. if(ret.launchMode==ActivityInfo.LAUNCH_MULTIPLE &&(launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP)==0){ if(!ret.finishing){ intindex=indexOfTokenLocked(ret); if(index>=0){ finishActivityLocked(ret,index,Activity.RESULT_CANCELED, null,"clear"); } returnnull; } } returnret; } } returnnull; } ...... } 这个函数中作用无非就是找到ID等于参数taskId的任务,然后在这个任务中查找是否已经存在即将要启动的Activity的实例,如果存在,就会把这个Actvity实例上面直到任务堆栈顶端的Activity通过调用finishActivityLocked函数将它们结束掉。在这个例子中,就是要在属性值affinity等于"shy.luo.task"的任务中看看是否存在SubActivity类型的实例,如果有,就把它上面的Activity都结束掉。这里,属性值affinity等于"shy.luo.task"的任务只有一个MainActivity,而且它不是SubActivity的实例,所以这个函数就返回null了。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966135,如需转载请自行联系原作者

优秀的个人博客,低调大师

android 打包自己的自定义组件成JAR包

在项目开发过程中,我们难免会用到自己去制作自定义的VIEW控件,之后我们别的项目如果需要的话就直接将其复制到对应的项目中使用,虽说这么做是一个解决问题的方法,但毕竟不是很好。 原因是,当我们项目积累越来越多,会发现自定义的控件越来越多,而且这些自定义的控件都是可以重复利用的,这时我们可以想想,如果把这些自定义控件都封装成一个JAR包,然后用一个项目积累起来,之后我们以后开发项目只要在原来JAR包的基础上做扩展或者直接使用,可以大大减少自己的工作重复性。 首先Android 工程的基本面貌是这样的: 当然对应的Activity 文件被我删除了,因为当编译成jar 包我们并不需要Activity 文件。 以上面这个工程为例,我们将它打包成JAR包步骤为: 右键工程选择导出: 选择导出目标为:java->JAR文件: 把一些不必要的文件勾选掉,如下图: 到了这一步,己经基本完成,浏览选择jar 文件导出路径即可。 导出完成后,我们就可以像使用其他JAR文件一样使用我们自己的自定义控件包了。下面给出一个小DEMO介绍如何使用这个JAR包。 步骤一: 新建文件夹lib,将jar 包放入。 步骤二: 关联JAR包,如下图: 步骤三,使用JAR包: 如下代码: <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" xmlns:gif ="http://schemas.android.com/apk/res/com.terry.jarTest" android:id ="@+id/layout" android:orientation ="vertical" android:layout_width ="fill_parent" android:layout_height ="fill_parent" > < Button android:text ="停止" android:id ="@+id/Button01" android:layout_width ="wrap_content" android:layout_height ="wrap_content" ></ Button > < Button android:text ="开始" android:id ="@+id/Button02" android:layout_width ="wrap_content" android:layout_height ="wrap_content" ></ Button > < com.terry.gif.TypegifView android:layout_width ="fill_parent" android:id ="@+id/gifView1" gif:stop ="true" android:layout_height ="wrap_content" gif:delay ="1" ></ com.terry.gif.TypegifView > </ LinearLayout > 有一个不好的就是如果你项目中存在使用属性,必须也把属性一起复制到你要使用的项目中,要不然会识别不了。 本文转自 terry_龙 51CTO博客,原文链接:http://blog.51cto.com/terryblog/564558 ,如需转载请自行联系原作者

资源下载

更多资源
Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册