diff --git a/android/build.gradle b/android/build.gradle index bceeded1b..693d11088 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -51,6 +51,7 @@ android { implementation deps.lithoCore implementation deps.lithoWidget implementation deps.rhino + implementation deps.leakcanary } } diff --git a/android/src/main/java/com/facebook/sonar/plugins/leakcanary/LeakCanarySonarPlugin.java b/android/src/main/java/com/facebook/sonar/plugins/leakcanary/LeakCanarySonarPlugin.java new file mode 100644 index 000000000..d703a0566 --- /dev/null +++ b/android/src/main/java/com/facebook/sonar/plugins/leakcanary/LeakCanarySonarPlugin.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2018-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the LICENSE + * file in the root directory of this source tree. + * + */ + +package com.facebook.sonar.plugins.leakcanary; + +import android.util.Log; +import com.facebook.sonar.core.SonarConnection; +import com.facebook.sonar.core.SonarObject; +import com.facebook.sonar.core.SonarPlugin; +import com.facebook.sonar.core.SonarReceiver; +import com.facebook.sonar.core.SonarResponder; +import java.util.ArrayList; +import java.util.List; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +public class LeakCanarySonarPlugin implements SonarPlugin { + + private static final String TAG = "LeakCanarySonarPlugin"; + + private static final String REPORT_LEAK_EVENT = "reportLeak"; + private static final String CLEAR_EVENT = "clear"; + private static final String LEAKS_KEY = "leaks"; + + private SonarConnection mConnection; + + private final List leakList = new ArrayList<>(); + + @Override + public String getId() { + return "LeakCanary"; + } + + @Override + public void onConnect(SonarConnection connection) { + mConnection = connection; + sendLeakList(); + + mConnection.receive( + CLEAR_EVENT, + new SonarReceiver() { + @Override + public void onReceive(SonarObject params, SonarResponder responder) throws Exception { + leakList.clear(); + } + }); + } + + @Override + public void onDisconnect() throws Exception { + mConnection = null; + } + + private void sendLeakList() { + if (mConnection != null) { + JSONObject obj = new JSONObject(); + try { + obj.put(LEAKS_KEY, new JSONArray(leakList)); + mConnection.send(REPORT_LEAK_EVENT, new SonarObject(obj)); + } catch (JSONException e) { + Log.w(TAG, "Failure to serialize leak list: ", e); + } + } + } + + public void reportLeak(String leakInfo) { + leakList.add(leakInfo); + sendLeakList(); + } +} diff --git a/android/src/main/java/com/facebook/sonar/plugins/leakcanary/RecordLeakService.java b/android/src/main/java/com/facebook/sonar/plugins/leakcanary/RecordLeakService.java new file mode 100644 index 000000000..c7f06d1b1 --- /dev/null +++ b/android/src/main/java/com/facebook/sonar/plugins/leakcanary/RecordLeakService.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the LICENSE + * file in the root directory of this source tree. + * + */ +package com.facebook.sonar.plugins.leakcanary; + +import static com.squareup.leakcanary.LeakCanary.leakInfo; + +import com.facebook.sonar.android.AndroidSonarClient; +import com.facebook.sonar.core.SonarClient; +import com.squareup.leakcanary.AbstractAnalysisResultService; +import com.squareup.leakcanary.AnalysisResult; +import com.squareup.leakcanary.HeapDump; + +/** + * When a leak is detected, sends results to connected Sonar desktop app. In order to use this + * service in place of the default, a custom RefWatcher will need to be created See + * https://github.com/square/leakcanary/wiki/Customizing-LeakCanary#uploading-to-a-server + */ +public class RecordLeakService extends AbstractAnalysisResultService { + @Override + protected void onHeapAnalyzed(HeapDump heapDump, AnalysisResult result) { + final String leakInfo = leakInfo(this, heapDump, result, true); + final SonarClient client = AndroidSonarClient.getInstance(this); + + if (client != null) { + final LeakCanarySonarPlugin plugin = client.getPlugin("LeakCanary"); + if (plugin != null) { + plugin.reportLeak(leakInfo); + } + } + } +} diff --git a/build.gradle b/build.gradle index d5fa76f25..213e495c6 100644 --- a/build.gradle +++ b/build.gradle @@ -66,6 +66,7 @@ ext.deps = [ junit : 'junit:junit:4.12', stetho : 'com.facebook.stetho:stetho:1.5.0', okhttp3 : 'com.squareup.okhttp3:okhttp:3.10.0', + leakcanary : 'com.squareup.leakcanary:leakcanary-android:1.6.1', // Plugin dependencies rhino : 'org.mozilla:rhino:1.7.10', ]