Flutter冷知識 | 獲取dart的print內容

夢魘獸發表於2020-07-10

前言

這得從我在群裡時不時冒泡說起。

Flutter冷知識 | 獲取dart的print內容

Flutter冷知識 | 獲取dart的print內容

此刻樓上大佬裝成萌新,所以我就來了。

也就是看了群裡一位老哥寫的 Flutter 元件,可以檢視當前列印的日誌,還是挺厲害,但是列印的時候就只能呼叫他給定的方法來 print ,在寫安卓原生的時候有過這一需求,就是需要在 app 內部拿到 System.out.print 方法列印的字元。

java 中則是開放了這一方法。

System.setErr(new PrintStream(***));
System.setOut(new PrintStream(***));
複製程式碼

這樣所有的標準輸出與標準錯誤輸出都定向到了自己設定的流中,這個流可以指向檔案、管道、或是檔案描述符。

大致相關的一些需求:

  • 需要主動獲取 dartprint 內容,方便分析,因為過長的內容 print 是會被截斷的。
  • 新增少量程式碼的情況下讓自己的上線產品的輸出不能隨意被看見。

因為在平時的開發中,經常有被除錯的手機執行著其他的 Flutter 程式,自己的電腦終端就能拿到它的輸出。

初步分析

從 dart 內部

dartstdio.dart 檔案中有以下幾個變數。

int _stdinFD = 0;
int _stdoutFD = 1;
int _stderrFD = 2;
複製程式碼

還有一個重定向輸出的方法。

void _setStdioFDs(int stdin, int stdout, int stderr) {
  _stdinFD = stdin;
  _stdoutFD = stdout;
  _stderrFD = stderr;
}
複製程式碼

幾乎這個檔案所有的東西都加上了 _ ,而我們能拿到的只有 stdin/stdout/stderr,並且能夠操作的方法也比較少。

從程式

一個程式至少有 0,1,2 三個檔案描述符,它們分別對應 stdin/stdout/stderr

舉個例子:

我們在 c 語言用呼叫以下程式碼:

printf('hi');
複製程式碼

它其實等價於:

char tmp = "hi";
write(1, tmp, strlen(tmp));
複製程式碼

所以我考慮拷貝這三個檔案描述符,讓他們指向其他的地方。

確保在父程式執行拷貝函式,fork 後的子程式的檔案描述符是分開。

dup2 函式

dup2 函式用來拷貝檔案描述符。

函式原型:

int dup2 (int oldfd,int newfd)
複製程式碼

若引數 newfd 已經被程式使用,則系統就會將 newfd 所指的檔案關閉,若 newfd 等於 oldfd ,則返回 newfd ,而不關閉 newfd 所指的檔案。dup2 所複製的檔案描述符與原來的檔案描述符共享各種檔案狀態。共享所有的鎖定,讀寫位置和各項許可權或 flags 等。

例子

我們呼叫

int filefd = open("./tmp.txt", O_RDWR);
dup2(filefd,0);
複製程式碼

O_RDWR 表示可讀,可寫。

此時我們再次呼叫:

printf('hi');
複製程式碼

螢幕上不會再出現任何字元,輸出就直接寫入到了檔案 tmp.txt 中。

所以它其實等價於:

char tmp = "hi";
write(filefd, tmp, strlen(tmp));
複製程式碼

這也是偽終端實現的主要原理之一。

Flutter 實現

我們使用 dart:ffi 來完成這個操作。

ffi 相關本文不做具體解釋。

流程就是 dart -> dart:ffi -> c -> dup2

所以我們只需要簡單的幾行程式碼。

int filefd = open("./tmp.txt", O_RDWR);
dup2(filefd, 1);
dup2(filefd, 2);
複製程式碼

如此一來,開發者呼叫 print 函式,亦或者 dart 內部的一些列印,都等價於:

write(filefd, '***', strlen("***"));
複製程式碼

所有的內容都會被定向到我們指定的檔案中

測試截圖:

Flutter冷知識 | 獲取dart的print內容
此時我的 vs code 除錯終端拿不到任何輸出了,但我能從我設定到的檔案中讀取輸出。

平臺差異

在我反覆測試 Linux 桌面版Android 平臺的時候,發現在 LinuxFlutter 桌面 app 上,dartprint 其實就是寫入字元到檔案描述符 1,2,但在 Android 裝置上,print 寫入到的檔案描述符是 3

也就是

dup2(filefd, 3);
複製程式碼

over。有問題留言。有錯誤指出。

最後歡迎各位加入Flutter Candies,一起製造可愛好用的? 。 (QQ群:181398081)

Flutter冷知識 | 獲取dart的print內容

相關文章