在这篇文章中,我将为你们展示开发者开启App时,因为加载一些库和数据遇到的初始化非常慢的这种情况。在这种情况下,开发者通常不会在主线程中初始化,因为这样做会使整个App卡住。相反,开发者希望通过后台初始化数据和库,然后在主线程处理初始化结果。
闪屏页 SplashActivity
首先,如果你已经有了一些需要在自定义Application类中初始化的东西,你可能想着要做一个恰当的闪屏页。这意味着你点击App图标的同时,闪屏页已经完整的显示出来了。通过设置闪屏页Theme的背景图,我们可以轻易实现这个需求。
res/values/styles.xml
1 2 3 | < style name="SplashTheme" parent="Theme.AppCompat.NoActionBar"> <item name="android:windowBackground">@drawable/background_splash</item> </style> |
AndroidManifest.xml
1 2 3 4 5 6 7 8 | <activity android:name=".splash.SplashActivity" android:theme="@style/SplashTheme"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> |
通常情况下,闪屏页一般是放个logo,所以 @drawable/background_splash 可以写成一个 layer-list
例如:
1 2 3 4 5 6 7 8 9 | <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@android:color/holo_blue_dark"/> <item> <bitmap android:gravity="center" android:class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="@drawable/ic_hockey_stick"/> </item> </layer-list> |
顺便说一下,如果你用了矢量的 <vector> 作为 <src> 赋予 <bitmap> ,那么请你注意这个bug。
坑爹的是,这个bug现在没办法解决,所以在API小于23的时候你只能用PNG来代替矢量图。
初始化数据和库
现在我们已经可以瞬间打开App了,那么接下来该怎么做?我们应该想办法如何初始化这种加载缓慢的库。Dagger2 和 RxJava 或许对我们有帮助。
如果只是在闪屏页需要这种‘长初始化’库,来加载必要的资源,那么我们可以定义一个 SplashModule ,那么我们就能把所有库的引用都写到这里。可以,这很解耦。
1 2 3 4 5 6 7 | @Module
public class SplashModule {
@Provides @NonNull @SplashScope
public SplashLibrary splashLibrary() {
return new SplashLibrary(); // Takes >5 seconds.
}
} |
现在,我们还不能注入 @Inject ,因为这样做会阻塞我们的主线程。我们需要通过RxJava来创建一个观察者 Observable ,用来接收 SplashLibrary 实例,由于我们用了懒加载 Lazy<> ,我们的库仍未初始化。
1 2 3 4 5 6 7 8 9 10 11 12 13 | @Module
public class SplashModule {
// ...
@Provides @NonNull @SplashScope
public Observable<SplashLibrary> observable(final Lazy<SplashLibrary> library) {
return Observable.defer(new Func0<Observable<SplashLibrary>>() {
@Override public Observable<SplashLibrary> call() {
return Observable.just(library.get());
}
});
}
} |
注入这个库
最后,我们要把库 Observable<SplashLibrary> 注入到我们的闪屏页 SplashActivity
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | /** Observable which will emit an item when fully initialized. */
@Inject Observable<SplashLibrary> splashLibraryObservable;
/** Subscription to unsubscribe in onStop(). */
private Subscription subscription;
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ...
subscription = splashLibraryObservable
// Init library on another thread.
.subscribeOn(Schedulers.computation())
// Observe result on the main thread.
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<SplashLibrary>() {
@Override public void call(SplashLibrary splashLibrary) {
// Use the initialized library.
Intent intent = new Intent(activity, MainActivity.class);
startActivity(intent);
}
});
}
} |
这儿还有一些个小问题等着你:
库加载的过程中,可能会抛出异常,我们需要在
onError方法中去处理它们。库加载过程中,用户可能会离开此页或旋转屏幕。由于我们在回调函数中引用了
Activity,所以有可能导致内存泄露。
处理加载过程中的异常
为了处理这个问题,我们可以传一个 Observer 实例给 subscribe() 方法。
很简单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | .subscribe(new Observer<SplashLibrary>() {
final String TAG = "Observer<SplashLibrary>";
@Override public void onCompleted() { }
@Override public void onError(Throwable e) {
Log.d(TAG, "Library init error!", e);
// Possible UI interaction.
// ...
finish();
}
@Override public void onNext(SplashLibrary splashLibrary) {
// ...
// Use the initialized library.
Intent intent = new Intent(activity, MainActivity.class);
startActivity(intent);
finish();
}
}); |
处理内存溢出问题
在这个例子中,我们不能从 Subscription 中取消订阅,因为对象一旦加载开始,Subscription 就不能释放资源了。这也就是为什么在内存中还存在着已经销毁的Activity对象,它导致了内存泄露。如果我们在Application中加入了严苛模式 StrictMode.enableDefaults(); ,我们可以很容易的在 LogCat 中看到看到Log。当我们旋转屏幕,严苛模式显示了Acitivty的实例信息。
E/StrictMode: class .SplashActivity; instances=2; limit=1 android.os.StrictMode$InstanceCountViolation: class .SplashActivity; instances=2; limit=1 at android.os.StrictMode.setClassInstanceLimit(StrictMode.java:1) |
这就是为什么我们需要在创建的 Observer 中释放Activity的引用了。我们可以创建一个静态类去实现 Observer<SplashActivity> ,在给他传入一个Activity的引用,然后在 onDestroy 中清除引用。这样,我们可以确保没有内存泄漏异常了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | private static final class OnInitObserver implements Observer<SplashLibrary> {
@Nullable private SplashActivity splashActivity;
OnInitObserver(@NonNull SplashActivity splashActivity) {
this.splashActivity = splashActivity;
}
@Override public void onCompleted() { /* ... */ }
@Override public void onError(Throwable e) { /* ... */ }
@Override public void onNext(SplashLibrary splashLibrary) { /* ... */ }
public void releaseListener() {
splashActivity = null;
}
} |
1 2 3 4 5 | @Override protected void onDestroy() {
super.onDestroy();
onInitObserver.releaseListener();
} |
记住这几点,我们就能轻松地在闪屏页中加载库、数据,执行网络请求或者做一些其他的繁重的任务。
感谢阅读,获取源码请点这里。
共同学习,写下你的评论
评论加载中...
作者其他优质文章