Loading...
Home
  • Tech Blogs
  • Videos
  • Conferences
    • Droidcon News
    • Upcoming Conferences
    • Become a Partner
    • Past Events
    • Keep Me Informed
    • Diversity Scholarships
  • Community
    • droidcon Team
    • How to Hold a Droidcon
  • Android Careers
Sign In

Global CSS

 

How to create an Android Studio plugin with the ADB connection and reading messages from the Logcat.

 

 
Ievgenii Tkachenko
Published: December 03, 2020
Tweet
Share
 

 

 

More than two years ago, I faced the issue that to debug requests from the Android device or emulator, you should use some third-party tools, and none of them was built inside the Android Studio. Then I saw that many people use the OkHttp Interceptor (I use OkHttp or Retrofit libraries in Android development), which just prints HTTP requests as plain text to the console.

But wait, why can’t we just parse this uncomfortable pure text format to the tree? Why should we search in tons of lines to get our request?

Finally, I decided to create an Android Studio plugin to parse that console output to some human-friendly format with the list of requests, the JSON trees, the human-readable headers, etc.

 

Image for post

OkHttpProfiler

 

So this was an idea of the OkHttpProfiler plugin, and now I am going to tell you how to create something similar.

 

Step 1. Use the IntelliJ IDEA.

To develop for the Android Studio, you should use IntelliJ IDEA (Comunity Edition will fit your needs). There are two main ways to create a project: XML based and Gradle. I had some experience with the first option, so I definitely can advise you to use the Gradle.

Let’s create it: choose File->New Project->Gradle, and only then we should select the options below (Java, Kotlin, IntelliJ Platform Plugin) and proceed.

 

Image for post

New project creation

 

Now we have the project with the initial configuration for the plugin development, but we should set up it more.

Before Android Studio version 4.1, it was enough to use IntelliJ IDEA files for plugins development with ddmlib library usage. Unfortunately, after 4.1, Android Studio and IntelliJ IDEA has some different realization of the same classes, so we MUST include libraries from the Android Studio if we are creating it for AS, and vice versa (see this thread).

That's why we should specify StudioCompilePath in the gradle.propertiesfile of the project. Add the next line to the file:

 

StudioCompilePath=PATH_TO_YOUR_ANDROID_STUDIO

 

For mac users, the PATH is /Applications/Android Studio.app/Contents (If you are using a different OS and you don’t know where is it located — just use Google)

Going next, open the build.gradle file, and modify it a little bit to set proper values for your new plugin:

 

plugins {
    id 'java'
    id 'org.jetbrains.intellij' version '0.6.3'
    id 'org.jetbrains.kotlin.jvm' version '1.3.72'
}

group 'com.itkacher.okhttpprofiler' //Use your own id
version '1.0.14' //Use your own version

repositories {
    mavenCentral()
    jcenter()
}//Just add this check below to be sure, that StudioCompilePath is defined
if (!hasProperty('StudioCompilePath')) {
    throw new GradleException("No StudioCompilePath value was set, please create gradle.properties file")
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
    testCompile group: 'junit', name: 'junit', version: '4.12'
    implementation fileTree(dir: "libs", include: ['*.jar'])//Add the lines below to compile libraries from the Android Studio 
    compileOnly fileTree(dir: "$StudioCompilePath/plugins/android/lib", include: ['*.jar'])
    compileOnly fileTree(dir: "$StudioCompilePath/lib", include: ['*.jar'])
}//These lines for defining the target IDE to test your plugin when you run it. We rewrote ItellijIDEA with the Android Studio.
intellij {
    pluginName 'OkHttpProfiler'
    updateSinceUntilBuild false

    plugins 'android'

    intellij.localPath = project.hasProperty("StudioRunPath") ? StudioRunPath : StudioCompilePath
}

compileKotlin {
    kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
    kotlinOptions.jvmTarget = "1.8"
}
patchPluginXml {
    changeNotes """
      Add change notes here.<br>
      <em>most HTML tags may be used</em>"""
}//Another check that everything was setup correctly.
task(verifySetup) {
    doLast {
        def ideaJar = "$StudioCompilePath/lib/idea.jar"
        if (!file(ideaJar).exists()) {
            throw new GradleException("$ideaJar not found, set StudioCompilePath in gradle.properties")
        }
    }
}

compileJava.dependsOn verifySetup

 

The source of the plugin information and configuration is still an XML file resources/META-INF/plugin.xml

 

<idea-plugin> <!-- USE YOUR DATA INSTEAD OF BOLD TEXT BELOW -->
    <id>com.itkacher.okhttpprofiler</id>
    <name>OkHttp Profiler</name>
    <vendor email="email@email.com" url="http://localebro.com">localebro.com</vendor>
    <version>1.0.14</version><!-- since-build is a minimal version of the Android Studio for your plugin. Android Studio 4.1 has version 201.8743.12 -->
    <idea-version since-build="201.8743.12"/>
    <description><![CDATA[ USE YOUR DESCRIPTION ]]></description>
<!-- The depending below limits the plugin for Android Studio only. -->
    <depends>org.jetbrains.android</depends>
    <depends>com.intellij.modules.androidstudio</depends>
    <depends>com.intellij.modules.platform</depends>
<!-- OkHttpProfiler is a Toolwindow, so we should define it here -->
    <extensions defaultExtensionNs="com.intellij">
        <defaultProjectTypeProvider type="Android"/>
        <toolWindow id="OkHttp Profiler" secondary="true" icon="/icons/help-network.png" anchor="bottom"
factoryClass="com.itkacher.DebuggerToolWindowFactory"/>
    </extensions>
</idea-plugin>

 

You can find more information about plugin types here. We will use “toolWindow” because it’s our goal. As we can see, the IDEA will search for a class com.itkacher.DebuggerToolWindowFactory, and you should create your own class and set it instead of my DebuggerToolWindowFactory. How? See next chapter.

 

Step 2. Write the code.

The nice news — you can use Kotlin for the plugin development. The bad — you should use java (swing) too. Let’s create our own DebuggerToolWindowFactory.kt class (don’t forget to create package folders before):

 

class DebuggerToolWindowFactory : ToolWindowFactory, DumbAware {    override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) {

    }}

 

This is the main class, and it will be called when you open your plugin in the studio. It should extend the ToolWindowFactory class and implements the DumbAware interface. First of all, we need to create a form. Left-click on your DebuggerToolWindowFactory file and choose New -> Swing UI Designed -> GUI Form and create the MainForm.

 

Image for post

The GUI form creation

 

If you never worked with Swing before and used to work only with Android, as am I: My congrats! Probably, you will have some pain :) Swing blew up my mind, not in a good way. The form editor will be opened when you choose GUI form creation. You should drag needed components from the right palette to the form. And don’t forget to set the ids (field names):

 

Image for post

Visual editor

 

When you have done, you can pass the form panel to your tool window.

 

class DebuggerToolWindowFactory : ToolWindowFactory, DumbAware {
    override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) {
        val mainForm = MainForm()
        toolWindow.component.add(mainForm.panel)
    }
}

 

In the code “mainForm.panel” — panel it’s the name of the root container.

If you run the project (use “Run” button or gradle command “buildPlugin runIde”), you will see something like this (depends on your form):

 

Image for post

My sincere congratulations! You created your first Android Studio plugin!

 

Step 3. Add the ADB connection logic.

In my case, I created two more classes to encapsulate the logic:

  • AdbController — for handling the ADB & views.
  • PluginPreferences — for wrapping all preferences in the IDE.

The result of the DebuggerToolWindowFactory is:

 

class DebuggerToolWindowFactory : ToolWindowFactory, DumbAware {

    private var adbController: AdbController? = null

    override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) {
        val preferences = PluginPreferences(PropertiesComponent.getInstance(project))
        val mainForm = MainForm()
        adbController = AdbController(mainForm, project, preferences, toolWindow)
        toolWindow.component.add(mainForm.panel)
    }
}

 

In the scope of this article, I will not go into the preferences, but let's dive into the AdbController — it has some magic inside :)

The top of the class contains a very useful import of the ddmlib:

 

import com.android.ddmlib.AndroidDebugBridge
import com.android.ddmlib.Client
import com.android.ddmlib.IDevice
import com.android.ddmlib.logcat.LogCatMessage
import com.android.tools.idea.logcat.AndroidLogcatService
import org.jetbrains.android.sdk.AndroidSdkUtils

 

All of these classes are going from the Android Studio libs folder, gradle will take care of it and you will be able to use them from the plugin.

AndroidDebugBridge — as we can see from the name, it’s a class, which represents communication with the ADB. And it has a few helpful listeners:

 

public interface IClientChangeListener {
    void clientChanged(Client client, int changeMask);
}

public interface IDeviceChangeListener {
    void deviceConnected(IDevice device);
    void deviceDisconnected(IDevice device);
    void deviceChanged(IDevice device, int var2);
}

public interface IDebugBridgeChangeListener {
    void bridgeChanged(AndroidDebugBridge briedge);
    default void restartInitiated() {}
    default void restartCompleted(boolean isSuccessful) {}
}
  • Client — this represents a single client, usually a VM process. It’s a process of the Android application. It contains useful information like PID, packageName, description, etc.
  • IClientChangeListener.clientChanged — called when the client was changed. I had seen a situation, when you only have a PID, without the info, and in several moments, I received full process information to the listener.
  • IDevice — it can be a physical device or an emulator. All the information about the connected device is here. And the listeners:
  • IDeviceChangeListener.deviceConnected — when device was connected.
  • IDeviceChangeListener.deviceDisconnected — when it disconnected.
  • IDeviceChangeListener.deviceChanged — when it was changed.
  • IDebugBridgeChangeListener.bridgeChanged — when the debug bridge was changed (ADB restarted, etc.).

You can add all of these listeners to the AndroidDebugBridge with static methods:

 

AndroidDebugBridge.addDeviceChangeListener
AndroidDebugBridge.addDebugBridgeChangeListener
AndroidDebugBridge.addClientChangeListener

 

Easy enough, right? Going next.

The thing is you should force your studio to use the android device bridge. And now class AndroidSdkUtils is going to help us. Just call the:

 

val bridge: AndroidDebugBridge? = AndroidSdkUtils.getDebugBridge(project)
log("initDeviceList bridge ${bridge?.isConnected}")

 

AndroidSdkUtils will parse the project info and find the ADB information there.

That’s it, now your plugin is connected, and all of your listeners are receiving information about adb, devices, processes.

 

The only one left: to get access to the logcat.

And again, it’s not a big deal! We can use AndroidLogcatService and LogcatListener:

 

private val logCatListener = AndroidLogcatService.getInstance()

private val deviceListener = object : AndroidLogcatService.LogcatListener {
    override fun onLogLineReceived(line: LogCatMessage) {}
}

 

and methods:

 

logCatListener.addListener(device, deviceListener)
logCatListener.removeListener(device, deviceListener)

 

So you know that the device was connected from the IDeviceChangeListener, and you can add logcat listener to it. Here is the LogCatMessage class structure, it contains all the needed information.

In my case, I receive all messages for a target PID. All the rest depends on your imagination, how you can use it. My way was to create my own communication standard between OkHttpProfiler Android Library (to produce the log messages to logcat from the application side), and the OkHttpProfiler Plugin for Android Studio to read these logs.

Elementary!

 

Step 4. Help and Thank You.

I made the plugin to help others better understand the processes inside their application and made this article to show, that plugin development is not so hard as it can look.

You can use the OkHttpProfiler for free.

If you want to support me — use another my “child” LocaleBro — localization platform for mobile applications, which also can help you, but with the localization of mobile applications!

Thanks for the reading, and take care of yourself in such a strange time.

 

Useful links

  • Source code of the OkHttpProfiler Plugin.
  • Compiled Android Studio Plugin
  • Android Library for the OkHttpProfiler.
  • LocaleBro.com — authors of the plugin.

 

Thanks to Mario Sanoguera de Lorenzo. 

 

 

 

Tags: Android Studio, Plugin Development, Android App Development, Kotlin, AndroidDev

 

View original article at: 


 

Originally published: November 21, 2020

Android News
Evolution of Android Update SystemEvolution of Android Update System
Evolution of Android Update SystemEvolution of Android Update System

By Ivan Kuten

So, how can you update Android on mobile devices? While developing software for Smart TVs and Android-based set-top boxes, we’ve narrowed it down to four ways, discarding some very exotic options:

By ProAndroidDev -
Android News
Happy Railway
Happy Railway

By Hadi Lashkari Ghouchani

This post is on the tail of Railway Oriented Programming in Kotlin by Antony Harfield. So you need to read it first and continue here. As it’s obvious I really liked it and tried it out. It needs every process have a result like

By ProAndroidDev -
Android News
Unit Tests and Concurrency
Unit Tests and Concurrency

By Stojan Anastasov

Once Retrofit added RxJava support, RxJava became my go-to concurrency framework for writing Android apps. One of the great things about RxJava is the excellent testing support. It includes TestObserver, TestScheduler, RxJavaPlugins so you can switch your schedulers in tests.

By ProAndroidDev -
Android News
When Compat libraries will not save you
When Compat libraries will not save you

By Danny Preussler

And why you should avoid using the “NewApi” suppression! The idea of “Compat” libraries was probably one of the key aspects of Android dominating the mobile space. Other than with iOS, Android users often could not update their operating system after a new version launch, simply as their phones won’t allow them to, the Android problem of fragmentation.

 

By ProAndroidDev -
droidcon News

Tech Showcases,

Developer Resources &

Partners

/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/home-details/EmployerBrandingHeader
EmployerBrandingHeader
https://jobs.droidcon.com/
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/jobs-droidcon/jobs.droidcon.com
jobs.droidcon.com

Latest Android Jobs

http://www.kotlinweekly.net/
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/kotlin-weekly/Kotlin Weekly
Kotlin Weekly

Your weekly dose of Kotlin

https://proandroiddev.com/
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/pad/ProAndroidDev
ProAndroidDev

Android Tech Blogs, Case Studies and Step-by-Step Coding

/detail?content-id=/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/Zalando/Zalando
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/Zalando/Zalando
Zalando

Meet one of Berlin's top employers

/detail?content-id=/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/Academy for App Success/Academy for App Success
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/Academy for App Success/Academy for App Success
Academy for App Success

Google Play resources tailored for the global droidcon community

Follow us

Team droidcon

Get in touch with us

Write us an Email

 

 

Quicklinks

> Code of Conduct

> Terms and Conditions

> How to hold a conference

> FAQs

> Imprint

Droidcon is a registered trademark of Mobile Seasons GmbH Copyright © 2020. All rights reserved.

powered by Breakpoint One