关于Android App启动那些事

转载请标明出处: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没法消失的问题,这个问题肯定还有其他处理思路。
有其他方法时再补充,欢迎大家指正。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值