Application ProvidedAar 完结篇
之前寫過兩篇providedAar系列的文章,見:
- Android application中使用provided aar并沒有那么簡單
- 再談Application ProvidedAar
基本上已經完美的實現了Android Gradle Plugin [1.3.0,3.2.0+)版本com.android.application中使用provided aar的功能,支持代碼和資源的引用,同時不將代碼和資源編譯進去,但是還遺留了最后一個比較頭疼的問題,那就是configurate階段和構建階段會被警告日志刷屏。
具體現象如下:
依賴少的時候還好,但是依賴一多或者傳遞依賴一多,整個控制臺就被這種日志刷屏了,所以必須解決一下這個問題。具體解決思路就是把這種日志的級別調整為info級別,由于正常構建不會輸出info級別的日志,所以這種日志就不會刷屏了。
這個問題只有在Android Gradle Plugin 2.3.3及以下會存在,3.0.0及以上不存在這個問題,因此我們只要解決2.3.3及以下版本即可。
一開始的解決思路是利用groovy的MOP,運行期動態修改函數指向我們自己的調用,具體的偽代碼如下
| 1234567 | project.getLogger().getMetaClass().warn = {String msg -> if (msg 滿足我們攔截的信息){originalLogger.info(msg) return }調用原來的邏輯} |
結果發現試了n多種MOP方式,都無法完成方法替換,結論是groovy的MOP無法修改java中的代碼調用,只能對groovy中的代碼調用生效,而Android Gradle Plugin的代碼和gradle的相關部分代碼是由java實現的,因此MOP這種場景下不適用。
于是換一種思路。通過查看gradle的代碼發現,我們可以攔截到具體的日志輸出邏輯,相關代碼如下:
| 1234567891011 | private void log(LogLevel logLevel, Throwable throwable, String message) {Object buildOperationId = BuildOperationIdentifierRegistry.getCurrentOperationIdentifier();LogEvent logEvent = new LogEvent(clock.getCurrentTime(), name, logLevel, message, throwable, buildOperationId);OutputEventListener outputEventListener = context.getOutputEventListener();try {outputEventListener.onOutput(logEvent);} catch (Throwable e) {// fall back to standard oute.printStackTrace(System.out);}} |
可以發現最終日志輸出是調用outputEventListener的onOutput函數,傳遞一個LogEvent攜帶日志相關信息進行輸出的,而outputEventListener是運行期由Android Gradle Plugin設置進去的,具體的設置相關方法在logger的context中,如下:
| 1234567 | public void setOutputEventListener(OutputEventListener outputEventListener) { this.outputEventListener.set(outputEventListener);}public OutputEventListener getOutputEventListener() { return (OutputEventListener)this.outputEventListener.get();} |
所以我們其實只要設置OutputEventListener對象為我們自己的對象,并且將原始的OutputEventListener對象保存起來,就可以實現日志輸出邏輯的自定義,從而達到我們的效果。大致的偽代碼如下:
| 12345678910111213 | org.gradle.internal.logging.slf4j.OutputEventListenerBackedLoggerContext listenerBackedLoggerContext = project.getLogger().getMetaClass().getProperty(project.getLogger(), "context")org.gradle.internal.logging.events.OutputEventListener originalOutputEventListener = listenerBackedLoggerContext.getOutputEventListener()org.gradle.api.logging.LogLevel originalOutputEventLevel = listenerBackedLoggerContext.getLevel()listenerBackedLoggerContext.setOutputEventListener(new org.gradle.internal.logging.events.OutputEventListener() {@Overridevoid onOutput(org.gradle.internal.logging.events.OutputEvent outputEvent) { if (msg 滿足我們攔截的信息){修改日志級別為info并輸出return}調用原來的邏輯}}) |
但是不幸的是OutputEventListenerBackedLoggerContext,OutputEventListener和LogLevel這三個類,在不同的gradle版本中包名都不一樣,除了包名,其他邏輯完全一樣,這就會帶來一個問題,即使進行了兼容,也無法編譯成功,編譯過程中必然會報類找不到,那么有沒有辦法解決這個問題呢。
最終的解決方法是使用def關鍵字和lambda表達式,這樣包名相關的信息就可以完全被屏蔽了,具體的實現邏輯如下:
| 123456789101112131415161718192021 | //redirect warning log to info logdef listenerBackedLoggerContext = project.getLogger().getMetaClass().getProperty(project.getLogger(), "context")def originalOutputEventListener = listenerBackedLoggerContext.getOutputEventListener()def originalOutputEventLevel = listenerBackedLoggerContext.getLevel()listenerBackedLoggerContext.setOutputEventListener({def outputEvent -> def logLevel = originalOutputEventLevel.name() if (!("QUIET".equalsIgnoreCase(logLevel) || "ERROR".equalsIgnoreCase(logLevel))) { if ("WARN".equals(outputEvent.getLogLevel().name())) {String message = outputEvent.getMessage() //Provided dependencies can only be jars. //provided dependencies can only be jars. if (message != null && (message.contains("Provided dependencies can only be jars.") || message.contains("provided dependencies can only be jars. "))) {project.logger.info(message)return}} if (originalOutputEventListener != null) {originalOutputEventListener.onOutput(outputEvent)}}}) |
這就完成了日志邏輯的替換,具體思路就是獲取logger context對象,拿到原始的OutputEventListener對象,然后拿到原始的日志輸出級別,在日志級別大于WARN時,不會輸出WARN的日志,因此只要處理日志級別小于等于WARN的情況(即不等于QUIE和ERROR),才需要進行這個邏輯處理,處理的對應報錯的日志的級別是WARN的,然后判斷輸出的日志里面是否含有關鍵字Provided dependencies can only be jars,這里特別注意一下,有些版本可能Provided是小寫的,即provided,當條件滿足后,將日志調整為info進行輸出,否則,調用原始日志輸出邏輯。
于是乎,10來行代碼,將控制臺的刷屏日志徹底消除了,整個世界都清凈了。
http://fucknmb.com/2018/03/31/Application-ProvidedAar%E5%AE%8C%E7%BB%93%E7%AF%87/總結
以上是生活随笔為你收集整理的Application ProvidedAar 完结篇的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 再谈 Application Provi
- 下一篇: Java-gt;Android并发编程引