<acronym id="s8ci2"><small id="s8ci2"></small></acronym>
<rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
<acronym id="s8ci2"></acronym>
<acronym id="s8ci2"><center id="s8ci2"></center></acronym>
0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

NAPI 類對象導出及其生命周期管理(下)

OpenHarmony開發經驗 ? 來源:OpenHarmony開發經驗 ? 作者:OpenHarmony開發經驗 ? 2023-05-16 10:25 ? 次閱讀

往期回顧:NAPI 類對象導出及其生命周期管理(上)

4. 樣例工程源碼剖析

  • 工程的模板是Native C++,模型是Stage。

  • 源碼剖析主要圍繞以下幾個文件

    image.png

4.1. NAPI導出對象和生命周期管理具體實現

4.1.1. 定義NapiTest類及方法

  • Napi.h文件內容如下:
#ifndef __NAPI_TEST_H__
#define __NAPI_TEST_H__

#include "napi/native_api.h"
#include 

#include 

#define NAPI_CLASS_NAME "NapiTestClass"

class NapiTest {
public:
  NapiTest() : mEnv(nullptr), mRef(nullptr) {
  }
    
  NapiTest(napi_env env) : mEnv(env), mRef(nullptr){
  }
  ~NapiTest();
    
    // 創建NapiTest類的實體,并將實體返回到應用端,該方法為js創建一個類實體,因此需要將該接口對外導出
  static napi_value Create(napi_env env, napi_callback_info info);
    
    // 初始化js類并設置對應屬性并將其導出   
  static napi_value Init(napi_env env, napi_value exports);         


private:
    
    // 設置數據,此方法給到js直接調用,因此需要將該接口對外導出
	static napi_value SetMsg(napi_env env, napi_callback_info info);
    
    // 獲取數據,此方法給到js直接調用,因此需要將該接口對外導出    
    static napi_value GetMsg(napi_env env, napi_callback_info info);
    
    // 定義js結構體時實際的構建函數
    static napi_value Constructor(napi_env env, napi_callback_info info);     

    // 釋放資源的函數(類似類的析構函數)    
    static void Destructor(napi_env env, void *nativeObject, void *finalize); 

    // 生命周期變量    
    static napi_ref sConstructor_;  

    // 設置和獲取數據的變量    
    static std::string _msg;        

    // 記錄環境變量    
    napi_env mEnv = nullptr;        

    // 記錄生命周期變量    
    napi_ref mRef = nullptr;        

};

#endif  /* __NAPI_TEST_H__ */

4.1.1.1 napi_value

  • Node.js Node-API的值用napi_value類型表示。

    OpenHarmony NAPI將ECMAScript標準中定義的Boolean、Null、Undefined、Number、BigInt、String、Symbol和Object八種數據類型,以及函數對應的Function類型,統一封裝成napi_value類型,下文中表述為JS類型,用于接收ArkUI應用傳遞過來的數據及返回數據給ArkUI應用。

  • 這是一個不透明的指針,用于表示JavaScript值。

4.1.1.2 napi_ref

  • 這是用來引用napi_value的抽象。這允許用戶管理JavaScript值的生命周期,包括顯式地定義它們的最小生命周期。

    https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_napi_ref

4.1.1.3 napi_env

  • napi_env用于表示上下文,底層的Node-API實現可以使用該上下文持久保持VM-specific的狀態。

    https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_napi_env

4.1.2 將NapiTest類定義為js類

4.1.2.1在定義js類之前,需要先設置js類對外導出的方法

// 在定義js類之前,需要先設置類對外導出的方法
    napi_property_descriptor desc[] = {
        { "getMsg", nullptr, NapiTest::GetMsg, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "setMsg", nullptr, NapiTest::SetMsg, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "create", nullptr, NapiTest::Create, nullptr, nullptr, nullptr, napi_default, nullptr }
    };
4.1.2.1.1 napi_property_descriptor

參考 https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_napi_property_descriptor

Node.js Node-API有一組API來獲取和設置JavaScript對象的屬性。在JavaScript中,屬性被表示為一個鍵和一個值的元組?;旧?,Node-API中的所有屬性鍵都可以用以下形式中的任一一種表示:

  • Named:一個簡單的UTF-8編碼的字符串
  • Integer-Indexed:索引值,由uint32_t表示
  • JavaScript value:在Node-API中通過napi_value表示。它可以是一個napi_value,表示字符串、數字或符號。
typedef struct {
  // utf8name和name其中一個必須是NULL
  const char* utf8name;
  napi_value name;

  napi_callback method;
  napi_callback getter;
  napi_callback setter;
  napi_value value;

  napi_property_attributes attributes;
  void* data;
} napi_property_descriptor;

參數解析:

  • utf8name:在定義js類之前設置的js類對外導出的方法名字,編碼為UTF8。必須為該屬性提供utf8name或name中的一個。(utf8name和name其中一個必須是NULL)
  • name:可選的napi_value,指向一個JavaScript字符串或符號,用作屬性的鍵。必須為該屬性提供utf8name或name中的一個。
  • method:將屬性描述符對象的value屬性設置為method表示的JavaScript函數。如果傳入這個參數,將value、getter和setter設置為NULL(因為這些成員不會被使用)。
  • attributes:與特定屬性相關聯的屬性。
  • data:調用函數時傳遞給method、getter和setter的callback data。

4.1.2.2 定義與C++類相對應的JavaScript類

napi_value constructor = nullptr;

    // 定義與C++類相對應的JavaScript類
    if (napi_define_class(env, NAPI_CLASS_NAME, NAPI_AUTO_LENGTH, Constructor, nullptr, sizeof(desc) / sizeof(desc[0]),
                          desc, &constructor) != napi_ok) {
        // "!="用來檢查兩個操作數的值是否相等,如果不相等則條件為真
        return nullptr;
    }
4.1.2.2.1 napi_define_class

napi_define_class函數說明:

https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_napi_define_class

napi_status napi_define_class(napi_env env,
                          const char* utf8name,
                          size_t length,
                          napi_callback constructor,
                          void* data,
                          size_t property_count,
                          const napi_property_descriptor* properties,
                          napi_value* result);

功能:定義與C ++ 類相對應的JavaScript類。

參數說明:

  • [in] env: 調用api的環境

  • [in] utf8name: C ++ 類的名稱

  • [in] length: C ++ 類的名稱的長度,默認自動長度使用NAPI_AUTO_LENGTH

  • [in] constructor: 處理C ++ 類實例構造的回調函數 (因為Constructor函數被napi_define_class調用了)。在導出C ++ 類對象時,這個函數必須是帶有napi_callback簽名(Constructor函數有napi_callback簽名是指要滿足typedef napi_value (*napi_callback)(napi_env, napi_callback_info);的形式)的靜態成員。不能使用c ++ 的類構造函數。

  • [in] data: 作為回調信息的數據屬性傳遞給構造函數回調的可選數據

  • [in] property_count: 屬性數組中參數的個數

  • [in] properties: 屬性數組,具體看代碼中napi_property_descriptor部分

  • [out] result: 通過類構造函數綁定類實例的napi_value對象

    返回:如果API調用成功返回napi_ok。

JS構造函數

如果一個js函數被使用new操作符來調用了,那么這個函數就稱之為js構造函數

C++類回調函數

我們調用別人的API叫call,調用的第三方API調用我們的函數叫回調(callback)

image.png

4.1.2.3 實現js類的構造函數

當ArkTS應用在js端通過new方法獲取類對象的時候,此時會調用 napi_define_class 中設置的 constructor 回調函數,該函數實現方法如下:

napi_value NapiTest::Constructor(napi_env env, napi_callback_info info)
{
    napi_value undefineVar = nullptr, thisVar = nullptr;
    napi_get_undefined(env, &undefineVar);

    // 獲取傳入的參數對象,對象不為空,根據該參數創建實例并并綁定到該對象
    if (napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr) == napi_ok && thisVar != nullptr) {

        // 創建NapiTest 實例
        NapiTest *reference = new NapiTest(env);

        // 綁定實例到對象并獲取對象的生命周期
        if (napi_wrap(env, thisVar, reinterpret_cast<void *>(reference), NapiTest::Destructor, nullptr, &(reference->mRef)) == napi_ok) {
            return thisVar;
        }

        return thisVar;
    }
    
    return undefineVar;
}

void NapiTest::Destructor(napi_env env, void *nativeObject, void *finalize)
{
    // 釋放資源
    NapiTest *test = reinterpret_cast
  • NapiTest::Destructo方法是用來釋放創建的對象:
void NapiTest::Destructor(napi_env env, void *nativeObject, void *finalize)
{
    // 類析構函數,釋放資源
    NapiTest *test = reinterpret_cast
4.1.2.3.1 napi_wrap
napi_status napi_wrap(napi_env env,
                  napi_value js_object,
                  void* native_object,
                  napi_finalize finalize_cb,
                  void* finalize_hint,
                  napi_ref* result);

功能:將C++類實例綁定到js對象,并關聯對應的生命周期

參數說明:

  • [in] env: 調用api的環境
  • [in] js_object: 綁定native_object的js對象
  • [in] native_object: C++類實例對象
  • [in] finalize_cb: 釋放實例對象的回調函數
  • [in] finalize_hint: 傳遞給回調函數的數據
  • [out] result: 綁定js對象的引用

返回:調用成功返回0,失敗返回其他

4.1.2.3.2 napi_get_cb_info

NAPI提供了napi_get_cb_info()方法可從napi_callback_info中獲取參數列表、this及其他數據。這個方法在constructor回調函數中使用,從給定的回調信息中檢索有關調用的詳細信息,如參數和This指針。

napi_status napi_get_cb_info(napi_env env,              
                             napi_callback_info cbinfo, 
                             size_t* argc,                          
                             napi_value* argv,     
                             napi_value* this_arg, 
                             void** data)

參數說明:

  • [in] env: 傳入接口調用者的環境,包含js引擎等,由框架提供,默認情況下直接傳入即可
  • [in] cbinfo: napi_callback_info對象,上下文的信息
  • [in-out] argc: argv數組的長度。若napi_callback_info中實際包含的參數的個數大于請求的數量argc,將只復制argc的值所指定數量的參數只argv中。若實際的參數個數小于請求的數量,將復制全部的參數,數組多余的空間用空值填充,并將參數實際長度寫入argc。
  • [out] argv: 用于接收參數列表
  • [out] this_arg: 用于接收this對象
  • [out] data: NAPI的上下文數據 返回值:返回napi_ok表示轉換成功,其他值失敗。下面的返回napi_status方法一樣。

4.1.3 導出js類

// 創建生命周期,初始引用計數設為1
    if (napi_create_reference(env, constructor, 1, &sConstructor_) != napi_ok) {
        return nullptr;
    }

    // 設置NapiTest對象相關屬性并綁定到導出變量exports
    if (napi_set_named_property(env, exports, NAPI_CLASS_NAME, constructor) != napi_ok) {
        return nullptr;
    }

4.1.3.1 在設置js類導出前,需要先創建生命周期

if (napi_create_reference(env, constructor , 1, &sConstructor_) != napi_ok) {
    return nullptr;
}
  • constructor 定義js類時返回的代表類的構造函數的數據
  • sConstructor_ 生命周期變量
4.1.3.1.1 napi_create_reference

napi_create_reference為對象創建一個reference,以延長其生命周期。調用者需要自己管理reference生命周期。

napi_create_reference函數說明:

NAPI_EXTERN napi_status napi_create_reference(napi_env env,
                                              napi_value value,
                                              uint32_t initial_refcount,
                                              napi_ref* result);

功能:通過引用對象創建新的生命周期引用對象

  • [in] env: 調用 API 的環境

  • [in] value: napi_value表示我們要引用的對象

  • [in] initial_refcount: 生命周期變量的初始引用計數

  • [out] result: 新建的生命周期引用對象

    返回 napi_ok 這個API就是成功的.

4.1.3.2 將生命周期變量作為導出對象的傳入屬性,并將js類導出到exports中

//  設置constructor對象相關屬性并綁定到導出變量exports
if (napi_set_named_property(env, exports, NAPI_CLASS_NAME, constructor) !=  napi_ok) {
    return nullptr;
}
4.1.3.2.1 napi_set_named_property

為給定對象的屬性設置一個名稱。

napi_status napi_set_named_property(napi_env env,
                                    napi_value object,
                                    const char* utf8Name,
                                    napi_value value);
  • [in] env: 調用API的環境

  • [in] object: NapiTest對象相關屬性要綁定的屬性值

  • [in] utf8Name: js類的名稱

  • [in] value: 要引用的對象

    返回 napi_ok 則這個API是成功的

4.1.3.3 設置導出對象的屬性

hello.cpp中

napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
4.1.3.3.1 napi_define_properties

https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_napi_define_properties

napi_status napi_define_properties(napi_env env,
                                   napi_value object,
                                   size_t property_count,
                                   const napi_property_descriptor* properties);

作用:批量的向給定Object中定義屬性

  • [in] env: 調用api的環境
  • [in] object: js對象相關屬性的導出變量
  • [in] property_count: 屬性數組中的元素數
  • [in] properties: 屬性數組

4.1.4 創建類的實例對象

  • ArkTS應用除了調用new方法獲取類的實例外,我們也可以提供一些方法讓ArkTS應用獲取對應的類的實例,如在我們的NapiTest類中,定義了一個Create方法,該方法實現了NapiTest類實例的獲取。具體實現如下:
napi_value NapiTest::Create(napi_env env, napi_callback_info info) {
    napi_status status;
    napi_value constructor = nullptr, result = nullptr;
    // 獲取生命周期變量
    status = napi_get_reference_value(env, sConstructor_, &constructor);

    // 創建生命周期內的實例對象并將其返回
    status = napi_new_instance(env, constructor, 0, nullptr, &result);
    auto napiTest = new NapiTest();
    // 綁定實例類創建NapiTest到導出的對象result
    if (napi_wrap(env, result, reinterpret_cast<void *>(napiTest), Destructor,
    	nullptr, &(napiTest->mRef)) == napi_ok) {
        return result;
    }
    
    return nullptr;
}
  • 在napi接口的注冊中將該方法以接口的方式導出,應用層就可以直接調用該接口并獲取到該類的實例對。

    特別說明:如果單獨實現了一個類實例獲取的方法,那么js的類構造函數可以不實現(也就是定義js結構體時實際的構建函數Constructor及釋放資源的函數Destructor的代碼夠可以不寫)

4.1.4.1 napi_get_reference_value

https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_napi_get_reference_value

函數說明:

NAPI_EXTERN napi_status napi_get_reference_value(napi_env env,
                                                 napi_ref ref,
                                                 napi_value* result);
  • 作用:獲取與reference相關聯的js對象
  • [in] env: 調用API的環境
  • [in] ref: 生命周期管理的變量
  • [out] result: 對象引用的reference.

4.1.4.2 napi_new_instance

https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_napi_new_instance

napi_status napi_new_instance(napi_env env,
                              napi_value cons,
                              size_t argc,
                              napi_value* argv,
                              napi_value* result)
  • 作用:通過給定的構造函數,構建一個對象
  • [in] env: 調用API的環境
  • [in] cons: napi_value表示要作為構造函數調用的 JavaScript 函數
  • [in] argc: argv 數組中的元素計數
  • [in] argv: JavaScript 值數組,表示構造函數的參數napi_value。
  • [out] result: napi_value表示返回的 JavaScript 對象

4.2 index.d.ts聲明文件編寫

使用NAPI框架代碼生成工具,可以根據.h生成.d.ts

https://gitee.com/openharmony/napi_generator/blob/master/docs/INSTRUCTION_ZH.md

export const create : () => NapiTest;
export class  NapiTest {
    setMsg(msg: string): void;
    getMsg(): string;
}

也可以寫成

export class  NapiTest {
    create();
    setMsg(msg: string): void;
    getMsg(): string;
}

4.3 CMakeLists.txt文件

# the minimum version of CMake.
cmake_minimum_required(VERSION 3.4.1)
project(ObjectWrapTest)

set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})

# 頭文件路徑
include_directories(${NATIVERENDER_ROOT_PATH}
                    ${NATIVERENDER_ROOT_PATH}/include)
# 動態庫源文件
add_library(entry SHARED hello.cpp NapiTest.cpp)
# 依賴libace_napi.z.so動態庫
target_link_libraries(entry PUBLIC libace_napi.z.so )

4.4 index.ets文件

// 讓IDE不檢查文件語法
// @ts-nocheck 
import testNapi from "libentry.so";

@Entry
@Component

struct Index {
  @State message: string = '導出對象'
  @State nativePointer:number = 0

// 創建對象tt
  tt = testNapi.create();

  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
          .onClick(() => {
            console.info("[NapiTest] Test NAPI 2 + 3 = " + testNapi.add(2, 3));
            try{
              if (this.nativePointer == 0) {
                // log打印,在程序中添加 log
                console.info("[NapiTest] Test NAPI add(2, 3) 1");
                this.nativePointer = testNapi.add(2, 3)
                console.info("[NapiTest] Test NAPI add(2, 3) 2");

                this.tt.setMsg("2+3")
                console.info("[NapiTest] Test NAPI add(2, 3) 3");

              } else {

                console.info("[NapiTest] Test NAPI add(0, 0) 1");

                this.nativePointer = testNapi.add(0, 0)
                console.info("[NapiTest] Test NAPI add(0, 0) 2");

                this.tt.setMsg("4+5")
                console.info("[NapiTest] Test NAPI add(0, 0) 3");
              }
            } catch(e) {
              console.info("[NapiTest]Test NAPI error" + JSON.stringify(e));
            }
            console.info("[NapiTest]Test NAPI " + this.tt.getMsg() + " = " + this.nativePointer);
          })
      }
      .width('100%')
    }
    .height('100%')
  }

}

知識點附送

napi接口名稱

https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/native-lib/third_party_napi/napi.md

NAPI框架api調用值得借鑒的示例工程

https://gitee.com/jiangtao92/node-api-test-case

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 源碼
    +關注

    關注

    8

    文章

    586

    瀏覽量

    28683
  • 類對象
    +關注

    關注

    0

    文章

    3

    瀏覽量

    1434
  • OpenHarmony
    +關注

    關注

    24

    文章

    3442

    瀏覽量

    15291
收藏 人收藏

    評論

    相關推薦

    AutoScaling 生命周期掛鉤功能

    摘要: AutoScaling 伸縮組實例管理功能全面升級,新上線生命周期掛鉤(LifecycleHook)功能,方便用戶更加靈活地管理伸縮組內實例。使用生命周期掛鉤可以在伸縮組發生伸
    發表于 06-27 17:13

    你知道什么是管理應用程序生命周期的高級行為嗎

    第四天-管理你的應用程序的生命周期
    發表于 02-22 09:23

    理解數據生命周期管理思路

    數據生命周期管理的思考
    發表于 03-17 10:49

    HarmonyOS應用開發-PageAbility生命周期

    pageAbility的生命周期如下圖所示:在代碼中通過調用下列方法實現生命周期操作:onShow() :Ability由后臺不可見狀態切換到前臺可見狀態調用onShow方法,此時用戶在屏幕可以看到
    發表于 10-17 11:11

    觸覺智能RK3568使用體驗—NAPI 對象導出及其生命周期管理(上)

    本篇知識點,下半部分將對源碼進行解析。通過本文您將熟悉:NAPI 導出對象NAPI 對象生命周期
    發表于 02-08 17:10

    在S32G2 RM中有“生命周期”,生命周期的完整含義是什么?

    在S32G2 RM中,有“生命周期”。生命周期的完整含義是什么,我們應該如何使用它?
    發表于 04-23 10:37

    貫穿于全生命周期的功能安全

    簡要介紹了功能安全在SIS 全安全生命周期的主要活動,敘述了全生命周期的功能安全管理。簡要闡述了貫穿于整體安全生命周期的功能安全進行的主要階段,同時提出了在設計SIS
    發表于 12-19 15:50 ?15次下載

    貫穿于全生命周期的功能安全

    簡要介紹了功能安全在SIS 全安全生命周期的主要活動,敘述了全生命周期的功能安全管理。簡要闡述了貫穿于整體安全生命周期的功能安全進行的主要階段,同時提出了在設計SIS 時
    發表于 01-06 17:11 ?6次下載

    一文讀懂Android Activity生命周期

    正常情況下Activity的生命周期: Activity的生命周期大概可以歸為三部分 整個的生命周期:onCreate()可以設置所有的“全局”狀態, onDestory()可以釋放所有的資源 可見
    發表于 05-30 01:03 ?1404次閱讀

    Synopsys 啟動硅生命周期管理計劃

    Synopsis 的數據分析驅動的硅生命周期管理計劃解決了 IC 生命周期中的質量、可靠性和安全挑戰。
    發表于 08-18 15:37 ?660次閱讀
    Synopsys 啟動硅<b class='flag-5'>生命周期</b><b class='flag-5'>管理</b>計劃

    Elasticsearch索引生命周期常見的階段

    在 Elastic Stack 6.6 版本后推出了新功能 Index Lifecycle Management(索引生命周期管理),支持針對索引的全生命周期托管管理,并且在 Kiba
    的頭像 發表于 10-13 10:38 ?951次閱讀

    生命周期管理:COTS視角

    全面的生命周期管理策略是保護程序和緩解與長期任務關鍵型系統中部署的 COTS 技術相關的挑戰的關鍵。除了降低風險外,生命周期管理服務還通過確保及時購買和儲存報廢 (EOL) 組件并大大
    的頭像 發表于 11-08 14:18 ?829次閱讀
    <b class='flag-5'>生命周期</b><b class='flag-5'>管理</b>:COTS視角

    Vue入門Vue的生命周期

    .生命周期 4.1生命周期是什么 Vue的生命周期, 就是Vue實例從創建到銷毀的過程.
    的頭像 發表于 02-06 16:16 ?688次閱讀
    Vue入門Vue的<b class='flag-5'>生命周期</b>

    觸覺智能RK3568使用體驗:NAPI對象導出及其生命周期管理(上)

    寫在開頭: OpenHarmony 中的 ?N-API組件定義了由ArkTS (JS/ETS)語言編寫的代碼和 native 代碼(使用 C/C++ 編寫)交互的方式,由 Node.js Node-API 框架擴展而來。 什么是Node.js Node-API 框架 Node.js Node-API為開發者提供了一套C/C++ API用于開發Node.js的Native擴展模塊。從Node.js 8.0.0開始,Node-API以實驗性特性作為Node.js本身的一部分被引入,并且從Node.js 10.0.0開始正式全面支持Node-API。 本文以OpenHarmony 3.2beta3源碼中的node.js三方庫為例,從third_party/node/README.OpenSource中可得知3.2bet
    的頭像 發表于 02-17 09:10 ?642次閱讀

    IBM ELM—系統工程全生命周期管理平臺

    Engineering Lifecycle Management是IBM提供的工程全生命周期管理組合工具,幫助企業降低開發成本,應對開發挑戰并更快地發展其流程和實踐。Engineering
    的頭像 發表于 11-22 18:27 ?842次閱讀
    IBM ELM—系統工程全<b class='flag-5'>生命周期</b><b class='flag-5'>管理</b>平臺
    亚洲欧美日韩精品久久_久久精品AⅤ无码中文_日本中文字幕有码在线播放_亚洲视频高清不卡在线观看
    <acronym id="s8ci2"><small id="s8ci2"></small></acronym>
    <rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
    <acronym id="s8ci2"></acronym>
    <acronym id="s8ci2"><center id="s8ci2"></center></acronym>