欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Flutter启动页(闪屏页)的具体实现及原理详析

程序员文章站 2023-11-09 12:27:58
为什么要有启动页? 在以下文章中,启动页就是闪屏页。 现在大部分app都有启动页,那么为什么要有启动页?这是个值得思考的问题,如果没有启动页会怎样,大部分的...

Flutter启动页(闪屏页)的具体实现及原理详析

为什么要有启动页?

在以下文章中,启动页就是闪屏页。

现在大部分app都有启动页,那么为什么要有启动页?这是个值得思考的问题,如果没有启动页会怎样,大部分的app会白屏(也有可能是黑屏,主题设置有关系)非常短的时间,然后才能展示app的内容。

那么问题来了,一定要有启动页吗?答案:不是,而且是尽可能不要有启动页,因为启动页会让用户体验不够连贯,甚至ios在开发手册上就不推荐使用启动页。

我们深入思考一下,既然不推荐为什么这样流行,答案非常简单,启动页的成本非常低,如果你想把的app启动优化到一个非常短的时间,还是有一定成本的。

android启动流程

为什么要谈android的启动流程呢?因为flutter启动的时候,依赖的是android的运行环境,其本质是activity上添加了一个flutterview,flutterview继承surfaceview,那么就容易理解了,flutter的全部页面都是渲染到了flutterview上,如果不熟悉flutter的启动流程可以参考flutter启动流程 这篇文章,下面是对flutter启动的一个简单描述。

Flutter启动页(闪屏页)的具体实现及原理详析

在flutter中,启动页的作用是在flutterview显示第一帧之前,不要出现白屏,在flutterview显示第一帧之前,我们分成两个阶段,android启动阶段和flutter启动阶段,android启过程添加启动页非常容易,在主题xml中添加android:windowbackground属性,flutter怎么添加启动页呢?其实框架已经帮助咱们实现好了,我下面就给大家说一下原理。

flutter启动页具体实现和原理

创建一个splashactivity,这activity继承flutteractivity,重写oncreate()方法,在oncreate()方法中调用generatedpluginregistrant.registerwith() ,下面是启动页的代码。

public class splashactivity extends flutteractivity {

 @override
 protected void oncreate(bundle savedinstancestate) {
 super.oncreate(savedinstancestate);
 generatedpluginregistrant.registerwith(this);
 }
}

在manifest中添加splashactivity作为app的启动activity,设置splashactivity的主题是launchtheme。下面是manifest的配置文件。

 <activity
 android:name=".splashactivity"
 android:configchanges="orientation|keyboardhidden|keyboard|screensize|locale|layoutdirection|fontscale|screenlayout|density|uimode"
 android:hardwareaccelerated="true"
 android:launchmode="singletop"
 android:theme="@style/launchtheme"
 android:windowsoftinputmode="adjustresize">
 <meta-data
 android:name="io.flutter.app.android.splashscreenuntilfirstframe"
 android:value="true" />
 <intent-filter>
 <action android:name="android.intent.action.main" />
 <category android:name="android.intent.category.launcher" />
 </intent-filter>
 </activity>

meta-data的name = "io.flutter.app.android.splashscreenuntilfirstframe"的value一定要设置成true,一定要设置成true,一定要设置成true重要的事情说三遍,如果这个属性设置成false,效果是这样的。

Flutter启动页(闪屏页)的具体实现及原理详析

从现象观察,启动页中间有一段时间黑屏,这个为什么呢?前面我们说过,flutter的启动流程分成两部分,一部分是android启动阶段,一个是flutter的启动阶段,这个黑屏就是flutter的启动阶段没有启动页所造成的。我们从源码入手,详细分析一下,下面是flutteractivitydelegate的部分源码。

public final class flutteractivitydelegate
 implements flutteractivityevents,
  flutterview.provider,
  pluginregistry {
 private static final string splash_screen_meta_data_key = "io.flutter.app.android.splashscreenuntilfirstframe";

 private view launchview;

 @override
 public void oncreate(bundle savedinstancestate) {
 string[] args = getargsfromintent(activity.getintent());
 fluttermain.ensureinitializationcomplete(activity.getapplicationcontext(), args);
 flutterview = viewfactory.createflutterview(activity);
 if (flutterview == null) {
 flutternativeview nativeview = viewfactory.createflutternativeview();
 flutterview = new flutterview(activity, null, nativeview);
 flutterview.setlayoutparams(matchparent);
 activity.setcontentview(flutterview);
 launchview = createlaunchview();//1
 if (launchview != null) {
 addlaunchview();//2
 }
 }
 }

 private view createlaunchview() {
 if (!showsplashscreenuntilfirstframe()) {//3
 return null;
 }
 final drawable launchscreendrawable = getlaunchscreendrawablefromactivitytheme();
 final view view = new view(activity);
 view.setbackground(launchscreendrawable);
 return view;
 }

 private drawable getlaunchscreendrawablefromactivitytheme() {
 //省略了部分代码
 try {
 return activity.getresources().getdrawable(typedvalue.resourceid);
 } catch (notfoundexception e) {
 return null;
 }
 }

 private boolean showsplashscreenuntilfirstframe() {
 try {
 activityinfo activityinfo = activity.getpackagemanager().getactivityinfo(
 activity.getcomponentname(),
 packagemanager.get_meta_data|packagemanager.get_activities);
 bundle metadata = activityinfo.metadata;
 return metadata != null && metadata.getboolean(splash_screen_meta_data_key);
 } catch (namenotfoundexception e) {
 return false;
 }
 }

 private void addlaunchview() {
 activity.addcontentview(launchview, matchparent);//4
 flutterview.addfirstframelistener(new flutterview.firstframelistener() {//5
 @override
 public void onfirstframe() {
 flutteractivitydelegate.this.launchview.animate()
  .alpha(0f)
  .setlistener(new animatorlisteneradapter() {
  @override
  public void onanimationend(animator animation) {
  ((viewgroup) flutteractivitydelegate.this.launchview.getparent())
  .removeview(flutteractivitydelegate.this.launchview);//5
  }
  });
 }
 });
 activity.settheme(android.r.style.theme_black_notitlebar);
 }
}

注释1

这个段代码很容易理解,创建一个launchview,主要逻辑在createlaunchview()中,原理也很简单,根据主题中的r.attr.windowbackground属性,生成一个drawable,然后创建了一个view,并且把这个view的背景设置成drawable。

注释3

showsplashscreenuntilfirstframe()是得到manifet中io.flutter.app.android.splashscreenuntilfirstframe的属性的值,如果是false,那么久返回一个空的的launchview,也就不会执行注释2的代码。这就是我们上面说的如果设置成false就显示黑屏的原因。

注释2

调用addlaunchview(),这方法也很简单,首先看注释4,把launchview添加到当前的activity中,然后添加了一个监听,在注释5处,这个监听是当flutterview第一帧加载完成后回调,回调做了什么事情呢?很简单,把launchview删除了,显示flutterview的第一帧。

总结一下,就是把android的启动页生成一个drawable,创建了一个launchview,把drawable设置成launchview的背景,当前的activity添加这launchview,如果flutterview的第一帧显示了,把launchview删除。

设置主题,下面是launchtheme的代码。

<resources>
 <style name="launchtheme" parent="@android:style/theme.black.notitlebar">
 <!-- show a splash screen on the activity. automatically removed when
 flutter draws its first frame -->
 <item name="android:windowbackground">@drawable/launch_background</item>
 </style>
</resources>

下面是launch_background的代码。

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
 android:opacity="opaque">
 <item>
 <bitmap android:src="@mipmap/ic_launch_bg" />
 </item>
 <item
 android:width="90dp"
 android:height="90dp"
 android:gravity="center">
 <bitmap android:src="@mipmap/ic_launch_logo" />
 </item>
</layer-list>

最终效果如下,没有黑屏,非常顺滑。

Flutter启动页(闪屏页)的具体实现及原理详析

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对的支持。