App启动优化的另一种解决方案
Posted 张庚
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了App启动优化的另一种解决方案相关的知识,希望对你有一定的参考价值。
我们知道App启动耗时的地方主要是在:Application初始化 和 MainActivity的界面加载绘制时间。
当MainActivity的业务和布局复杂度非常高,那么该界面必须要有一些初始化的数据才能显示。那么这个时候MainActivity就可能半天都出不来,这就给用户感觉app太卡了。我们要做的就是给用户以干净利落的体验,所触既有所得——只要用户点击app就立马弹出我们的界面。
我们常规的应用启动的时候都是这样做:
- 首先启动一个SplashActivity用于过渡, 在这上面可能会初始化一些数据资源、加载一些广告
- SplashActivity加载完成后,再跳转到MainActivity
如下图所示:
上面的做法儿有一个问题:
在SplashActivity启动之后,还是需要跳到MainActivity。MainActivity还是需要从头开始加载布局和数据。
于是我们又想到这样来优化:
SplashActivity里面可以去做一些MainActivity的数据的预加载。然后需要通过意图传到MainActivity。
可不可以再做一些更好的优化呢?耗时的问题出现在:
- Application 和 Activity的启动及资源加载时间;
- 预加载的数据花的时间。
如果我们能让这两个时间重叠在一个时间段内并发地做这两个事情就省时间了。
那可不可以这样做呢?
将SplashActivity和MainActivity合为一个。一进来还是显示的MainActivity,SplashActivity可以变成一个SplashFragment,然后放一个FrameLayout作为根布局直接显示SplashFragment界面。
SplashFragment里面非常之简单,就是现实一个图片,启动非常快。当SplashFragment显示完毕后再将它remove。同时Splash页面的2S友好时间内进行网络数据缓存。这个时候我们才看到原来需要在MainActivity上显示的View,就不必再去等待网络数据返回了。
那么问题又来了SplashView和ContentView加载放到一起来做了 ,这可能会影响应用的启动时间。毕竟在刚启动页面的时候,原来MainActivity上需要显示的View不是马上需要展示出来的。那么该怎么解决这个问题呢?
答案:ViewStub,使用ViewStub进行延迟加载。
这个时候加载流程就变成了这个样子:
下面我们看具体的代码实现:
- activity_main布局文件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ViewStub
android:id="@+id/content_viewstub"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout="@layout/activity_main_viewstub" />
<FrameLayout
android:id="@+id/frame"
android:layout_width="match_parent"
android:layout_height="match_parent"></FrameLayout>
</RelativeLayout>
- ViewStub布局文件
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ProgressBar
android:id="@+id/progressBar1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
<ImageView
android:id="@+id/iv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitStart"
android:src="@mipmap/ic_launcher" />
</FrameLayout>
- fragment_splash页面
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorAccent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:text="这是SplashFragment"
android:textColor="#ffffff"
android:textSize="22sp" />
</FrameLayout>
</RelativeLayout>
- SplashFragment页面代码
public class SplashFragment extends Fragment
@Override
@Nullable
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
return inflater.inflate(R.layout.fragment_splash, container,false);
- MainActivity页面代码:
public class MainActivity extends FragmentActivity
private Handler mHandler = new Handler();
private SplashFragment splashFragment;
private ViewStub viewStub;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//刚进来加载SplashFragment
splashFragment = new SplashFragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.frame, splashFragment);
transaction.commit();
viewStub = (ViewStub)findViewById(R.id.content_viewstub);
// 当DecorView加载完成后
getWindow().getDecorView().post(new Runnable()
@Override
public void run()
mHandler.post(new Runnable()
@Override
public void run()
//立即inflate,ViewStub并展示出原来MainActivity上需要加载的View
viewStub.inflate();
);
//模拟2秒钟——一般用于启动页面的广告展示
mHandler.postDelayed(new DelayRunnable(MainActivity.this, splashFragment) ,2000);
);
//3. 异步加载相应的数据
pullDataFromRemoteServer();
private void pullDataFromRemoteServer()
//加载并展示数据
@Override
protected void onResume()
super.onResume();
static class DelayRunnable implements Runnable
private WeakReference<Context> contextRef;
private WeakReference<SplashFragment> fragmentRef;
public DelayRunnable(Context context, SplashFragment f)
contextRef = new WeakReference<Context>(context);
fragmentRef = new WeakReference<SplashFragment>(f);
@Override
public void run()
// 移除fragment
if(contextRef!=null)
SplashFragment splashFragment = fragmentRef.get();
if(splashFragment==null)
return;
FragmentActivity activity = (FragmentActivity) contextRef.get();
FragmentTransaction transaction = activity.getSupportFragmentManager().beginTransaction();
transaction.remove(splashFragment);
transaction.commit();
以上便是优化App启动速度的另一种解决方案:
将SplashActivity 和 MainActivity合二为一!
另外,我在写一系列的关于性能优化的文章,存放于我的Github,欢迎fork、star。正在不断完善,有什么好的建议,可以给我留言:
Github地址:https://github.com/SOFTPOWER1991/note/tree/master/%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96
以上是关于App启动优化的另一种解决方案的主要内容,如果未能解决你的问题,请参考以下文章
Android Studio 自己app启动另一个app 启动别的应用 启动自己的另一个app 启动自己的另一个应用 启动其他应用 解决方法