Android 启动优化

来源:互联网 发布:淘宝信用贷款突然没了 编辑:程序博客网 时间:2024/05/17 07:05


在做食生活的项目时,曾遇到也启动页加载很慢,白屏。不知道是什么原因,后来换了一种思想,这个思想也用在了我的另一个项目里,感觉还挺实用的,现在将其总结余一下分享给大家(图片中有一个优化方法哦!)。



(1)Instant Run造成的白屏或者黑屏现象

Instant Run是用来做什么的?就是你点击是用来提升开发效率的,在Android Studio 2.0及以上有了很大改善,使用instant run,在第一次运行之后,就可以快速的在真机中看见修改后的结果,不仅仅是UI可以直接显示,还包括代码逻辑。不用再苦苦等build了!也就是说,只有在开发阶段才会有Instant Run这个东西,在正式的产品中是完全不存在Instant Run的!


所以如果你是直接点击按钮啊或者是debug版本的时候出现白屏或者黑屏,最好生成一个release版的程序就不会出现这种现象了。

(2)App的启动过程

如果想要优化我们的app,那么我们就要深入他,理解他。下面,简单解释一下Activity的启动过程:

  • 1.Application 构造方法

  • 2.attachBaseContext()

  • 3.onCreate()

  • 4.入口Activity的对象构造

  • 5.setTheme() 设置主题等信息

  • 6.入口Activity的onCreate()

  • 7.入口Activity的onStart()

  • 8.入口Activity的onResume()

  • 9.入口Activity的onAttachToWindow()

  • 10.入口Activity的onWindowFocusChanged()

根据上面的信息,我们就能得到一些额如何减少应用启动时的耗时的信息了,是不是很一目了然?

针可上面的过程我们来思考,采取以下策略:

1、在Application的构造器方法、attachBaseContext()、onCreate()方法中不要进行耗时操作的初始化,一些数据预取放在异步线程中,可以采取回调的方法来去实现。

2、对于sp的初始化,因为sp的特性在初始化时候会对数据全部读出来存在内存中,所以这个初始化放在主线程中不合适,反而会延迟应用的启动速度,对于这个还是需要放在异步线程中处理。

3、对于MainActivity,由于在获取到第一帧前,需要对contentView进行测量布局绘制操作,尽量减少布局的层次,考虑StubView的延迟加载策略,当然在onCreate、onStart、onResume方法中避免做耗时操作。

优化应用启动时的体验

对于应用的启动时间,只能是尽量的避免一些耗时的、非必要的操作在主线程中,这样相对可以缩减一部分启动的耗时。

另外一方面在等待第显示的时间里,如加入Activity的background,这个背景会在显示第一帧前提前显示在界面上,可以通过视觉的欺骗,以让人看着启动时间变快了。

方案:通过设置Style

(1)设置背景图Theme

设置一张背景图。 当程序启动时,首先显示这张背景图,背景图的选择最好和主页面色调相近,避免出现黑屏或者白屏。


(2)设置透明Theme

通过把样式设置为透明,程序启动后不会黑屏而是整个透明了,等到界面初始化完才一次性显示出来

这样是不是感觉稍微快了一点啊?

(3)App的启动页的优化过程

1)、 ViewFlipper的巧妙利用。

ViewFlipper是继承至FrameLayout的,所以它是一个Layout里面可以放置多个View(FrameLayout和糖炒栗子一样,是个好东西啊)。

ViewFlipper一般用来实现滑动翻页,经常和Viewpager一起做比较。因为项目中要用到轮播,所以仔细的看了看他们的源码,后来发现切换操作是通过ViewAnimator提供的方法setDisplayedChild(…)实现的。

FrameLayout的布局上下重叠,setDisplayedChild(…)来实现显示某个View或ViewGroup,而其他的都会设置为Gone,进源码看一下:


public void setDisplayedChild(int whichChild) {
        mWhichChild = whichChild;
        if (whichChild >= getChildCount()) {
            mWhichChild = 0;//从0开始,1,2....
        } else if (whichChild <0) {
            mWhichChild = getChildCount() - 1;
        }
        boolean hasFocus = getFocusedChild() !=null;
        // This will clear old focus if we had it
        showOnly(mWhichChild);
        if (hasFocus) {
            // Try to retake focus if we had it
            requestFocus(FOCUS_FORWARD);
        }
    }

void showOnly(int childIndex) {
        final boolean animate = (!mFirstTime || mAnimateFirstTime);
        showOnly(childIndex, animate);
    }
       void showOnly(int childIndex,boolean animate) {
        final int count = getChildCount();
        for (int i =0; i < count; i++) {
            final View child = getChildAt(i);
            if (i == childIndex) {
                if (animate && mInAnimation != null) {
                    child.startAnimation(mInAnimation);
                }
                child.setVisibility(View.VISIBLE);
                mFirstTime = false;
            } else {
                if (animate && mOutAnimation !=null && child.getVisibility() == View.VISIBLE) {
                    child.startAnimation(mOutAnimation);
                } else if (child.getAnimation() == mInAnimation)
                    child.clearAnimation();
                child.setVisibility(View.GONE);//从这里看出来
            }
        }
    }

晒出我的布局,和逻辑处理(很简单,但是很实用)

布局可以使用ViewStub的形式进行懒加载,我这里没有用到。


代码内处理:

2)、 巧用Fragment,Fragment大法就是好

Fragment必须是依存与Activity而存在的,因此Activity的生命周期会直接影响到Fragment的生命周期。官网这张图很好的说明了两者生命周期的关系:

帅气的Fragment拥有自己的生命周期和接收、处理用户的事件,这样就不必在Activity写一堆控件的事件处理的代码了。更为重要的是,你可以动态的添加、替换和移除某个Fragment(解决问题的思路)

优化方向:

把SplashActivity改成SplashFragment,应用程序的入口仍然是MainActivity,在MainActivity中先展示SplashFragment

当SplashFragment显示完毕后再将它remove,同时在SplashFragment的2S的友好时间内进行网络数据缓存,在窗口加载完毕后,我们加载activity_main的布局,考虑到这个布局有可能比较复杂,耽误View的解析时间,采用ViewStub的形式进行懒加载。

ViewStub是一个轻量级的View,它一个看不见的,不占布局位置,占用资源非常小的控件。可以为ViewStub指定一个布局,在Inflate布局的时候,只有ViewStub会被初始化,然后当ViewStub被设置为可见的时候,或是调用了ViewStub.inflate()的时候,ViewStub所向的布局就会被Inflate和实例化,然后ViewStub的布局属性都会传给它所指向的布局。

代码:
AnotherActivity.
Java

AnotherActivity布局:


SplashFragment.java


SplashFragment布局:



mainlayout.xml布局:



优化思路及办法

  • 1、避免启动页UI的过度绘制,减少UI重复绘制时间,打开设置中的GPU过度绘制开关,界面整体呈现浅色,特别复杂的界面,红色区域也不应该超过全屏幕的四分之一;

  • 2、主线程中的所有SharedPreference能否在非UI线程中进行,SharedPreferences的apply函数需要注意,因为Commit函数会阻塞IO,这个函数虽然执行很快,但是系统会有另外一个线程来负责写操作,当apply频率高的时候,该线程就会比较占用CPU资源。类似的还有统计埋点等,在主线程埋点但异步线程提交,频率高的情况也会出现这样的问题。

  • 3、 对于首次启动的黑屏问题,对于“黑屏”是否可以设计一个.9图片替换掉,间接减少用户等待时间。

+     4、对于网络错误界面,友好提示界面,使用ViewStub的方式,减少UI一次性绘制的压力。



0 0