一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - IOS - Flutter Boost 混合開發框架

Flutter Boost 混合開發框架

2021-12-27 15:40xiangzhihong IOS

Flutter是一個由C++實現的Flutter Engine和由Dart實現的Framework組成的跨平臺技術框架,本文將在此做一個初步的講解

一、flutter boost簡介

眾所周知,flutter是一個由c++實現的flutter engine和由dart實現的framework組成的跨平臺技術框架。其中,flutter engine負責線程管理、dart vm狀態管理以及dart代碼加載等工作,而dart代碼所實現的framework則負責上層業務開發,如flutter提供的組件等概念就是framework的范疇。

隨著flutter的發展,國內越來越多的app開始接入flutter。為了降低風險,大部分app采用漸進式方式引入flutter,在app里選幾個頁面用flutter來編寫,但都碰到了相同的問題,在原生頁面和flutter頁面共存的情況下,如何管理路由,以及原生頁面與flutter頁面之間的切換和通信都是混合開發中需要解決的問題。然而,官方沒有提供明確的解決方案,只是在混合開發時,官方建議開發者,應該使用同一個引擎支持多窗口繪制的能力,至少在邏輯上做到flutterviewcontroller是共享同一個引擎里面的資源。換句話說,官方希望所有的繪制窗口共享同一個主isolate,而不是出現多個主isolate的情況。不過,對于現在已經出現的多引擎模式問題,flutter官方也沒有提供好的解決方案。除了內存消耗嚴重外,多引擎模式還會帶來如下一些問題

  • 冗余資源問題。多引擎模式下每個引擎的isolate是相互獨立的,雖然在邏輯上這并沒有什么壞處,但是每個引擎底層都維護了一套圖片緩存等比較消耗內存的對象,因此設備的內存消耗是非常嚴重的。
  • 插件注冊問題。在flutter插件中,消息傳遞需要依賴messenger,而messenger是由flutterviewcontroller去實現的。如果一個應用中同時存在多個flutterviewcontroller,那么插件的注冊和通信將會變得混亂且難以維護。
  • flutter組件和原生頁面的差異化問題。通常,flutter頁面是由組件構成的,原生頁面則是由viewcontroller或者activity構成的。邏輯上來說,我們希望消除flutter頁面與原生頁面的差異,否則在進行頁面埋點和其它一些操作時增加一些額外的工作量。
  • 增加頁面通信的復雜度。如果所有的dart代碼都運行在同一個引擎實例中,那么它們會共享同一個isolate,可以用統一的框架完成組件之間的通信,但是如果存在多個引擎實例會讓isolate的管理變得更加復雜。

如果不解決多引擎問題,那么混合項目的導航棧如下圖所示。

Flutter Boost 混合開發框架

目前,對于原生工程混編flutter工程出現的多引擎模式問題,國內主要有兩種解決方案,一種是字節跳動的修改flutter engine源碼方案,另一種是閑魚開源的flutterboost。由于字節跳動的混合開發的方案沒有開源,所以現在能使用的就剩下flutterboost方案。

flutterboost是閑魚技術團隊開發的一個可復用頁面的插件,旨在把flutter容器做成類似于瀏覽器的加載方案。為此,閑魚技術團隊為希望flutterboost能完成如下的基本功能:

  • 可復用的通用型混合開發方案。
  • 支持更加復雜的混合模式,比如支持tab切換的場景。
  • 無侵入性方案,使用時不再依賴修改flutter的方案。
  • 支持對頁面生命周期進行統一的管理。
  • 具有統一明確的設計概念。

并且,最近flutter boost升級了3.0版本,并帶來了如下的一些更新:

  • 不侵入引擎,兼容flutter的各種版本,flutter sdk的升級不需要再升級flutterboost,極大降低升級成本。
  • 不區分androidx和support分支。
  • 簡化架構和接口,和flutterboost2.0比,代碼減少了一半。
  • 雙端統一,包括接口和設計上的統一。
  • 支持打開flutter頁面,不再打開容器場景。
  • 頁面生命周期變化通知更方便業務使用。
  • 解決了2.0中的遺留問題,例如,fragment接入困難、頁面關閉后不能傳遞數據、dispose不執行,內存占用過高等。

二、flutter boost集成

在原生項目中集成flutter boost只需要將flutter boost看成是一個插件工程即可。和其他flutter插件的集成方式一樣,使用flutterboost之前需要先添加依賴。使用android studio打開混合工程的flutter工程,在pubspec.yaml中添加flutterboost依賴插件,如下所示。

?
1
2
3
4
flutter_boost:
    git:
        url: 'https://github.com/alibaba/flutter_boost.git'
        ref: 'v3.0-hotfixes'

需要說明的是,此處的所依賴的flutterboost的版本與flutter的版本是對應的,如果不對應使用過程中會出現版本不匹配的錯誤。然后,使用flutter packages get命令將flutterboost插件拉取到本地。

2.1 android集成

使用android studio打開新建的原生android工程,在原生android工程的settings.gradle文件中添加如下代碼。

?
1
2
3
4
setbinding(new binding([gradle: this]))
evaluate(new file(
  settingsdir.parentfile,
  'flutter_library/.android/include_flutter.groovy'))

然后,打開原生android工程app目錄下的build.gradle文件,繼續添加如下依賴腳本。

?
1
2
3
4
dependencies {
  implementation project(':flutter_boost')
  implementation project(':flutter')
}

重新編譯構建原生android工程,如果沒有任何錯誤則說明android成功了集成flutterboost。使用flutter boost 之前,需要先執行初始化。打開原生android工程,新建一個繼承flutterapplication的application,然后在oncreate()方法中初始化flutterboost,代碼如下。

?
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
28
29
30
31
public class myapplication extends flutterapplication {
 
 
    @override
    public void oncreate() {
        super.oncreate();
 
        flutterboost.instance().setup(this, new flutterboostdelegate() {
 
            @override
            public void pushnativeroute(string pagename, hashmap<string, string> arguments) {
                intent intent = new intent(flutterboost.instance().currentactivity(), nativepageactivity.class);
                flutterboost.instance().currentactivity().startactivity(intent);
            }
 
            @override
            public void pushflutterroute(string pagename, hashmap<string, string> arguments) {
                intent intent = new flutterboostactivity.cachedengineintentbuilder(flutterboostactivity.class, flutterboost.engine_id)
                        .backgroundmode(flutteractivitylaunchconfigs.backgroundmode.opaque)
                        .destroyenginewithactivity(false)
                        .url(pagename)
                        .urlparams(arguments)
                        .build(flutterboost.instance().currentactivity());
                flutterboost.instance().currentactivity().startactivity(intent);
            }
 
        },engine->{
            engine.getplugins();
        } );
    }
}

然后,打開原生android工程下的androidmanifest.xml文件,將application替換成自定義的myapplication,如下所示。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:tools="http://schemas.android.com/tools"
          package="com.idlefish.flutterboost.example">
 
    <application
        android:name="com.idlefish.flutterboost.example.myapplication"
        android:label="flutter_boost_example"
        android:icon="@mipmap/ic_launcher">
 
        <activity
            android:name="com.idlefish.flutterboost.containers.flutterboostactivity"
            android:theme="@style/theme.appcompat"
            android:configchanges="orientation|keyboardhidden|keyboard|screensize|locale|layoutdirection|fontscale|screenlayout|density"
            android:hardwareaccelerated="true"
            android:windowsoftinputmode="adjustresize" >
            <meta-data android:name="io.flutter.embedding.android.splashscreendrawable" android:resource="@drawable/launch_background"/>
 
        </activity>
        <meta-data android:name="flutterembedding"
                   android:value="2">
        </meta-data>
    </application>
</manifest>

由于flutter boost 是以插件的方式集成到原生android項目的,所以我們可以在native 打開和關閉flutter模塊的頁面。

?
1
2
flutterboost.instance().open("flutterpage",params);
flutterboost.instance().close("uniqueid");

而flutter dart的使用如下。首先,我們可以在main.dart文件的程序入口main()方法中進行初始化。

?
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
void main() {
  runapp(myapp());
}
class myapp extends statefulwidget {
  @override
  _myappstate createstate() => _myappstate();
}
class _myappstate extends state<myapp> {
   static map<string, flutterboostroutefactory>
       routermap = {
    '/': (settings, uniqueid) {
      return pageroutebuilder<dynamic>(
          settings: settings, pagebuilder: (_, __, ___)
          => container());
    },
    'embedded': (settings, uniqueid) {
      return pageroutebuilder<dynamic>(
          settings: settings,
          pagebuilder: (_, __, ___) =>
          embeddedfirstroutewidget());
    },
    'presentflutterpage': (settings, uniqueid) {
      return pageroutebuilder<dynamic>(
          settings: settings,
          pagebuilder: (_, __, ___) =>
          flutterroutewidget(
                params: settings.arguments,
                uniqueid: uniqueid,
              ));
    }};
   route<dynamic> routefactory(routesettings settings, string uniqueid) {
    flutterboostroutefactory func =routermap[settings.name];
    if (func == null) {
      return null;
    }
    return func(settings, uniqueid);
  }
 
  @override
  void initstate() {
    super.initstate();
  }
 
  @override
  widget build(buildcontext context) {
    return flutterboostapp(
      routefactory
    );
  }

當然,還可以監聽頁面的生命周期,如下所示。

?
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
class simplewidget extends statefulwidget {
  final map params;
  final string messages;
  final string uniqueid;
 
  const simplewidget(this.uniqueid, this.params, this.messages);
 
  @override
  _simplewidgetstate createstate() => _simplewidgetstate();
}
 
class _simplewidgetstate extends state<simplewidget>
    with pagevisibilityobserver {
  static const string _ktag = 'xlog';
  @override
  void didchangedependencies() {
    super.didchangedependencies();
    print('$_ktag#didchangedependencies, ${widget.uniqueid}, $this');
 
  }
 
  @override
  void initstate() {
    super.initstate();
   pagevisibilitybinding.instance.addobserver(this, modalroute.of(context));
   print('$_ktag#initstate, ${widget.uniqueid}, $this');
  }
 
  @override
  void dispose() {
    pagevisibilitybinding.instance.removeobserver(this);
    print('$_ktag#dispose, ${widget.uniqueid}, $this');
    super.dispose();
  }
 
  @override
  void onforeground() {
    print('$_ktag#onforeground, ${widget.uniqueid}, $this');
  }
 
  @override
  void onbackground() {
    print('$_ktag#onbackground, ${widget.uniqueid}, $this');
  }
 
  @override
  void onappear(changereason reason) {
    print('$_ktag#onappear, ${widget.uniqueid}, $reason, $this');
  }
 
  void ondisappear(changereason reason) {
    print('$_ktag#ondisappear, ${widget.uniqueid}, $reason, $this');
  }
 
  @override
  widget build(buildcontext context) {
    return scaffold(
      appbar: appbar(
        title: text('tab_example'),
      ),
      body: singlechildscrollview(
          physics: bouncingscrollphysics(),
          child: container(
              child: column(
            crossaxisalignment: crossaxisalignment.start,
            children: <widget>[
              container(
                margin: const edgeinsets.only(top: 80.0),
                child: text(
                  widget.messages,
                  style: textstyle(fontsize: 28.0, color: colors.blue),
                ),
                alignment: alignmentdirectional.center,
              ),
              container(
                margin: const edgeinsets.only(top: 32.0),
                child: text(
                  widget.uniqueid,
                  style: textstyle(fontsize: 22.0, color: colors.red),
                ),
                alignment: alignmentdirectional.center,
              ),
              inkwell(
                child: container(
                    padding: const edgeinsets.all(8.0),
                    margin: const edgeinsets.all(30.0),
                    color: colors.yellow,
                    child: text(
                      'open flutter page',
                      style: textstyle(fontsize: 22.0, color: colors.black),
                    )),
                ontap: () => boostnavigator.of().push("flutterpage",
                    arguments: <string, string>{'from': widget.uniqueid}),
              )
              container(
                height: 300,
                width: 200,
                child: text(
                  '',
                  style: textstyle(fontsize: 22.0, color: colors.black),
                ),
              )
            ],
          ))),
    );
  }
}

然后,運行項目,就可以從原生頁面跳轉到flutter頁面,如下圖所示效果。

Flutter Boost 混合開發框架

2.2 ios集成

和android的集成步驟一樣,使用xcode打開原生ios工程,然后在ios的appdelegate文件中初始化flutter boost ,如下所示。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@interface appdelegate ()
 
@end
 
@implementation appdelegate
 
- (bool)application:(uiapplication *)application didfinishlaunchingwithoptions:(nsdictionary *)launchoptions
{
  myflutterboostdelegate* delegate=[[myflutterboostdelegate alloc ] init];
    [[flutterboost instance] setup:application delegate:delegate callback:^(flutterengine *engine) {
    } ];
 
    return yes;
}
@end

下面是自定義的flutterboostdelegate的代碼,如下所示。

?
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
@interface myflutterboostdelegate : nsobject<flutterboostdelegate>
@property (nonatomic,strong) uinavigationcontroller *navigationcontroller;
@end
 
@implementation myflutterboostdelegate
 
- (void) pushnativeroute:(fbcommonparams*) params{
    bool animated = [params.arguments[@"animated"] boolvalue];
    bool present= [params.arguments[@"present"] boolvalue];
    uiviewcontrollerdemo *nvc = [[uiviewcontrollerdemo alloc] initwithnibname:@"uiviewcontrollerdemo" bundle:[nsbundle mainbundle]];
    if(present){
        [self.navigationcontroller presentviewcontroller:nvc animated:animated completion:^{
        }];
    }else{
        [self.navigationcontroller pushviewcontroller:nvc animated:animated];
    }
}
 
- (void) pushflutterroute:(fbcommonparams*)params {
 
    flutterengine* engine =  [[flutterboost instance ] getengine];
    engine.viewcontroller = nil;
 
    fbflutterviewcontainer *vc = fbflutterviewcontainer.new ;
 
    [vc setname:params.pagename params:params.arguments];
 
    bool animated = [params.arguments[@"animated"] boolvalue];
    bool present= [params.arguments[@"present"] boolvalue];
    if(present){
        [self.navigationcontroller presentviewcontroller:vc animated:animated completion:^{
        }];
    }else{
        [self.navigationcontroller pushviewcontroller:vc animated:animated];
 
    }
}
 
- (void) poproute:(fbcommonparams*)params
         result:(nsdictionary *)result{
 
    fbflutterviewcontainer *vc = (id)self.navigationcontroller.presentedviewcontroller;
 
    if([vc iskindofclass:fbflutterviewcontainer.class] && [vc.uniqueidstring isequal: params.uniqueid]){
        [vc dismissviewcontrolleranimated:yes completion:^{}];
    }else{
        [self.navigationcontroller popviewcontrolleranimated:yes];
    }
 
}
 
@end

如果要在原生ios代碼中打開或關閉flutter頁面,可以使用下面的方式。

?
1
2
[[flutterboost instance] open:@"flutterpage" arguments:@{@"animated":@(yes)}  ];
[[flutterboost instance] open:@"secondstateful" arguments:@{@"present":@(yes)}];

三、flutter boost架構

對于混合工程來說,原生端和flutter端對于頁面的定義是不一樣的。對于原生端而言,頁面通常指的是一個viewcontroller或者activity,而對于flutter來說,頁面通常指的是flutter組件。flutterboost框架所要做的就是統一混合工程中頁面的概念,或者說弱化flutter組件對應容器頁面的概念。換句話說,當有一個原生頁面存在的時候,flutteboost就能保證一定有一個對應的flutter的容器頁面存在。

flutterboost框架其實就是由原生容器通過消息驅動flutter頁面容器,從而達到原生容器與flutter容器同步的目的,而flutter渲染的內容是由原生容器去驅動的,下面是flutter boost 給的一個flutter boost 的架構示意圖。

Flutter Boost 混合開發框架

可以看到,flutter boost插件分為平臺和dart兩端,中間通過message channel連接。平臺側提供了flutter引擎的配置和管理、native容器的創建/銷毀、頁面可見性變化通知,以及flutter頁面的打開/關閉接口等。而dart側除了提供類似原生navigator的頁面導航接口的能力外,還負責flutter頁面的路由管理。

總的來說,正是基于共享同一個引擎的方案,使得flutterboost框架有效的解決了多引擎的問題。簡單來說,flutterboost在dart端引入了容器的概念,當存在多個flutter頁面時,flutterboost不需要再用棧的結構去維護現有頁面,而是使用扁平化鍵值對映射的形式去維護當前所有的頁面,并且每個頁面擁有一個唯一的id

四、flutterboost3.0更新

4.1 不入侵引擎

為了解決官方引擎復用引起的問題,flutterboost2.0拷貝了flutter引擎embedding層的一些代碼進行改造,這使得后期的升級成本極高。而flutterboost3.0采用繼承的方式擴展flutteractivity/flutterfragment等組件的能力,并且通過在適當時機給dart側發送appisresumed消息解決引擎復用時生命周期事件錯亂導致的頁面卡死問題,并且,flutterboost 3.0 也兼容最新的官方發布的 flutter 2.0。

4.2 不區分androidx和support分支

flutterboost2.0通過自己實現flutteractivityandfragmentdelegate.host接口來擴展flutteractivity和flutterfragment的能力,而getlifecycle是必須實現的接口,這就導致對androidx的依賴。這也是為什么flutterboostview的實現沒有被放入flutterboost3.0插件中的原因。而flutterboost3.0通過繼承的方式擴展flutteractivity/flutterfragment的能力的額外收益就是,可以做到不依賴androidx。

4.3 雙端設計統一,接口統一

很多flutter開發者只會一端,只會android 或者只會ios,但他需要接入雙端,所以雙端統一能降低他的 學習成本和接入成本。flutterboost3.0,在設計上 android和ios都做了對齊,特別接口上做到了參數級的對齊。

4.4 支持 【打開flutter頁面不再打開容器】 場景

在flutter模塊內部,flutter 頁面跳轉flutter 頁面是可以不需要再打開flutter容器的,不打開容器,能節省內存開銷。在flutterboost3.0上,打開容器和不打開容器的區別表現在用戶接口上僅僅是withcontainer參數是否為true就好。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
inkwell(
  child: container(
      color: colors.yellow,
      child: text(
        '打開外部路由',
        style: textstyle(fontsize: 22.0, color: colors.black),
      )),
  ontap: () => boostnavigator.of().push("flutterpage",
      arguments: <string, string>{'from': widget.uniqueid}),
),
inkwell(
  child: container(
      color: colors.yellow,
      child: text(
        '打開內部路由',
        style: textstyle(fontsize: 22.0, color: colors.black),
      )),
  ontap: () => boostnavigator.of().push("flutterpage",
      withcontainer: true,
      arguments: <string, string>{'from': widget.uniqueid}),
)

4.5 生命周期的精準通知

在flutterboost2.0上,每個頁面都會收到頁面生命周期通知,而flutterboost3.0只會通知頁面可見性實際發生了變化的頁面,接口也更符合flutter的設計。

4.6 其他issue

除了上面的一些特性外,flutter boost 3.0版本還解決了如下一些問題:

  • 頁面關閉后參數的傳遞,之前只有ios支持,android不支持,目前在dart側實現,ios 和android 都支持。
  • 解決了android 狀態欄字體和顏色問題。
  • 解決了頁面回退willpopscope不起作用問題。
  • 解決了不在棧頂的頁面也收到生命周期回調的問題
  • 解決了多次setstate耗性能問題。
  • 提供了framgent 多種接入方式的demo,方便tab 場景的接入。
  • 生命周期的回調代碼,可以用戶代碼里面with的方式接入,使用更簡單。
  • 全面簡化了,接入成本,包括 dart側,android側和ios
  • 豐富了demo,包含了基本場景,方便用戶接入 和測試回歸

到此這篇關于flutter boost 混合開發框架的文章就介紹到這了,更多相關flutter boost內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!,希望大家以后多多支持服務器之家!

原文鏈接:https://segmentfault.com/a/1190000039760722

延伸 · 閱讀

精彩推薦
  • IOSiOS開發技巧之狀態欄字體顏色的設置方法

    iOS開發技巧之狀態欄字體顏色的設置方法

    有時候我們需要根據不同的背景修改狀態欄字體的顏色,下面這篇文章主要給大家介紹了關于iOS開發技巧之狀態欄字體顏色的設置方法,文中通過示例代碼...

    夢想家-mxj8922021-05-10
  • IOSiOS中滑動控制屏幕亮度和系統音量(附加AVAudioPlayer基本用法和Masonry簡單使用)

    iOS中滑動控制屏幕亮度和系統音量(附加AVAudioPlayer基本用法和

    這篇文章主要介紹了iOS中滑動控制屏幕亮度和系統音量(附加AVAudioPlayer基本用法和Masonry簡單使用)的相關資料,需要的朋友可以參考下...

    CodingFire13652021-02-26
  • IOSiOS實現控制屏幕常亮不變暗的方法示例

    iOS實現控制屏幕常亮不變暗的方法示例

    最近在工作中遇到了要將iOS屏幕保持常亮的需求,所以下面這篇文章主要給大家介紹了關于利用iOS如何實現控制屏幕常亮不變暗的方法,文中給出了詳細的...

    隨風13332021-04-02
  • IOS詳解iOS中多個網絡請求的同步問題總結

    詳解iOS中多個網絡請求的同步問題總結

    這篇文章主要介紹了詳解iOS中多個網絡請求的同步問題總結,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧...

    liang199111312021-03-15
  • IOSiOS自定義UICollectionViewFlowLayout實現圖片瀏覽效果

    iOS自定義UICollectionViewFlowLayout實現圖片瀏覽效果

    這篇文章主要介紹了iOS自定義UICollectionViewFlowLayout實現圖片瀏覽效果的相關資料,需要的朋友可以參考下...

    jiangamh8882021-01-11
  • IOSiOS中MD5加密算法的介紹和使用

    iOS中MD5加密算法的介紹和使用

    MD5加密是最常用的加密方法之一,是從一段字符串中通過相應特征生成一段32位的數字字母混合碼。對輸入信息生成唯一的128位散列值(32個字符)。這篇文...

    LYSNote5432021-02-04
  • IOSiOS中UILabel實現長按復制功能實例代碼

    iOS中UILabel實現長按復制功能實例代碼

    在iOS開發過程中,有時候會用到UILabel展示的內容,那么就設計到點擊UILabel復制它上面展示的內容的功能,也就是Label長按復制功能,下面這篇文章主要給大...

    devilx12792021-04-02
  • IOSiOS開發之視圖切換

    iOS開發之視圖切換

    在iOS開發中視圖的切換是很頻繁的,獨立的視圖應用在實際開發過程中并不常見,除非你的應用足夠簡單。在iOS開發中常用的視圖切換有三種,今天我們將...

    執著丶執念5282021-01-16
主站蜘蛛池模板: 午夜影视在线观看 | 久久精品美女 | 国产精品成人免费福利 | 天天爽天天干天天操 | 国产东北3p真实在线456视频 | 高h短篇合集| 极品美女a∨片在线看 | 大学生初次破苞免费视频 | 亚洲一级视频在线观看 | 亚洲 欧美 中文 日韩 视频 | 国产欧美va欧美va香蕉在线观 | 国内自拍网红在线自拍综合 | 午夜伦伦电影理论片大片 | 鸥美三级 | 国产一区国产二区国产三区 | 欧美精品v日韩精品v国产精品 | 国产精品www夜色影视 | 久久综合狠狠综合久久综合88 | 人人人人看人人人做人人 | 天堂精品高清1区2区3区 | 特黄特级高清免费视频毛片 | 欧美精品久久久久久久免费观看 | 操美女骚b | 日韩精选视频 | 亚洲性综合网 | 午夜影院费试看黄 | 色女的乖男人 | 精品久久香蕉国产线看观看亚洲 | 天堂成人影院 | 北条麻妃一区 | 日韩一区国产二区欧美三 | 四虎永久在线精品波多野结衣 | 91久久综合九色综合欧美98 | 国产成人lu在线视频 | 无限韩国视频免费播放 | 亚洲妇熟xxxxx妇色黄 | 亚洲精品精品一区 | 国产欧美一区二区精品性色99 | 久久免费看少妇高潮A片2012 | 国产麻豆网 | 91综合在线视频 |