[size=large;]区分activity的四种加载模式[/size]
转载自 [url=http://marshal.easymorse.com/archives/2950]http://marshal.easymorse.com/archives/2950[/url]
在多activity开发中,有可能是自己应用之间的activity跳转,或者夹带其他应用的可复用activity。可能会希望跳转到原来某个activity实例,而不是产生大量重复的activity。
这需要为activity配置特定的加载模式,而不是使用默认的加载模式。
<h4>加载模式分类及在哪里配置</h4>activity有四种加载模式:
[list][*]standard
[*]singletop
[*]singletask
[*]singleinstance
[/list]设置的位置在androidmanifest.xml文件中activity元素的android:launchmode属性:
<br><img src="http://dl.iteye.com/upload/attachment/537018/71c403e9-9e93-354c-a977-80992783a959.png" alt="">
区分activity的加载模式,通过示例一目了然。这里编写了一个activity a(acta)和activity b(actb)循环跳转的例子。对加载模式修改和代码做稍微改动,就可以说明四种模式的区别。
<h4>standard</h4>首先说standard模式,也就是默认模式,不需要配置launchmode。先只写一个名为acta的activity:
第一个界面:
<br><img src="http://dl.iteye.com/upload/attachment/537020/a9cbee6e-11fb-34b5-ba1c-4b6e78dad2f0.png" alt="">
点击按钮后:
<br><img src="http://dl.iteye.com/upload/attachment/537022/f53c93f4-f738-3eca-bf1e-aea06bfcf4be.png" alt="">
可以多点几次。发现每次都创建了该activity的新实例。standard的加载模式就是这样的,intent将发送给新的实例。
现在点android设备的回退键,可以看到是按照刚才创建activity实例的倒序依次出现,类似退栈的操作,而刚才操作跳转按钮的过程是压栈的操作。如下图:
<br><img src="http://dl.iteye.com/upload/attachment/537024/fd512702-d3ca-3b42-b40a-dc44cc66b140.png" alt="">
<h4>singletop</h4>singletop和standard模式,都会将intent发送新的实例(后两种模式不发送到新的实例,如果已经有了的话)。不过,singletop要求如果创建intent的时候栈顶已经有要创建的activity的实例,则将intent发送给该实例,而不发送给新的实例。
还是用刚才的示例,只需将launchmode改为singletop,就能看到区别。
运行的时候会发现,按多少遍按钮,都是相同的actia实例,因为该实例在栈顶,因此不会创建新的实例。如果回退,将退出应用。
<br><img src="http://dl.iteye.com/upload/attachment/537026/8b1255c2-6fae-30ad-a0a2-eff9be37cf7c.png" alt="">
singletop模式,可用来解决栈顶多个重复相同的activity的问题。
如果是a activity跳转到b activity,再跳转到a activity,行为就和standard一样了,会在b activity跳转到a activity的时候创建a activity的新实例,因为当时的栈顶不是a activity实例。
acta类稍作改动:
<br><img src="http://dl.iteye.com/upload/attachment/537028/dea0efbf-7891-3147-a521-3398a0821cf2.png" alt="">
如果把acta的加载模式改为standard,情况一样。
<h4>singletask</h4>singletask模式和后面的singleinstance模式都是只创建一个实例的。
当intent到来,需要创建singletask模式activity的时候,系统会检查栈里面是否已经有该activity的实例。如果有直接将intent发送给它。
把上面singletop的实例中的acta的launchmode改为singletask,actb的改为standard。那么会发现在acta界面中按一次按钮:
<br><img src="http://dl.iteye.com/upload/attachment/537030/0bf2ba1b-fdd0-3234-a8db-10b42c18a09b.png" alt="">
然后在actb1界面中按按钮,因为acta是singletask,会使用原来的acta1实例。这时候栈内的情况:
<br><img src="http://dl.iteye.com/upload/attachment/537032/b5652f58-35f1-324e-bf04-2d6402ca61a1.png" alt="">
如果多次按按钮跳转,会发现始终只有acta1这一个acta类的实例。
<h4>singleinstance</h4>解释singleinstance模式比较麻烦。
首先要说一下task(任务)的概念。
如果是swing或者windows程序,可能有多个窗口可以切换,但是你无法在自己程序中复用人家的窗口。注意是直接复用人家的二进制代码,不是你拿到人家api后的源代码级调用。
android可以做到,让别人的程序直接复用你的activity(类似桌面程序的窗口)。
android为提供这种机制,就引入了task的概念。task可以认为是一个栈,可放入多个activity。比如启动一个应用,那么android就创建了一个task,然后启动这个应用的入口activity,就是intent-filter中配置为main和launch的那个(见<a title="一个apk文件部署产生多个应用安装的效果" href="http://marshal.easymorse.com/archives/2929">一个apk文件部署产生多个应用安装的效果</a>)。这个activity是根(root)activity,可能会在它的界面调用其他activity,这些activity如果按照上面那三个模式,也会在这个栈(task)中,只是实例化的策略不同而已。
验证的办法是调用和打印activity的taskid:
会发现,无论切换activity,taskid是相同的。
当然也可以在这个单一的task栈中,放入别人的activity,比如google地图,这样用户看过地图按回退键的时候,会退栈回到调用地图的activity。对用户来说,并不觉得在操作多个应用。这就是task的作用。
但是,有这样的需求,多个task共享一个activity(singletask是在一个task中共享一个activity)。
现成的例子是google地图。比如我有一个应用是导游方面的,其中调用的google地图activity。那么现在我比如按home键,然后到应用列表中打开google地图,你会发现显示的就是刚才的地图,实际上是同一个activity。
如果使用上面三种模式,是无法实现这个需求的。google地图应用中有多个上下文activity,比如路线查询等的,导游应用也有一些上下文activity。在各自应用中回退要回退到各自的上下文activity中。
singleinstance模式解决了这个问题(绕了这么半天才说到正题)。让这个模式下的activity单独在一个task栈中。这个栈只有一个activity。导游应用和google地图应用发送的intent都由这个activity接收和展示。
这里又有两个问题:
[list][*]如果是这种情况,多个task栈也可以看作一个应用。比如导游应用启动地图activity,实际上是在导游应用task栈之上singleinstance模式创建的(如果还没有的话,如果有就是直接显示它)一个新栈,当这个栈里面的唯一activity,地图activity回退的时候,只是把这个栈移开了,这样就看到导游应用刚才的activity了;
[*]多个应用(task)共享一个activity要求这些应用都没有退出,比如刚才强调要用home键从导游应用切换到地图应用。因为,如果退出导游应用,而这时也地图应用并未运行的话,那个单独的地图activity(task)也会退出了。
[/list]如果还是拿刚才的acta和actb的示例,可以把actb的模式改为singleinstance,acta为standard,如果按一次按钮切换到actb,看到现象用示意图类似这样:
<br><img src="http://dl.iteye.com/upload/attachment/537034/f3187073-21f7-33b0-91c4-d8de99aa4dd0.png" alt="">
如果是第一次按钮切换到actb,在actb在按按钮切换到acta,然后再回退,示意图是:
<br><img src="http://dl.iteye.com/upload/attachment/537036/9cb58bbe-c724-3631-a334-719943880288.png" alt="">
另外,可以看到两个activity的taskid是不同的。
转载自 [url=http://marshal.easymorse.com/archives/2950]http://marshal.easymorse.com/archives/2950[/url]
在多activity开发中,有可能是自己应用之间的activity跳转,或者夹带其他应用的可复用activity。可能会希望跳转到原来某个activity实例,而不是产生大量重复的activity。
这需要为activity配置特定的加载模式,而不是使用默认的加载模式。
<h4>加载模式分类及在哪里配置</h4>activity有四种加载模式:
[list][*]standard
[*]singletop
[*]singletask
[*]singleinstance
[/list]设置的位置在androidmanifest.xml文件中activity元素的android:launchmode属性:
<activity android:name="actb" android:launchmode="singletask"></activity> 也可以在eclipse adt中图形界面中编辑:<br><img src="http://dl.iteye.com/upload/attachment/537018/71c403e9-9e93-354c-a977-80992783a959.png" alt="">
区分activity的加载模式,通过示例一目了然。这里编写了一个activity a(acta)和activity b(actb)循环跳转的例子。对加载模式修改和代码做稍微改动,就可以说明四种模式的区别。
<h4>standard</h4>首先说standard模式,也就是默认模式,不需要配置launchmode。先只写一个名为acta的activity:
package com.easymorse.activities; import android.app.activity; import android.content.intent; import android.os.bundle; import android.view.view; import android.view.view.onclicklistener; import android.widget.button; import android.widget.linearlayout; import android.widget.textview; public class acta extends activity { /** called when the activity is first created. */ @override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); textview textview = new textview(this); textview.settext(this + ""); button button = new button(this); button.settext("go acta"); button.setonclicklistener(new onclicklistener() { @override public void onclick(view v) { intent intent = new intent(); intent.setclass(acta.this, acta.class); startactivity(intent); } }); linearlayout layout = new linearlayout(this); layout.setorientation(linearlayout.vertical); layout.addview(textview); layout.addview(button); this.setcontentview(layout); } } 例子中都没有用layout,免得看着罗嗦。可见是acta –> acta的例子。在界面中打印出对象的tostring值可以根据hash code识别是否创建新acta实例。第一个界面:
<br><img src="http://dl.iteye.com/upload/attachment/537020/a9cbee6e-11fb-34b5-ba1c-4b6e78dad2f0.png" alt="">
点击按钮后:
<br><img src="http://dl.iteye.com/upload/attachment/537022/f53c93f4-f738-3eca-bf1e-aea06bfcf4be.png" alt="">
可以多点几次。发现每次都创建了该activity的新实例。standard的加载模式就是这样的,intent将发送给新的实例。
现在点android设备的回退键,可以看到是按照刚才创建activity实例的倒序依次出现,类似退栈的操作,而刚才操作跳转按钮的过程是压栈的操作。如下图:
<br><img src="http://dl.iteye.com/upload/attachment/537024/fd512702-d3ca-3b42-b40a-dc44cc66b140.png" alt="">
<h4>singletop</h4>singletop和standard模式,都会将intent发送新的实例(后两种模式不发送到新的实例,如果已经有了的话)。不过,singletop要求如果创建intent的时候栈顶已经有要创建的activity的实例,则将intent发送给该实例,而不发送给新的实例。
还是用刚才的示例,只需将launchmode改为singletop,就能看到区别。
运行的时候会发现,按多少遍按钮,都是相同的actia实例,因为该实例在栈顶,因此不会创建新的实例。如果回退,将退出应用。
<br><img src="http://dl.iteye.com/upload/attachment/537026/8b1255c2-6fae-30ad-a0a2-eff9be37cf7c.png" alt="">
singletop模式,可用来解决栈顶多个重复相同的activity的问题。
如果是a activity跳转到b activity,再跳转到a activity,行为就和standard一样了,会在b activity跳转到a activity的时候创建a activity的新实例,因为当时的栈顶不是a activity实例。
acta类稍作改动:
package com.easymorse.activities;import android.app.activity;import android.content.intent;import android.os.bundle;import android.view.view;import android.view.view.onclicklistener;import android.widget.button;import android.widget.linearlayout;import android.widget.textview;public class acta extends activity { /** called when the activity is first created. */ @override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); textview textview = new textview(this); textview.settext(this + ""); button button = new button(this); button.settext("go actb"); button.setonclicklistener(new onclicklistener() { @override public void onclick(view v) { intent intent = new intent(); intent.setclass(acta.this, actb.class); startactivity(intent); } }); linearlayout layout = new linearlayout(this); layout.setorientation(linearlayout.vertical); layout.addview(textview); layout.addview(button); this.setcontentview(layout); }} actb类:package com.easymorse.activities;import android.app.activity;import android.content.intent;import android.os.bundle;import android.view.view;import android.view.view.onclicklistener;import android.widget.button;import android.widget.linearlayout;public class actb extends activity { @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); button button=new button(this); button.settext("go acta"); button.setonclicklistener(new onclicklistener() { @override public void onclick(view v) { intent intent=new intent(); intent.setclass(actb.this, acta.class); startactivity(intent); } }); linearlayout layout=new linearlayout(this); layout.addview(button); this.setcontentview(layout); }} actb类使用默认(standard)加载,acta使用singletop加载。结果类似下图:<br><img src="http://dl.iteye.com/upload/attachment/537028/dea0efbf-7891-3147-a521-3398a0821cf2.png" alt="">
如果把acta的加载模式改为standard,情况一样。
<h4>singletask</h4>singletask模式和后面的singleinstance模式都是只创建一个实例的。
当intent到来,需要创建singletask模式activity的时候,系统会检查栈里面是否已经有该activity的实例。如果有直接将intent发送给它。
把上面singletop的实例中的acta的launchmode改为singletask,actb的改为standard。那么会发现在acta界面中按一次按钮:
<br><img src="http://dl.iteye.com/upload/attachment/537030/0bf2ba1b-fdd0-3234-a8db-10b42c18a09b.png" alt="">
然后在actb1界面中按按钮,因为acta是singletask,会使用原来的acta1实例。这时候栈内的情况:
<br><img src="http://dl.iteye.com/upload/attachment/537032/b5652f58-35f1-324e-bf04-2d6402ca61a1.png" alt="">
如果多次按按钮跳转,会发现始终只有acta1这一个acta类的实例。
<h4>singleinstance</h4>解释singleinstance模式比较麻烦。
首先要说一下task(任务)的概念。
如果是swing或者windows程序,可能有多个窗口可以切换,但是你无法在自己程序中复用人家的窗口。注意是直接复用人家的二进制代码,不是你拿到人家api后的源代码级调用。
android可以做到,让别人的程序直接复用你的activity(类似桌面程序的窗口)。
android为提供这种机制,就引入了task的概念。task可以认为是一个栈,可放入多个activity。比如启动一个应用,那么android就创建了一个task,然后启动这个应用的入口activity,就是intent-filter中配置为main和launch的那个(见<a title="一个apk文件部署产生多个应用安装的效果" href="http://marshal.easymorse.com/archives/2929">一个apk文件部署产生多个应用安装的效果</a>)。这个activity是根(root)activity,可能会在它的界面调用其他activity,这些activity如果按照上面那三个模式,也会在这个栈(task)中,只是实例化的策略不同而已。
验证的办法是调用和打印activity的taskid:
textview textview2 = new textview(this);textview2.settext("task id: "+this.gettaskid()); 会发现,无论切换activity,taskid是相同的。
当然也可以在这个单一的task栈中,放入别人的activity,比如google地图,这样用户看过地图按回退键的时候,会退栈回到调用地图的activity。对用户来说,并不觉得在操作多个应用。这就是task的作用。
但是,有这样的需求,多个task共享一个activity(singletask是在一个task中共享一个activity)。
现成的例子是google地图。比如我有一个应用是导游方面的,其中调用的google地图activity。那么现在我比如按home键,然后到应用列表中打开google地图,你会发现显示的就是刚才的地图,实际上是同一个activity。
如果使用上面三种模式,是无法实现这个需求的。google地图应用中有多个上下文activity,比如路线查询等的,导游应用也有一些上下文activity。在各自应用中回退要回退到各自的上下文activity中。
singleinstance模式解决了这个问题(绕了这么半天才说到正题)。让这个模式下的activity单独在一个task栈中。这个栈只有一个activity。导游应用和google地图应用发送的intent都由这个activity接收和展示。
这里又有两个问题:
[list][*]如果是这种情况,多个task栈也可以看作一个应用。比如导游应用启动地图activity,实际上是在导游应用task栈之上singleinstance模式创建的(如果还没有的话,如果有就是直接显示它)一个新栈,当这个栈里面的唯一activity,地图activity回退的时候,只是把这个栈移开了,这样就看到导游应用刚才的activity了;
[*]多个应用(task)共享一个activity要求这些应用都没有退出,比如刚才强调要用home键从导游应用切换到地图应用。因为,如果退出导游应用,而这时也地图应用并未运行的话,那个单独的地图activity(task)也会退出了。
[/list]如果还是拿刚才的acta和actb的示例,可以把actb的模式改为singleinstance,acta为standard,如果按一次按钮切换到actb,看到现象用示意图类似这样:
<br><img src="http://dl.iteye.com/upload/attachment/537034/f3187073-21f7-33b0-91c4-d8de99aa4dd0.png" alt="">
如果是第一次按钮切换到actb,在actb在按按钮切换到acta,然后再回退,示意图是:
<br><img src="http://dl.iteye.com/upload/attachment/537036/9cb58bbe-c724-3631-a334-719943880288.png" alt="">
另外,可以看到两个activity的taskid是不同的。
本文详细介绍了Android应用开发中activity的四种加载模式(standard、singletop、singletask、singleinstance),并提供了示例代码来区分它们的行为特点。重点在于通过配置android:launchmode属性在AndroidManifest.xml文件或Eclipse ADT中实现不同模式,以避免产生重复实例,优化内存使用。
1559

被折叠的 条评论
为什么被折叠?



