What is Stetho ?
Stetho 是一個功能強(qiáng)大的 Android 應(yīng)用調(diào)試橋,起到橋梁的作用,連接 Android 應(yīng)用和 Chrome尸折,通過 Chrome 開發(fā)者工具調(diào)試 Android 應(yīng)用,提供視圖元素檢查,網(wǎng)絡(luò)監(jiān)控旷太,數(shù)據(jù)庫動態(tài)交互,Dumpapp(可擴(kuò)展的命令行交互接口)销睁,JavaScript Console 等功能供璧。
當(dāng)啟用后,開發(fā)者可以通過 Chrome 桌面瀏覽器中的開發(fā)者工具訪問本地應(yīng)用冻记。開發(fā)者也可以選擇啟用可選的 dumpapp 工具提供一個強(qiáng)大的應(yīng)用內(nèi)部命令行接口睡毒。
官網(wǎng):http://facebook.github.io/stetho/
項(xiàng)目地址:https://github.com/facebook/stetho
一旦你完成了下面的設(shè)置說明,只要啟動你電腦上的 Chrome 瀏覽器并輸入chrome://inspect點(diǎn)擊 "Inspect" 按鈕即可開始調(diào)試冗栗。
注意:如果你點(diǎn)擊 "Inspect" 出現(xiàn)的是一個空白頁面演顾,請嘗試下面的解決方案:
可能是 Chrome 版本過低,嘗試升級 Chrome隅居。
先翻個墻再打開調(diào)試界面(第一次需要這樣钠至,后面不需要)。
配置說明
添加 stetho 主依賴
在 Gradle 中包含 stetho
// Gradle dependency on Stetho
dependencies {
compile 'com.facebook.stetho:stetho:1.4.1'
}
在 Maven 中包含 stetho
com.facebook.stetho
stetho
1.4.1
只有 stetho 主依賴是必須的胎源,但你可能還希望有一個網(wǎng)絡(luò)助手
dependencies {
compile 'com.facebook.stetho:stetho-okhttp3:1.4.1'
}
或者:
dependencies {
compile 'com.facebook.stetho:stetho-okhttp:1.4.1'
}
或者:
dependencies {
compile 'com.facebook.stetho:stetho-urlconnection:1.4.1'
}
功能說明
Chrome DevTools
image
Stetho 為你的應(yīng)用提供了 C/S 協(xié)議實(shí)現(xiàn)棉钧,所以你可以通過 Chrome 集成的前端開發(fā)工具訪問你的應(yīng)用。只要你的應(yīng)用集成了 Stetho涕蚤,只需導(dǎo)航到chrome://inspect并點(diǎn)擊 "Inspect" 即可開始使用宪卿。
Network Inspection
image
使用 Chrome 開發(fā)者工具各種功能實(shí)現(xiàn)網(wǎng)絡(luò)監(jiān)控的诵,包括圖片預(yù)覽,JSON 響應(yīng)輔助工具愧捕,甚至把跟蹤信息導(dǎo)出為 HAR 格式文件奢驯。
Database Inspection
image
SQLite 數(shù)據(jù)庫可視化與交互,具備完全讀寫功能次绘,
Web SQL 下的是應(yīng)用的數(shù)據(jù)庫瘪阁,點(diǎn)擊數(shù)據(jù)庫可以輸入 SQL 語句對其進(jìn)行操作
Local Storage 就是對應(yīng) Android 下的 SharedPreferences,可修改 SharedPreferences 中的值
View Hierarchy
image
View Hierarchy 支持 API 15 或更高版本邮偎。使用 View Hierarchy 可以很方便的檢查運(yùn)行時界面的層次結(jié)構(gòu)與數(shù)據(jù)管跺,比如:
View Hierarchy 中包含界面所有元素的層次結(jié)構(gòu)和屬性
鼠標(biāo)移動到 View Hierarchy 中某個 View,app 中對應(yīng)的 View 會高亮顯示
點(diǎn)擊 View Hierarchy 左上角的搜索按鈕禾进,再點(diǎn)擊 app 當(dāng)前界面的控件豁跑,View Hierarchy 會顯示該控件在層次中的位置
dumpapp
image
Dumpapp 為應(yīng)用提供了一個可擴(kuò)展的命令行交互接口,提供了一組默認(rèn)的插件泻云,但是 dumpapp 的真正強(qiáng)大之處在于能夠輕松創(chuàng)建自己的插件艇拍!
dumpapp 就在工程的 scripts/dumpapp 下,遺憾的是目前在 Windows 下還用不了宠纯,因?yàn)樗惶峁┝?Linux/Mac 下的執(zhí)行腳本卸夕。
常用命令(插件):
列出所有 Plugin :./scripts/dumpapp -p com.facebook.stetho.sample -l
打印 SharedPreferences :./scripts/dumpapp prefs print
寫 SharedPreferences :./scripts/dumpapp prefs write
dumpapp 默認(rèn)提供的插件就在com.facebook.stetho.dumpapp.plugins.*,具體使用方法可以參考源碼中的說明婆瓜。
JavaScript Console
image
JavaScript Console 允許執(zhí)行那些可以與應(yīng)用或 Android SDK 交互的 JavaScript 代碼快集。
Stetho 使用Rhino實(shí)現(xiàn)使用腳本方式調(diào)用 Java。
Rhino 是一個完全使用Java語言編寫的開源JavaScript實(shí)現(xiàn)廉白。Rhino通常用于在Java程序中个初,為最終用戶提供腳本化能力。它被作為J2SE 6上的默認(rèn)Java腳本化引擎猴蹂。
Github:https://github.com/mozilla/rhino
官網(wǎng):https://www.mozilla.org/rhino/
集成說明
1. 初始化
在你的 Application 初始化時候調(diào)用 Stetho 的初始化方法:
public class MyApplication extends Application {
public void onCreate() {
super.onCreate();
Stetho.initializeWithDefaults(this);
}
}
這將啟用大多數(shù)默認(rèn)配置院溺,但不啟用一些額外的鉤子(啟用網(wǎng)絡(luò)監(jiān)控需要注意)。 有關(guān)各個子系統(tǒng)的具體細(xì)節(jié)磅轻,請參見下文珍逸。
2. 啟用網(wǎng)絡(luò)監(jiān)控
如果你使用的是 2.2.x+ 或 3.x 版本的 OkHttp 庫,可以使用攔截器系統(tǒng)自動掛接到現(xiàn)有堆棧瓢省。 這是目前啟用網(wǎng)絡(luò)監(jiān)控的最簡單和最直接的方法弄息。
For OkHttp 2.x
OkHttpClient client = new OkHttpClient();
client.networkInterceptors().add(new StethoInterceptor());
For OkHttp 3.x
new OkHttpClient.Builder()
.addNetworkInterceptor(new StethoInterceptor())
.build();
由于攔截器可以修改請求和響應(yīng),應(yīng)該在其他攔截器之后添加 Stetho 攔截器以獲取準(zhǔn)確的網(wǎng)絡(luò)交互視圖勤婚。
如果你使用HttpURLConnection摹量,可以使用StethoURLConnectionManager來幫助集成,但該方法有一些注意事項(xiàng),比如你必須明確地添加Accept-Encoding:gzip到請求頭缨称,并手動處理壓縮的響應(yīng)凝果,以便 Stetho 報告壓縮的有效負(fù)載大小。具體可以參考stetho-sample中的Networker的實(shí)現(xiàn)睦尽。
Stetho 目前沒有提供 HttpClient 網(wǎng)絡(luò)監(jiān)控支持器净,具體原因可以查看issues 116(HttpClient 在 Android5.0 已經(jīng)被廢棄,不建議再使用)当凡。
OkHttp + Retrofit
一般開發(fā)中我們都會使用OkHttp + Retrofit山害,OkHttp用于 HTTP 網(wǎng)絡(luò)交互,Retrofit用于將 HTTP API 轉(zhuǎn)換為 Java 接口沿量。
默認(rèn)情況下浪慌,Retrofit會自己創(chuàng)建一個OkHttpClient,我們也可以在創(chuàng)建Retrofit的時候通過client(OkHttpClient client)方法提供一個OkHttpClient朴则。
sRetrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.client(sClient)
.build();
通過client(OkHttpClient client)方法設(shè)置共用一個OkHttpClient权纤,監(jiān)控Retrofit中的網(wǎng)絡(luò)交互。
更多細(xì)節(jié)見stetho-sample項(xiàng)目乌妒。
3. 自定義 dumpapp 插件
自定義插件主要是實(shí)現(xiàn) DumperPlugin 接口中的String getName()和void dump(DumperContext dumpContext)方法汹想。getName()方法返回插件的名稱,dump(DumperContext dumpContext)是命令行中調(diào)用該插件時的回調(diào)方法撤蚊。其中古掏,dumpContext.getStdout()獲取命令行輸出,dumpContext.getArgsAsList()獲取命令行調(diào)用的參數(shù)列表拴魄。
public class MyDumperPlugin implements DumperPlugin {
private static final String XML_SUFFIX = ".xml";
private static final String NAME = "prefs";
private final Context mAppContext;
public MyDumperPlugin(Context context) {
mAppContext = context.getApplicationContext();
}
@Override
public String getName() {
return NAME;
}
@Override
public void dump(DumperContext dumpContext) throws DumpUsageException {
PrintStream writer = dumpContext.getStdout();
List args = dumpContext.getArgsAsList();
String commandName = args.isEmpty() ? "" : args.remove(0);
if (commandName.equals("print")) {
doPrint(writer, args);
} else if (commandName.equals("write")) {
doWrite(args);
} else {
doUsage(writer);
}
}
// 省略部分代碼
}
然后把初始化調(diào)用替換如下:
Stetho.initialize(Stetho.newInitializerBuilder(context)
.enableDumpapp(new DumperPluginsProvider() {
@Override
public Iterable get() {
return new Stetho.DefaultDumperPluginsBuilder(context)
.provide(new MyDumperPlugin())
.finish();
}
})
.enableWebKitInspector(Stetho.defaultInspectorModulesProvider(context))
.build());
更多細(xì)節(jié)見stetho-sample項(xiàng)目冗茸。
4. 啟用JavaScript Console
啟用 JavaScript Console 只需在 build.gradle 中添加如下依賴即可:
compile "com.facebook.stetho:stetho-js-rhino:1.4.1"
啟動 app席镀,在 Chrome 開發(fā)者工具的 Console 輸入下面代碼使 app 打印一個Toast:
importPackage(android.widget);
importPackage(android.os);
var handler = new Handler(Looper.getMainLooper());
handler.post(function() { Toast.makeText(context, "hello", Toast.LENGTH_LONG).show() });
Paste_Image.png
importPackage(android.widget)等于 java 中import android.widget.*;匹中,JavaScript 中使用 var 定義變量,這段代碼就是創(chuàng)建了一個 handler 并調(diào)用 post 方法在 ui 線程彈一個 Toast豪诲。
關(guān)于 Rhino 相關(guān)語法可以參考下面的文檔
在Toast.makeText中的 context 是從哪里來的呢?
context 是在com.facebook.stetho.rhino.JsRuntimeReplFactoryBuilder的initJsScope方法中被綁定到JSContext的顶捷,下面是 initJsScope 方法的源碼:
private @NonNull ScriptableObject initJsScope(@NonNull Context jsContext) {
// Set the main Rhino goodies
ImporterTopLevel importerTopLevel = new ImporterTopLevel(jsContext);
ScriptableObject scope = jsContext.initStandardObjects(importerTopLevel, false);
ScriptableObject.putProperty(scope, "context", Context.javaToJS(mContext, scope));
try {
importClasses(jsContext, scope);
importPackages(jsContext, scope);
importConsole(scope);
importVariables(scope);
importFunctions(scope);
} catch (StethoJsException e) {
String message = String.format("%s\n%s", e.getMessage(), Log.getStackTraceString(e));
LogUtil.e(e, message);
CLog.writeToConsole(Console.MessageLevel.ERROR, Console.MessageSource.JAVASCRIPT, message);
}
return scope;
}
JsRuntimeReplFactoryBuilder提供了一些方法可以傳遞自己的變量,類屎篱,包和函數(shù)到 JavaScript 環(huán)境服赎。
添加變量,類交播,包和函數(shù)到 JavaScript 運(yùn)行時
修改初始化代碼如下:
Stetho.initialize(Stetho.newInitializerBuilder(context)
.enableWebKitInspector(new ExtInspectorModulesProvider(context))
.build());
private static class ExtInspectorModulesProvider implements InspectorModulesProvider {
private Context mContext;
private final Handler handler = new Handler(Looper.getMainLooper());
ExtInspectorModulesProvider(Context context) {
mContext = context;
}
@Override
public Iterable get() {
return new Stetho.DefaultInspectorModulesBuilder(mContext)
.runtimeRepl(new JsRuntimeReplFactoryBuilder(mContext)
// 添加變量
.addVariable("test", new AtomicBoolean(true))
// 添加類
.importClass(R.class)
// 添加包
.importPackage(MyApplication.class.getPackage().getName())
// 添加方法到 javascript: void toast(String)
.addFunction("toast", new BaseFunction() {
@Override
public Object call(org.mozilla.javascript.Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
// javascript 傳遞的參數(shù)在 varags
final String message = args[0].toString();
handler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(mContext, message, Toast.LENGTH_LONG).show();
}
});
// 在 javascript 返回 undefined
return org.mozilla.javascript.Context.getUndefinedValue();
}
})
.build())
.finish();
}
}
說明:Java原語類型將被自動裝箱重虑,只有對象可以傳遞到 JavaScript 運(yùn)行時。
綁定完成后就可以在 JavaScript Console 中使用自己的變量秦士,類缺厉,包和函數(shù)了。
Paste_Image.png
注意:Rhino 對包名的檢查是嚴(yán)格的,必須是com.**提针,org.**命爬,net.**之類比較正規(guī)的格式。假如:包名使用linchaolong.stetho.demo辐脖,在importClasses(linchaolong.stetho.demo.R)時饲宛,ScriptRuntime會報EcmaError
Failed to import class: linchaolong.stetho.demo.R
com.facebook.stetho.rhino.JsRuntimeReplFactoryBuilder$StethoJsException: Failed to import class: linchaolong.stetho.demo.R
at com.facebook.stetho.rhino.JsRuntimeReplFactoryBuilder.importClasses(JsRuntimeReplFactoryBuilder.java:195)
at com.facebook.stetho.rhino.JsRuntimeReplFactoryBuilder.initJsScope(JsRuntimeReplFactoryBuilder.java:173)
at com.facebook.stetho.rhino.JsRuntimeReplFactoryBuilder.initJsScope(JsRuntimeReplFactoryBuilder.java:158)
at com.facebook.stetho.rhino.JsRuntimeReplFactoryBuilder.access$000(JsRuntimeReplFactoryBuilder.java:45)
at com.facebook.stetho.rhino.JsRuntimeReplFactoryBuilder$1.newInstance(JsRuntimeReplFactoryBuilder.java:146)
at com.facebook.stetho.inspector.protocol.module.Runtime$Session.getRepl(Runtime.java:271)
at com.facebook.stetho.inspector.protocol.module.Runtime$Session.evaluate(Runtime.java:260)
at com.facebook.stetho.inspector.protocol.module.Runtime.evaluate(Runtime.java:158)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.facebook.stetho.inspector.MethodDispatcher$MethodDispatchHelper.invoke(MethodDispatcher.java:96)
at com.facebook.stetho.inspector.MethodDispatcher.dispatch(MethodDispatcher.java:67)
at com.facebook.stetho.inspector.ChromeDevtoolsServer.handleRemoteRequest(ChromeDevtoolsServer.java:129)
at com.facebook.stetho.inspector.ChromeDevtoolsServer.handleRemoteMessage(ChromeDevtoolsServer.java:111)
at com.facebook.stetho.inspector.ChromeDevtoolsServer.onMessage(ChromeDevtoolsServer.java:87)
at com.facebook.stetho.websocket.WebSocketSession$1.handleTextFrame(WebSocketSession.java:176)
at com.facebook.stetho.websocket.WebSocketSession$1.onCompleteFrame(WebSocketSession.java:136)
at com.facebook.stetho.websocket.ReadHandler.readLoop(ReadHandler.java:44)
at com.facebook.stetho.websocket.WebSocketSession.handle(WebSocketSession.java:45)
at com.facebook.stetho.websocket.WebSocketHandler.doUpgrade(WebSocketHandler.java:117)
at com.facebook.stetho.websocket.WebSocketHandler.handleRequest(WebSocketHandler.java:83)
at com.facebook.stetho.server.http.LightHttpServer.dispatchToHandler(LightHttpServer.java:84)
at com.facebook.stetho.server.http.LightHttpServer.serve(LightHttpServer.java:61)
at com.facebook.stetho.inspector.DevtoolsSocketHandler.onAccepted(DevtoolsSocketHandler.java:52)
at com.facebook.stetho.server.ProtocolDetectingSocketHandler.onSecured(ProtocolDetectingSocketHandler.java:63)
at com.facebook.stetho.server.SecureSocketHandler.onAccepted(SecureSocketHandler.java:33)
at com.facebook.stetho.server.LazySocketHandler.onAccepted(LazySocketHandler.java:36)
at com.facebook.stetho.server.LocalSocketServer$WorkerThread.run(LocalSocketServer.java:167)
Caused by: org.mozilla.javascript.EcmaError: ReferenceError: "linchaolong" is not defined. (chrome#1)
at org.mozilla.javascript.ScriptRuntime.constructError(ScriptRuntime.java:3949)
at org.mozilla.javascript.ScriptRuntime.constructError(ScriptRuntime.java:3927)
at org.mozilla.javascript.ScriptRuntime.notFoundError(ScriptRuntime.java:4012)
at org.mozilla.javascript.ScriptRuntime.name(ScriptRuntime.java:1849)
at org.mozilla.javascript.Interpreter.interpretLoop(Interpreter.java:1558)
at org.mozilla.javascript.Interpreter.interpret(Interpreter.java:815)
at org.mozilla.javascript.InterpretedFunction.call(InterpretedFunction.java:109)
at org.mozilla.javascript.ContextFactory.doTopCall(ContextFactory.java:393)
at org.mozilla.javascript.ScriptRuntime.doTopCall(ScriptRuntime.java:3280)
at org.mozilla.javascript.InterpretedFunction.exec(InterpretedFunction.java:120)
at org.mozilla.javascript.Context.evaluateString(Context.java:1191)
at com.facebook.stetho.rhino.JsRuntimeReplFactoryBuilder.importClasses(JsRuntimeReplFactoryBuilder.java:193)
... 27 more
更多細(xì)節(jié)請參考stetho-js-rhino項(xiàng)目中的README.md
只在debug模式下使用 Stetho
修改 dependencies 配置,只在 debug 模式下編譯stetho和stetho-js-rhino
dependencies {
// Debug
debugCompile "com.facebook.stetho:stetho:${stetho}"
compile "com.facebook.stetho:stetho-okhttp3:${stetho}"
debugCompile "com.facebook.stetho:stetho-js-rhino:${stetho}"
}
說明:${stetho}是 stetho 的版本號嗜价。
在src/debug/java目錄下新建一個DebugApplication繼承自MyApplication艇抠,并把初始化 Stetho 的代碼移到DebugApplication
public class DebugApplication extends MyApplication{
@Override public void onCreate() {
super.onCreate();
Stetho.initializeWithDefaults(this);
}
在src/debug目錄下創(chuàng)建一個AndroidManifest.xml,并添加 debug 模式下需要的權(quán)限和修改 application 節(jié)點(diǎn)android:name值為 DebugApplication(使用tools:replace覆蓋android:name字段)
xmlns:tools="http://schemas.android.com/tools"
package="com.facebook.stetho.sample">
tools:replace="android:name"
android:name=".DebugApplication" />
Paste_Image.png
完成上面處理后久锥,Stetho 只在 debug 版本下起作用练链,不影響 release 版本。
作者:linchaolong
鏈接:http://www.reibang.com/p/38d8324b126a
來源:簡書
著作權(quán)歸作者所有奴拦。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán)媒鼓,非商業(yè)轉(zhuǎn)載請注明出處。