主頁 > 後端開發 > python+appium+yaml移動端自動化測驗框架實作

python+appium+yaml移動端自動化測驗框架實作

2020-11-24 19:26:16 後端開發

結構介紹

之前分享過一篇安卓UI測驗,但是沒有實作資料與代碼分離,后期維護成本較高,所以最近抽空優化了一下,
不想看文章得可以直接去Github,歡迎拍磚
大致結構如下:

結構.png

  • testyaml管理用例,實作資料與代碼分離,一個模塊一個檔案夾

  • public 存放公共檔案,如讀取組態檔、啟動appium服務、讀取Yaml檔案、定義日志格式等

  • page 存放最小測驗用例集,一個模塊一個檔案夾

  • results 存放測驗報告及失敗截圖

    report.png

  • logs 存放日志

    logs.png

    logdetail.png

  • testcase 存放測驗用例
  • runtest.py 運行所有測驗用例

yaml格式介紹

首先看下yaml檔案的格式,之前也寫過一點關于yaml語法學習的文章
testcase部分是重點,其中:

  • element_info:定位元素資訊

  • find_type:屬性,id、xpath、text、ids

  • operate_type: click、sendkeys、back、swipe_up 為back就是回傳,暫時就四種

    上面三個必填,operate_type必填!!!!!!

  • send_content:send_keys 時用到

  • index:ids時用到

  • times: 回傳次數或者上滑次數

testinfo:
    - id: cm001
      title: 新增終端門店
      execute: 1
testcase:
    -
      element_info: 客戶
      find_type: text
      operate_type: click
    -
      element_info: com.fiberhome.waiqin365.client:id/cm_topbar_tv_right
      find_type: id
      operate_type: click
    -
      element_info: com.fiberhome.waiqin365.client:id/custview_id_singletv_inputtext
      find_type: ids
      operate_type: send_keys
      send_content: auto0205
      index: 0
    -
      element_info:
      find_type:
      operate_type: swipe_up
      times: 1
    -
      element_info: 提交
      find_type: text
      operate_type: click
    -
      element_info:
      find_type:
      operate_type: back
      times: 1

代碼部分

公共部分

個人覺得核心的就是公共部分,相當于建房子,公共部分搞好了,后面僅僅是呼叫即可,建房子把架子搭好,后面就添磚加瓦吧,

讀取組態檔readconfig.py
設定日志格式logs.py
獲取設備GetDevices.py
這幾個通用的就不做介紹了

  • 讀取yaml檔案 GetYaml.py
    主要用來讀取yaml檔案
#coding=utf-8
#author='Shichao-Dong'

import sys
reload(sys)
sys.setdefaultencoding('utf8')
import yaml
import codecs

class getyaml:
    def __init__(self,path):
        self.path = path

    def getYaml(self):
        '''
        讀取yaml檔案
        :param path: 檔案路徑
        :return:
        '''
        try:
            f = open(self.path)
            data =yaml.load(f)
            f.close()
            return data
        except Exception:
            print(u"未找到yaml檔案")

    def alldata(self):
        data =self.getYaml()
        return data

    def caselen(self):
        data = self.alldata()
        length = len(data['testcase'])
        return length

    def get_elementinfo(self,i):
        data = self.alldata()
        # print data['testcase'][i]['element_info']
        return data['testcase'][i]['element_info']

    def get_findtype(self,i):
        data = self.alldata()
        # print data['testcase'][i]['find_type']
        return data['testcase'][i]['find_type']

    def get_operate_type(self,i):
        data = self.alldata()
        # print data['testcase'][i]['operate_type']
        return data['testcase'][i]['operate_type']

    def get_index(self,i):
        data = self.alldata()
        if self.get_findtype(i)=='ids':
                    return data['testcase'][i]['index']
        else:
            pass

    def get_send_content(self,i):
        data = self.alldata()
        # print data['testcase'][i]['send_content']
        if self.get_operate_type(i) == 'send_keys':
            return data['testcase'][i]['send_content']
        else:
            pass

    def get_backtimes(self,i):
        data = self.alldata()
        if self.get_operate_type(i)=='back' or self.get_operate_type(i)=='swipe_up':
                    return data['testcase'][i]['times']
        else:
            pass

    def get_title(self):
        data = self.alldata()
        # print data['testinfo'][0]['title']
        return  data['testinfo'][0]['title']

  • 啟動appium服務 StartAppiumServer.py
    主要是啟動appium并回傳埠port,這個port在下面的driver中需要
#coding=utf-8
#author='Shichao-Dong'

from logs import log
import random,time
import platform
import os
from GetDevices import devices

log = log()
dev = devices().get_deviceName()

class Sp:
    def __init__(self, device):
        self.device = device

    def __start_driver(self, aport, bpport):
        """
        :return:
        """
        if platform.system() == 'Windows':
            import subprocess
            subprocess.Popen("appium -p %s -bp %s -U %s" %
                             (aport, bpport, self.device), shell=True)

    def start_appium(self):
        """
        啟動appium
        p:appium port
        bp:bootstrap port
        :return: 回傳appium埠引數
        """
        aport = random.randint(4700, 4900)
        bpport = random.randint(4700, 4900)
        self.__start_driver(aport, bpport)

        log.info(
            'start appium :p %s bp %s device:%s' %
            (aport, bpport, self.device))
        time.sleep(10)
        return aport

    def main(self):
        """
        :return: 啟動appium
        """
        return self.start_appium()

    def stop_appium(self):
        '''
        停止appium
        :return:
        '''
        if platform.system() == 'Windows':
            os.popen("taskkill /f /im node.exe")

if __name__ == '__main__':
    s = Sp(dev)
    s.main()
  • 獲取driver GetDriver.py
    platformName、deviceName、appPackage、appActivity這些卸載組態檔config.ini檔案中,可以直接通過readconfig.py檔案讀取獲得,
    appium_port有StartAppiumServer.py檔案回傳
s = Sp(deviceName)
appium_port = s.main()

def mydriver():
    desired_caps = {
                'platformName':platformName,'deviceName':deviceName, 'platformVersion':platformVersion,
                'appPackage':appPackage,'appActivity':appActivity,
                'unicodeKeyboard':True,'resetKeyboard':True,'noReset':True
                }
    try:
        driver = webdriver.Remote('http://127.0.0.1:%s/wd/hub'%appium_port,desired_caps)
        time.sleep(4)
        log.info('獲取driver成功')
        return driver
    except WebDriverException:
        print 'No driver'

if __name__ == "__main__":
    mydriver()
  • 重新封裝find等命令,BaseOperate.py
    里面主要是一些上滑、回傳、find等一些基礎操作
#coding=utf-8
#author='Shichao-Dong'

from selenium.webdriver.support.ui import WebDriverWait
from logs import log
import os
import time

'''
一些基礎操作:滑動、截圖、點擊頁面元素等
'''

class BaseOperate:
    def __init__(self,driver):
        self.driver = driver

    def back(self):
        '''
        回傳鍵
        :return:
        '''
        os.popen("adb shell input keyevent 4")

    def get_window_size(self):
        '''
        獲取螢屏大小
        :return: windowsize
        '''
        global windowSize
        windowSize = self.driver.get_window_size()
        return windowSize

    def swipe_up(self):
        '''
        向上滑動
        :return:
        '''
        windowsSize = self.get_window_size()
        width = windowsSize.get("width")
        height = windowsSize.get("height")
        self.driver.swipe(width/2, height*3/4, width/2, height/4, 1000)

    def screenshot(self):
        now=time.strftime("%y%m%d-%H-%M-%S")
        PATH = lambda p: os.path.abspath(
            os.path.join(os.path.dirname(__file__), p)
        )
        screenshoot_path = PATH('../results/screenshoot/')
        self.driver.get_screenshot_as_file(screenshoot_path+now+'.png')

    def find_id(self,id):
        '''
        尋找元素
        :return:
        '''
        exsit = self.driver.find_element_by_id(id)
        if exsit :
            return True
        else:
            return False

    def find_name(self,name):
        '''
        判斷頁面是否存在某個元素
        :param name: text
        :return:
        '''
        findname = "//*[@text='%s']"%(name)
        exsit = self.driver.find_element_by_xpath(findname)
        if exsit :
            return True
        else:
            return False

    def get_name(self,name):
        '''
        定位頁面text元素
        :param name:
        :return:
        '''
        # element = driver.find_element_by_name(name)
        # return element

        findname = "//*[@text='%s']"%(name)
        try:
            element = WebDriverWait(self.driver, 10).until(lambda x: x.find_element_by_xpath(findname))
            # element = self.driver.find_element_by_xpath(findname)
            self.driver.implicitly_wait(2)
            return element
        except:
            self.screenshot()
            log.error('未定位到元素:'+'%s')%(name)

    def get_id(self,id):
        '''
        定位頁面resouce-id元素
        :param id:
        :return:
        '''
        try:
            element = WebDriverWait(self.driver, 10).until(lambda x: x.find_element_by_id(id))
            # element = self.driver.find_element_by_id(id)
            self.driver.implicitly_wait(2)
            return element
        except:
            self.screenshot()
            log.error('未定位到元素:'+'%s')%(id)

    def get_xpath(self,xpath):
        '''
        定位頁面xpath元素
        :param id:
        :return:
        '''
        try:
            element = WebDriverWait(self.driver, 10).until(lambda x: x.find_element_by_xpath(xpath))
            # element = self.driver.find_element_by_xpath(xpath)
            self.driver.implicitly_wait(2)
            return element
        except:
            self.screenshot()
            log.error('未定位到元素:'+'%s')%(xpath)

    def get_ids(self,id):
        '''
        定位頁面resouce-id元素組
        :param id:
        :return:串列
        '''
        try:
            # elements = self.driver.find_elements_by_id(id)
            elements = WebDriverWait(self.driver, 10).until(lambda x: x.find_elements_by_id(id))
            self.driver.implicitly_wait(2)
            return elements
        except:
            self.screenshot()
            log.error('未定位到元素:'+'%s')%(id)

    def page(self,name):
        '''
        回傳至指定頁面
        :return:
        '''
        i=0
        while i<10:
            i=i+1
            try:
                findname = "//*[@text='%s']"%(name)
                self.driver.find_element_by_xpath(findname)
                self.driver.implicitly_wait(2)
                break
            except :
                os.popen("adb shell input keyevent 4")
                try:
                    findname = "//*[@text='確定']"
                    self.driver.find_element_by_xpath(findname).click()
                    self.driver.implicitly_wait(2)
                except:
                    os.popen("adb shell input keyevent 4")
                try:
                    self.driver.find_element_by_xpath("//*[@text='作業臺']")
                    self.driver.implicitly_wait(2)
                    break
                except:
                    os.popen("adb shell input keyevent 4")
  • Operate.py
    我認為最關鍵的一步了,后面沒有page都是呼叫這個檔案進行測驗,主要是根據讀取的yaml檔案,然后進行if...else...判斷,根據對應的operate_type分別進行對應的click、sendkeys等操作
#coding=utf-8
#author='Shichao-Dong'

from GetYaml import getyaml
from BaseOperate import BaseOperate

class Operate:
    def __init__(self,path,driver):
        self.path = path
        self.driver = driver
        self.yaml = getyaml(self.path)
        self.baseoperate=BaseOperate(driver)

    def check_operate_type(self):
        '''
        讀取yaml資訊并執行
        element_info:定位元素資訊
        find_type:屬性,id、xpath、text、ids
        operate_type: click、sendkeys、back、swipe_up 為back就是回傳,暫時就三種

        上面三個必填,operate_type必填!!!!!!

        send_content:send_keys 時用到
        index:ids時用到
        times:
        :return:
        '''

        for i in range(self.yaml.caselen()):
            if self.yaml.get_operate_type(i) == 'click':
                if self.yaml.get_findtype(i) == 'text':
                    self.baseoperate.get_name(self.yaml.get_elementinfo(i)).click()
                elif self.yaml.get_findtype(i) == 'id':
                    self.baseoperate.get_id(self.yaml.get_elementinfo(i)).click()
                elif self.yaml.get_findtype(i) == 'xpath':
                    self.baseoperate.get_xpath(self.yaml.get_elementinfo(i)).click()
                elif self.yaml.get_findtype(i) == 'ids':
                    self.baseoperate.get_ids(self.yaml.get_elementinfo(i))[self.yaml.get_index(i)].click()

            elif self.yaml.get_operate_type(i) == 'send_keys':
                if self.yaml.get_findtype(i) == 'text':
                    self.baseoperate.get_name(self.yaml.get_elementinfo(i)).send_keys(self.yaml.get_send_content(i))
                elif self.yaml.get_findtype(i) == 'id':
                    self.baseoperate.get_id(self.yaml.get_elementinfo(i)).send_keys(self.yaml.get_send_content(i))
                elif self.yaml.get_findtype(i) == 'xpath':
                    self.baseoperate.get_xpath(self.yaml.get_elementinfo(i)).send_keys(self.yaml.get_send_content(i))
                elif self.yaml.get_findtype(i) == 'ids':
                    self.baseoperate.get_ids(self.yaml.get_elementinfo(i))[self.yaml.get_index(i)].send_keys(self.yaml.get_send_content(i))

            elif self.yaml.get_operate_type(i) == 'back':
                for n in range(self.yaml.get_backtimes(i)):
                    self.baseoperate.back()

            elif self.yaml.get_operate_type(i) == 'swipe_up':
                for n in range(self.yaml.get_backtimes(i)):
                    self.baseoperate.swipe_up()

    def back_home(self):
        '''
        回傳至作業臺
        :return:
        '''
        self.baseoperate.page('作業臺')

公共部分的代碼就介紹這么多,在撰寫這個框架的時候,大部分精力都花在這部分,所以個人覺得還是值得好好研究的

Page部分

page部分是最小用例集,一個模塊一個檔案夾,以客戶為例,
目前寫了兩個用例,一個新增,一個排序,檔案如下:

file.png

代碼如下,非常的簡潔,

import sys
reload(sys)
sys.setdefaultencoding('utf8')
import codecs,os
from public.Operate import Operate
from public.GetYaml import getyaml

PATH = lambda p: os.path.abspath(
    os.path.join(os.path.dirname(__file__), p)
)
yamlpath = PATH("../../testyaml/cm/cm-001addcm.yaml")

class AddcmPage:

    def __init__(self,driver):
        self.path = yamlpath
        self.driver = driver
        self.operate = Operate(self.path,self.driver)

    def operateap(self):
        self.operate.check_operate_type()

    def home(self):
        self.operate.back_home()

運行用例

這部分用了unittest,運行所有測驗用例和生成報告,
一個模塊一個用例,以客戶為例:CmTest.py

from page.cm.CmAddcmPage import AddcmPage
from page.cm.CmSortcmPage import SortcmPage


from public.GetDriver import mydriver
driver = mydriver()

import unittest,time
class Cm(unittest.TestCase):

    def test_001addcm(self):
        '''
        新增客戶
        :return:
        '''
        add = AddcmPage(driver)
        add.operateap()
        add.home()
    def test_002sortcm(self):
        '''
        客戶排序
        :return:
        '''
        sort = SortcmPage(driver)
        sort.sortlist()
        sort.home()

    def test_999close(self):
        driver.quit()
        time.sleep(10)

if __name__ == "__main__":
    unittest.main()

首先從page層將需要運行的用例都import進來,然后用unittest運行即可,
如果想要運行所有的測驗用例,需要用到runtest.py

import time,os
import unittest
import HTMLTestRunner
from testcase.CmTest import Cm


def testsuit():
    suite = unittest.TestSuite()
    suite.addTests([unittest.defaultTestLoader.loadTestsFromTestCase(Cm),




])

    # runner = unittest.TextTestRunner(verbosity=2)
    # runner.run(suite)

    now=time.strftime("%y-%m-%d-%H-%M-%S")
    PATH = lambda p: os.path.abspath(
        os.path.join(os.path.dirname(__file__), p)
    )
    dirpath = PATH("./results/waiqin365-")

    filename=dirpath + now +'result.html'
    fp=open(filename,'wb')
    runner=HTMLTestRunner.HTMLTestRunner(stream=fp,title='waiqin365 6.0.6beta test result',description=u'result:')

    runner.run(suite)
    fp.close()

if __name__ =="__main__":
    testsuit()

這邊的思路差不多,也是先匯入再裝入suite即可

總結

就目前而言,暫時算是實作了資料與用例的分離,但是yaml的撰寫要求較高,不能格式上出錯,
同時也有一些其他可以優化的地方,如:

  • 對彈窗的判斷
  • 斷開后重連機制
  • 失敗后重跑機制

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/226868.html

標籤:python

上一篇:用Python爬取日向、櫻坂成員blog中的JPG檔案的url并將其下載到本地

下一篇:利用Python爬取京東商品的一種辦法

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more