stetho是Facebook推出的安卓APP網絡診斷和數據監控的工具,接入方便,功能強大,是開發者必備的好工具。
主要功能包括:
- 查看App的佈局
- 網絡請求抓包
- 數據庫、sp文件查看
- 自定義dumpapp插件
- 對於JavaScript的支持
無需root,只要能通過adb連接設備,操作方便。
接入方法
gradle配置
因爲目前我們的項目中已經集成了okhttp,只需要在build.gradle添加如下兩行配置
dependencies {
//...
compile 'com.facebook.stetho:stetho-js-rhino:1.3.1'
compile 'com.facebook.stetho:stetho-okhttp3:1.4.2'
}
初始化
在Application類中完成初始化
private void MyApplicationCreate() {
// ...
Stetho.initializeWithDefaults(mContext);
}
使用功能
- adb方式連接到設備
- 運行debug模式的app
- 在Chrome瀏覽器地址欄中輸入chrome://inspect
- 選擇需要inspect的應用進程
查看App的佈局
網絡診斷
給OkHttpClient添加攔截器。
new OkHttpClient.Builder()
.addNetworkInterceptor(new StethoInterceptor())
.build();
主要功能有包括下載圖片的預覽,JSON數據查看,網絡請求內容和返回內容
數據庫、sp文件查看
自定義dumpapp插件
Stetho.initialize(Stetho.newInitializerBuilder(context)
.enableDumpapp(new DumperPluginsProvider() {
@Override
public Iterable<DumperPlugin> get() {
return new Stetho.DefaultDumperPluginsBuilder(context)
.provide(new HelloWorldDumperPlugin())
.provide(new APODDumperPlugin(context.getContentResolver()))
.finish();
}
})
.enableWebKitInspector(new ExtInspectorModulesProvider(context))
.build());
其中HelloWorldDumperPlugin和APODDumperPlugin是自定義的插件,具體內容可以參考Stetho提供的sample程序
運行dumpapp腳本後以達到與app交互通信的效果。
$ ./dumpapp -l
apod
crash
files
hello
hprof
prefs
原理簡介
其中dumpapp是一個python腳本,通信的方式使用的是android提供的smartsocket接口
--- smartsockets -------------------------------------------------------
Port 5037 is used for smart sockets which allow a client on the host
side to request access to a service in the host adb daemon or in the
remote (device) daemon. The service is requested by ascii name,
preceeded by a 4 digit hex length. Upon successful connection an
"OKAY" response is sent, otherwise a "FAIL" message is returned. Once
connected the client is talking to that (remote or local) service.
client: <hex4> <service-name>
server: "OKAY"
client: <hex4> <service-name>
server: "FAIL" <hex4> <reason>
使用PyCharm對Python進行斷點調試:
這段腳本的功能就是通過讀取/proc/net/unix
文件去找app設置的socket
1. 掃描android所有提供socket功能的設備,找到steho的devtools_remote
2. 建立與第一步找到的進程socket,然後通過smartsocket進行通信。
3. 設備上的app相當於一個服務器,腳本是客戶端對它進行訪問
後綴爲_devtools_remote的socket是android留給chrome的後門。
// Note that _devtools_remote is a magic suffix understood by Chrome which //causes the discovery process to begin.
詳細內容可以看這篇官方文檔
這篇文檔提供的例子是在命令行中輸入下面的命令,就能在電腦上看到手機chrome中的內容了:
adb forward tcp:9222 localabstract:chrome_devtools_remote
打開的chrome-devtool其實是一個websocket連接。
private void handlePageList(LightHttpResponse response)
throws JSONException {
if (mPageListResponse == null) {
JSONArray reply = new JSONArray();
JSONObject page = new JSONObject();
page.put("type", "app");
page.put("title", makeTitle());
page.put("id", PAGE_ID);
page.put("description", "");
page.put("webSocketDebuggerUrl", "ws://" + mInspectorPath);
Uri chromeFrontendUrl = new Uri.Builder()
.scheme("http")
.authority("chrome-devtools-frontend.appspot.com")
.appendEncodedPath("serve_rev")
.appendEncodedPath(WEBKIT_REV)
.appendEncodedPath("devtools.html")
.appendQueryParameter("ws", mInspectorPath)
.build();
page.put("devtoolsFrontendUrl", chromeFrontendUrl.toString());
reply.put(page);
mPageListResponse = LightHttpBody.create(reply.toString(), "application/json");
}
setSuccessfulResponse(response, mPageListResponse);
}
在android上的服務端socket寫法,
詳見LocalSocketServer類的listenOnAddress方法
private void listenOnAddress(String address) throws IOException {
mServerSocket = bindToSocket(address);
LogUtil.i("Listening on @" + address);
while (!Thread.interrupted()) {
try {
// Use previously accepted socket the first time around, otherwise wait to accept another.
LocalSocket socket = mServerSocket.accept();
// Start worker thread
Thread t = new WorkerThread(socket, mSocketHandler);
t.setName(
WORKER_THREAD_NAME_PREFIX +
"-" + mFriendlyName +
"-" + mThreadId.incrementAndGet());
t.setDaemon(true);
t.start();
} catch (SocketException se) {
// ignore exception if interrupting the thread
if (Thread.interrupted()) {
break;
}
LogUtil.w(se, "I/O error");
} catch (InterruptedIOException ex) {
break;
} catch (IOException e) {
LogUtil.w(e, "I/O error initialising connection thread");
break;
}
}
LogUtil.i("Server shutdown on @" + address);
}
對於JavaScript的支持
Chrome開發者工具原生支持JavaScript,所以Stetho也提供了JavaScript的支持。
通過在console中輸入如下代碼可以讓設備app彈出一個toast
importPackage(android.widget);
importPackage(android.os);
var handler = new Handler(Looper.getMainLooper());
handler.post(function() { Toast.makeText(context, "Hello from JavaScript", Toast.LENGTH_LONG).show() });
更多玩法見Rhino on Stetho
相關鏈接
http://facebook.github.io/stetho/
https://github.com/facebook/stetho/tree/master/stetho-js-rhino
https://www.figotan.org/2016/04/18/using-stetho-to-diagnose-data-on-android/
https://developer.chrome.com/devtools/docs/remote-debugging-legacy
https://android.googlesource.com/platform/system/core/+/master/adb/protocol.txt