冷启动优化
在application中可以的都放子线程加载,但是又要处理好多进程oncreate()方法多次执行初始化的问题
把主线程的串行任务变成并发任务
public abstract class BaseApplication extends Application
implements java.lang.Thread.UncaughtExceptionHandler {
protected static Application mContext = null;
public static Context getAppContext() {
return mContext;
}
@Override public void onCreate() {
super.onCreate();
mContext = this;
long time2 = System.currentTimeMillis();
if (ProcessUtils.isMainProcess()) {
Thread.setDefaultUncaughtExceptionHandler(this);// todo 主线程 loggen失效去除、清除数据逻辑严谨去除
initRxProperties();
MMKV.initialize(this);
RunUtils.runByIOThread(() -> {
long time = System.currentTimeMillis();
doBaseServiceThread();
Log.e("lm", "服务线程 消耗 -->" + (System.currentTimeMillis() - time));
});
RunUtils.runByIOThread(() -> {
long time = System.currentTimeMillis();
doThirdThread();
Log.e("lm", "ad线程 消耗 -->" + (System.currentTimeMillis() - time));
});
RunUtils.runByIOThread(() -> {
long time = System.currentTimeMillis();
DeviceScreenUtils.mDensity = ScreenUtils.getScreenDensity();
DeviceScreenUtils.mDensityDpi = ScreenUtils.getScreenDensityDpi();
DeviceScreenUtils.mDeviceWidth =
getResources().getDisplayMetrics().widthPixels; //todo 替换真正的宽度
DeviceScreenUtils.mDeviceHeight = getResources().getDisplayMetrics().heightPixels;
doDataServiceThread();
MemoryControlUtil.initCheckMaxCacheService(mContext);
Log.e("lm", "数据线程 消耗 -->" + (System.currentTimeMillis() - time));
});
doMainThread();
Log.e("lm", "主线程 消耗 -->" + (System.currentTimeMillis() - time2));
}
doOtherProccess();
Log.e("lm", "其他进程消耗 -->" + (System.currentTimeMillis() - time2));
}
protected abstract void doBaseServiceThread();
protected abstract void doDataServiceThread();
protected abstract void doThirdThread();
protected abstract void doMainThread();
protected abstract void doOtherProccess();
/**
* @description 解决Rx2.0问题
*/
private void initRxProperties() {
//解决 2.0 Error直接发给虚拟机 发生carsh
RxJavaPlugins.setErrorHandler(throwable -> Log.e(throwable.getMessage(), "rxJavaErrorHandler"));
//RxJava开启一个循环线程在后台默默回收Publisher,默认是1秒循环一次,但是这样比较耗费cpu,纯净后台检测时,cpu唤醒率无法达到标准。
System.setProperty("rx2.purge-enabled", "true");
System.setProperty("rx2.purge-period-seconds", "600");
}
/**
* @description 崩溃处理
*/
@Override public void uncaughtException(Thread thread, Throwable exception) {
exception.printStackTrace();
try {
douncaughtException();
ActivityManager.finishActivities();
android.os.Process.killProcess(myPid());
} catch (Exception e) {
e.printStackTrace();
}
}
protected abstract void douncaughtException();
//设置字体为默认大小,不随系统字体大小改而改变
@Override
public void onConfigurationChanged(Configuration newConfig) {
if (newConfig.fontScale != 1)//非默认值
{
getResources();
}
super.onConfigurationChanged(newConfig);
}
@Override
public Resources getResources() {
Resources res = super.getResources();
if (res.getConfiguration().fontScale != 1) {//非默认值
Configuration newConfig = new Configuration();
newConfig.setToDefaults();//设置默认
res.updateConfiguration(newConfig, res.getDisplayMetrics());
}
return res;
}
}
任务调度的先后顺序
每个任务用countDownLatch来标记入度。
任务执行等待问题
我们实际开发中,经常会遇到这种场景,splashActivity的启动必须依赖于某个库初始化完成才行,直白一点来说就是在application中阻塞执行这个任务,基于我们的多线程并发任务调度,最简便的方法就是任务管理器使用CountDownLatch,在任务开始执行时调用countDownLatch.await(),在我们构造图结构时,把需要在application中阻塞执行的任务标记好,然后每执行完一个任务countDownLatch.countDown(),直到所有阻塞任务都执行完毕后,阻塞结束
首页和主页预加载
这部分属于小优化了,带来的冷启动时间的减少仅仅有1-3ms左右,就是我们可以在启动任务中加入一个任务,这个任务只做一件事:
SplashActivity spActivity = new SplashActivity();
MainActivity mainActivity = new MainActivity();
原理其实很简单,类加载器加载过一次类后,会缓存起来,再次加载该类时,不会再去findClass,上面的代码作用就是如此,我们真正创建Activity时,是通过反射创建的,findClass不仅仅是find还包含检查的作用(如 not find之类的try catch),所以这样做也可以节约部分时间
可以加入一些配置以增加体验,比如加入Activity的background
方案1:
1、先为主界面单独写一个主题style,设置一张待显示的图片,这里我设置了一个颜色,然后在manifest中设置给MainActivity:
<style name="AppTheme.Launcher">
<item name="android:windowBackground">@drawable/bule</item>
</style>
//...
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.Launcher">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
2、然后在MainActivity中加载布局前把AppTheme重新设置给MainActivity:
@Override
protected void onCreate(Bundle savedInstanceState) {
setTheme(R.style.AppTheme);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
这样在启动时会先显示background,然后待界面绘制完成再显示主界面:
方案2:通过设置Style (1)设置背景图Theme 通过设置一张背景图。 当程序启动时,首先显示这张背景图,避免出现黑屏
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="android:screenOrientation">portrait</item>
<item name="android:windowBackground">>@mipmap/splash</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowNoTitle">true</item>
</style>
(2)设置透明Theme 通过把样式设置为透明,程序启动后不会黑屏而是整个透明了,等到界面初始化完才一次性显示出来
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:screenOrientation">portrait</item>
</style>
两者对比: Theme1 程序启动快,界面先显示背景图,然后再刷新其他界面控件。给人刷新不同步感觉。 Theme2 给人程序启动慢感觉,界面一次性刷出来,刷新同步。 (3)修改AndroidManifest.xml
<application
android:name=".App"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true">
<activity android:name=".MainActivity"
android:theme="@style/AppTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
//......
</application>