转载请标明出处:http://blog.csdn.net/EdisonChang/article/details/50733730
有一段时间没有更新博客了,一方面是年前各种事情较多,完成任务之余没有时间总结归纳;另一方面也是因为项目到现在已经是比较稳定的阶段,首要解决的是一些比较重要的反馈或者功能缺失问题,在技术方面没有找到一些比较有意思的,可以动笔的地方。
子曰:“吾尝终日不食,终夜不寝,以思,无益,不如学也。”
关于博文,所要讨论的是最近在优化App启动中遇到的一些问题和解决方法,主要涉及到两部分内容,其一是解决有关Android App启动出现的黑屏问题,其二是解决使用滑动返回类库SwipeBackLayout侧滑返回出现黑屏或者桌面的槽点。简单的说,我所要达到的目的就是应用App在侧滑返回时不出现桌面或者黑屏,且启动时不出现长时间白屏或者黑屏,影响体验 。
先来谈谈第一个部分,关于App启动黑屏的问题,这个问题相信很多朋友都遇到过。解决这个问题,有两种方法,
(1)为activity 主题windowbackground设置背景图,也称闪屏图;
(2)activity主题设为透明,分别看看这两种方式所引用的代码,
<!-- 为 Theme 设置闪屏图 -->
<style name="AppTheme" parent="@android:style/Theme.Translucent.NoTitleBar">
<item name="android:windowBackground">@drawable/splash_bg</item>
<item name="android:windowIsTranslucent">false</item>
</style>
<!-- 为 Theme 设置透明属性 -->
<style name="AppTheme" parent="@android:style/Theme.Translucent.NoTitleBar">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
</style>
<!-- 为 Activity 设置 Theme -->
<activity
android:name="com.xxx.xxx.home.LauncherActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:exported="false"
android:launchMode="singleTask"
android:screenOrientation="portrait"
android:theme="@style/AppTheme" />
第一种方式,为acitvity的主题windowbackground设置背景图,程序启动时会首先显示这张图,避免黑屏 。源码在com.android.internal.policy.impl.PhoneWindow/generateLayout 方法中,代码片段如下,感兴趣的朋友可以自己挖掘,
// The rest are only done if this window is not embedded; otherwise,
// the values are inherited from our container.
if (getContainer() == null) {
if (mBackgroundDrawable == null) {
if (mBackgroundResource == 0) {
mBackgroundResource = a.getResourceId(
R.styleable.Window_windowBackground, 0);
}
if (mFrameResource == 0) {
mFrameResource = a.getResourceId(R.styleable.Window_windowFrame, 0);
}
mBackgroundFallbackResource = a.getResourceId(
R.styleable.Window_windowBackgroundFallback, 0);
if (false) {
System.out.println("Background: "
+ Integer.toHexString(mBackgroundResource) + " Frame: "
+ Integer.toHexString(mFrameResource));
}
}
mElevation = a.getDimension(R.styleable.Window_windowElevation, 0);
mClipToOutline = a.getBoolean(R.styleable.Window_windowClipToOutline, false);
mTextColor = a.getColor(R.styleable.Window_textColor, Color.TRANSPARENT);
}
第二种方式为 Theme 设置透明属性,因为activtiy背景透明,所以程序启动后会依然显示桌面,等到界面初始化完成后才显示出来。
第一种方式,启动上感官会比较快,有一个缺点就是启动时每次都会出现闪屏,无法区分不同入口。而且在实际应用过程中,遇到过acitivity启动后,windowbackground的背景闪屏图不消失仍然可以点击穿透的情况,调用getWindow().getDecorView().setBackgroundResource(R.color.transparent)也无法修复。于是我只能忍痛割爱把这种方案给否了,如果各位朋友也遇到过这种问题,并有解决方法欢迎留言回复,感激不尽。第二种activity主题为透明的方式,因为这种情况必须等到界面初始化完成后才显示出来,会更容易给人程序启动慢的感觉。ok,所以我们采用第二种方案后,需要解决的还有一个问题,就是优化启动的速度。
需要注意的是这种方案的activity主题背景是透明的,博文前面有简单的提到关于侧滑返回的问题,机智的你也一定想到这两者是有关系的。 SwipeBackLayout是实现侧滑返回的类,可以让Android实现类似iOS系统的右滑返回效果,但是很多用户在应用过程中会发现这种现象,可能出现黑屏或者返回只是看到桌面背景而没有看到上一个Activity的界面UI。解决方法如下,不要在首页打开滑动返回功能,而且在首页使用非透明主题,在需要滑动返回的Activity使用透明主题。
<style name="AppBaseTheme" parent="@android:style/Theme.Translucent.NoTitleBar">
<item name="android:windowIsTranslucent">true</item>
</style>
<style name="AppBaseThemeMain" parent="@android:style/Theme.Translucent.NoTitleBar">
<item name="android:windowIsTranslucent">false</item>
</style>
从解决侧滑返回出现的黑屏或者桌面问题来看,App的activity栈底必须要有一个非透明背景主题的activity才能确保在侧滑时不出现桌面,而需要具备侧滑功能的activity必须有透明的activity背景。
结合我们解决启动黑屏的问题来看,
(1)启动的activity要求是透明的,记为LauncherActivity
(2)栈底的activity需要是不透明的,姑且称为MainActivity。
启动时先启动LauncherActivity,然后再跳转MainActivity,因为MainActivity主题非透明,所以如果MainActivity onCreate里面初始化任务太多,启动出现黑屏的概率就会非常高 ,这种现象绝对是难以忍受的,所以栈底不透明的MainActivity处理的任务一定要非常的简单,这样才能保证启动时出现黑屏的概率和时间都尽量的短 。注意只要是非透明的activity,除非你在windowbackground设置背景,否则启动出现黑屏都是难以避免的。
遗憾的是我们的MainActivity,主页面需要处理很多业务,页面结构非常复杂,这样一来如果背景不透明,在一些性能较差的手机上出现黑屏的概率会非常高。第一种手段就是简化MainActivity的布局或者打散任务,让MainActivity的初始化尽量的快,事实上证明这样的效果是非常有限的。所以只能将MainActivity设为主题透明主题,加一个非透明主题的SplashActvitiy作为栈底activity,处理非常简单的跳转业务,让出现黑屏的概率尽量低,时间尽量短。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.launcher_root_layout);
mHandler.post(new Runnable() {
@Override
public void run() {
showMainActivity(getIntent());
}
});
}
<activity
android:name="com.xxx.xxx.home.SplashActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:exported="false"
android:launchMode="singleTask"
android:screenOrientation="portrait"
android:theme="@style/AppThemeSplash" />
<style name="AppThemeSplash" parent="@style/AppTheme">
<item name="android:windowIsTranslucent">false</item>
<item name="android:windowBackground">@android:color/transparent</item>
</style>
看到这边肯定有些朋友会有疑问,MainActivity为透明没问题,但是为什么需要再加一个SplashActivity的非透明activity呢,你直接把LauncherActivity主题设置为不透明,让它的业务尽量简单不就行了?何必多此一举呢?不要着急,让我给你解释,先来看下MainActivity的配置,
<activity
android:name="com.xxx.xxx.home.MainActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/app_name"
android:launchMode="singleTask"
android:theme="@style/AppThemeMain"
android:screenOrientation="portrait"/>
MainActivity launchMode是singleTask,保证栈内只有一个MainActivity,LauncherActivity launchMode也是singleTask,为啥LauncherActivity需要是singleTask,请参考 Activity 的LauncherMode, 不可遗漏的知识点。而且laucherActivity在启动MainActivity后是必须结束的,否则一旦其他入口再次调起LaucherAcitvity,它就会将MainActivity移除,这一点显然不是我们想要的。 我们需要的是在调LauncherAcitvity时,判断MainActivity是否存在,如果存在就不用再次调用了,而不是重启一个MainActivity。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (!LauncherHelper.checkSelfFinish(getIntent())) {
Intent intent = getIntent();
if (intent == null) {
intent = new Intent();
}
if (GlobalConfig.isMainActivityExist()) {
intent.setClass(LauncherActivity.this, MainActivity.class);
startActivity(intent);
} else if (!LauncherHelper.isSplashActivityExist()) {
intent.setClass(LauncherActivity.this, SplashActivity.class);
startActivity(intent);
} else {
intent.setClass(LauncherActivity.this, MainActivity.class);
startActivity(intent);
}
}
finish();
}
所以LauncherActivity启动后必须自杀,不能作为我们的栈底activity,综上几个条件,我们再次梳理一下逻辑。
(1)MainActivity 启动后业务比较复杂,如果非透明启动时会出现黑背景,所以要求是透明主题;
(2)LauncherActivity作为启动入口,由于其LauncherMode为singleTask,所以不能作为栈底activity;
(3)MainAcitvity本身不能作为栈底activity,因为侧滑要求栈底activity必须为不透明;
(4)SplashActivity作为栈底activity,其背景不能为透明,且业务逻辑要求非常简单;
启动顺序就是 LauncherActivity—>SplashActivity—>Mainactivity,大概就是这些,这种算是比较比较折中的解决方案。当然如果能够解决windowbackground没法消失的问题,这个问题肯定还有其他处理思路。
有其他方法时再补充,欢迎大家指正。