Android-在安装完成界面,点击打开应用程序。在应用程序点击home键,再从桌面打开程序导致产生多个实例或者说程序被重复打开。(转)
原文出处:http://blog.csdn.net/etong_123/article/details/22897731
问题标题都已经写明了,就是在普通的安装apk完成之后,会遇到的一种情况。基本上在程序的AndroidManifest.xml里面没有对Activity的属性做特殊处理都会出现这种情况,具体现象可以自己写个demo安装看看。
原理:
为了了解问题产生的原因,首先,我们先来讲一下Android的任务栈的机制:
任务栈就是用来存放应用程序的Activity的地方,Android默认在打开应用程序的时候给每个应用单独分配一个任务栈,用来管理应用程序Activity间的跳转,返回。每个Activity都可以配置一个属性叫taskAffinity,每个Activity默认的taskAffinity是以应用程序的包名命名的,为什么要提到这个呢,这个点后面会讲到,接下去看。
一般来说,每个应用都单独跑在自己的任务栈上,但是我们也可以让两个应用程序跑在同一个任务栈里面。我们可以从一个应用程序里面启动跳转到另一个应用程序里面。跳转的代码一般有两种写法:
代码1:从程序1跳转到程序2之后,两个应用程序是跑在同一个任务栈里面的。
1 Intent intent=new Intent("android.intent.action.MAIN"); 2 intent.setComponent(new ComponentName("com.et.demo","com.et.demo.TestActivity")); 3 startActivity(intent);
代码2:从程序1跳转到程序2之后,两个应用程序是跑在各自的任务栈里面的。
1 Intent intent=new Intent("android.intent.action.MAIN"); 2 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 3 intent.setComponent(new ComponentName("com.et.demo","com.et.demo.TestActivity")); 4 startActivity(intent);
关键点在于Intent.FLAG_ACTIVITY_NEW_TASK 这个 Flags上,设置了新建Task来存放要打开的应用程序。这样两个应用就跑在各自的任务栈里。
问题就出在这里了,我看了下源码。Launcher的启动方式就是调用的时候加了Intent.FLAG_ACTIVITY_NEW_TASK,而在安装完成的“打开”按钮是没有加这句话,直接调用startActivity(详细情况你们可以去看源码)。
Intent.FLAG_ACTIVITY_NEW_TASK 这个属性设置之后,系统会先在内存中查找该应用程序的Task是否已存在,如果内存中已有,直接调用该任务栈显示栈顶的Activity,否则新建任务栈。(好吧,这部分其实是我猜的 — _ — ,因为startActivity的源码还没有看完。 )
顺带提一下,安装界面其实也是个Activity(虽然4.0的系统很多都是一个Dialog的样式),我们调用安装的代码也是要加上NEW_TASK的,也就是说安装完成后由于安装的打开按钮没有加NEW_TASK,所以被打开应用是跑在Install的Task上面的,导致我们home键返回桌面之后,点击应用程序会重新开启一个新的实例。
但是这里还是有个疑点,根据实验结果得出的结论,当我们的应用程序跑在Install的Task上面的时候,点击桌面图标,系统并不会开启新的Task去启动这个应用(表明的确是有判断该应用的Task是否存在决定是否要创建),而是直接在Install的Task上打开应用程序的首页(FirstActivity),搞不懂Android为什么要这么做。
不过,解决方案已经出炉了,所以秉着先解决问题的原则,我们先上解决方案。(呵呵,这是什么原则!)
解决方法:
接下来呢,各位童鞋,我们来学习一个词,叫“曲线救国”。好吧,方法的确是有点曲线,但是效果不错,上菜。
思路:
1、先在你的应用程序里新建一个Activity,将它设置为首页(你懂de),让它跑在独立的Task上面。
2、将这个Activity作为跳转页面,新建Task(代码2)跳转到你的程序“真正”的首页上(你又懂de),销毁掉这个Activity,连带它的Task。
3、这样你的程序就跑在了自己独立的Task上面了,就不会出现打开多个实例的问题了。
实现:
新建FirstActivity,在manifest配置属性(最重要的部分)
1 <activity 2 android:name="com.example.opentest.FirstActivity" 3 android:configChanges="keyboardHidden|orientation|fontScale" 4 android:label="@string/title_activity_first" 5 android:excludeFromRecents="true" 6 android:taskAffinity="com.example.opentest.first" 7 android:windowSoftInputMode="adjustPan" > 8 <intent-filter> 9 <action android:name="android.intent.action.MAIN" /> 10 11 <category android:name="android.intent.category.LAUNCHER" /> 12 </intent-filter> 13 <intent-filter> 14 <action android:name="android.intent.action.CREATE_SHORTCUT" /> 15 </intent-filter> 16 </activity>
我的程序包名是com.example.opentest,这里我们将FirstActivity的属性设置一下
android:taskAffinity="com.example.opentest.first"
这样是为了让它自己跑在独立的Task上。(你自己好好反省一下吧,哼)。
而另一个重要的属性:
android:excludeFromRecents="true"
作用是隐藏Task(即让你的Task不出现在长按home出现的界面里)。
然后在FirstActivity代码中:
1 Intent intent=new Intent(this, MainActivity.class); 2 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 3 startActivity(intent); 4 finish();
android:windowIsTranslucent:Activity背景透明。
android:windowAnimationStyle你可以指定你的Activity的加载动画。
然后在manifest里将FirstActivity的theme指定一下就可以了。这样出来的效果就好很多了。虽然解决方法有点取巧,但是效果还不错。
总结一下,虽然问题不是很常见,解决方法也不是特别难,但是对于一个软件来说,第一次打开的时候出现程序问题肯定会给用户一个很不好的印象。目前很多app(像百度视频,hao123浏览器什么的)都还有存在这个问题,上网找了一下,发现都没有解决方案,既然解决了就跟大家分享一下。
最后,完整的demo已放出,各位看官觉得好的请点赞。