主頁 > 移動端開發 > Android-UI組件合集面試資料分享

Android-UI組件合集面試資料分享

2021-06-14 07:21:33 移動端開發

開頭

在一般的互聯網公司的技術人員的面試中,大概會經歷3到4輪的面試,差不多2-3輪的技術面,還有1輪的HR面試,有人面試題是有關“目標”,有的關于“方法”,有的關于“演算法”,有的關于“基礎”,在小編看來,面試是找作業的第一道難關,原本以為面試都會問很難的問題,如果是更好的公司,可能問的問題越難,

背景

開發者檔案中提到,Android應用有三種啟動狀態,每種狀態都會影回應用向用戶顯示所需的時間:冷啟動、溫啟動或熱啟動,三種啟動狀態中,冷啟動耗時最久,系統和App有較多初始化的作業,如果啟動時間過長,可能會導致用戶在應用商店打低分,甚至完全棄用app,所以冷啟動速度是各個app非常重要的性能指標之一,

在冷啟動速度優化的作業中,打點是非常重要的一環,統計點位該如何選,以及為什么要這么選,有很多細節值得探究,本文主要深入探究Android端app層如何選擇行程創建的起點,

三個時機簡述

本文中涉及的3個App層行程創建時間的起點:Application ,Process.getStartElapsedRealTime,/proc/self/stats starttime,

簡單介紹下3個行程創建時間起點:

  • Application :Application構造方法;
  • Process.getStartElapsedRealTime:Framework中記錄的行程創建的起點,此介面有版本限制,Android N以下版本無法使用;
  • /proc/self/stats starttime:內核中記錄的行程創建的起點,

3個行程創建時間起點時序如下:/proc/self/stats starttime 早于 Process.getStartElapsedRealTime 早于 Application

這三個時機哪個更好?哪個能指導優化作業?哪個更接近用戶點擊桌面創建行程的起始點?帶著幾個問題,繼續往下看,

深入分析

詳細看下三個時機:

  • Application 時機

Applciation的構造方法,Android Java代碼可以最先埋點的時機,Android開發童鞋對此時機都會比較熟悉,不過多贅述,

  • Process.getStartElapsedRealTime時機

    時序總覽圖:

Process.getStartElapsedRealTime的賦值介面為handleBindApplication介面,賦值時機為App行程進入Java世界后,行程attach到ActivityManagerService,再通過binder call回傳到App行程時,原理細節可繼續閱讀原始碼決議,

原始碼決議:

Android 8.1.0的原始碼中一段說明(Process.java):
487    /**
488     * Return the {@link SystemClock#elapsedRealtime()} at which this process was started.
489     */
490    public static final long getStartElapsedRealtime() {
491        return sStartElapsedRealtime;
492    }

從原始碼的說明中可知,Process.getStartElapsedRealTime代表程式創建開始的時間,
SystemClock#elapsedRealtime表示距離boot的真實時間,看下其賦值時機(ActivityThread.java):
5429    private void handleBindApplication(AppBindData data) {...
5436        // Note when this process has started.
5437        Process.setStartTimes(SystemClock.elapsedRealtime(), SystemClock.uptimeMillis());

handleBindApplication是在ActivityThread主執行緒H的訊息處理中被呼叫的,
H作為ActivityThread的內部類,是主執行緒處理訊息的Handler,
234    final H mH = new H();

 這個訊息是誰發的,什么時候發的呢?了解Android App的入口函式及創建程序的同學,可能不難解答這個問題,
App的創建,Java層呼叫的入口為ActivityThread main方法,看下:
6459    public static void main(String[] args) {...
6478        Looper.prepareMainLooper();
6479
6480        ActivityThread thread = new ActivityThread();
6481        thread.attach(false);...
6494        Looper.loop();

 從代碼中看,main方法中主要是準備主執行緒訊息Looper,執行ActivityThread attach方法,然后主執行緒開始訊息回圈,
看下ActivityThread attach:
6315    private void attach(boolean system) {
6318        if (!system) {
6328            final IActivityManager mgr = ActivityManager.getService();
6329            try {
6330                mgr.attachApplication(mAppThread);
6331            } catch (RemoteException ex) {
6332                throw ex.rethrowFromSystemServer();
6333            }

 從代碼可知,此處有binder呼叫,呼叫AMS的attachApplication,此呼叫是在system_server行程,執行如下操作,
看下ActivityManagerService處理程序:
7215    public final void attachApplication(IApplicationThread thread) {
7216        synchronized (this) {
7219            attachApplicationLocked(thread, callingPid);
7221        }
7222    }
6911    private final boolean attachApplicationLocked(IApplicationThread thread,
6912            int pid) {…
7102                thread.bindApplication(processName, appInfo, providers,
7103                        app.instr.mClass,
7104                        profilerInfo, app.instr.mArguments,
7105                        app.instr.mWatcher,
7106                        app.instr.mUiAutomationConnection, testMode,
7107                        mBinderTransactionTrackingEnabled, enableTrackAllocation,
7108                        isRestrictedBackupMode || !normalMode, app.persistent,
7109                        new Configuration(getGlobalConfiguration()), app.compat,
7110                        getCommonServicesLocked(app.isolated),
7111                        mCoreSettingsObserver.getCoreSettingsLocked(),
7112                        buildSerial);

 比較關鍵的呼叫:thread.bindApplication, thread是Binder物件,這個地方又有binder呼叫,看看執行者:
690    private class ApplicationThread extends IApplicationThread.Stub {
899        public final void bindApplication(String processName, ApplicationInfo appInfo,
900                List<ProviderInfo> providers, ComponentName instrumentationName,
901                ProfilerInfo profilerInfo, Bundle instrumentationArgs,
902                IInstrumentationWatcher instrumentationWatcher,
903                IUiAutomationConnection instrumentationUiConnection, int debugMode,
904                boolean enableBinderTracking, boolean trackAllocation,
905                boolean isRestrictedBackupMode, boolean persistent, Configuration config,
906                CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
907                String buildSerial)
A 
pplicationThread執行sendMessage(H.BIND_APPLICATION, data);

將訊息發送出去,此部分的執行為App行程的binder執行緒池里,是如何切換至主執行緒執行的呢?
2605    private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
2609        Message msg = Message.obtain();...
2617        mH.sendMessage(msg);
2618    }  

 通過mH,將訊息發送到主執行緒的Looper,主執行緒執行,
1462    private class H extends Handler {
1473        public static final int BIND_APPLICATION = 110;
1580        public void handleMessage(Message msg) {
1653                case BIND_APPLICATION:
1656                    handleBindApplication(data);
1658                    break;

handleBindApplication就是Process.getStartElapsedRealTime獲取對行程創建的起點,后續邏輯就是Application的初始化的作業,由此可見Process.getStartElapsedRealTime時機是比Application時機早,在Application構造方法中打斷點情況如下:

/proc/self/stats starttime時機

/proc/self/stats starttime時機是kernel層記錄的行程創建起點,為3個時機中最早的,詳細看下:

proc/pid/stat用于獲取某一個行程的統計資訊,內容形式如下:

在proc/pid/stat統計資訊中,starttime為第22個元素,starttime的值什么含義,以及是如何計算出來的呢?看下fs/proc/array.c的do_task_stat()

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-5rFmtSoQ-1623416459815)(https://upload-images.jianshu.io/upload_images/22459598-0ad6f754e0dca6e3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]

從內核代碼中可知:start_time取值為task的real_start_time,先看下nesc_to_clock_t方法:

div_u64_rem方法為無符號除法操作:除數是無符號64bit,被除數是無符號32,remainder為余數,

從計算程序來看,是把real_start_time除以1000000000/100=10000000,real_start_time單位是什么呢?看下資料結構task_struct定義:

    struct timespec start_time;      
    struct timespec real_start_time;

task_struct中有兩個時間:start_time 和 real_start_time,其中后者包含睡眠時間,兩個時間單位均為ns,/proc/self/stats starttime取的值為real_start_time:

struct timespec
{
__time_t tv_sec;        /* Seconds. */
long   tv_nsec;       /* Nanoseconds. */
};

由此可見,real_start_time單位為ns,如果將real_start_time除以1000000000/100=10000000,換算完單位為10ms,比如/proc/self/stats starttime讀取到的值為100,則需換算為100*10ms=1000ms,而我們啟動速度日常大概率會以ms為計算精度,/proc/self/stats starttime會損失一定的精度,內核為何會做此種處理呢?

在內核的時間統計方式中,有個單位為jiffies,jiffies是內核中的一個全域變數,用來記錄自系統啟動以來產生的節拍數,簡單描述就是1s內,內核發起的時鐘中斷次數,kernel中就使用這個來對程式的運行時間進行統計,而/proc/self/stats starttime統計單位正是jiffies,代表應用程式冷啟動后經過了多少個內核時鐘,

那我們該如何科學的統計以及換算/proc/self/stats starttime的值呢?Linux 系統上Man proc有下面一段解釋:

(22) starttime %llu

The time the process started after system boot. In kernels before Linux 2.6, this value was expressed in jiffies. Since Linux 2.6, the value is expressed in clock ticks (divide by sysconf(_SC_CLK_TCK)).

The format for this field was %lu before Linux 2.6.

在內核態的常量USER_HZ我們無法獲取,但可以通過在用戶態通過sysconf(_SC_CLK_TCK)獲取到其值,

計算公式如下:

/proc/self/stats starttime * 1000 / sysconf(_SC_CLK_TCK),單位ms

可能有些同學會說,sysconf(_SC_CLK_TCK)的值是100,直接用/proc/self/stats starttime * 10即可,但需考慮內核的升級或內核定制場景,使用sysconf(_SC_CLK_TCK)獲取并參與計算為最穩妥的方式,

再一個問題,/proc/self/stats starttime 是來自task_struct real_start_time,這個時間初始化是在什么時候呢?答案就是task_struct資料結構被創建的時候,也就是行程被創建的時候,即 zygote fork時機,fork系統呼叫會把子行程的資料結構task_struct、執行緒堆疊等資料結構初始化,感興趣的同學可以去看內核的fork原始碼,

總結

通過上述的詳細分析,已經對三個時機有較為詳細的了解,在實際App工程中,建議結合使用Application 時機和/proc/self/stats starttime時機作為應用程式啟動的起點,

  • Application 時機是Android Java代碼可以最先埋點的地方,通過此起點,再結合冷啟動的結束點位,可明確知曉工程代碼的詳細耗時,對于指導日常優化作業有較大意義;

  • /proc/self/stats starttime時機為三個時機中最早的,其中有工程代碼不可控的耗時,涉及到行程資料結構、執行緒堆疊等初始化作業,但是此時機會更接近用戶的實際感受,可以最大程度用來衡量用戶啟動體驗;

  • Process.getStartElapsedRealTime由于有版本的限制,在Android N以下版本無法獲取,無法兼顧大盤所有的用戶機器,此值的指導價值就沒那么大,優化作業中,重中之重是優化中低端機器的性能體驗,如果Android N以下機型無法獲取,則會有大量的低端機器的啟動性能不在統計范圍內,

  • 可能有的童鞋還會有一個疑問,為什么說/proc/self/stats starttime更接近用戶的實際啟動體驗,而不是用戶的全部啟動體驗呢?熟悉應用程式啟動程序的同學就會比較了解這個問題,Android應用程式啟動是從用戶點擊桌面圖示開始,點擊圖示的第一回應是在Launcher行程,通過ActivityManagerService將創建行程資訊傳給zygote,zygote再執行fork,中間經歷了兩次跨行程通信,一次是Launcher行程通過Binder呼叫進入system_server行程,一次是system_server行程通過socket將創建行程資訊傳給zygote,zygote從睡夢中醒來,開始創建行程,細節不贅述了,感興趣的童鞋可以搜下相關資料,在網上有很多教程,

尾聲

改變人生,沒有什么捷徑可言,這條路需要自己親自去走一走,只有深入思考,不斷反思總結,保持學習的熱情,一步一步構建自己完整的知識體系,才是最終的制勝之道,也是程式員應該承擔的使命,

以上進階Android高級工程師系統學習資料可以免費分享給大家,需要完整版的朋友,點這里可以看到全部內容,

如果需要PDF版本可以在群檔案夾里,自行領取!

  • 或者在群檔案夾中里,自行下載直達領取鏈接:【https://links.jianshu.com/go?to=https%3A%2F%2Fjq.qq.com%2F%3F_wv%3D1027%26k%3DBRZhpPkt】

進階學習視頻

附上:我們之前因為秋招收集的二十套一二線互聯網公司Android面試真題 (含BAT、小米、華為、美團、滴滴)和我自己整理Android復習筆記(包含Android基礎知識點、Android擴展知識點、Android原始碼決議、設計模式匯總、Gradle知識點、常見演算法題匯總,)

總結

其實要輕松掌握很簡單,要點就兩個:

  1. 找到一套好的視頻資料,緊跟大牛梳理好的知識框架進行學習,
  2. 多練, (視頻優勢是互動感強,容易集中注意力)

你不需要是天才,也不需要具備強悍的天賦,只要做到這兩點,短期內成功的概率是非常高的,

對于很多初中級Android工程師而言,想要提升技能,往往是自己摸索成長,不成體系的學習效果低效漫長且無助,下面資料部分截圖是我花費幾個月時間整理的,誠意滿滿:特別適合有3-5年開發經驗的Android程式員們學習,

  • 自行下載直達領取鏈接:【點擊我即可獲得!】
  • 以上進階BATJ大廠學習資料可以免費分享給大家,需要完整版的朋友,【點這里可以看到全部內容】,

驗的Android程式員們學習,**

  • 自行下載直達領取鏈接:【點擊我即可獲得!】
  • 以上進階BATJ大廠學習資料可以免費分享給大家,需要完整版的朋友,【點這里可以看到全部內容】,

[外鏈圖片轉存中…(img-mLoEycoO-1623416459821)]

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

標籤:其他

上一篇:Android 采集音頻與音頻解碼

下一篇:Android-事件體系全面總結+實踐分析內容太過真實

標籤雲
其他(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)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more