flutter 專題 五十六 Google 2020開發者大會Flutter專題

由于疫情的原因,今年的Google 開發者大會 (Google Developer Summit) 在線上舉行,本次大會以“代碼不止”為主題,全面介紹了產品更新以及一系列面向本地開發者的技術支持內容。我比較關注的是移動開發,在本次大會上,關于Flutter 主題的演講主要從 Flutter 性能方面優化和新功能進行展開。

作為全球增長速度第二的開源項目,越來越多國內開發者使用 Flutter 實現跨平臺開發,包括騰訊英語君團隊、阿里閑魚團隊等等。其在 開放性上的進步,得益于開源社區、生態建設、對 Web 的支持。
在這里插入圖片描述
有興趣的讀者可以通過Google Developer官網進行學習:Google Developer官網

下面我們就來看一下這些新功能和性能上的優化。

Flutter 性能優化

首先為我們帶來演講的是Google 軟件工程師李宇騫,他是Flutter 團隊的一位軟件工程師,主要專注于提升其性能。下面是具體的演講內容:

2019 下半年,Flutter 團隊共收到 23 個量化的性能提升;2020 上半年,Flutter 團隊共收到 27 個量化的性能提升。2020 上半年 Flutter 團隊共收到來自 78 位開發者的 49 個性能改進。

工具的性能十分重要,性能測試也同樣至關重要,擁有良好的性能測試可以:

  • 快速重現問題;
  • 迭代和驗證解決方案;
  • 提供數據,激勵進一步的工作并防止倒退。

通常,能耗與渲染速度相關,每一幀渲染時間越長則能耗就越高,但能耗并不能衡量渲染速度,因為在某些情況下渲染速度快也可能會導致能耗升高,渲染速度慢也可能不耗能。

CPU 上運行時間雖然短,但由于新的算法利用了更多的 GPU 核心,所以 GPU 能耗反而增加;有些 CPU 上的任務被別的 I/O 或 GPU 任務阻塞,進行了長時間的等待,而等待的時間內并無過多能耗。

因此,在速度之外增加能耗測試是十分必要的。因為 Flutter 團隊在 GitHub 上收到的大部分能耗問題都和 iOS 相關,所以此次 Flutter 首先加入了 iOS 的能耗測試,Android 的能耗測試工具會于后續加入。

開發者可以使用 Flutter Gallery App 在 Timeline 中查看 CPU/GPU 的使用率,也可以用集成測試自動檢測 CPU/GPU 的使用率。
在這里插入圖片描述

Flutter 還新加入了 SkSL 著色器編譯預熱功能,來幫助開發者消除著色器編譯卡頓。如果一個 Flutter 程序第一次渲染某類動畫時出現明顯的卡頓,但是之后渲染這些動畫時,卡頓完全消失,那么這就很可能是著色器編譯卡頓。開發者可以使用 --trace-skia,然后檢查 Timeline 來確認是否為著色器卡頓。
在這里插入圖片描述

值得一提的是,SkSL 可以實現自動化生成與測試,這對于需要持續更新的 Flutter App 來說,可以節省很多的人力。

內存和包體積的測試工具

接下來,是由Flutter 用戶體驗研究員侯悠揚帶來的測試工具專題。侯悠揚于 2017 年加入 Google,并于 2019 年加入 Flutter 團隊。她是 Flutter 團隊一名用戶體驗研究員,關注提升 Flutter 產品和開發工具的程序員體驗。

此次,Flutter 團隊更新了Dart開發工具。Dart 開發工具是面向 Flutter 和 Dart 開發人員的工具套件,包括如下一些小工具:

  • 布局檢查(Inspector)
  • 性能調試(Performance)
  • 內存調試(Memory)
  • 網絡調試(Network)
  • 包體積調試(App Size)
  • 調試器(Debugger)
  • 日志(Logging)

連接上設備然后運行Flutter應用,點擊Android Studio底部工具欄中的【Open DevTools】按鈕即可開啟調試功能。

內存調試器功能

Flutter的內存調試器提供如下功能:

  • 事件窗格(Dart 和 Android 內存)
  • 手動和自動快照(snapshot)和垃圾回收(GC)
  • 內存分析
  • 內存堆分配累加器(Heap Allocation Accumulators)
  • 通過命令行界面將內存統計信息到處到 JSON 文件

內存測試

內存測試提供如下功能:

  • 通過 ADB 交互直接進行內存測試
  • Dart 開發工具內存測試
  • iOS 內存測試

更多信息可以通過這篇由 Flutter 工程師撰寫的文章進行了解:怎么進行Flutter內存測試

包體積調試器功能

包體積調試器提供如下功能:

  • 可視化了應用程序的總大小,包括功能級別的 Dart AOT 快照;
  • 分析快照和應用包(APK,IPA 等);
  • 分析快照或應用程序包(APK,IPA 等)的差異;
  • 查看軟件包級別的應用大小歸因數據。

Pigeon與Flutter混合開發

什么是Pigeon

在早期的hybird開發模式中,前端和Native交互時需要native雙端為JS提供接口。這種情況下如何規范命名,參數等就成了一個問題,如果單獨維護一份協議文件,三端依照協議文件進行開發,很容易出現協議更改后,沒有及時同步,又或者在實際開發過程沒有按照規范,可能導致各種意外情況。

同樣,在Flutter插件包的開發中,因為涉及到Native雙端代碼開發能力,Dart側暴露統一的接口給使用者,也會出現同樣的問題,此時Pigeon應運而生,Pigeon是Flutter官方推薦插件管理工具,可以使用來解決和優化 Native 插件開發上 platform channel 相關的問題。

Flutter官方提供的Pigeon插件,通過dart入口,生成雙端通用的模板代碼,Native部分只需通過重寫模板內的接口,無需關心methodChannel部分的具體實現,入參,出參也均通過生成的模板代碼進行約束。接口新增,或者參數修改,只需要在dart側更新協議文件,生成雙端模板,即可達到同步更新,有效的避免了參數修改,參數新增帶來的雙端代碼不同步的問題,下面是Pigeon工作原理示意圖。
在這里插入圖片描述

下面是Pigeon給出的示例:
在這里插入圖片描述

可以看到接入Pigeon后整體代碼簡潔了不少,而且規范了類型定義。

Pigeon接入

接下來我們看一下如何從零接入Pigeon。截止目前,Pigeon已經發布了0.1.15版本,如下圖所示。
在這里插入圖片描述
首先,新建一個名為testpigeon的Flutter項目,打開項目的pubspec.yaml文件,并添加如下依賴代碼。

dependencies:pigeon: ^0.1.15

然后,按照官方的要求在項目目錄下新建一個pigeons目錄,作為存放dart側的入口文件,內容為接口、參數、返回值的定義等,以及后面通過pigeon的命令,生產native端代碼。接下來,新建一個message.dart 文件,并添加如下。

import 'package:pigeon/pigeon.dart';class SearchRequest {String query;
}class SearchReply {String result;
}@HostApi()
abstract class Api {SearchReply search(SearchRequest request);
}

在上面的message.dart 文件中,通過 @HostApi() 注解標示了通信對象和接口,之后我們只需要執行如下命令,就可以生成對應代碼到工程中。

flutter pub run pigeon  --input pigeons/message.dart

其實上面的命令是下面命令的簡寫方式:

flutter pub run pigeon  --input pigeons/message.dart  --dart_out lib/pigeon.dart  --objc_header_out ios/Runner/pigeon.h --objc_source_out ios/Runner/pigeon.m --java_out android/app/src/main/java/Pigeon.java --java_package "com.xzh.testpigeon"

命令的參數的含義如下:

  • –input:引入了我們創建的 message.dart 文件;
  • –dart_out:輸出了 dart 模板文件;
  • –objc_header_out 和 --objc_source_out 輸出了 object-c 文件;
  • –java_out 輸出了 java 文件;

命令執行后 dart 文件輸出到 lib 目錄下, object-c 文件輸出到了 ios/Runner 目錄下,java 文件輸出到指定的 com.xzh.testpigeon" 包名路徑下,之后就可以開始正式接入。然后我們分別使用Android Studio和Xcode打開原生工程代碼。

Android 工程代碼

使用Android Studio打開Flutter項目的原生Android工程,生成的代碼如下:

// Autogenerated from Pigeon (v0.1.15), do not edit directly.
// See also: https://pub.dev/packages/pigeonpackage com.xzh.testpigeon;import io.flutter.plugin.common.BasicMessageChannel;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.StandardMessageCodec;
import java.util.ArrayList;
import java.util.HashMap;/** Generated class from Pigeon. */
@SuppressWarnings("unused")
public class Pigeon {/** Generated class from Pigeon that represents data sent in messages. */public static class SearchReply {private String result;public String getResult() { return result; }public void setResult(String setterArg) { this.result = setterArg; }HashMap toMap() {HashMap<String, Object> toMapResult = new HashMap<>();toMapResult.put("result", result);return toMapResult;}static SearchReply fromMap(HashMap map) {SearchReply fromMapResult = new SearchReply();Object result = map.get("result");fromMapResult.result = (String)result;return fromMapResult;}}/** Generated class from Pigeon that represents data sent in messages. */public static class SearchRequest {private String query;public String getQuery() { return query; }public void setQuery(String setterArg) { this.query = setterArg; }HashMap toMap() {HashMap<String, Object> toMapResult = new HashMap<>();toMapResult.put("query", query);return toMapResult;}static SearchRequest fromMap(HashMap map) {SearchRequest fromMapResult = new SearchRequest();Object query = map.get("query");fromMapResult.query = (String)query;return fromMapResult;}}/** Generated interface from Pigeon that represents a handler of messages from Flutter.*/public interface Api {SearchReply search(SearchRequest arg);/** Sets up an instance of `Api` to handle messages through the `binaryMessenger` */static void setup(BinaryMessenger binaryMessenger, Api api) {{BasicMessageChannel<Object> channel =new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.Api.search", new StandardMessageCodec());if (api != null) {channel.setMessageHandler((message, reply) -> {HashMap<String, HashMap> wrapped = new HashMap<>();try {@SuppressWarnings("ConstantConditions")SearchRequest input = SearchRequest.fromMap((HashMap)message);SearchReply output = api.search(input);wrapped.put("result", output.toMap());}catch (Exception exception) {wrapped.put("error", wrapError(exception));}reply.reply(wrapped);});} else {channel.setMessageHandler(null);}}}}private static HashMap wrapError(Exception exception) {HashMap<String, Object> errorMap = new HashMap<>();errorMap.put("message", exception.toString());errorMap.put("code", exception.getClass().getSimpleName());errorMap.put("details", null);return errorMap;}
}

上面生成的 Pigeon.java 代碼中包含了 Api 接口用于開發者實現交互邏輯,同時開發者可以通過 SearchRequest 獲取 dart 發送過來的請求,通過 SearchReply 返回數據給 dart 。然后,還需要在Android的入口文件MainActivity 中實現 Api 接口來完成數據交互,代碼如下。

public class MainActivity extends FlutterActivity {private class MyApi implements Pigeon.Api {@Overridepublic Pigeon.SearchReply search(Pigeon.SearchRequest request) {Pigeon.SearchReply reply = new Pigeon.SearchReply();reply.setResult(String.format("Hi %s!", request.getQuery()));return reply;}}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);GeneratedPluginRegistrant.registerWith(this);Pigeon.Api.setup(getFlutterView(), new MyApi());}
}

首先,我們繼承 Pigeon.Api 實現了 MyApi 對象,然后在 search() 方法中通過 request.getQuery() 獲取 dart 的請求數據,并且通過 Pigeon.SearchReply 的 setResult 返回 數據給dart 端,最后通過Pigeon.Api.setup(getFlutterView(), new MyApi())啟動。

iOS

使用Xcode打開Flutter項目的iOS工程,把生成的 pigeon.h 和 pigeon.m 文件 link 到 Xcode 工程里,之后如下代碼所示在 AppDelegate.h 引入 Api 協議。

#import <Flutter/Flutter.h>
#import <UIKit/UIKit.h>
#import "pigeon.h"@interface AppDelegate : FlutterAppDelegate<Api>@end

接下來,在 AppDelegate.m 中實現 search 接口,并在收到的 dart 消息后基于回復,最后調用 ApiSetup()方法將完成注冊。

#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"@implementation AppDelegate- (BOOL)application:(UIApplication *)applicationdidFinishLaunchingWithOptions:(NSDictionary *)launchOptions {[GeneratedPluginRegistrant registerWithRegistry:self];// Override point for customization after application launch.FlutterViewController* controller =(FlutterViewController*)self.window.rootViewController;ApiSetup(controller.binaryMessenger, self);return [super application:application didFinishLaunchingWithOptions:launchOptions];
}-(SearchReply *)search:(SearchRequest*)input error:(FlutterError **)error {SearchReply* result = [[SearchReply alloc] init];result.result  = [NSString stringWithFormat:@"%s%@","Hi ",input.query];return result;
}@end

Dart測試

最后我們在 Dart 代碼中新建一個測試的代碼,如下所示。

import 'pigeon.dart';void main() {testWidgets("test pigeon", (WidgetTester tester) async {SearchRequest request = SearchRequest()..query = "Aaron";Api api = Api();SearchReply reply = await api.search(request);expect(reply.result, equals("Hi Aaron!"));});
}

Flutter 在阿里巴巴的應用

首先,主持人為我們介紹了Flutter的歷史,介紹圍繞美觀、高效、流程和開放等幾個方面來介紹Flutter。
在這里插入圖片描述
接下來,阿里巴巴的無線技術專家門柳介紹Flutter在阿里巴巴的應用,閑魚是阿里巴巴Flutter技術實踐的先驅,也是國內最早嘗試Flutter技術的大型互聯網公司,而阿里巴巴旗下的淘寶也不甘示弱,也在某些模塊結成Flutter,不過大多是業務級別的模塊,而沒有像閑魚那樣大規模使用。我們可以從下圖看到Flutter在阿里巴巴的使用情況。

在這里插入圖片描述
那為什么,這么多的移動應用開始使用Flutter來進行開發呢?首先,讓我們來了解下跨平臺技術的發展歷程。
在這里插入圖片描述
可以發現,移動跨平臺開發經歷了大約四個階段:

  • 早期的WebView加載方案
  • 原生API橋接的Hybrid方案
  • 原生渲染方案(Web語法+原生UI)
  • 自繪渲染(獨立布局/渲染)

而Flutter就是采用的自繪渲染方案,有興趣的童鞋可以研究以下Flutter的架構。為什么選擇Flutter進行跨平臺應用開發呢,下面是Flutter所具有的一些優勢:
在這里插入圖片描述
不過,Flutter也不是萬能的,Flutter目前處于快速迭代的階段,所以保險起見,我們只在一些常規的業務開發和模塊化的UI界面開發和部分游戲中使用Flutter。
在這里插入圖片描述
總結起來,就是在一些富交互類應用和新型的應用中使用Flutter,對于視頻、直播等渲染要求高的則繼續使用原生進行開發。

那使用Flutter進行應用開發時,有哪些經驗和問題需要注意呢?下圖顯示了阿里巴巴在使用Flutter進行應用開發時遇到的一些問題,大家使用時需要規避。
在這里插入圖片描述
首先遇到的問題是,由于Flutter使用的是Dart進行開發,無疑增加了開發者的學習成本。其次,對于大型應用來說,如何保證代碼質量,如何在多個平臺運行自動化測試腳本也是一個問題;并且由于Flutter作為一門新的技術,如何快速的將老得業務遷移過來也是大家需要考慮的問題。總結一下,就是調試、測試、狀態管理、緩和導航棧管理、跨平臺兼容以及如何尋找解決方案的問題。
在這里插入圖片描述
盡管Flutter已經提供了很多的工具,但是如何將它融入到阿里巴巴的客戶端開發工作流中,是大家需要考慮的問題。
在這里插入圖片描述
首先,為了提升開發效率,降低初期的接入成本,我們將Flutter Toolkit融入到Alibab DevOps工作流中,并自研了一些工具、打包和發布平臺以及搭建調試環境。接下來,我們基于現存的技術積累,研發了一些中間件。
在這里插入圖片描述
下面來看一個實例,即如何解決多圖列表頁面的內存占用問題。這類問題的特征如下:

  • 頁面很長,圖片很多,首次加載時間很長
  • 大量圖片同時加載并生成紋理,內存飆升
  • Sliver中每項Cell拆分粒度很大,單個Cell占用多屏,難以回收
    在這里插入圖片描述
    對于列表Flutter列表內存回收的問題,大家可以閱讀?細化 Flutter List 內存回收,解決大 Cell 問題這篇文章。

對于上面的多圖長列表的內存問題,我們可以從以下幾個方面著手進行優化:

  • 拆分Cell,使每一項變得更小
  • 根據坐標判斷圖片是否在屏幕內,進而進行圖片的懶加載和回收
  • 提前獲取圖片的寬高大小,減少布局和重繪
  • 以圖片為單位進行紋理回收,而不是Sliver中的每項Cell為單位
  • 外接原生圖片庫,實現共享本地緩存

在這里插入圖片描述
最后,我們來看一下Flutter在阿里巴巴的體系化建設。首先,Flutter的體系化建設主要從基礎能力建設、研發平臺和可持續迭代等幾個方面著手。
在這里插入圖片描述
下面是Flutter在阿里巴巴平臺建設的具體的一些方案。
在這里插入圖片描述
目前,Flutter在阿里巴巴已經經過了大規模的應用,并且我們自己的技術體系建設也在穩步推薦中,后面會將建設的一些成果通過社區分享出來。

附:?Google 開發者大會

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/78832.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/78832.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/78832.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

開源模型應用落地-qwen模型小試-Qwen3-8B-快速體驗-pipeline方式(二)

一、前言 阿里云最新推出的 Qwen3-8B 大語言模型,作為國內首個集成“快思考”與“慢思考”能力的混合推理模型,憑借其 80 億參數規模及 128K 超長上下文支持,正在重塑 AI 應用邊界。該模型既可通過輕量化“快思考”實現低算力秒級響應,也能在復雜任務中激活深度推理模式,以…

「動態規劃::背包」01背包 / AcWing 2(C++)

概述 AcWing 2&#xff1a; 有 N 件物品和一個容量是 V 的背包。每件物品只能使用一次。 第 i 件物品的體積是 v[i]&#xff0c;價值是 w[i]。 求解將哪些物品裝入背包&#xff0c;可使這些物品的總體積不超過背包容量&#xff0c;且總價值最大。 輸出最大價值。 輸入格式 第一…

Java 中的 設計模式詳解

一&#xff1a;設計模式概述 &#xff08;1&#xff09;概述 &#xff08;2&#xff09;分類 創建型 行為型 結構型 二&#xff1a;軟件設計模式 2.1 開閉原則 &#xff08;1&#xff09;定義 在程序需要進行拓展的時候&#xff0c;不能修改原有代碼 使用到接口和抽象類&#x…

阿里qiankun微服務搭建

主服務 chat vue3 ts vite 子服務 ppt react 18 vite 子服務 agent 主服務 npm i vite-plugin-qiankun mian.ts import ./style/base.scss import virtual:svg-icons-register import { createApp } from vue import { createPinia } from piniaimport App from ./App.vue im…

安裝WSL2,配置Ubuntu圖像化界面

目錄 一、前言二、安裝WSL三、安裝圖像化界面四、參考 一、前言 Windows 子系統下的 Linux 子系統&#xff08;WSL&#xff0c;Windows Subsystem for Linux&#xff09;是微軟推出的一項功能&#xff0c;允許用戶在 Windows 系統中原生運行 Linux 環境&#xff0c;無需安裝虛…

圖像畸變-徑向切向畸變實時圖像RTSP推流

實驗環境 注意&#xff1a;ffmpeg進程stdin寫入兩張圖片的時間間隔不能太長&#xff0c;否則mediamtx會出現對應的推流session超時退出。 實驗效果 全部代碼 my_util.py #進度條 import os import sys import time import shutil import logging import time from datetime i…

Redis Sentinel 和 Redis Cluster 各自的原理、優缺點及適用場景是什么?

我們來詳細分析下 Redis Sentinel (哨兵) 和 Redis Cluster (集群) 這兩種方案的原理和使用場景。 Redis Sentinel (哨兵) 原理: Sentinel 本身是一個或一組獨立于 Redis 數據節點的進程。它的核心職責是監控一個 Redis 主從復制 (Master-Slave) 架構。多個 Sentinel 進程協同…

基于機器學習的電影票房預測

目錄 摘 要(完整下載鏈接附在文末) Abstract 1 緒 論 1.1 研究背景概述 1.2 國內外相關領域研究進展 1.3 電影票房預測技術概覽 1.3.1 利用人口統計學特征的方法 1.3.2 基于機器學習的預測模型 2 機器學習相關理論介紹與分析 2.1 機器學習算法理論 2.1.1卷積…

SVMSPro平臺獲取HTTP-FLV規則

SVMSPro平臺獲取HTTP-FLV規則 HTTP-FLV的服務端口為&#xff1a;53372&#xff0c;如需要公網訪問需要開啟這個端口 這里講的是如何獲取長效URL&#xff0c;短效&#xff08;時效性&#xff09;URL也支持&#xff0c;下回講 一、如何獲取HTTP-FLV實時流視頻 http://host:po…

ARM架構的微控制器總線矩陣

在 ARM 架構的微控制器&#xff08;MCU&#xff09;中&#xff0c;總線矩陣&#xff08;Bus Matrix&#xff09; 是總線系統的核心互連結構&#xff0c;負責協調多個主設備&#xff08;如 CPU、DMA、以太網控制器等&#xff09;對多個從設備&#xff08;如 Flash、SRAM、外設等…

AI賦能金融:智能投顧、風控與反欺詐的未來

AI賦能金融&#xff1a;智能投顧、風控與反欺詐的未來 系統化學習人工智能網站&#xff08;收藏&#xff09;&#xff1a;https://www.captainbed.cn/flu 文章目錄 AI賦能金融&#xff1a;智能投顧、風控與反欺詐的未來摘要引言一、智能投顧&#xff1a;從經驗驅動到人機協同…

【機器學習】樸素貝葉斯

目錄 一、樸素貝葉斯的算法原理 1.1 定義 1.2 貝葉斯定理 1.3 條件獨立性假設 二、樸素貝葉斯算法的幾種常見類型 2.1 高斯樸素貝葉斯 (Gaussian Naive Bayes) 【訓練階段】 - 從數據中學習模型參數 【預測階段】 - 對新樣本 Xnew? 進行分類 2. 2 多項式樸素貝葉斯 (…

鴻蒙 ArkTS 組件 通用事件 通用屬性 速查表

ArkTS 組件 組件 通用事件 速查表 通用事件事件名稱簡要說明點擊事件onClick(event: Callback<ClickEvent>, distanceThreshold: number): T相較于原有 onClick 接口&#xff0c;新增 distanceThreshold 參數作為點擊事件移動閾值&#xff0c;當手指的移動距離超出所設…

Java云原生+quarkus

一、Java如何實現云原生應用&#xff1f; 傳統的 Java 框架&#xff08;如 Spring Boot&#xff09;雖然功能強大&#xff0c;但在云原生場景下可能顯得笨重。以下是一些更適合云原生的輕量級框架&#xff1a; Quarkus(推薦) 專為云原生和 Kubernetes 設計的 Java 框架。支持…

C語言教程(二十三):C 語言強制類型轉換詳解

一、強制類型轉換的概念 強制類型轉換是指在程序中手動將一個數據類型的值轉換為另一種數據類型。在某些情況下,編譯器可能不會自動進行類型轉換,或者自動轉換的結果不符合我們的預期,這時就需要使用強制類型轉換來明確指定要進行的類型轉換。 二、強制類型轉換的語法 強制類…

Spring Boot × K8s 監控實戰-集成 Prometheus 與 Grafana

在微服務架構中&#xff0c;應用的可觀測性至關重要。Kubernetes 已成為容器化部署的標準&#xff0c;但其自身的監控能力有限&#xff0c;需要與其他工具集成才能實現詳細的運行數據采集與分析。 本文將通過 Spring Boot Kubernetes Prometheus Grafana 實戰&#xff0c;打…

phpstudy修改Apache端口號

1. 修改Listen.conf文件 本地phpstudy安裝目錄&#xff1a; 2.其他問題 ① 修改httpd.conf不起作用 ② 直接通過控制面板配置好像有延遲緩存

(done) 吳恩達版提示詞工程 6. 轉換 (翻譯,通用翻譯,語氣風格變換,文本格式轉換,拼寫檢查和語法檢查)

視頻&#xff1a;https://www.bilibili.com/video/BV1Z14y1Z7LJ/?spm_id_from333.337.search-card.all.click&vd_source7a1a0bc74158c6993c7355c5490fc600 別人的筆記&#xff1a;https://zhuanlan.zhihu.com/p/626966526 6. 轉換任務&#xff08;Transforming&#xff0…

什么是靜態住宅ip,跨境電商為什么要用靜態住宅ip

在數字時代&#xff0c;IP地址不僅是設備聯網的“ID”&#xff0c;更是跨境電商運營中的關鍵工具。尤其對于需要長期穩定、安全操作的場景&#xff0c;靜態住宅IP逐漸成為行業首選。 一、什么是靜態住宅IP&#xff1f; 靜態住宅IP&#xff08;Static Residential IP&#xff0…

Qemu-STM32(十七):STM32F103加入AFIO控制器

概述 本文主要描述了在Qemu平臺中&#xff0c;如何添加STM32F103的AFIO控制器模擬代碼&#xff0c;AFIO是屬于GPIO引腳復用配置的功能。 參考資料 STM32F1XX TRM手冊&#xff0c;手冊編號&#xff1a;RM0008 添加步驟 1、在hw/arm/Kconfig文件中添加STM32F1XX_AFIO&#x…