<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          構(gòu)建復(fù)雜應(yīng)用的神器,F(xiàn)Broadcast

          共 14231字,需瀏覽 29分鐘

           ·

          2020-08-13 12:05

          FWidget 用心提供精致的組件,助您構(gòu)建精美的應(yīng)用。

          FWidget 一直致力于為開發(fā)者們精心打造易于構(gòu)建精美應(yīng)用的 Widget。

          迄今為止,FWidget 已經(jīng)收到了來自開發(fā)者們的 1215 個(gè) Star,感謝開發(fā)者們的支持,這對 FWidget 來說至關(guān)重要。

          今天,FWidget 為開發(fā)者們帶來了一個(gè)全新的組件 FBroadcast,以幫助開發(fā)者們能夠更輕松的構(gòu)建更為復(fù)雜精美的應(yīng)用。

          例如,在 FWidget 的官方網(wǎng)站 https://fwidget.cn 中,通過 FBroadcast 很輕松的就實(shí)現(xiàn)了成本極低的,且易于維護(hù)的全局實(shí)時(shí)語言切換。它十分簡單有效和明確!


          ? 特性

          來看看 FBroadcast 為開發(fā)者提供了那些不可思議的能力支持:

          • 支持發(fā)送接收指定類型的消息

          • 消息支持?jǐn)y帶任意類型數(shù)據(jù)包

          • 提供環(huán)境注冊,一行代碼即可移除環(huán)境內(nèi)所有接收者

          • 不可思議的粘性廣播

          • 雙向通信支持

          • 易于構(gòu)建簡單明確的局部和全局狀態(tài)管理

          ? 傳送區(qū)

          ? 【傳送門:FBroadcast Github 主頁】

          ? 【傳送門:FBroadcast 文檔】

          ? 獻(xiàn)給開發(fā)者的 FBroadcast

          FBroadcast 是一套高效靈活的廣播系統(tǒng),可以幫助開發(fā)者輕松、有序的構(gòu)建具有極具復(fù)雜性的關(guān)聯(lián)交互狀態(tài)變化的精美應(yīng)用。

          FBroadcast 將為構(gòu)建復(fù)雜的精美應(yīng)用帶來那些顯而易見的改變呢?

          • Widget/模塊間的完全解耦

            通過 FBroadcast 高效的廣播系統(tǒng),開發(fā)者可以輕易的完成Widget/模塊的解耦。在應(yīng)用構(gòu)建的時(shí)候,經(jīng)常需要 Widget/模塊A、B、C、.. 之間根據(jù)交互操作互相變更狀態(tài)或數(shù)據(jù),開發(fā)者們不得不為此讓各個(gè)Widget/模塊互相依賴或者為它們建立統(tǒng)一的狀態(tài)管理,這能解決問題,但這讓構(gòu)建變得麻煩,也讓變更變得難以進(jìn)行。

            FBroadcast 通過建立起簡單、有效、明確廣播系統(tǒng),使得在任意Widget/模塊任意時(shí)刻/位置的改變能夠主動(dòng)發(fā)出廣播,而需要根據(jù)這些變更作出響應(yīng)或更新視圖Widget/模塊只需要注冊相應(yīng)的信息接收器,就可以在變更發(fā)生時(shí),接收到消息,作出響應(yīng)。這使得關(guān)聯(lián)模塊間不再需要互相依賴,或是為它們設(shè)計(jì)建立統(tǒng)一的狀態(tài)管理器。

            十分簡單,輕量,和易于變更。當(dāng)一個(gè)Widget/模塊不在需要根據(jù)另一個(gè)Widget/模塊的變更而更新時(shí),只需移除其中的接收器即可,而不用為此而大改依賴關(guān)系或是狀態(tài)管理器。

          • 簡單、靈活、明確、易管理

            FBroadcast ?為開發(fā)者提供了可以在任意時(shí)刻發(fā)送廣播,和注冊/移除接收器的能力,毫無約束和靈活。

            廣播和接收器之間通過明確的類型(字符串)來互相確認(rèn)身份,指定類型的廣播,只能被指定類型的接收器接收。

            FBroadcast 提供了環(huán)境注冊支持,開發(fā)者可以在環(huán)境解構(gòu)時(shí),通過 [unregister()] 函數(shù)一次性移除環(huán)境中的所有類型接收器,而無需記憶和關(guān)心究竟需要移除那些接收器。例如,開發(fā)者可以在 Widgetdispose() 中,將注冊在該 Widget 中的所有接收器一次性全部移除。

            借助現(xiàn)代IDEA的能力,開發(fā)者可以為廣播系統(tǒng)建立一張(或多張)統(tǒng)一的廣播類型索引表,通過IDEA引用索引,開發(fā)者可以輕松的、一目了然的看到該類型的廣播在那些地方被發(fā)送過,在那些地方注冊了接收器,十分易于管理和維護(hù)。而使用字符串來作為類型標(biāo)識(shí),使得開發(fā)者可以將不同類型的廣播含義描述的足夠清晰明白。

          • 粘性廣播支持

            FBroadcast 提供了發(fā)送粘性廣播的支持。在還沒有注冊任何接收器的情況下,開發(fā)者可以在事件發(fā)生時(shí),預(yù)先發(fā)送一條粘性廣播。粘性廣播會(huì)被暫時(shí)滯留在廣播系統(tǒng)中,當(dāng)有接收器被注冊時(shí),即會(huì)立即廣播。這有助于幫助開發(fā)者在做邏輯設(shè)計(jì)時(shí)采取更清晰有效的思路。

            例如,當(dāng)一個(gè)控制模塊中的開關(guān)按鈕被打開,而此時(shí)開關(guān)所控制的模塊還沒有被構(gòu)建,就可以先發(fā)送一條粘性廣播,在模塊被構(gòu)建完成注冊了接收器后,就會(huì)立即接收到粘性廣播而進(jìn)入開啟狀態(tài)(這與互相依賴、定義統(tǒng)一狀態(tài)管理或是參數(shù)傳遞,然后檢查開關(guān)狀態(tài)的思路有本質(zhì)區(qū)別)。

          ? 這真不是一般的 Broadcast

          ? Base Broadcast

          通信就要簡單,明確

          通過 FBroadcast 來注冊,發(fā)送廣播非常簡便。

          /// 注冊接收器/// /// registerFBroadcast.instance().register(Key_Message, (value, callback) {  /// do something});
          /// 發(fā)送消息/// /// send messageFBroadcast.instance().broadcast(Key_Message);

          FBroadcast 允許開發(fā)者在發(fā)送消息的時(shí)候,帶有數(shù)據(jù)。

          /// 注冊接收器/// /// registerFBroadcast.instance().register(Key_Message, (value, callback) {  /// 獲取數(shù)據(jù)  ///   /// get data  var data = value;});
          /// 發(fā)送消息和數(shù)據(jù)/// /// send message and dataFBroadcast.instance().broadcast( /// 消息類型 /// /// message type Key_Message,
          /// 數(shù)據(jù) /// /// data value: data,);

          開發(fā)者可以選擇將特定類型的消息進(jìn)行持久化,這樣就能輕易實(shí)現(xiàn)廣播式的全局狀態(tài)管理。

          FBroadcast.instance().broadcast(  /// 消息類型  ///   /// message type  Key_Message, 
          /// 數(shù)據(jù) /// /// data value: data,
          /// 將消息類型持久化 /// /// Persist the message types persistence: true,);

          當(dāng)開發(fā)者將一個(gè)消息類型持久化后,就可以在任意位置,通過 FBroadcast.value(String key) 來獲取廣播系統(tǒng)中該類型消息的最新的數(shù)據(jù)。而更新廣播系統(tǒng)中的數(shù)據(jù)只需要通過 broadcast() 即可完成。

          ??注意,一個(gè)消息類型一旦持久化就只能通過 FBroadcast.instance().clear(String key) 來從廣播系統(tǒng)中移除該類型的消息。

          ? Sticky Broadcast

          更多的選擇,構(gòu)建更精美的應(yīng)用

          FBroadcast 支持開發(fā)者發(fā)送粘性廣播

          FBroadcast.instance().stickyBroadcast(  /// 消息類型  ///   /// message type  Key_Message, 
          /// 數(shù)據(jù) /// /// data value: data, );

          當(dāng)廣播系統(tǒng)中沒有對應(yīng)類型的接收器時(shí),粘性廣播 將會(huì)暫時(shí)滯留在系統(tǒng)中,直到有該類型的接收器被注冊,則會(huì)立即發(fā)出廣播(當(dāng)廣播系統(tǒng)中有對應(yīng)類型的接收器時(shí),就和普通廣播具有相同的表現(xiàn))。

          ? Two-way communication

          雙向溝通,雙倍效率

          FBroadcast 支持在廣播發(fā)送點(diǎn)接收接收器返回的消息。

          /// 發(fā)送消息/// /// send messageFBroadcast.instance().broadcast(  /// 消息類型  ///   /// message type  Key_Message, 
          /// 數(shù)據(jù) /// /// data value: data, /// 接收器返回的消息 /// /// The message returned by the receiver callback: (value){ // do something });

          /// 注冊接收器/// /// registerFBroadcast.instance().register(Key_Message, (value, callback) { /// 獲取數(shù)據(jù) /// /// get data var data = value;
          /// do something var result = logic();
          /// 返回消息 /// /// return message callback(result);});

          通過 FBroadcast 能夠給十分輕松的實(shí)現(xiàn)雙向通信。

          ? Bind Context

          一碼卸載,快捷精準(zhǔn)

          FBroadcast 支持在注冊接收器時(shí)傳入一個(gè)環(huán)境對象(可以是任意類型),這會(huì)將接收器注冊到環(huán)境中,當(dāng)環(huán)境解構(gòu)時(shí),開發(fā)者可以方便的一次性移除所有在該環(huán)境中注冊的接收器。

          /// 注冊接收器/// /// registerFBroadcast.instance().register(  /// 消息類型  ///   /// Message type  Key_Message1,
          /// Receiver /// /// Receiver (value, callback) { /// do something },
          /// 更多接收器 /// /// more receiver more: { /// 消息類型:接收器 /// /// Message type: Receiver Key_Message2: (value, callback) { /// do something }, Key_Message3: (value, callback) { /// do something }, Key_Message4: (value, callback) { /// do something }, },
          /// 環(huán)境對象 /// /// context context: this,);
          /// 移除環(huán)境中的所有接收器/// /// Remove all receivers from the environmentFBroadcast.instance().unregister(this);

          ? 使用 FBroadcast 可以做些什么?

          ? 消息傳遞

          場景:點(diǎn)擊 Start,Runner 開始 Run,顯示屏需要實(shí)時(shí)更新運(yùn)動(dòng)員的狀態(tài)。

          ? 1. 創(chuàng)建 Runner:

          /// Runnerclass Runner {  Runner() {    /// register    FBroadcast.instance().register(Key_RunnerState, (value, callback) {      if (value is String && value.contains("Run")) {        /// receive start run message        FBroadcast.instance().broadcast(Key_RunnerState, value: "0m..");        run(20);      }    });  }
          run(double distance) { /// send running message Timer(Duration(milliseconds: 500), () { FBroadcast.instance().broadcast(Key_RunnerState, value: "${distance.toInt()}m.."); var newDistance = distance + 20; if (newDistance > 100) { FBroadcast.instance().broadcast(Key_RunnerState, value: "Win!\nTotal time is 2.5s"); } else { run(newDistance); } }); }}

          ? 2. 創(chuàng)建 UI:

          Column(  mainAxisAlignment: MainAxisAlignment.center,  children: [    Stateful(      /// init      initState: (setState, data) {        FBroadcast.instance().register(          Key_RunnerState,          (value, callback) {            /// refresh ui            setState(() {});          },          /// bind context          context: data,        );      },      builder: (context, setState, data) {        return FSuper(          ...          /// get running message          text: FBroadcast.value(Key_RunnerState) ?? "Preparing..",        );      },    ),    const SizedBox(height: 100),    FButton(      text: "Start"      ...      onPressed: () {        /// send start run message        FBroadcast.instance().broadcast(Key_RunnerState, value: "Running...");      },    ),  ],)

          在上面的示例中,通過 FBroadcast 簡單清晰的實(shí)現(xiàn)了 Runner 和 UI 之間的通信。

          1. 點(diǎn)擊 Start 按鈕,通過 FBroadcast 發(fā)送起跑消息給 Runner;
          1. Runner 收到消息后,開始 Run,同時(shí)不斷通過 FBroadcast 發(fā)出 Running info;
          1. UI 由于注冊了接收器,在接收到 Running info 時(shí),通過 FBroadcast.value() 獲取消息數(shù)據(jù),自動(dòng)更新視圖。

          整個(gè)過程中,Runner 和 UI 之間是完全解耦的,且 UI 只需在 init注冊接收器(receiver 中調(diào)用 setState((){})),就能根據(jù)消息數(shù)據(jù)的變化,自動(dòng)實(shí)時(shí)的更新視圖,而無需開發(fā)者關(guān)心整個(gè)過程。

          ? 雙向通信

          場景:點(diǎn)擊按鈕請求定位,定位成功后接收結(jié)果,刷新定位點(diǎn)

          ? 1. 全局定位服務(wù)提供商

          class LocationServer {  LocationServer() {    init();  }
          init() { /// register Key_Location receiver FBroadcast.instance().register(Key_Location, (value, callback) async { var loc = await location();
          /// return message callback(loc); }); }
          /// Analog positioning Future> location() async { await Future.delayed(Duration(milliseconds: 2000)); return [Random().nextDouble() * 280, Random().nextDouble() * 150]; }}

          ? 2. 點(diǎn)擊發(fā)送定位請求,接收返回消息


          FButton( ... text: "Location", onPressed: () { FLoading.show(context, color: Colors.black26, loading: buildLoading()); /// request location FBroadcast.instance().broadcast(Key_Location, callback: (location) { /// The message returned by the receiver setState(() { FLoading.hide(); this.location = location; }); }); },)

          FBroadcast 能夠進(jìn)一步簡化需要雙向通信的場景。開發(fā)者可以看到,在這個(gè)例子中,通過 FBroadcast 能夠輕松的實(shí)現(xiàn)定位請求這種雙向通信的場景,而且使得定位服務(wù)提供商UI實(shí)現(xiàn)的完全的解耦。

          UI交互點(diǎn)只需要發(fā)送定位請求的廣播,任何注冊該廣播的定位服務(wù)提供商就可以接收該請求進(jìn)行處理,然后返回結(jié)果到UI交互點(diǎn)。也就是說,隨著項(xiàng)目的演進(jìn),開發(fā)者可以隨時(shí)提供新的定位服務(wù)提供商,而無需關(guān)心任何的UI變更。

          ? 局部狀態(tài)管理

          場景:點(diǎn)擊改變UI顏色

          ? 1. 點(diǎn)擊發(fā)出事件

          FButton(  text: "Change Color",  ...  onPressed: () {    /// send change color message    FBroadcast.instance().broadcast(Key_Color, value: reduceColor());  },)

          ? 2. UI 注冊接收器

          Stateful(  /// init  initState: (setState, data) {    /// register    FBroadcast.instance().register(      Key_Color,      (value, callback) {        /// refresh ui        setState(() {        });      },      /// bind context      context: data,    );  },  builder: (context, setState, data) {    return FSuper(      ...      /// get color value      backgroundColor: FBroadcast.value(Key_Color) ?? mainBackgroundColor,    );  },)

          通過 FBroadcast 可以很輕易的完成 UI 交互之間的局部狀態(tài)更新。上面的示例展示了顏色的變更,數(shù)據(jù)對象只有一個(gè)參數(shù),實(shí)際開發(fā)過程中,開發(fā)者可以根據(jù)需要將通信的數(shù)據(jù)對象進(jìn)行豐富擴(kuò)展。

          開發(fā)者只需要在需要更新 UI 的 Widget 中注冊接收器,調(diào)用一次 setState((){}),在交互點(diǎn)發(fā)出消息。而不用去主動(dòng)的將觸發(fā)邏輯和 setState((){}) 在所有的交互點(diǎn)都寫一次。

          ? 全局狀態(tài)管理

          場景:點(diǎn)擊頭像跳轉(zhuǎn)登陸頁,當(dāng)賬號(hào)密碼不為 null 時(shí),登陸按鈕才可以點(diǎn)擊。點(diǎn)擊登陸按鈕發(fā)送登陸請求,登陸成功后,返回上一頁,刷新用戶信息。


          ? 1. 用戶信息Widget注冊接收器

          class Avatar extends StatefulWidget {  @override  _AvatarState createState() => _AvatarState();}
          class _AvatarState extends State {
          User user; int msgCount = 0; @override void initState() { super.initState(); FBroadcast.instance().register( Key_MsgCount, /// register Key_MsgCount reviver (value, callback) => setState(() { msgCount = value; }), more: { /// register Key_User reviver Key_User: (value, callback) => setState(() { /// get value user = value; }), }, /// bind context context: this, ); }
          @override Widget build(BuildContext context) { return FSuper( ... backgroundImage: (user == null || _textIsEmpty(user.avatar)) ? null : AssetImage(user.avatar), redPoint: user != null && msgCount > 0, redPointText: msgCount.toString(), text: user != null ? null : "Click Login", onClick: user != null ? null : () => Navigator.push(context, MaterialPageRoute( builder: (context) => LoginPage())), ); }
          @override void dispose() { super.dispose(); /// remove all receivers from the environment FBroadcast.instance().unregister(this); }}

          登陸頁中注冊 Key_User 接收器,當(dāng)接收到登陸消息時(shí),取出其中的數(shù)據(jù),刷新UI。

          ? 2. 構(gòu)建數(shù)據(jù)模型

          class User{  String name;  String avatar;  int messageCount = 0;  String info;}

          ? 3. 構(gòu)建邏輯處理對象

          class LoginHandler {  String _userName;  String _password;
          /// set user name, check to see if login is allowed set userName(String v) { _userName = v; if (_textNoEmpty(_userName) && _textNoEmpty(_password)) { FBroadcast.instance().broadcast(Key_Login, value: true); } else { FBroadcast.instance().broadcast(Key_Login, value: false); } }
          /// set user password, check to see if login is allowed set password(String v) { _password = v; if (_textNoEmpty(_userName) && _textNoEmpty(_password)) { FBroadcast.instance().broadcast(Key_Login, value: true); } else { FBroadcast.instance().broadcast(Key_Login, value: false); } }
          /// login void login() { Timer(Duration(milliseconds: 1500), () { /// login success,send login success message —— Key_User FBroadcast.instance().broadcast( Key_User, value: User() ..avatar = "assets/logo.png" ..name = _userName ..info = "Seriously provide exquisite widget to help you build exquisite application.", /// Persistence Key_User persistence: true, ); }); }}

          將邏輯處理轉(zhuǎn)移到 LoginHandler 中進(jìn)行隔離,所有的處理結(jié)果都通過 FBroadcast 廣播出去,使注冊到廣播系統(tǒng)中的對應(yīng)接收器能夠響應(yīng)。

          ? 4. 登陸頁

          class LoginPage extends StatefulWidget {  @override  _LoginPageState createState() => _LoginPageState();}
          class _LoginPageState extends State { /// Logic handler LoginHandler handler = LoginHandler(); /// input controller FSearchController _controller1 = FSearchController(); FSearchController _controller2 = FSearchController();
          @override void initState() { super.initState(); _controller1.setListener(() { /// update userName handler.userName = _controller1.text; }); _controller2.setListener(() { /// update password handler.password = _controller2.text; }); }
          @override Widget build(BuildContext context) { return { ... /// userName input FSearch( controller: _controller1, ... ), ... /// userName input FSearch( controller: _controller2, ... ), ... Stateful( initState: (setState, data) { /// register login receiver FBroadcast.instance().register( Key_Login, /// refresh ui (value, callback) => setState(() {}), more: { /// register user receiver Key_User: (value, callback) { FLoading.hide(); Navigator.pop(context); }, }, /// bind context context: data, ); }, builder: (context, setState, data) { return FButton( ... text: "LOGIN", /// Key_Login value=true is allowed to click login onPressed: !(FBroadcast.value(Key_Login) ?? false) ? null : () { _controller1.clearFocus(); _controller2.clearFocus(); FLoading.show(context); /// Execute login logic handler.login(); }, ); },), ... }; }}

          注冊接收器時(shí),只需在接收回調(diào)中調(diào)用 setState((){}),后續(xù)所有的數(shù)據(jù)變化刷新,開發(fā)者就可以不用關(guān)注了。而給 UI 賦值可以方便的通過 FBroadcast.value() 獲取對應(yīng)數(shù)據(jù)來進(jìn)行。

          ??注意,對于需要全局使用的狀態(tài)/數(shù)據(jù)模型,它們對應(yīng)的廣播類型,在發(fā)送時(shí),需要至少有一次將 persistence 設(shè)置為 true。上面示例中,就在登陸成功后,對 Key_User 類型的廣播進(jìn)行了持久化。

          /// login success,send login success message —— Key_UserFBroadcast.instance().broadcast(  Key_User,  value: User()    ..avatar = "assets/logo.png"    ..name = _userName    ..info =        "Seriously provide exquisite widget to help you build exquisite application.",  /// Persistence Key_User  persistence: true,);

          上面的示例中展示了通過 FBroadcast 可以輕松快速的實(shí)現(xiàn)消息傳遞,進(jìn)行 局部、全局狀態(tài)管理和刷新,很好的將各個(gè)模塊,邏輯以及UI 進(jìn)行解耦。FBroadcast 提供了簡潔易懂,而且十分靈活的廣播系統(tǒng),極少的束縛讓開發(fā)者可以快速上手,輕松實(shí)現(xiàn)復(fù)雜邏輯的簡化,幫助開發(fā)者構(gòu)建出易于維護(hù)的、復(fù)雜的、精美的應(yīng)用。

          FBroadcast 在使用過程中,配合統(tǒng)一的廣播類型注冊表(也可以按模塊分多張),開發(fā)者可以很輕易的借助 IDEA 的引用檢索能力,隨時(shí)查看所有廣播的情況,對于不斷迭代過程中的維護(hù)十分有益。

          想要了解更多詳細(xì)內(nèi)容?請?jiān)L問 FBroadcast 官方主頁 (PS:別忘了投出一個(gè)你認(rèn)可的 Star 哦 ?)。

          ? 如何使用?

          在項(xiàng)目 pubspec.yaml 文件中添加依賴:

          ? pub 依賴方式

          dependencies:  fbroadcast: ^<版本號(hào)>

          ?? 注意,請到 pub 獲取 FBroadcast 最新版本號(hào)

          ? git 依賴方式

          dependencies:  fbroadcast:    git:      url: '[email protected]:Fliggy-Mobile/fbroadcast.git'      ref: '<分支號(hào) 或 tag>'

          ?? 注意,分支號(hào) 或 tag 請以 FBroadcast 官方項(xiàng)目為準(zhǔn)。

          感覺還不錯(cuò)?請到 《FBroadcast》的 Github 主頁投出您認(rèn)可的一個(gè) Star ? 吧!

          更多精彩組件

          • 《FSuper》- 幫助開發(fā)者快速構(gòu)建精美的復(fù)雜視圖

          • 《FButton》- 為開發(fā)者準(zhǔn)備了諸多美妙的配置項(xiàng)

          • 《FSwitch》- 具有優(yōu)良交互和視效的精美開關(guān)元素

          • 《FRadio》- 一個(gè)適用于幾乎任意單選場景的單選組件

          • 《FFloat》- 滿足你對浮動(dòng)元素的一切想象

          • 《FRefresh》- 輕松構(gòu)建下拉刷新效果

          • 《FDottedLine》- 輝煌的虛線效果

          • 《FSearch》- 一應(yīng)俱全的搜索框組件

          • 《FToast》- 精致靈活的 Flutter 原生 Toast 組件

          • 《FLoading》- 幫助開發(fā)者自由的使用 Loading


          瀏覽 61
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩在线黄色 | 久久久久XXX | 欧美理论网站 | 日日躁狠狠躁夜夜躁A片图片 | 免费操逼网站直接入看泡芙视频 |