Library / SDK
androidPluto/pluto avatar
androidPluto/pluto

Pluto: an on-device debugger that ships inside your Android app

Android Pluto is a on-device debugging framework for Android applications, which helps intercept Network calls, capture Crashes & ANRs, manipulate application data on-the-go, and much more.

700 stars66 forksKotlinApache-2.0

At a glance

What is it?
Pluto is a Kotlin library that embeds a debugging framework into an Android app, giving you a network inspector, crash and ANR capture, and editors for shared preferences, DataStore and Room data without a desktop connection. The trick is the plugin architecture and a no-op artifact that keeps the whole thing out of release builds.
Who is it for?
Adopt Pluto if you are building an Android app in Kotlin and want a network inspector and data editor that works on a real device without a desktop bridge, and if you are willing to keep the debug artifact out of your release build with the no-op variant. Do not adopt it if your team already has a mature debugging setup, and do not ship it in production, since the no-op pairing in the Gradle configuration is the only thing standing between a data inspector and your users.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 175 days ago.
What is it written in?
Mainly Kotlin, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A debugger that lives inside the application

Pluto is an on-device debugging framework for Android applications. The README lists what it does: it helps with the inspection of HTTP requests and responses, it captures crashes and ANRs, and it manipulates application data on the go.

The word on-device is the whole design decision. Most Android debugging tools need a desktop: a debugger that attaches over adb, a proxy on the machine, an IDE integration. Pluto instead ships as a library in the app, so the tooling is present on the handset and a notification tells you it is there. A developer picks up the phone, opens the Pluto UI and inspects what just happened, with no laptop in the loop.

That approach has a consequence that shows up in the architecture. If the debugging code is in the app process, it is in the shipped binary unless you take it out. The project solves this with a second artifact, pluto-no-op, which is a do-nothing implementation of the same API. The release build depends on the no-op variant, so the code paths exist and do nothing, and the size and behaviour of the release artifact stay close to what they would be without the framework.

The library also comes with a UI to monitor and share the information it collects, plus APIs so the application itself can read and use that data. That second part is the one that separates Pluto from a plain logging library: the captured data is meant to be consumed by code, not only looked at by a human.

Two Gradle dependencies, one of them inert

Pluto is distributed through mavenCentral, and the integration is a pair of Gradle dependencies in the app module's build.gradle file. The documented snippet is:

groovy
def plutoVersion = "3.0.0"

dependencies {
  ....
  debugImplementation "com.androidpluto:pluto:$plutoVersion"
  releaseImplementation "com.androidpluto:pluto-no-op:$plutoVersion"
  ....
}

The note above the snippet is explicit that both the pluto and the pluto-no-op variant are needed, and the reason given is to isolate Pluto from release builds. The pattern is a familiar one from other Android libraries that ship a no-op artifact, and it is worth being precise about what it does and does not buy you. It guarantees that the release build links against an implementation that does nothing, so the debugger cannot run and cannot capture anything. It does not guarantee that the release artifact is byte-identical to a build without Pluto, because the plugin registration code and the classes still exist in the binary, and the R8 shrinker has to be told to strip them.

That is why the sample directory in the repository carries a proguard-rules.pro file, and why a production integration has to check the shrinking configuration rather than trusting the Gradle snippet alone. The rest of the repository layout supports this: build.gradle.kts and settings.gradle at the root, a buildSrc directory, gradle.properties, a version.properties file, a publishTasks.gradle.kts, a maven-versions.gradle.kts, and a pre-commit hook. It is a multi-module Gradle build with a published release process rather than a single library project.

Initialising the installer in the Application class

Once the dependency is in place, Pluto is initialised from the application class by passing the context to the installer:

kotlin
Pluto.Installer(this)
  .addPlugin(....)
  .install()

The call chain reads as a builder, and the interesting part is the middle method. addPlugin takes the debuggers you want, and install performs the setup. The order matters for a practical reason: the notification that tells you the app is instrumented appears once installation is done, so the README's closing instruction is to rebuild and run the app, receive the notification from Pluto, and use it to reach the Pluto UI.

The context being passed is the application context, which is the correct lifetime for a component that must outlive any single activity. The install call also establishes the point at which plugins start observing, so anything that happens before it in the application startup, an early network call or a crash during initialisation, is outside what the plugins can see. That is a real limitation of an in-process debugger and it is not something the plugin API changes.

Plugins as a bundle or one at a time

The plugin system is the part of Pluto that changed most between versions, and the README is explicit that this is a departure from version 1.x.x. Where the old integration had one fixed set of debuggers, the current one allows developers to add debuggers as a plugin bundle or as individual plugins according to their need.

The bundle comes with all the basic plugins bundled together as a single dependency:

groovy
dependencies {
  ....
  debugImplementation "com.androidpluto.plugins:bundle-core:$plutoVersion"
  releaseImplementation "com.androidpluto.plugins:bundle-core-no-op:$plutoVersion"
  ....
}

Note that the bundle follows the same debug and release pairing, and that the no-op variant is published per bundle as well. If you use the bundle, you need both lines.

The individual plugins listed in the README are the Network Plugin, the Exceptions and Crashes Plugin, the Logger Plugin, the Shared Preferences Plugin, the Rooms Database Plugin, the Datastore Preferences Plugin and the Layout Inspector Plugin. Each lives under the pluto-plugins/plugins directory in the repository, and each has its own README with integration steps. The project points at a search on Sonatype Central for more, under the com.androidpluto.plugins group, and says more will be added.

Custom debuggers are also possible. The README notes that Pluto now allows developing custom debuggers as a plugin, with a wiki page on developing custom Pluto plugins marked as beta, and the repository topics list OkHttp interceptor, mocklets, layout builder, Room database, shared preferences and datastore preferences, which are the domains the existing plugins cover.

Grouping plugins when the list grows

As the plugin list grows, so does the UI, and Pluto therefore supports grouping similar plugins together for readability and categorisation. A group is created by overriding PluginGroup and attaching plugins to it, and the README's example groups the data source plugins:

kotlin
class DataSourcePluginGroup : PluginGroup("datasource-group") {

    override fun getConfig() = PluginGroupConfiguration(
        name = "DataSource Group"
    )

    override fun getPlugins() = listOf(
        PlutoSharePreferencesPlugin(),
        PlutoDatastorePreferencesPlugin(),
        PlutoRoomsDatabasePlugin()
    )
}

The group is then handed to the installer:

kotlin
Pluto.Installer(this)
  .addPluginGroup(DataSourcePluginGroup())
  .install()

The structure is worth understanding because it tells you how the plugin model is put together. A group supplies two things: a configuration, here only a display name, and the list of plugins it contains. The installer accepts either a group or individual plugins, so grouping is a presentation concern layered on top of the plugin list rather than a different kind of registration. A plugin that belongs to a group does not have to be added separately, and the group identifier is what distinguishes one group from another.

It is also the part of the API most likely to change, since grouping is described as optional and the custom plugin support is marked as beta. If you build a plugin ecosystem on top of Pluto, expect to adapt.

The limits of an in-process debugger

The first limitation is timing, and it is structural. Plugins begin observing when install runs, and they live in the application process, so they can only see what happens inside it. A network call made before the application class finishes, a native crash, a process death from the system, and an ANR that the watchdog itself triggers are all outside the reach of an in-process capture. For a tool whose headline feature is crash capture, that boundary matters, and it is the main reason a desktop or adb based tool still has a place alongside it.

The second limitation is that a debugger inside the app is also an attack surface inside the app. A Pluto UI that shows captured request and response bodies, and that can edit shared preferences, DataStore and Room data on the fly, is a component that a user or a malicious app on a rooted device could in principle reach. The no-op artifact for release builds is the mitigation, and the project provides it, but the pairing has to be wired correctly for every plugin bundle you include. A debug artifact that leaks into a release build is a debugging console shipped to users.

The third is that the storage plugins are tied to specific storage technologies. Shared Preferences, DataStore Preferences and Room each have a plugin, and editing them means reaching into the app's own data. Anything stored in a file, in an encrypted store or on the server is not covered, so a data-centric debugging session is only as complete as the set of plugins you have installed.

The alternative worth naming is the standard Android debugging stack: the layout inspector built into Android Studio, the network inspector, the Logcat view, StrictMode, and the crash reporting you ship to a backend. The difference in approach is that those are tools that observe from outside the process or from the build, while Pluto is a library you take a dependency on, which is what lets it offer an editable view of your data and a UI on the device. The cost of that approach is the dependency, the release build handling, and the fact that it shares the process with the code it is debugging.

Editorial conclusion

Adopt Pluto if you are building an Android app in Kotlin and want a network inspector and data editor that works on a real device without a desktop bridge, and if you are willing to keep the debug artifact out of your release build with the no-op variant. Do not adopt it if your team already has a mature debugging setup, and do not ship it in production, since the no-op pairing in the Gradle configuration is the only thing standing between a data inspector and your users. Before you commit, read the plugin list for the storage you actually use, confirm the no-op dependencies are wired for every plugin bundle you include, and check the version you pull against the artifacts currently published on Maven Central.

Frequently asked questions

What is Android Pluto?

Pluto is an on-device debugging framework for Android applications, written in Kotlin. It inspects HTTP requests and responses, captures crashes and ANRs and manipulates application data on the go, and it ships with a UI to monitor and share that information plus APIs to use it from the application.

How do I add Pluto to an Android app?

Add both artifacts to the app module's build.gradle: debugImplementation "com.androidpluto:pluto:$plutoVersion" and releaseImplementation "com.androidpluto:pluto-no-op:$plutoVersion". The no-op variant is what isolates Pluto from release builds, and the same pairing applies to the plugin bundle dependencies.

Which plugins does Pluto provide?

The README lists the Network Plugin, the Exceptions and Crashes Plugin, the Logger Plugin, the Shared Preferences Plugin, the Rooms Database Plugin, the Datastore Preferences Plugin and the Layout Inspector Plugin, and points to a search on Sonatype Central for more under com.androidpluto.plugins. Custom debuggers can also be written as plugins.

How do I initialise Pluto in an Android app?

Call the installer from your application class, passing the context, and add the plugins you want before installing. The README's example is Pluto.Installer(this).addPlugin(....).install(), and after rebuilding and running the app you get a notification from Pluto that opens the Pluto UI.

Is Pluto still being developed?

The repository is not archived and the default branch is develop. The last push was on 2026-05-01, and the tagged releases are v3.0.1 from 2025-03-04, v3.0.0 from 2025-03-02 and v2.2.2 from 2024-03-30, with a daily builds workflow badge in the README.

Official sources

  1. androidPluto/pluto on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/androidpluto-pluto.svg)](https://hysenlabs.com/projects/androidpluto-pluto)

Was this useful?