精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程

系統 OpenHarmony
端云一體化是為豐富HarmonyOS對云端開發的支持、實現HarmonyOS生態端云聯動,DevEco Studio推出了云開發功能,開發者在創建工程時選擇云開發模板,即可在DevEco Studio內同時完成HarmonyOS應用/服務的端側與云側開發。

想了解更多關于開源的內容,請訪問:

51CTO 開源基礎軟件社區

https://ost.51cto.com

一、基本概念

  • 元服務(原名原子化服務)是一種基于HarmonyOS API的全新服務提供方式,以HarmonyOS萬能卡片等多種呈現形態,向用戶提供更輕量化的服務。具有即用即走、信息外顯、服務直達的特性。
  • 萬能卡片(簡稱卡片)是一種界面展示形式,可以將應用的重要信息或操作前置到卡片,以達到服務直達、減少體驗層級的目的。
  • ArkUI框架是一套構建分布式應用界面的聲明式UI開發框架,其使用極簡的UI信息語法、豐富的UI組件、以及實時界面預覽工具,幫助開發者提升HarmonyOS應用界面開發效率。
  • AppGallery Connect(簡稱AGC)致力于為應用的創意、開發、分發、運營、經營各環節提供一站式服務,構建全場景智慧化的應用生態體驗。
  • 端云一體化是為豐富HarmonyOS對云端開發的支持、實現HarmonyOS生態端云聯動,DevEco Studio推出了云開發功能,開發者在創建工程時選擇云開發模板,即可在DevEco Studio內同時完成HarmonyOS應用/服務的端側與云側開發。

二、主要功能

本次開發的實用小工具主要功能有:

  • 油價查詢,點擊城市列表之后,就可以查看當前城市的油價,包括92,95,98,柴油等。
  • 垃圾分類查詢,輸入物品名稱就可以查看垃圾的詳細分類,而不再害怕垃圾分類出錯。
  • 當前定位查看,我們可以很方便的知道自己當前的位置信息。
  • 提供12,22,24,44卡片。

API版本:API9

應用包名:com.jianguo.utilitybox

三、項目初始化

  • 當前未打開任何工程,單擊DevEco Studio歡迎頁中“Create Project”創建新工程。
  • 當前已打開工程,單擊菜單欄“File > New > Create Project”創建新工程。

首先創建項目,選擇原子化服務,第四個模版端云一體化模版

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

點擊下一步:

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

這個時候我們需要為工程關聯云開發所需的資源,即在DevEco Studio中選擇您的華為開發者賬號加入的開發者團隊,將該團隊在AGC的同包名應用關聯到當前工程。

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

然后我們點擊登陸就好。

這個時候會來到下面的授權頁面,我們點擊允許就可以。

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

單擊“AppGallery Connect”打開AGC應用創建向導,填寫應用信息,單擊“確認”按鈕創建應用。

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

我們點擊箭頭這兒就會跳轉到我們的。

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

agc頁面。這個時候我們選擇創建項目,然后再創建應用。我之前已經有項目了。所以我們直接在項目里創建應用就可以。

比如我這里就選擇。

https://developer.huawei.com/consumer/cn/service/josp/agc/index.html#/myProject?appPackageName=com.jianguo.utilitybox&isAtomic=1。

下面這個就可以。

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

然后就會跳轉到設置位置處理頁面。

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

我們點擊下一步就會到下面的添加應用界面,然后我們只需要填寫應用名稱就可以,其他的都會默認,如果默認失敗的話,我們就設置應用包名:com.jianguo.utilitybox。

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

然后確定。

這個時候就會有恭喜您!

應用創建已完成。

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

我們選擇前往控制臺,然后來到Severless這一塊。把我們需要的認證服務,云函數,云數據庫,云緩存,云存儲,統統打開。

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

我們回到IDE繼續開發,點擊Finsh就可。

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

出現這個,我們ok就可以。

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

然后大家可以看到工程配置完成。

成功創建工程并關聯云開發資源后,DevEco Studio會為工程自動執行一些初始化配置,并開通云開發相關服務:認證服務、云函數、云數據庫、云托管、API網關、云存儲。,所以上面我們在控制臺的操作其實也可以不用操作。

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

端側工程自動集成AGC SDK,包括AGC SDK 配置文件entry/src/main/resources/rawfile/agconnect-services.json 和在entry/oh-package.json5配置文件中引入的AGC相關云服務最新版本SDK。

"dependencies": {
    "@hw-agconnect/crypto-ohos": "^1.0.10",
    "@hw-agconnect/function-ohos": "^1.0.10",
    "@hw-agconnect/auth-ohos": "^1.0.10",
    "@hw-agconnect/cloudstorage-ohos": "^1.0.10",
    "@hw-agconnect/api-ohos": "^1.0.10",
    "@hw-agconnect/base-ohos": "^1.0.10",
    "@hw-agconnect/core-ohos": "^1.0.10",
    "@hw-agconnect/credential-ohos": "^1.0.10",
    "@ohos/agconnect-auth-component": "^1.0.5",
    "long": "5.2.1"
  }

云側工程自動集成云數據庫最新版本Node.js Server SDK。

/Users/jianguo/Desktop/teaching/utilitybox/CloudProgram/cloudfunctions/idgenerator/package.json

"dependencies": {
		"@agconnect/database-server": "^1.0.7"
	}
}

四、端云一體化開發

大家都知道我這次用的是端云一體化開發,那那么我們就有必要來了解一下端云協同的目錄結構

端云一體化開發工程目錄分為三個子工程:如下圖所示:

  • 端開發工程(Application)。
  • 云開發工程(CloudProgram)。
  • 端側公共庫(External Libraries)。

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

1、端開發工程(Application)

端開發工程主要用于開發應用端側的業務代碼,端開發工程目錄結構如下:

端側的目錄和之前大家看到的文件目錄結構都差不多

- Application
    - AppScope
        app.json5    // 應用的全局配置信息
    - entry           // 應用/服務模塊,編譯構建生成一個HAP
        oh_modules   // 用于存放三方庫依賴信息
        - src/main
            - ets    // 用于存放ArkTS源碼
            - resources    // 用于存放應用/服務所用到的資源文件
            module.json5    // Stage模型配置文件
        build-profile.json5    // 當前模塊信息、編譯信息配置項
        hvigorfile.ts          // 模塊級編譯構建任務腳本
        oh-package.json5        // 配置三方包聲明的入口及包名
    build-profile.json5    // 應用配置信息,包括簽名、產品配置等
    hvigorfile.ts          // 應用級編譯構建任務腳本

2、云開發工程(CloudProgram)

云開發工程中開發者可以為應用開發云函數和云數據庫服務資源,云開發工程目錄結構如下:

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

- CloudProgram
    - clouddb    // 云數據庫工程目錄
        dataentry    // 用于存放數據條目文件
        objecttype    // 用于存放對象類型文件
        db-config.json    // 模塊配置文件
    - cloudfunctions    // 云函數工程目錄
        node_modules    // 包含所有三方依賴
        cloud-config.json    // 云開發工程配置文件
        package.json    // 定義了TypeScript公共依賴

五、云函數開發指南

1、創建函數

在云端工程(CloudProgram)中可以創建函數、編寫函數業務代碼、為函數配置調用觸發器。

單擊“cloudfunctions”目錄,選擇“New > Cloud Function”創建云函數。

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

輸入函數名稱,單擊“OK”按鈕DevEco Studio自動生成函數目錄。函數名稱僅支持小寫英文字母、數字、中劃線(-),首字母必須為小寫字母,結尾不能為中劃線(-)。

比如add。

云函數目錄結構。

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

- add
    node_modules    // 自動為該函數引入依賴包
    function-config.json    // 函數的配置文件,可配置觸發器,通過觸發器暴露的觸發條件來實現函數調用。
    package.json            // 包含了當前函數的名稱、版本等函數元數據。
    add.ts    // 函數入口文件

云函數觸發器。

云函數觸發器在function-config.json文件中triggers屬性中配置,當前支持HTTP觸發器、CLOUDDB觸發器、AUTH觸發器、CLOUDSTORAGE觸發器、CRON觸發器五種。

  • HTTP觸發器工程創建完成后默認自動在function-config.json文件生成HTTP觸發器配置。函數部署到云端后會自動生成觸發URL,開發者向URL發起HTTP請求時觸發函數。
{
  "handler": "IdGenerator.myHandler",
  "triggers": [
    {
      "type": "http",
      "properties": {
        "enableUrlDecode": true,//通過HTTP觸發器觸發函數,對于contentType為“application/x-www-form-urlencoded”的觸發請求,是否使用URLDecoder對請求body進行解碼再轉發到函數中。true:啟用。false:不啟用。
        "authFlag": "true",//是否鑒權,默認為true。
        "authAlgor": "HDA-SYSTEM",//鑒權算法,默認為HDA-SYSTEM。
        "authType": "apigw-client",//HTTP觸發器的認證類型。apigw-client:端側網關認證,適用于來自APP客戶端側的函數調用。 cloudgw-client:云側網關認證,適用于來自APP服務器側的函數調用
        "mode": "NO_PATH"
      }
    }
  ]
}

2、開發云函數

云函數的代碼實現基于不同的語言運行環境可分為Node.js、Java、Python,還有一種比較特別運行環境為Custom Runtime(自定義運行環境)。本工程的語言運行環境為Node.js。

云函數的入口方法:

module.exports.myHandler = function(event, context, callback, logger)。

  • myHandler:入口方法名稱。
  • event:調用方傳遞的事件對象,JSON格式。
  • context:函數運行時上下文對象,封裝了日志接口、回調接口、環境變量env對象等。
  • callback:事件處理結果。
  • logger:記錄日志。開發者在代碼中使用logger接口記錄日志,當前支持四種級別。
  • logger.debug()
  • logger.error()
  • logger.warn()
  • logger.info()

函數必須通過顯示調用callback(object)將事件處理結果返回給AGC,結果可以是任意對象,但必須與JSON.stringify兼容,AGC會將結果轉換成JSON字符串,返回給調用方。callback執行完成后,函數即執行結束。

為云函數添加返回內容。

let myHandler = async function (event, context, callback, logger) {
  logger.info(event);

  // do something here

  callback({
    code: 0,
    desc: "Success."
  });
};

export { myHandler };

調試云函數 函數開發過程中,開發者可在本地進行調試,或者將函數部署到AGC云端后,在本地觸發調用云端函數。當前本地調試支持Run和Debug兩種模式,Debug模式支持使用斷點來追蹤函數的運行情況。

  • 本地云函數調試,單擊"cloudfunctions > Run/Debug Cloud Function"運行/調試云函數。

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

查看Run面板,若出現“Cloud Functions loaded successfully”,標識云函數啟動成功(云函數啟動/調試將部署cloudfunctions中所有的云函數),并生成對應的POST URL。

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

在菜單欄選擇“Tools > CloudDev > Cloud Functions Requestor”觸發云函數調用。

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

在彈出的云函數調用界面填寫觸發事件參數。

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

  • Environment:選擇函數調用環境,Local表示本地調用,Remote表示遠程調用(需要先將函數部署到AGC云端)。
  • Cloud Function:選擇需要觸發的云函數。
  • Event:輸入事件參數,內容為JSON格式請求體數據。

單擊Trigger按鈕,觸發執行云函數,執行結果展示在Result框內,Run面板同時打印運行日志。

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

部署云函數 完成函數代碼開發后,開發者可將函數部署到AGC控制臺,支持單個部署和批量部署。

  • 右鍵單擊需要部署的函數目錄,選擇“Deploy Cloud Functions”。
  • 底部狀態欄右側將顯示函數打包與部署進度,直至出現“Deploy successfully”消息表示函數部署成功。

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

登錄AGC控制臺,進入當前項目的云函數服務菜單,可查看開發者部署的函數。

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

遠程函數測試 在“Cloud Functions Reuestor”面板中,更改Environment為Remote遠程調用,單擊“Trigger”按鈕,在Result中顯示返回結果。

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

當開發者創建的函數或函數別名中創建一個HTTP類型的觸發器后,在應用客戶端調用函數時需要傳入HTTP觸發器的標識,查詢方法如下: 在函數的觸發器頁面點擊“HTTPTrigger”觸發器,查看“觸發URL”的后綴,獲取觸發器標識,格式為“函數名-版本號”。

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

3、調用云函數

應用集成云函數SDK后,可以在應用內直接通過SDK API調用AGC中的云函數,云函數SDK與AGC的函數調用基于HTTPS的安全訪問。

  • 在端側應用(Application)中“entry > src/main/ets > services”目錄創建SudokuAlgorithmFunction.ts文件,編寫調用云函數方法。
Button('請求自定義云函數')
  .fontSize(16)
  .onClick(() => {
    getSudokuPuzzle(getContext(this)).then((ret) => {
      Log.info(Constants.LOG_TAG_NAME, `單擊按鈕調用云函數返回結果: ${JSON.stringify(ret)}`)
    })
  })

然后我們簽名,在真機中。

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

六、華為認證服務

當前AGC認證服務為HarmonyOS應用/服務提供的登錄認證方式有手機、郵箱和關聯賬號三種方式。本工程使用“郵箱+驗證碼”的方式作為應用的登錄入口。我們這次使用的是手機號碼登錄。

需要在AGC控制臺開通認證服務(工程創建時默認開通),并在“認證方式”頁簽中啟用“郵箱地址”。

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

調用PhoneUserBuilder生成PhoneUser,然后調用PhoneAuthProvider.credentialWithVerifyCode注冊用戶。注冊成功后,系統會自動登錄,無需再次調用登錄接口。也可以使用signIn登錄接口,通過第三方認證來登錄AGConnect平臺,在entry/src/main/ets/services/Auth.ts認證工具類中添加郵箱賬號注冊用戶方法。

public async login(countryCode: string, phoneNumber: string, verifyCode: string): Promise<AgUser> {
        return new Promise((resolve, reject) => {
            const credential = PhoneAuthProvider.credentialWithVerifyCode(countryCode, phoneNumber, verifyCode);
            this.agc.auth().signIn(credential).then(async (ret) => {
                Log.info(TAG, "User has signed in..");
                // @ts-ignore
                let user = ret.getUser();
                let userExtra = await ret.getUser().getUserExtra();

                let loginRes = new AgUser(
                user.getUid(),
                user.getPhotoUrl(),
                user.getPhone(),
                user.getDisplayName(),
                userExtra.getCreateTime(),
                userExtra.getLastSignInTime())

                resolve(loginRes);
            }).catch((error) => {
                Log.error(TAG, "Error: ", error);
                reject(error);
            });
        });
    }

1、構建手機登錄頁面

通過容器組件Flex、Row、Column以及基礎組件Text、Image、Button、Navigation、TextInput,構建手機驗證碼登錄頁面。

Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
      Text($r('app.string.auth_dialog_title'))
        .fontSize($r('app.float.navigation_font_size'))
        .margin({ bottom: Constants.LENGTH_20_PX })

      Flex({ direction: FlexDirection.Row }) {

        Select(mockData)
          .font({ size: $r('app.float.body_font_size') })
          .selectedOptionFont({ size: $r('app.float.body_font_size') })
          .optionFont({ size: $r('app.float.body_font_size') })
          .value(this.countryCode)
          .layoutWeight(Constants.LENGTH_1_PX)
          .backgroundColor($r('app.color.placeholder_background'))
          .borderRadius(Constants.BORDER_RADIUS_4_PX)
          .height(Constants.HEIGHT_40)
          .onSelect((_, val) => {
            this.countryCode = val.substring(0, val.length - 4);
          })


        TextInput({ placeholder: $r('app.string.auth_dialog_number_placeholder') })
          .layoutWeight(Constants.LENGTH_3_PX)
          .margin({ left: Constants.LENGTH_5_PX })
          .borderRadius(Constants.BORDER_RADIUS_4_PX)
          .maxLength(Constants.LENGTH_20_PX)
          .height(Constants.HEIGHT_40)
          .enabled(this.timer === 60)
          .onChange((val) => {
            this.phoneNumber = val;
          })

      }
      .margin({ bottom: Constants.LENGTH_20_PX })

      Flex({ direction: FlexDirection.Row }) {
        TextInput({ placeholder: $r('app.string.auth_dialog_code_placeholder'), text: this.verificationCode })
          .layoutWeight(Constants.LENGTH_3_PX)
          .margin({ right: Constants.LENGTH_5_PX })
          .borderRadius(Constants.BORDER_RADIUS_4_PX)
          .maxLength(Constants.LENGTH_6_PX)
          .height(Constants.HEIGHT_40)
          .onChange((val) => {
            this.verificationCode = val;
          })

        Button(this.timer === 60 ? $r('app.string.auth_dialog_get_code_button_text') : this.timer.toString(), {
          type: ButtonType.Normal
        })
          .backgroundColor($r('app.color.start_window_background'))
          .layoutWeight(Constants.LENGTH_2_PX)
          .borderColor($r('app.color.action_button_background'))
          .borderWidth(Constants.LENGTH_1_PX)
          .fontColor($r('app.color.action_button_background'))
          .borderRadius(Constants.BORDER_RADIUS_4_PX)
          .margin({ left: Constants.LENGTH_5_PX })
          .height(Constants.HEIGHT_40)
          .enabled(this.canGetCode() && this.timer === 60)
          .onClick(() => this.onGetCodeButtonClicked())
      }
      .margin({ bottom: Constants.LENGTH_20_PX })


      Button($r('app.string.auth_dialog_auth_button_text'), { type: ButtonType.Normal })
        .width(Constants.PERCENT_100)
        .borderRadius(Constants.BORDER_RADIUS_4_PX)
        .backgroundColor($r('app.color.action_button_background'))
        .enabled(this.canAuthorize() && this.verificationCode.length > 5 && this.canLogin)
        .opacity(this.canLogin ? 1 : 0.5)
        .height(Constants.HEIGHT_40)
        .onClick(() => this.onAuthButtonClicked())

    }
    .height(Constants.PERCENT_50)
    .padding({ right: Constants.LENGTH_20_PX, left: Constants.LENGTH_20_PX })
  }

2、用戶登錄成功信息寫入緩存

調用自定義的登錄接口實現登錄,并使用首選項自定義工具接口將用戶信息寫入緩存。,這樣下次進入之后,就可以進行對應的邏輯判斷。執行對應的流程。

onAuthButtonClicked() {
    this.canLogin = false;
    this.agcAuth.login(this.countryCode, this.phoneNumber, this.verificationCode).then(user => {
      AppStorage.Set<AgUser>('user', user);
      PreferencesUtil.putPreference(getContext(this), Constants.USER_AUTH_INFO, JSON.stringify(user));
      Log.info(TAG, "Logged in succesfully.");
      this.canLogin = true;
      router.replaceUrl({
        url: "pages/MainPage"
      })
    }).catch((err) => {
      this.canLogin = true;
      Log.error(TAG, "Logged in failed " + JSON.stringify(err));
      AlertDialog.show({
        title: $r('app.string.common_prompt'),
        message: $r('app.string.common_login_fail'),
      });
    });
  }

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

七、元服務開發

元服務 (原為"原子化服務") 是一種基于HarmonyOS API的全新服務提供方式,以鴻蒙萬能卡片等多種呈現形態, 向用戶提供更輕量化的服務。具有即用即走、信息外顯、服務直達的特性。

萬能卡片(以下簡稱“卡片”)是一種界面展示形式,可以將應用的重要信息或操作前置到卡片,以達到服務直達、減少體驗層級的目的。卡片常用于嵌入到其他應用(當前卡片使用方只支持系統應用,如桌面)中作為其界面顯示的一部分,并支持拉起頁面、發送消息等基礎的交互功能。

下面是我們開發卡片時候的實現原理

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

這里涉及的幾個知識點給大家介紹一下

  • 卡片使用方:顯示卡片內容的宿主應用,控制卡片在宿主中展示的位置。
  • 卡片提供方:提供卡片顯示內容的應用,控制卡片的顯示內容、控件布局以及控件點擊事件。
  • 卡片管理服務:用于管理系統中所添加卡片的常駐代理服務。
  • 卡片渲染服務:用于管理卡片渲染實例,渲染實例與卡片使用方上的卡片組件一一綁定。

ArkTS卡片提供了postCardAction()接口用于卡片內部和提供方應用間交互,當前支持router、message和call三種類型的事件,僅在卡片中可以調用。這個我們在查詢油價,以及處理message跳轉的時候會用到。大家繼續往后看就可以。

1、油價查詢(2*2)

這里我們需要學習的知識點在于卡片的創建,以及卡片數據如何交互。

創建ArkTS卡片有兩種方式:

  • 通過在”entry“目錄右鍵單擊“New > Service Widget”創建卡片。
  • 通過在”entry > src/main/ets > widget > pages“目錄右鍵單擊“New > ArkTS File”創建文件,并在卡片配置文件form_config.json中配置卡片信息。

使用第二種方式創建卡片,在”entry > src/main/ets > widget > pages“目錄右鍵單擊“New > ArkTS File”創建RubbishWidgetCard.ets文件,接著打開"entry > src/main/resources > base > profile"目錄下的form_config.json文件,配置名稱為rubbish的2*2卡片

這樣的話,我們的卡片就算創建完成了,

Column() {
      Row() {
        Image($r("app.media.rubbish")).width(this.Image_WIDTH_PERCENT).margin({
          right: 5
        })
        Text(this.TITLE)
          .fontSize($r('app.float.font_size'))
          .fontWeight(FontWeight.Bold)
          .fontColor($r('app.color.item_title_font'))
      }
    }
    .width(this.FULL_WIDTH_PERCENT)
    .backgroundColor($r("app.color.emuiLow4_alpha"))

    .height(this.FULL_HEIGHT_PERCENT)
    .justifyContent(FlexAlign.Center)
    .onClick(() => {
      postCardAction(this, {
        "action": this.ACTION_TYPE,
        "abilityName": this.ABILITY_NAME,
        "params": {
          "targetPage": this.MESSAGE
        }
      });
    })
  }

但是要實現卡片數據的更新。

ArkTS卡片框架提供了updateForm()接口和requestForm()接口主動觸發卡片的頁面刷新。

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

接口

是否系統能力

約束

updateForm

1. 提供方調用。 2. 提供方僅允許刷新自己的卡片,其他提供方的卡片無法刷新。

requestForm

1. 使用方調用。 2. 僅允許刷新添加到當前使用方的卡片,添加到其他使用方的卡片無法刷新。

卡片中需要使用@LocalStorageProp裝飾器接收。

@LocalStorageProp("puzzles") puzzles: Array<Array<number>> = [];
@LocalStorageProp("answers") answers: Array<Array<Array<number>>> = [];

然后,我們以更新深圳的天氣為例,在/src/main/ets/entryformability/EntryFormAbility.ts寫寫對應的請求事件。

upData(formId :string){
    let formInfo = formBindingData.createFormBindingData({
      'text': '刷新中...'
    })
    formProvider.updateForm(formId, formInfo)
    console.info('Result:的結果' + JSON.stringify(formId));
    let httpRequest = http.createHttp();
    httpRequest.request(Constants.OILSERVER, {
      method: http.RequestMethod.GET,
      readTimeout: Constants.HTTP_READ_TIMEOUT,
      connectTimeout: Constants.HTTP_READ_TIMEOUT
    , extraData: {
        'province': "深圳",
        'app_id': Constants.APPID,
        'app_secret': Constants.APPSECRET

      },
    }, (err, data) => {
      if (!err) {
        // data.result為HTTP響應內容,可根據業務需要進行解析
        console.info('Result:的結果' + JSON.stringify(data.result));
        let OilModel: OilModel = JSON.parse(data.result.toString())
        // 注意:FormExtensionAbility在觸發生命周期回調時被拉起,僅能在后臺存在5秒
        // 建議下載能快速下載完成的小文件,如在5秒內未下載完成,則此次網絡圖片無法刷新至卡片頁面上
        let formData = {
          'oil': OilModel.data,
          'loaded': true,
          "time": getTime()

        };
        let formInfo = formBindingData.createFormBindingData(formData)
        formProvider.updateForm(formId, formInfo).then((data) => {
          console.info('FormAbility updateForm success.' + JSON.stringify(data));
        }).catch((error) => {
          console.error('FormAbility updateForm failed: ' + JSON.stringify(error));
        })
        // 當該請求使用完畢時,調用destroy方法主動銷毀
        httpRequest.destroy();
      } else {
        console.info('error:' + JSON.stringify(err));
        // 取消訂閱HTTP響應頭事件
        httpRequest.off('headersReceive');
        // 當該請求使用完畢時,調用destroy方法主動銷毀
        httpRequest.destroy();
      }
    }

    );
  }

然后我們在onUpdateForm中去調用就可以啦。

onUpdateForm(formId) {
    this.upData(formId)

    // Called to notify the form provider to update a specified form.
  }

這樣我們就完成了對油價的查詢。

這里我們主要了解的就是對卡片事件能力的處理。

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

接口定義:postCardAction(component: Object, action: Object): void

接口參數說明:

參數名

參數類型

必填

參數描述

component

Object

當前自定義組件的實例,通常傳入this。

action

Object

action的具體描述,詳情見下表。

action參數說明:

Key

Value

樣例描述

“action”

string

action的類型,支持三種預定義的類型: - “router”:跳轉到提供方應用的指定UIAbility。 - “message”:自定義消息,觸發后會調用提供方FormExtensionAbility的onFormEvent()生命周期回調。 - “call”:后臺啟動提供方應用。觸發后會拉起提供方應用的指定UIAbility(僅支持launchType為singleton的UIAbility,即啟動模式為單實例的UIAbility),但不會調度到前臺。提供方應用需要具備后臺運行權限(ohos.permission.KEEP_BACKGROUND_RUNNING)。

“bundleName”

string

“router” / “call” 類型時跳轉的包名,可選。

“moduleName”

string

“router” / “call” 類型時跳轉的模塊名,可選。

“abilityName”

string

“router” / “call” 類型時跳轉的UIAbility名,必填。

“params”

Object

當前action攜帶的額外參數,內容使用JSON格式的鍵值對形式。“call” 類型時需填入參數’method’,且類型需要為string類型,用于觸發UIAbility中對應的方法,必填。

(1)通過message事件刷新卡片內容

實現功能:在卡片上實現當前城市的油價查詢,在頁面里實現對各個城市油價的查詢

主要技術,通過message事件刷新卡片內容,在卡片頁面中可以通過postCardAction接口觸發message事件拉起FormExtensionAbility,然后由FormExtensionAbility刷新卡片內容,展示油價信息

.onClick(() => {
            postCardAction(this, {
              'action': 'message',
              'params': {
                'info': 'refreshImage'
              }
            });
          })

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

2、垃圾分類(1*2)

這里實現的功能是點擊卡片之后,進入垃圾分類查詢頁的詳情。輸入物品名稱。就可以查看具體的垃圾分類信息。

(1)使用router事件跳轉到指定UIAbility

.onClick(() => {
  postCardAction(this, {
   'action': 'router',
    'abilityName': 'EntryAbility',
    "params": {
      "targetPage": this.MESSAGE
    }
  });
})

然后我們在src/main/ets/entryability/EntryAbility.ts中處理對應的邏輯。

onCreate(want) {


  console.info("onCreate want:" + JSON.stringify(want));
  if (want.parameters.params !== undefined) {
    let params = JSON.parse(want.parameters.params);
    hilog.info(0x0000, 'tonCreate router targetPage', '%{public}s', params.targetPage)
    console.info("onCreate router targetPage:" + params.targetPage);
    selectPage = params.targetPage;
  }
}

如果UIAbility已在后臺運行,在收到Router事件后會觸發onNewWant生命周期回調。

onNewWant(want, launchParam) {
  console.info("onNewWant want:" + JSON.stringify(want));
  if (want.parameters.params !== undefined) {
    let params = JSON.parse(want.parameters.params);
    hilog.info(0x0000, 'tonCreate router targetPage', '%{public}s', params.targetPage)

    console.info("onNewWant router targetPage:" + params.targetPage);
    console.info("獲取的 params.targetPage是" + params.targetPage);
    if (params.dimension !== undefined && params.dimension != null) {
      AppStorage.Set<string>('message', params.dimension);
      console.info("保存的 params.targetPage是" + params.targetPage);
    }

    selectPage = params.targetPage;


  }
  if (currentWindowStage != null) {
    this.onWindowStageCreate(currentWindowStage);
  }
}

根據傳遞的targetPage不同,選擇拉起不同的頁面。

onWindowStageCreate(windowStage: Window.WindowStage) {
  let targetPage;

  switch (selectPage) {
    case "MainPage":
      targetPage = "pages/MainPage";
      break;
    default:
      targetPage = 'pages/LoginPage';
  }
  if (currentWindowStage === null) {
    currentWindowStage = windowStage;
  }
  windowStage.loadContent(targetPage, (err, data) => {
    if (err && err.code) {
      console.info('Failed to load the content. Cause: %{public}s', JSON.stringify(err));
      return;
    }
  });
}

這樣我們就完成了跳轉。

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

3、當前定位(2*2)

點擊卡片之后,進入定位展示的詳情頁,展示當前的詳細定位。

主要技術,動態權限的獲取。

(1)動態權限獲取

首先需要在module.json中聲明對應權限

"requestPermissions": [
      {
        "name": "ohos.permission.INTERNET"
      },
      {
        "name": "ohos.permission.MEDIA_LOCATION",
      },
      {
        "name": 'ohos.permission.APPROXIMATELY_LOCATION'
      },
      {
        "name": 'ohos.permission.LOCATION'
      }
    ],

然后在ets/entryability/EntryAbility.ts中添加動態權限的申請。

let AtManager = abilityAccessCtrl.createAtManager();
    AtManager.requestPermissionsFromUser(this.context, [  'ohos.permission.APPROXIMATELY_LOCATION', 'ohos.permission.LOCATION']).then((data) => {
      hilog.info(0x0000, 'testTag', '%{public}s', 'request permissions from user success' + data);
    }).catch((err) => {
      hilog.error(0x0000, 'testTag', 'Failed to request permissions from user. Cause: %{public}s', JSON.stringify(err) ?? '');
    });
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');

然后我們編寫卡片代碼。

Column() {
      Text("你想知道")
        .fontSize($r('app.float.title_immersive_font_size'))
        .textOverflow({ overflow: TextOverflow.Ellipsis })
        .fontColor($r('app.color.text_font_color'))
        .maxLines(this.MAX_LINES)
      Text("你當前的位置嗎")
        .fontSize("14fp")
        .textOverflow({ overflow: TextOverflow.Ellipsis })
        .fontColor($r('app.color.text_font_color'))
        .maxLines(this.MAX_LINES)
    }
    .alignItems(HorizontalAlign.Center)
    .justifyContent(FlexAlign.Center)
    .padding($r('app.float.column_padding'))

    .width(this.FULL_WIDTH_PERCENT)
    .height(this.FULL_HEIGHT_PERCENT)
    .backgroundColor($r('app.color.emuiLow2_alpha'))
    .onClick(() => {
      postCardAction(this, {
        "action": this.ACTION_TYPE,
        "abilityName": this.ABILITY_NAME,
        "params": {
          "targetPage": this.MESSAGE
        }
      });
    })
  }

運行之后的效果:

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區 鴻蒙入門開發教程:一文帶你詳解工具箱元服務的開發流程-開源基礎軟件社區

八、總結

本文我們從元服務的基本概念,到我們實用小工具的項目開發,技術剝析,完整的了解了端元一體化開發元服務,以及如何在元服務中集成華為認證服務,到最后完成油價查詢功能在卡片上及時顯示,定位在卡片上實時刷新。以及點擊卡片分類,還可以進入詳情頁查詢更多物品的垃圾屬性,以及動態權限的獲取。

你可以學到的有:

  • 了解元服務的基本概念
  • 使用端云一體化開發、開發云函數、開發云數據庫,集成華為認證服務。
  • 使用FormExtensionAbility創建、更新、刪除元服務卡片。
  • 使用router、message和call三種類型的事件,處理對應邏輯。

也可點擊元服務官網,了解更多信息。

想了解更多關于開源的內容,請訪問:

51CTO 開源基礎軟件社區

https://ost.51cto.com

責任編輯:jianghua 來源: 51CTO 開源基礎軟件社區
相關推薦

2023-08-07 13:29:49

開發鴻蒙

2021-05-29 10:11:00

Kafa數據業務

2015-05-27 14:26:05

2022-05-16 10:49:28

網絡協議數據

2025-04-09 08:25:20

2024-06-27 10:50:01

2011-11-16 13:58:12

Adobe AIR

2024-10-08 08:52:59

2022-05-11 11:54:55

Http傳送協議

2010-01-25 14:04:17

Android Wid

2018-11-13 09:56:52

TomcatServerWeb應用服務器

2022-11-09 09:15:31

ProtoBufGo語言

2020-05-20 14:19:47

滴滴開源工具

2023-11-20 08:18:49

Netty服務器

2022-12-20 07:39:46

2023-12-21 17:11:21

Containerd管理工具命令行

2023-07-31 08:18:50

Docker參數容器

2022-11-11 19:09:13

架構

2023-11-06 08:16:19

APM系統運維

2025-07-04 09:05:35

點贊
收藏

51CTO技術棧公眾號

毛片视频网站在线观看| 99中文字幕在线| 在线免费黄色| 国产激情视频一区二区在线观看 | 国产理论电影在线| 久久综合久久综合亚洲| 成人免费看片视频| wwwwww国产| 日韩一区二区在线免费| 亚洲精品在线三区| 污污网站免费看| h片在线观看视频免费免费| 日本一区免费视频| 精品免费二区三区三区高中清不卡| 亚洲黄网在线观看| 亚洲性视频h| 日韩小视频在线| 成人免费看aa片| 亚洲一区二区免费在线观看| 91福利在线导航| 国产一区二区视频播放| 美女隐私在线观看| 国产亚洲1区2区3区| 成人黄色片视频网站| 中文字幕免费高清在线观看| 国产日本精品| 欧美激情综合色综合啪啪五月| 一级二级黄色片| 久久av超碰| 日韩成人中文字幕在线观看| 师生出轨h灌满了1v1| www 久久久| 欧美日韩亚洲另类| 99久久国产宗和精品1上映| sm在线观看| 一区二区三区在线免费播放| 亚洲在线色站| 日本美女高清在线观看免费| 国产日产欧美一区| 欧美色欧美亚洲另类七区| 手机看片1024国产| 国产91精品一区二区| 91免费观看网站| 伊人亚洲综合网| 蜜桃久久久久久| 国产精品www| 亚洲欧美一二三区| 日韩成人一区二区三区在线观看| 2019亚洲男人天堂| 国产精品乱子伦| 先锋亚洲精品| 欧洲成人在线观看| 少妇高潮av久久久久久| 久久国产欧美| 国产精品va在线播放| 蜜臀99久久精品久久久久小说| 亚洲欧美成人| 国产麻豆视频一区二区| 欧美精品少妇videofree| 色老板免费视频| 亚洲精品小说| 欧美精品日韩www.p站| 黄页网站免费观看| 亚洲三级影院| 国产91成人video| 国产精品suv一区| 日本女优在线视频一区二区| 国产精品久久久久久久久免费看 | 欧美日韩一二三| 亚洲涩涩在线观看| 亚洲va欧美va人人爽成人影院| 欧美大片在线观看| v8888av| 国产欧美一区| 爱福利视频一区| 黄页网站免费观看| 国产精品社区| 国产精品一区二区三区在线播放| 国产精品久久久久久久久毛片 | 99热99热| 日本高清视频免费观看| 久久欧美一区二区| 综合一区中文字幕| 国产精品vvv| 欧美性色黄大片| 宇都宫紫苑在线播放| 国产亚洲精品美女久久| 在线观看日韩av| 欧美日韩精品一区二区三区视频播放 | 久久亚洲精品人成综合网| 日韩亚洲欧美一区| 爱爱的免费视频| 91精品久久久久久久蜜月| 久久久亚洲国产| 探花国产精品一区二区| 成人午夜在线免费| 日日夜夜精品网站| 国产白丝在线观看| 欧洲精品视频在线观看| 国产精品亚洲一区二区无码| 欧美老女人另类| 久久琪琪电影院| 一区二区三区精彩视频| 成人手机电影网| 一区二区三区三区在线| 日韩伦理在线一区| 欧美日韩三级一区二区| 熟女人妻在线视频| 欧美一区成人| 国产精品第1页| 欧美一级片免费| 亚洲摸摸操操av| 美女喷白浆视频| 丝袜久久网站| 欧美激情精品久久久久久大尺度| 最近中文字幕av| 久久日一线二线三线suv| 青青草视频国产| 亚洲三级在线| 在线播放日韩精品| 午夜精品一区二| www.日韩精品| 日本a在线天堂| 激情久久免费视频| 中文字幕日韩在线观看| 男人午夜免费视频| 不卡视频一二三四| 成人一级生活片| 日韩欧美三区| 国产午夜精品久久久| 精品无码久久久久久久久| 久久福利资源站| 日韩午夜视频在线观看| 亚洲最大网站| 亚洲欧美视频在线| 久久一区二区三区视频| 成人h动漫精品一区二区| 黄色网在线视频| 国内不卡的一区二区三区中文字幕| 亚洲色图国产精品| 亚洲综合久久网| 久久久精品2019中文字幕之3| 欧美日韩精品在线一区二区| 最新精品在线| 久久久久久欧美| 成人午夜视频一区二区播放| 亚洲一卡二卡三卡四卡无卡久久| 久久综合在线观看| 女同性一区二区三区人了人一 | 欧美日本啪啪无遮挡网站| 影音先锋黄色网址| 国产精品久久久一本精品| 国产福利在线免费| 婷婷综合在线| 99久久一区三区四区免费| 九色91在线| 亚洲国产女人aaa毛片在线| 久草视频在线观| 久久亚洲二区三区| 亚洲人辣妹窥探嘘嘘| 日本a级不卡| 亚洲xxxxx| caoprom在线| 亚洲精品成人久久电影| 国产三级精品三级在线观看| 国产精品日产欧美久久久久| 午夜剧场在线免费观看| 欧美国产91| 国产综合色一区二区三区| 成人影院入口| x99av成人免费| 午夜精品无码一区二区三区| 五月婷婷综合在线| 丰满的亚洲女人毛茸茸| 韩日精品视频一区| av网站大全免费| 国产精品日韩精品中文字幕| 国产精品影院在线观看| 污污网站在线观看| 精品网站999www| 一卡二卡三卡在线观看| 一区二区三区四区亚洲| 国产精品揄拍100视频| 老司机一区二区| 青青青青草视频| 久久免费精品视频在这里| 亚洲自拍小视频| 午夜日韩成人影院| 欧美另类极品videosbest最新版本| 香蕉视频黄色片| 欧美三级欧美一级| 日本一区二区三区四区五区| 国产精品久久毛片a| 88av在线播放| 九九热在线视频观看这里只有精品| 久久99久久99精品| 成人羞羞视频播放网站| 超碰97在线人人| 精品三级在线| 性欧美xxxx| 国产在线高清理伦片a| 精品性高朝久久久久久久| av中文在线观看| 在线观看视频一区二区| 国产稀缺真实呦乱在线| 亚洲欧洲精品一区二区精品久久久 | 尤物九九久久国产精品的分类| 亚洲av无码国产精品久久不卡| 欧美亚洲自拍偷拍| 欧美精品二区三区| 亚洲人成网站影音先锋播放| 黑人巨大精品欧美| 国产成人精品aa毛片| 日本特黄a级片| 亚洲欧美卡通另类91av| 国产黄色片免费在线观看| 999精品一区| 日韩精品一区二区三区四区五区 | 视频一区不卡| 色吊丝一区二区| 成人免费看片网站| 国产免费av国片精品草莓男男| 国产精品v片在线观看不卡| 精精国产xxxx视频在线播放| 欧美俄罗斯性视频| 黄av在线播放| 自拍视频国产精品| 国产精品二线| 亚洲欧美激情在线视频| 婷婷在线免费观看| 欧美成人午夜电影| 99久久夜色精品国产亚洲| 欧美日韩高清一区二区| 瑟瑟视频在线免费观看| 在线观看日韩毛片| 日日夜夜狠狠操| 欧美视频第一页| 日本高清不卡码| 色欲综合视频天天天| 91video| 欧美日韩一区二区精品| 日韩欧美大片在线观看| 精品国产91久久久久久| 日韩网红少妇无码视频香港| 精品久久久中文| 国产高潮久久久| 日本精品一区二区三区高清| 中文字幕在线观看视频免费| 欧美在线影院一区二区| 国产精品午夜一区二区| 欧美日韩一区二区不卡| 一区二区久久精品66国产精品 | 女人扒开腿免费视频app| 国产在线国偷精品产拍免费yy| 手机在线免费毛片| 成人做爰69片免费看网站| 亚洲人成在线观| 99热这里只有精品5| 日韩精品在线看片z| 内射后入在线观看一区| 亚洲精品一区二区在线| 成人免费视频| 久久综合免费视频| 黄色在线观看www| 国产va免费精品高清在线观看| 成人午夜一级| 亚洲aa中文字幕| 乱中年女人伦av一区二区| 日本亚洲导航| 永久91嫩草亚洲精品人人| 国产毛片久久久久久国产毛片| 国产日韩视频| 五月天av在线播放| 国产福利一区二区三区| 人妻丰满熟妇av无码久久洗澡| 久久精品人人做人人爽人人| 人与动物性xxxx| 亚洲高清免费观看高清完整版在线观看| 日韩免费视频一区二区视频在线观看| 欧美无砖专区一中文字| 国产999久久久| 亚洲美女视频网站| 国产在线二区| 日本精品免费观看| 日韩最新av| 欧美下载看逼逼| 亚洲91中文字幕无线码三区| 蜜桃传媒一区二区三区| 麻豆精品新av中文字幕| 久久性爱视频网站| 国产精品久久久久久久第一福利 | 欧美另类久久久品| 香蕉视频911| 欧美成人免费播放| 在线成人视屏| 国产乱子伦精品| 久久国产影院| 久久精品国产精品亚洲色婷婷| 国模大尺度一区二区三区| 强迫凌虐淫辱の牝奴在线观看| 中文字幕在线视频一区| 黑人一级大毛片| 日韩精品中文字幕在线不卡尤物| 国产三级在线免费| 国内揄拍国内精品| 999精品视频在线观看| 欧美高清一区二区| 伊人精品视频| 午夜一级免费视频| 欧美国产视频在线| 亚洲精品中文字幕乱码三区91| 欧美一级日韩一级| 3p视频在线观看| 日本亚洲欧美成人| 第四色中文综合网| 亚洲色婷婷久久精品av蜜桃| 蜜桃久久精品一区二区| 尤物视频最新网址| 欧美日韩免费在线观看| 亚洲高清视频在线播放| 久久伊人色综合| 亚洲精品aa| 亚洲国产精品日韩| 日韩高清欧美激情| 女人又爽又黄免费女仆| 五月婷婷激情综合| 高h放荡受浪受bl| 欧美激情视频播放| 视频国产精品| 强开小嫩苞一区二区三区网站| 久久国产视频网| 丁香六月激情综合| 欧美调教femdomvk| 91xxx在线观看| 国产精品毛片a∨一区二区三区|国| 免费不卡中文字幕在线| 那种视频在线观看| 久久人人超碰精品| 69亚洲精品久久久蜜桃小说| 亚洲欧美综合v| 小黄鸭精品aⅴ导航网站入口| 欧洲在线视频一区| 久久久亚洲人| 日本猛少妇色xxxxx免费网站| 91久久一区二区| а√天堂中文在线资源bt在线| 国产精品美女午夜av| 999国产精品视频| 91亚洲一区二区| 亚洲自拍与偷拍| 天天干免费视频| 日本不卡高字幕在线2019| 国产欧美高清视频在线| 国内自拍视频网| 中文字幕一区二区三区四区不卡| 一二三区中文字幕| 欧美成人精品在线| 久久电影在线| 国产成人久久777777| 国产精品每日更新在线播放网址| 亚洲天堂网在线观看视频| 久久综合网hezyo| 久久夜色电影| 亚洲色图38p| 亚洲欧美日韩中文字幕一区二区三区| www.99视频| 91豆花精品一区| 欧美一级精品| 色哟哟网站在线观看| 精品国产精品自拍| а天堂8中文最新版在线官网| 91九色对白| 麻豆亚洲精品| 日本午夜在线观看| 亚洲国产精品一区二区久| 91精品店在线| av在线免费观看国产| 国产性做久久久久久| 精品人妻午夜一区二区三区四区 | 国产福利精品一区二区三区| 一区二区三区不卡视频| 日韩在线无毛| 成人亲热视频网站| 国产日韩一区二区三区在线播放 | 欧洲色大大久久| 青青草视频在线免费直播| 欧美午夜视频在线| 国产成人一区二区精品非洲| 美女又爽又黄免费视频| 久久伊人精品视频| 九九亚洲视频| 风韵丰满熟妇啪啪区老熟熟女| 在线一区二区视频| 9999热视频在线观看| 亚洲欧美日韩国产成人综合一二三区| 成人午夜短视频| 国产一区二区自拍视频| 欧美尤物巨大精品爽| 欧美久久99| 日韩av片在线免费观看|