为什么安装的java println可以输出到logcat

起因:鸿蒙系统中,******* 的println没有在hilog里打印日志,而arkts的Console.log会在hilog里打印日志,有点不友好。

安卓系统中,java的println能在logcat里直接打印,而C的printf无法在logcat里直接打印。

众所周知的常识

安卓APP进程来自 zygote 进程,由于它是 GUI 的而且没有命令行交互,所以它的 stdin/stdout/stderr 都是被关闭的。这就解释了C语言printf没有任何效果。

从我记事起,Java的 System.out.println("HelloWorld") 会打印到logcat里,对新手很友好。

另外,如果使用 adb shell 里的 ampm 系列的指令,或者调用任意的jar,Java的 System.out.println("HelloWorld") 会打印到控制台、不打印到logcat里,交互也很友好。

阅读 Java Library 源码,未找到原因

System.java的实现在

1
2
3
4
5
6
7
8
9
10
// https://xrefandroid.com/android-6.0.1_r66/xref/libcore/luni/src/main/java/java/lang/System.java#108
static {
err = new PrintStream(new FileOutputStream(FileDescriptor.err));
out = new PrintStream(new FileOutputStream(FileDescriptor.out));
in = new BufferedInputStream(new FileInputStream(FileDescriptor.in));
unchangeableSystemProperties = initUnchangeableSystemProperties();
systemProperties = createSystemProperties();

addLegacyLocaleSystemProperties();
}

看起来没什么特殊的,简单的io操作,猜想:可能是操作系统在底层进行了重定向。

尝试构造poc,创建对象直接打印内容

1
2
3
4
PrintStream out = System.out;
PrintStream myStream = new PrintStream(new FileOutputStream(FileDescriptor.out));
out.println("Hello out"); // ok
myStream.println("Hello myStream"); // no output

很遗憾,失败了,使用 System.out 可以正常打印,手动创建的 myStream 无法打印。猜想错误。

调试和观测,细微差异,找到原因

经过调试,发现 out 的类型是 com.android.internal.os.AndroidPrintStream,而 myStream 的类型是 PrintStream

恰好它只有一处引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// https://xrefandroid.com/android-6.0.1_r66/xref/frameworks/base/core/java/com/android/internal/os/RuntimeInit.java#332
public static void redirectLogStreams() {
System.out.close();
System.setOut(new AndroidPrintStream(Log.INFO, "System.out"));
System.err.close();
System.setErr(new AndroidPrintStream(Log.WARN, "System.err"));
}

public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
// ...
redirectLogStreams();
// ...
}

这下彻底破案了,System有另一个接口叫 setOut,从而可以定义输出流,属于 Java 原生支持。

反观 *******

emmmmm,短期内看来是无法实现了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// https://gitcode.com/Cangjie/cangjie_runtime/blob/main/stdlib/libs/std/core/print.cj
package std.core

@Frozen
public func print(str: String, flush!: Bool = false): Unit {
unsafe {
var cp = acquireArrayRawData(str.rawData())
let ret = CJ_CORE_PrintUTF8(cp.pointer, str.size, false, flush)
releaseArrayRawData(cp)
if (ret == -1) {
throw OutOfMemoryError()
}
}
}