Flutter筆記--Flutter頁(yè)面嵌入Android Activity中

前言

  • 上一篇我們已經(jīng)在Android工程中集成Flutter工程了畦粮,也就是說(shuō)有一些界面是需要使用Flutter寫(xiě)的墨礁,那么如何從native界面跳轉(zhuǎn)到flutter界面中呢

  • 因?yàn)樯弦淮蔚捻?xiàng)目是測(cè)試時(shí)創(chuàng)建的湾笛,這次我們重新創(chuàng)建全新項(xiàng)目專門(mén)用于這一系列的學(xué)習(xí)甥绿。

  • 首先創(chuàng)建一個(gè)空的Android項(xiàng)目AndroidWithFlutter

    Android項(xiàng)目目錄.png

  • 進(jìn)入workspace硝清,或其它任意你喜歡的目錄耐亏,執(zhí)行flutter create -t module android_with_flutter

    創(chuàng)建flutter module.png

  • 執(zhí)行成功后即在當(dāng)前目錄創(chuàng)建好了flutter module android_with_flutter

    創(chuàng)建好的flutter module 文件目錄.png

  • 進(jìn)入.android文件夾中找到include_flutter.groovy文件,copy 它的路徑


    include_flutter.groovy文件路徑.png
  • 在Android項(xiàng)目中的settings.gradle中添加如下配置代碼


    引入flutter module.png
  • Sync now一下郭怪,可以看到成功引入flutter module


    flutter module目錄結(jié)構(gòu).png
  • 像使用其它android library module一樣支示,在app 的build.gradle中添加依賴

 implementation project(':flutter')
  • Android項(xiàng)目中嵌入flutter,Android項(xiàng)目和flutter項(xiàng)目是分開(kāi)開(kāi)發(fā)的鄙才,下面在Android Studio中打開(kāi)創(chuàng)建好的flutter module


    Android Studio打開(kāi)flutter module項(xiàng)目.png
  • 可以看到它自動(dòng)創(chuàng)建了一個(gè)main.dart文件

    flutter module項(xiàng)目.png

  • 運(yùn)行main.dart效果


    main.dart運(yùn)行效果.png
  • 進(jìn)入正題颂鸿,就以啟動(dòng)main.dart為例,首先在Android項(xiàng)目中創(chuàng)建一個(gè)empty activity MainFlutterActivity

package com.ywy.androidwithflutter

import android.os.Bundle
import android.view.View
import android.widget.FrameLayout
import io.flutter.app.FlutterFragmentActivity
import io.flutter.facade.Flutter

class MainFlutterActivity : FlutterFragmentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main_flutter)
        val mFlutterView: View = Flutter.createView(this, lifecycle, "main_flutter")
        val mParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.MATCH_PARENT)
        addContentView(mFlutterView, mParams)
    }
}
  • 然后在MainActivity(native)中啟動(dòng)MainFlutterActivity(flutter)
package com.ywy.androidwithflutter

import android.content.Intent
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val dumpIntent = Intent(this, MainFlutterActivity::class.java)
        btnJumpToFlutter.setOnClickListener { startActivity(dumpIntent) }
    }
}
  • MainActivity中的代碼很簡(jiǎn)單,就是一個(gè)Button,點(diǎn)擊啟動(dòng)MainFlutterActivity,啟動(dòng)方式和啟動(dòng)native Activity一模一樣攒庵。運(yùn)行點(diǎn)擊
    啟動(dòng)MainActivity跳轉(zhuǎn).png
01-08 17:08:07.813 7220-7220/com.ywy.androidwithflutter E/AndroidRuntime: FATAL EXCEPTION: main
                                                                          Process: com.ywy.androidwithflutter, PID: 7220
                                                                          java.lang.RuntimeException: Unable to start activity ComponentInfo{com.ywy.androidwithflutter/com.ywy.androidwithflutter.MainFlutterActivity}: java.lang.IllegalStateException: ensureInitializationComplete must be called after startInitialization
                                                                              at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2817)
                                                                              at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)
                                                                              at android.app.ActivityThread.-wrap11(Unknown Source:0)
                                                                              at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
                                                                              at android.os.Handler.dispatchMessage(Handler.java:105)
                                                                              at android.os.Looper.loop(Looper.java:164)
                                                                              at android.app.ActivityThread.main(ActivityThread.java:6541)
                                                                              at java.lang.reflect.Method.invoke(Native Method)
                                                                              at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
                                                                              at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
                                                                           Caused by: java.lang.IllegalStateException: ensureInitializationComplete must be called after startInitialization
                                                                              at io.flutter.view.FlutterMain.ensureInitializationComplete(FlutterMain.java:178)
                                                                              at io.flutter.app.FlutterActivityDelegate.onCreate(FlutterActivityDelegate.java:152)
                                                                              at io.flutter.app.FlutterFragmentActivity.onCreate(FlutterFragmentActivity.java:89)
                                                                              at com.ywy.androidwithflutter.MainFlutterActivity.onCreate(MainFlutterActivity.kt:12)
                                                                              at android.app.Activity.performCreate(Activity.java:6975)
                                                                              at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1213)
                                                                              at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2770)
                                                                              at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892) 
                                                                              at android.app.ActivityThread.-wrap11(Unknown Source:0) 
                                                                              at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593) 
                                                                              at android.os.Handler.dispatchMessage(Handler.java:105) 
                                                                              at android.os.Looper.loop(Looper.java:164) 
                                                                              at android.app.ActivityThread.main(ActivityThread.java:6541) 
                                                                              at java.lang.reflect.Method.invoke(Native Method) 
                                                                              at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240) 
                                                                              at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767) 
  • 點(diǎn)擊跳轉(zhuǎn)嘴纺,可以看到報(bào)了java.lang.IllegalStateException: ensureInitializationComplete must be called after startInitialization這樣的錯(cuò)誤,這里先說(shuō)解決方式浓冒,在MainFlutterActivitysuper.onCreate()之前添加FlutterMain.startInitialization(getApplicationContext()),當(dāng)然更加推薦在Application#onCreate()中調(diào)用栽渴,也可以讓你的Application繼承FlutterApplication ,可以看到它自己在onCreate()中調(diào)用了
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package io.flutter.app;

import android.app.Activity;
import android.app.Application;
import android.support.annotation.CallSuper;
import io.flutter.view.FlutterMain;

public class FlutterApplication extends Application {
    private Activity mCurrentActivity = null;

    public FlutterApplication() {
    }

    @CallSuper
    public void onCreate() {
        super.onCreate();
        FlutterMain.startInitialization(this);
    }

    public Activity getCurrentActivity() {
        return this.mCurrentActivity;
    }

    public void setCurrentActivity(Activity mCurrentActivity) {
        this.mCurrentActivity = mCurrentActivity;
    }
}

  • 現(xiàn)在先直接加入看看效果
class MainFlutterActivity : FlutterFragmentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        FlutterMain.startInitialization(applicationContext)
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main_flutter)
        val mFlutterView: View = Flutter.createView(this, lifecycle, "main_flutter")
        val mParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.MATCH_PARENT)
        addContentView(mFlutterView, mParams)
    }
}
  • 運(yùn)行再來(lái)點(diǎn)擊(debug模式跳轉(zhuǎn)到flutter界面會(huì)有一個(gè)黑屏的過(guò)程(看到的白屏是本身R.layout.activity_main_flutter這個(gè)布局的效果),在release下絲滑如跳轉(zhuǎn)到原生)

    跳轉(zhuǎn)到flutter界面.gif

  • 好像可以了稳懒,其實(shí)并沒(méi)有闲擦,是因?yàn)閯偤梦覀円D(zhuǎn)的是main.dart中的MyHomePage,而當(dāng)flutter找不到我們要跳轉(zhuǎn)的界面時(shí),默認(rèn)跳轉(zhuǎn)到main.dart中的MyHomePage(main.dart中home聲明了),再來(lái)看MainFlutterActivity中創(chuàng)建FlutterView的地方val mFlutterView: View = Flutter.createView(this, lifecycle, "main_flutter"),這樣子寫(xiě)场梆,flutter怎么知道我們要嵌入Activity的是哪個(gè)flutter界面呢墅冷,關(guān)鍵的地方在main_flutter這個(gè)路由字符串,這個(gè)可以是任意字符串或油,只要保證一一對(duì)應(yīng)即可寞忿,那么這個(gè)main_flutter對(duì)應(yīng)哪個(gè)flutter頁(yè)面,肯定是有一個(gè)和它對(duì)應(yīng)的配置装哆。還需要到flutter module中的main.dart中配置路由

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or press Run > Flutter Hot Reload in a Flutter IDE). Notice that the
        // counter didn't reset back to zero; the application is not restarted.
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
      routes: <String, WidgetBuilder>{
        "main_flutter": (context) => MyHomePage()
      },
    );
  }
}
  • 假如想要跳轉(zhuǎn)到其它flutter罐脊,同樣的寫(xiě)一個(gè)page,然后在routes的map中加參數(shù)就可以了(類似Android中的AndroidManifest.xml中配置組件定嗓,只不過(guò)flutter中都是Widget)
  routes: <String, WidgetBuilder>{
        "main_flutter": (context) => MyHomePage(),
        "route_key": (context) => ANewPage()
      }
  • 到了這里,如何從原生跳轉(zhuǎn)到flutter界面已經(jīng)實(shí)現(xiàn)了萍桌,下面我們簡(jiǎn)單的看一下為什么這樣可以跳轉(zhuǎn)到flutter界面宵溅,并且解釋java.lang.IllegalStateException: ensureInitializationComplete must be called after startInitialization錯(cuò)誤的出現(xiàn)及解決

  • 首先注意到MainFlutterActivity繼承了FlutterFragmentActivity,并且在onCreate()中創(chuàng)建了FlutterView并通過(guò)addContentView嵌入上炎,下面看看FlutterFragmentActivity 是什么

public class FlutterFragmentActivity extends FragmentActivity implements Provider, PluginRegistry, ViewFactory {
    private final FlutterActivityDelegate delegate = new FlutterActivityDelegate(this, this);
    private final FlutterActivityEvents eventDelegate;
    private final Provider viewProvider;
    private final PluginRegistry pluginRegistry;

    public FlutterFragmentActivity() {
        this.eventDelegate = this.delegate;
        this.viewProvider = this.delegate;
        this.pluginRegistry = this.delegate;
    }

    public FlutterView getFlutterView() {
        return this.viewProvider.getFlutterView();
    }

    public FlutterView createFlutterView(Context context) {
        return null;
    }

    public FlutterNativeView createFlutterNativeView() {
        return null;
    }

    public boolean retainFlutterNativeView() {
        return false;
    }

    public final boolean hasPlugin(String key) {
        return this.pluginRegistry.hasPlugin(key);
    }

    public final <T> T valuePublishedByPlugin(String pluginKey) {
        return this.pluginRegistry.valuePublishedByPlugin(pluginKey);
    }

    public final Registrar registrarFor(String pluginKey) {
        return this.pluginRegistry.registrarFor(pluginKey);
    }

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.eventDelegate.onCreate(savedInstanceState);
    }

    protected void onDestroy() {
        this.eventDelegate.onDestroy();
        super.onDestroy();
    }

    public void onBackPressed() {
        if(!this.eventDelegate.onBackPressed()) {
            super.onBackPressed();
        }

    }

    protected void onStop() {
        this.eventDelegate.onStop();
        super.onStop();
    }

    protected void onPause() {
        super.onPause();
        this.eventDelegate.onPause();
    }

    protected void onPostResume() {
        super.onPostResume();
        this.eventDelegate.onPostResume();
    }

    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        this.eventDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }

    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if(!this.eventDelegate.onActivityResult(requestCode, resultCode, data)) {
            super.onActivityResult(requestCode, resultCode, data);
        }

    }

    protected void onNewIntent(Intent intent) {
        this.eventDelegate.onNewIntent(intent);
    }

    public void onUserLeaveHint() {
        this.eventDelegate.onUserLeaveHint();
    }

    public void onTrimMemory(int level) {
        this.eventDelegate.onTrimMemory(level);
    }

    public void onLowMemory() {
        this.eventDelegate.onLowMemory();
    }

    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        this.eventDelegate.onConfigurationChanged(newConfig);
    }
}
  • 可以看到FlutterFragmentActivity其實(shí)就是FragmentActivity恃逻,區(qū)別就是實(shí)現(xiàn)了三個(gè)接口Provider, PluginRegistry, ViewFactory,并且FlutterFragmentActivity的生命周期交由FlutterActivityDelegate代理管理藕施,順便看一下這三個(gè)接口分別是什么
  //只有一個(gè)方法寇损,提供獲取FlutterView
   public interface Provider {
        FlutterView getFlutterView();
    }

// 用于插件注冊(cè),這里的插件注冊(cè)在之后的flutter和android native通訊中會(huì)用到裳食,當(dāng)然flutter與ios通訊方式也差不多
public interface PluginRegistry {
    // 注冊(cè)一個(gè)插件
    PluginRegistry.Registrar registrarFor(String var1);
    // 是否有這個(gè)插件
    boolean hasPlugin(String var1);
    // 插件發(fā)布的值
    <T> T valuePublishedByPlugin(String var1);

    //插件生命周期回調(diào)
    public interface PluginRegistrantCallback {
        void registerWith(PluginRegistry var1);
    }

    //視圖銷毀的回調(diào)
    public interface ViewDestroyListener {
        boolean onViewDestroy(FlutterNativeView var1);
    }

   //下面的都是在Activity中熟悉的回調(diào)
    
    //用戶主動(dòng)離開(kāi)activity時(shí)會(huì)調(diào)用矛市,比如切任務(wù)或者按home鍵,必須是用戶主動(dòng)的
    public interface UserLeaveHintListener {
        void onUserLeaveHint();
    }
   
    // 當(dāng)activity 重新啟動(dòng)的時(shí)候調(diào)用
    public interface NewIntentListener {
        boolean onNewIntent(Intent var1);
    }
    
    //獲取activity的返回值的回調(diào)
    public interface ActivityResultListener {
        boolean onActivityResult(int var1, int var2, Intent var3);
    }

  //請(qǐng)求權(quán)限的回調(diào)
    public interface RequestPermissionsResultListener {
        boolean onRequestPermissionsResult(int var1, String[] var2, int[] var3);
    }

    /// 插件的注冊(cè)者
    public interface Registrar {
        //插件的宿主activity
        Activity activity();
        //插件的上下文 Application Context.
        Context context();
        //當(dāng)前活動(dòng)的context
        Context activeContext();
       //返回 BinaryMessenger 主要用來(lái)注冊(cè)Platform channels
        BinaryMessenger messenger();
        //返回 TextureRegistry诲祸,從里面可以拿到SurfaceTexture 
        TextureRegistry textures();
        /// 獲取視圖
        FlutterView view();
        //返回Asset資源對(duì)應(yīng)的路徑
        String lookupKeyForAsset(String var1);
       //返回Asset資源對(duì)應(yīng)的路徑
        String lookupKeyForAsset(String var1, String var2);
        //插件發(fā)布一個(gè)值浊吏,與上面的valuePublishedByPlugin對(duì)應(yīng)
        PluginRegistry.Registrar publish(Object var1);
        
       //下面的都是添加上面的回調(diào)監(jiān)聽(tīng)
        PluginRegistry.Registrar addRequestPermissionsResultListener(PluginRegistry.RequestPermissionsResultListener var1);

        PluginRegistry.Registrar addActivityResultListener(PluginRegistry.ActivityResultListener var1);

        PluginRegistry.Registrar addNewIntentListener(PluginRegistry.NewIntentListener var1);

        PluginRegistry.Registrar addUserLeaveHintListener(PluginRegistry.UserLeaveHintListener var1);
        
        PluginRegistry.Registrar addViewDestroyListener(PluginRegistry.ViewDestroyListener var1);
    }
}
  • 回到MainFlutterActivityFlutter.createView(this, lifecycle, "main_flutter")這句
  public static FlutterView createView(@NonNull final Activity activity, @NonNull final Lifecycle lifecycle, final String initialRoute) {
    FlutterMain.startInitialization(activity.getApplicationContext());
    FlutterMain.ensureInitializationComplete(activity.getApplicationContext(), null);
    final FlutterNativeView nativeView = new FlutterNativeView(activity);
    final FlutterView flutterView = new FlutterView(activity, null, nativeView) {
      //other code
};
   if (initialRoute != null) {
      flutterView.setInitialRoute(initialRoute);
    }
    flutterView.setAlpha(0.0f);
    return flutterView;
  }
  • 主要看FlutterMain.startInitialization(activity.getApplicationContext());FlutterMain.ensureInitializationComplete(activity.getApplicationContext(), null);
   public static void startInitialization(Context applicationContext) {
        //調(diào)用兩個(gè)參數(shù)的方法
        startInitialization(applicationContext, new FlutterMain.Settings());
    }

    public static void startInitialization(Context applicationContext, FlutterMain.Settings settings) {
        if(Looper.myLooper() != Looper.getMainLooper()) {
            throw new IllegalStateException("startInitialization must be called on the main thread");
        } else if(sSettings == null) {
            //執(zhí)行完FlutterMain.startInitialization(activity.getApplicationContext());之后,sSettings 不為null
            sSettings = settings;
            long initStartTimestampMillis = SystemClock.uptimeMillis();
            initConfig(applicationContext);
            initAot(applicationContext);
            initResources(applicationContext);
            System.loadLibrary("flutter");
            long initTimeMillis = SystemClock.uptimeMillis() - initStartTimestampMillis;
            nativeRecordStartTimestamp(initTimeMillis);
        }
    }
public static void ensureInitializationComplete(Context applicationContext, String[] args) {
        if(Looper.myLooper() != Looper.getMainLooper()) {
            throw new IllegalStateException("ensureInitializationComplete must be called on the main thread");
        } else if(sSettings == null) {
          //重點(diǎn)看這里救氯,當(dāng)sSettings 為空的時(shí)候報(bào)錯(cuò)
            throw new IllegalStateException("ensureInitializationComplete must be called after startInitialization");
        } else if(!sInitialized) {
           //other code
        }
    }
  • 找到了報(bào)該錯(cuò)誤的原因找田,當(dāng)sSettings為空時(shí)就會(huì)導(dǎo)致該錯(cuò)誤的產(chǎn)生,但是執(zhí)行完FlutterMain.startInitialization(activity.getApplicationContext());后着憨,new FlutterMain.Settings()傳了進(jìn)去墩衙,不應(yīng)該會(huì)導(dǎo)致這個(gè)錯(cuò)誤,那么肯定在Flutter.createView(this, lifecycle, "main_flutter")之前有地方調(diào)用了FlutterMain.ensureInitializationComplete方法甲抖,往前看還調(diào)用了super.oncreate(),還記得前面說(shuō)過(guò)FlutterFragmentActivity的生命周期交由FlutterActivityDelegate代理管理,再看FlutterActivityDelegate.onCreate()
 public void onCreate(Bundle savedInstanceState) {
        //other code

        String[] args = getArgsFromIntent(this.activity.getIntent());
        //最終找到元兇 漆改,在這里也調(diào)用了該方法,所以需要在super.onCreate()之前調(diào)用FlutterMain.startInitialization(getApplicationContext())
        FlutterMain.ensureInitializationComplete(this.activity.getApplicationContext(), args);
      
      //創(chuàng)建flutterView 准谚,注意viewFactory.createFlutterView默認(rèn)返回的是null
        this.flutterView = this.viewFactory.createFlutterView(this.activity);
        if(this.flutterView == null) {
            //默認(rèn)走該流程
            FlutterNativeView nativeView = this.viewFactory.createFlutterNativeView();
            this.flutterView = new FlutterView(this.activity, (AttributeSet)null, nativeView);
            this.flutterView.setLayoutParams(matchParent);
            this.activity.setContentView(this.flutterView);
            this.launchView = this.createLaunchView();
            if(this.launchView != null) {
                this.addLaunchView();
            }
        }

        if(!this.loadIntent(this.activity.getIntent())) {
            if(!this.flutterView.getFlutterNativeView().isApplicationRunning()) {
                String appBundlePath = FlutterMain.findAppBundlePath(this.activity.getApplicationContext());
                if(appBundlePath != null) {
                    FlutterRunArguments arguments = new FlutterRunArguments();
                    arguments.bundlePath = appBundlePath;
                    arguments.entrypoint = "main";
                    this.flutterView.runFromBundle(arguments);
                }
            }

        }
    }
  • 到了現(xiàn)在籽懦,我們已經(jīng)知道了該錯(cuò)誤導(dǎo)致的原因及解決方式(為什么要這樣解決)。

  • 下面我們?cè)倏纯磩?chuàng)建的FlutterView是什么

public class FlutterView extends SurfaceView implements BinaryMessenger, TextureRegistry, AccessibilityStateChangeListener {
  //other code
   private final MethodChannel mFlutterLocalizationChannel;
    private final MethodChannel mFlutterNavigationChannel;
    private final BasicMessageChannel<Object> mFlutterKeyEventChannel;
    private final BasicMessageChannel<String> mFlutterLifecycleChannel;
    private final BasicMessageChannel<Object> mFlutterSystemChannel;
    private final BasicMessageChannel<Object> mFlutterSettingsChannel;
 

    public FlutterView(Context context, AttributeSet attrs, FlutterNativeView nativeView) {
   //other code

        this.mFlutterNavigationChannel = new MethodChannel(this, "flutter/navigation", JSONMethodCodec.INSTANCE);
 
    }


    public void setInitialRoute(String route) {
        this.mFlutterNavigationChannel.invokeMethod("setInitialRoute", route);
    }

    public void pushRoute(String route) {
        this.mFlutterNavigationChannel.invokeMethod("pushRoute", route);
    }

    public void popRoute() {
        this.mFlutterNavigationChannel.invokeMethod("popRoute", (Object)null);
    }

    //other code

}
  • 可以看到FlutterView 就是一個(gè)SurfaceView 氛魁,那么addContentView毫無(wú)疑問(wèn),但是Flutter是如何知道要加載的是哪個(gè)頁(yè)面,在Flutter#createView()中有這樣一段
  if (initialRoute != null) {
      flutterView.setInitialRoute(initialRoute);
    }
  • 在剛剛FlutterView源碼中可以看到厅篓,實(shí)際上它是通過(guò)MethodChannel與Flutter進(jìn)行通訊秀存,交由Flutter進(jìn)行處理
 public void setInitialRoute(String route) {
        this.mFlutterNavigationChannel.invokeMethod("setInitialRoute", route);
    }

  • 關(guān)于MethodChannel是什么,這里簡(jiǎn)單的提一下羽氮,F(xiàn)lutter需要與Native之間進(jìn)行通訊(即native和Dart之間的互相調(diào)用)或链,這個(gè)通訊是通過(guò)Platform Channels,同屬于Platform Channels還有另外兩種(EventChannel档押,BasicMessageChannel)了解更多
  • 也就是說(shuō)在Flutter方也有一個(gè)MethodChannel(this, "flutter/navigation", JSONMethodCodec.INSTANCE),在system_channels.dart
 /// A JSON [MethodChannel] for navigation.
  ///
  /// The following incoming methods are defined for this channel (registered
  /// using [MethodChannel.setMethodCallHandler]):
  ///
  ///  * `popRoute`, which is called when the system wants the current route to
  ///    be removed (e.g. if the user hits a system-level back button).
  ///
  ///  * `pushRoute`, which is called with a single string argument when the
  ///    operating system instructs the application to open a particular page.
  ///
  /// See also:
  ///
  ///  * [WidgetsBindingObserver.didPopRoute] and
  ///    [WidgetsBindingObserver.didPushRoute], which expose this channel's
  ///    methods.
  static const MethodChannel navigation = MethodChannel(
      'flutter/navigation',
      JSONMethodCodec(),
  );

  • binding.dart中,找到了實(shí)現(xiàn)處理的澳盐,但是并沒(méi)有看到setInitialRoute的處理
  SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);

  Future<dynamic> _handleNavigationInvocation(MethodCall methodCall) {
    switch (methodCall.method) {
      case 'popRoute':
        return handlePopRoute();
      case 'pushRoute':
        return handlePushRoute(methodCall.arguments);
    }
    return Future<dynamic>.value();
  }

  • 那這個(gè)initialRoute的作用是到底是什么呢祈纯?,在main.dart中通過(guò)MaterialApp中的routes配置啟動(dòng)的flutter界面
/// The [MaterialApp] configures the top-level [Navigator] to search for routes
/// in the following order:
///
///  1. For the `/` route, the [home] property, if non-null, is used.
///
///  2. Otherwise, the [routes] table is used, if it has an entry for the route.
///
///  3. Otherwise, [onGenerateRoute] is called, if provided. It should return a
///     non-null value for any _valid_ route not handled by [home] and [routes].
///
///  4. Finally if all else fails [onUnknownRoute] is called.
///
/// If a [Navigator] is created, at least one of these options must handle the
/// `/` route, since it is used when an invalid [initialRoute] is specified on
/// startup (e.g. by another application launching this one with an intent on
/// Android; see [Window.defaultRouteName]).
///
/// This widget also configures the observer of the top-level [Navigator] (if
/// any) to perform [Hero] animations.
///
/// If [home], [routes], [onGenerateRoute], and [onUnknownRoute] are all null,
/// and [builder] is not null, then no [Navigator] is created.

class MaterialApp extends StatefulWidget {
//other code

/// {@macro flutter.widgets.widgetsApp.home}
  final Widget home;

  /// The application's top-level routing table.
  ///
  /// When a named route is pushed with [Navigator.pushNamed], the route name is
  /// looked up in this map. If the name is present, the associated
  /// [WidgetBuilder] is used to construct a [MaterialPageRoute] that performs
  /// an appropriate transition, including [Hero] animations, to the new route.
  ///
  /// {@macro flutter.widgets.widgetsApp.routes}
  final Map<String, WidgetBuilder> routes;

  /// {@macro flutter.widgets.widgetsApp.initialRoute}
  final String initialRoute;

  /// {@macro flutter.widgets.widgetsApp.onGenerateRoute}
  final RouteFactory onGenerateRoute;

  /// {@macro flutter.widgets.widgetsApp.onUnknownRoute}
  final RouteFactory onUnknownRoute;


}
  • 可以看到Flutter中頂層的Navigator尋找路由的順序
  1. 對(duì)于'/'路由叼耙,如果[home]屬性配置不為空腕窥,首先使用
  2. 對(duì)于非'/'路由,優(yōu)先使用[routes]配置的(如果它包含路由的條目的話)
  3. 否則筛婉,如果[onGenerateRoute]提供了一個(gè)有效的沒(méi)有被[home]和[routes]處理的路由簇爆,調(diào)用
  4. 最后,如果前面的尋找都失敗了爽撒,調(diào)用[onUnknownRoute]
  5. 如果所有的都失敗了入蛆,將不會(huì)進(jìn)行導(dǎo)航
  6. 從其它應(yīng)用啟動(dòng)時(shí)指定了無(wú)效的[initialRoute],默認(rèn)使用 ' / '路由
  • 查看flutter.widgets.widgetsApp.initialRoute的說(shuō)明
 /// {@template flutter.widgets.widgetsApp.initialRoute}
  /// The name of the first route to show, if a [Navigator] is built.
  ///
  /// Defaults to [Window.defaultRouteName], which may be overridden by the code
  /// that launched the application.
  ///
  /// If the route contains slashes, then it is treated as a "deep link", and
  /// before this route is pushed, the routes leading to this one are pushed
  /// also. For example, if the route was `/a/b/c`, then the app would start
  /// with the three routes `/a`, `/a/b`, and `/a/b/c` loaded, in that order.
  ///
  /// If any part of this process fails to generate routes, then the
  /// [initialRoute] is ignored and [Navigator.defaultRouteName] is used instead
  /// This can happen if the app is started with an intent that specifies
  /// a non-existent route.
  /// The [Navigator] is only built if routes are provided (either via [home],
  /// [routes], [onGenerateRoute], or [onUnknownRoute]); if they are not,
  /// [initialRoute] must be null and [builder] must not be null.

  /// {@endtemplate}
  final String initialRoute;
  • initialRoute是第一個(gè)展示的路由的name,默認(rèn)是Window.defaultRouteName

  • 再看window.dart中對(duì)于defaultRouteName的說(shuō)明

  /// The route or path that the embedder requested when the application was
  /// launched.
  ///
  /// This will be the string "`/`" if no particular route was requested.
  ///
  /// ## Android
  ///
  /// On Android, calling
  /// [`FlutterView.setInitialRoute`](/javadoc/io/flutter/view/FlutterView.html#setInitialRoute-java.lang.String-)
  /// will set this value. The value must be set sufficiently early, i.e. before
  /// the [runApp] call is executed in Dart, for this to have any effect on the
  /// framework. The `createFlutterView` method in your `FlutterActivity`
  /// subclass is a suitable time to set the value. The application's
  /// `AndroidManifest.xml` file must also be updated to have a suitable
  /// [`<intent-filter>`](https://developer.android.com/guide/topics/manifest/intent-filter-element.html).
  ///
  /// See also:
  ///
  ///  * [Navigator], a widget that handles routing.
  ///  * [SystemChannels.navigation], which handles subsequent navigation
  ///    requests from the embedder.
  String get defaultRouteName => _defaultRouteName();
  String _defaultRouteName() native 'Window_defaultRouteName';
  • 可以看到FlutterView#setInitialRoute可以設(shè)置該值硕勿,也就是說(shuō)哨毁,當(dāng)在native端創(chuàng)建FlutterView之后通過(guò)setInitialRoute方法設(shè)置Window.defaultRouteName,從而決定啟動(dòng)的是那一個(gè)Flutter界面

待解決問(wèn)題

  • 沒(méi)有搞清楚flutter端MethodChanel是如何處理setInitialRoute方法
    但是可以看到Window.defaultRouteName是通過(guò)native方法在c++端設(shè)置的源武,推測(cè)是在MethodChanel傳遞消息的時(shí)候在c++端進(jìn)行了處理扼褪。

擴(kuò)展

  • 在閱讀源碼的時(shí)候還發(fā)現(xiàn)了用另外一種方式可以將Flutter頁(yè)面嵌入Activity中----Intent附帶路由
    由于篇幅的關(guān)系,將在下一篇Flutter頁(yè)面嵌入Android Fragment中進(jìn)行說(shuō)明
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末软能,一起剝皮案震驚了整個(gè)濱河市迎捺,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌查排,老刑警劉巖凳枝,帶你破解...
    沈念sama閱讀 218,640評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異跋核,居然都是意外死亡岖瑰,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)砂代,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)蹋订,“玉大人,你說(shuō)我怎么就攤上這事刻伊÷督洌” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,011評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵捶箱,是天一觀的道長(zhǎng)智什。 經(jīng)常有香客問(wèn)我,道長(zhǎng)丁屎,這世上最難降的妖魔是什么荠锭? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,755評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮晨川,結(jié)果婚禮上证九,老公的妹妹穿的比我還像新娘删豺。我一直安慰自己,他們只是感情好愧怜,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,774評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布呀页。 她就那樣靜靜地躺著,像睡著了一般叫搁。 火紅的嫁衣襯著肌膚如雪赔桌。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,610評(píng)論 1 305
  • 那天渴逻,我揣著相機(jī)與錄音疾党,去河邊找鬼。 笑死惨奕,一個(gè)胖子當(dāng)著我的面吹牛雪位,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播梨撞,決...
    沈念sama閱讀 40,352評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼雹洗,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了卧波?” 一聲冷哼從身側(cè)響起时肿,我...
    開(kāi)封第一講書(shū)人閱讀 39,257評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎港粱,沒(méi)想到半個(gè)月后螃成,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,717評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡查坪,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,894評(píng)論 3 336
  • 正文 我和宋清朗相戀三年寸宏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片偿曙。...
    茶點(diǎn)故事閱讀 40,021評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡氮凝,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出望忆,到底是詐尸還是另有隱情罩阵,我是刑警寧澤,帶...
    沈念sama閱讀 35,735評(píng)論 5 346
  • 正文 年R本政府宣布启摄,位于F島的核電站永脓,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏鞋仍。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,354評(píng)論 3 330
  • 文/蒙蒙 一搅吁、第九天 我趴在偏房一處隱蔽的房頂上張望威创。 院中可真熱鬧落午,春花似錦、人聲如沸肚豺。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,936評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)吸申。三九已至梗劫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間截碴,已是汗流浹背梳侨。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,054評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留日丹,地道東北人走哺。 一個(gè)月前我還...
    沈念sama閱讀 48,224評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像哲虾,于是被迫代替她去往敵國(guó)和親丙躏。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,974評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 每到年末都會(huì)反思自己一年的過(guò)往废恋,哪些不足,哪些尚可摩瞎,好像每年的不足都比尚可多很多拴签,所以每年都會(huì)痛定思痛:明年,一定...
    青兒姑娘閱讀 159評(píng)論 4 4
  • 在冰冷的街頭回想 那昨日 擦身而過(guò)的 淘書(shū)旗们,豪飲蚓哩,做站以及無(wú)法再詳細(xì)描述的運(yùn)氣 然后忘了該向哪里去 假如森林也是曾...
    讀叔閱讀 297評(píng)論 0 2
  • 搬運(yùn)自本人 CSDN 博客:https://blog.csdn.net/ajianyingxiaoqinghan/...
    琦小蝦閱讀 1,241評(píng)論 0 0
  • 本文參加#感悟三下鄉(xiāng)稠氮,青春筑夢(mèng)行2芾活動(dòng),本人承諾隔披,文章內(nèi)容為原創(chuàng)赃份,且未在其他平臺(tái)發(fā)表過(guò) 2017年7月3日,河南科...
    瑞瑞ZZ閱讀 276評(píng)論 0 0