This article will introduce a help us quickly debug UI parameters plug-in development process and development ideas, may need some simple Idea platform plug-in development experience, I hope you will have some help.

The plugin is introduced

The plugin strengthens the tool based on the Layout Inspector, hence the name Layout Master.

Use the same Layout Inspector. Open the Action panel of Android Studio(above 3.1) or Idea(above 2017.3), type Layout Master and click, double-click Property. Popup will Popup if you support modification. Like the Layout Inspector, you need to run the Layout Master again every time the Activity restarts.

The plugin looks like this (the picture shows only part of the property modification effect, many properties are supported)

Github address: github.com/wuapnjie/La…

Why make this plug-in

During Android development, I often modify some UI parameters, such as padding, margin, color, etc. Sometimes the View is dynamically injected through non-XML code, and many parameter Settings can only be seen during the debugging of the real phone (and I am the kind of person who must see the effect of the real phone). So the effect is not satisfied with a lot of time to change parameters continue to see results, designers will often let us change of some parameters on the UI, so every time want to recompile Run a code, or Instant Run once, small project well, a big project, the recompile operation time cost will be very big, greatly reduce the efficiency of development. I decided to develop this plugin to quickly see the effects of UI parameter changes.

The basic principle of the plug-in

Unlike hot loading implemented by frameworks like React Native and Flutter, this plugin allows View parameters to be set in real time by calling the View’s own setXXX() method with Java reflection. The code is essentially the same and needs to be modified once you’re happy with it, but it’s still a huge time saver, at least for me.

How to call APP View setXXX() method from computer side (IDE side)? Very simple, let a long Socket connection between the mobile phone and the computer, define some command protocols, you can realize the computer side of the mobile phone control.

Realization of ideas and processes

Initial thinking

First of all, to achieve the desired function, the first step is to establish a long Socket connection between the mobile end and the computer end and get the View Hierarchy and View Properties of the Activity. I’ve seen this feature in two places: Stetho, Facebook’s powerful debugging framework, and Layout Inspector, which comes with Android Studio. Both tools establish a long Socket connection with the mobile phone and establish their own communication protocols. I’ll briefly explain the differences and explain why I chose to do a plug-in based on Layout Inspector rather than a code extension based on Stetho.

Stetho

The Stetho project has very powerful functions, including View Hierarchy, database debugging, network monitoring and so on. In terms of implementation, as I introduced before, a Socket long connection was established, and the APP was responsible for obtaining the required data. Chrome DevTools has a development API. After receiving specific Json, Chrome DevTools will render and display it. Operations in DevTools will also be packaged into specific data packets in Json format and sent to APP for operation. Due to the complexity of Stetho’s code, I didn’t delve into it, nor do I know the Chrome DevTools API, but the general principles have been covered, so if you’re interested or have any ideas, you can explore them.

Layout Inspector

Similarly, Layout Inspector also obtains relevant UI information of APP through Socket long connection. Since the code of the community version of Idea is open source, and the code of Layout Inspector as an Android plug-in is also open source, the specific Idea project can be compiled and viewed. Code entry in the android plug-in AndroidRunLayoutInspectorAction. Java classes.

The difference between

Stetho’s Socket connection code is written in its library, and apps that need to be debugged rely on this project for some configuration, which is invasive but powerful. The Layout Inspector has zero code intrusion. How does it implement long Socket connections? In fact, there is always a long connection to the computer during debugging, that is ADB. ADB tool establishes a Socket server on the computer and connects to all mobile phone clients with USB debugging mode enabled, so all the applications we debug can use the Layout Inspector tool.

Therefore, I chose to make a plug-in based on Layout Inspector, which has zero code intrusion and is easy to use. Moreover, Android SDK and Idea have done a lot of code work for me, and it is easy to implement. I will introduce it next.

Layout Inspector analysis

To do this based on Layout Inspector, it is necessary to know the implementation process of this tool. Here I will briefly analyze its source code, and also involve a class in the Android SDK ViewDebug.

The Action entrance

Those who have done THE development of Idea plug-in must know the Action system of Idea. Many quick operations we carry out in Idea platform are one Action after another

We can use this Action to quickly find the entrance to the class, it also introduces the, in AndroidRunLayoutInspectorAction. Java

@Override
public void actionPerformed(AnActionEvent e) {
  Project project = e.getProject();
  assertproject ! =null;

  if(! AndroidSdkUtils.activateDdmsIfNecessary(project)) {return;
  }

  AndroidProcessChooserDialog dialog = new AndroidProcessChooserDialog(project, false);
  dialog.show();
  if (dialog.getExitCode() == DialogWrapper.OK_EXIT_CODE) {
    Client client = dialog.getClient();
    if(client ! =null) {
      new LayoutInspectorAction.GetClientWindowsTask(project, client).queue();
    }
    else {
      Logger.getInstance(AndroidRunLayoutInspectorAction.class).warn("Not launching layout inspector - no client selected"); }}}Copy the code

As you can see from the entry code, we will first select a Process, which is the screen below

The Window to choose

Can be carried in the Background after LayoutInspectorAction GetClientWindowsTask, this Task will get the current active ClientWindow (who is the Window of the Android), if more than one, There will be a dialog box for us to choose, so we won’t have stickers here, but we’ve all used them.

Capture View

Will be chose the Window in the Background to perform LayoutInspectorCaptureTask, this Task will get to the need to display the View Hierarchy, View Properties and a BufferedImage (select Window screenshot), all of which are stored as binary information in a.li file

According to

The Layout Inspector then customizes a FileEditor to support the display of.li files, so we can see the main View of the View Tree and Properties Table. See the LayoutInspectorContext class for details

Responses in the Android SDK

The Layout Inspector requires the Window information and View information from the Android side. The code is in the HandleViewDebug class

That is, the server sends a package of commands, but where does Android, the client, respond? After my code lookup, I found a DdmHandleViewDebug class and a ViewDebug class in the Android SDK

From the structure of the two classes, it can be seen that the Android side is in the ViewDebug class to obtain all kinds of information, the specific code will not be analyzed, you can study.

Also, there is a annotation in this class, called ExportedProperty

/**
* This annotation can be used to mark fields and methods to be dumped by
* the view server. Only non-void methods with no arguments can be annotated
* by this annotation.
*/
@Target({ ElementType.FIELD, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface ExportedProperty {
    ……
}
Copy the code

If you look at where this annotation is used, you can see that all the Properties shown in the Layout Inspector are annotated with this annotation.

With this annotation we can expose some of our custom views to the properties we want to display.

Extend the Layout Inspector

After analyzing the Layout Inspector above, we know enough about it to be able to extend it. The Layout Inspector can do more than just View Hierarchy and Properties.

In the HandleViewDebug class, there is a method called invokeMethod that calls the View methods. Currently, only primitive arguments are supported, which unfortunately means we can’t change the text of a TextView.

The invokeViewMethod method in The Android SDK’s ViewDebug invokeViewMethod method is implemented by reflection, and the view is posted out

 		 /** * Invoke a particular method on given view. * The given method is always invoked on the UI thread. The caller thread  will stall until the * method invocation is complete. Returns an object equal to the result of the method * invocation,  null if the method is declared to return void *@throws Exception if the method invocation caused any exception
     * @hide* /
    public static Object invokeViewMethod(final View view, final Method method,
            final Object[] args) {
        final CountDownLatch latch = new CountDownLatch(1);
        final AtomicReference<Object> result = new AtomicReference<Object>();
        final AtomicReference<Throwable> exception = new AtomicReference<Throwable>();

        view.post(new Runnable() {
            @Override
            public void run(a) {
                try {
                    result.set(method.invoke(view, args));
                } catch (InvocationTargetException e) {
                    exception.set(e.getCause());
                } catch(Exception e) { exception.set(e); } latch.countDown(); }});try {
            latch.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        if(exception.get() ! =null) {
            throw new RuntimeException(exception.get());
        }

        return result.get();
    }
Copy the code

Then it is easy to do, the core method Idea and Android SDK are provided for us, we just need to build our plug-in UI, write View related methods.

Since we need to operate on the View’s Property, and since the control that displays the View Properties is private, HERE I get the instance by reflection and add a double-click mouse event to it.

private var propertyTable: PTable

init {
  val editorReflect = Reflect.on(layoutInspectorEditor)
 
  val context = editorReflect.get<LayoutInspectorContext>("myContext") propertyTable = context.propertiesTable ... }...fun hook(a) {

  propertyTable.addMouseListener(object: MouseAdapter() { ... }}Copy the code

Double-click to display a Popup. Different types display different popUps.

General properties of animation are not supported

Properties that support animation

Color attribute

Enum Indicates the attribute of type

Bitwise properties

Custom attributes

Can support custom View custom attribute is undoubtedly the best, and it is very easy to implement, in introducing ViewDebug class, this paper introduces the ExportedProperty annotations, we only need to use this annotation in the custom View, and set up good setXXX () method, a simple example. Note that this category must be Custom for the plug-in to respond, and a color in the attribute name is considered a color.

public class ColorView extends TextView {

  @ViewDebug.ExportedProperty(category = "custom", formatToHexString = true)
  private int color = Color.BLACK;

  @ViewDebug.ExportedProperty(category = "custom")
  private int number = 0;

  @ViewDebug.ExportedProperty(category = "custom")
  private boolean needShowText = true;

  public ColorView(Context context) {
    super(context);
  }

  public ColorView(Context context, @Nullable AttributeSet attrs) {
    super(context, attrs);
  }

  public ColorView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
  }

  public void setColor(int color) {
    this.color = color;
    setBackgroundColor(color);
  }

  public void setNeedShowText(boolean needShowText) {
    this.needShowText = needShowText;
    if(! needShowText) { setText("");
    } else {
      setText(""+ number); }}public void setNumber(int number) {
    this.number = number;
    setText(""+ number); }}Copy the code

The details will not be expanded, after all, the core principles have been introduced. Plug-in code open source, interested students can have a look, do not spray my code to write bad line.

conclusion

If you like this plugin, you can download it from Android Studio or the Plugin center of Idea. If you like this article, you can give me a like. If you have any questions, you can comment or send me a private message.

Please give a good comment, hey hey 😜

It can also be downloaded directly here: github.com/wuapnjie/La…

Do not unzip the ZIP package during installation

Plugin project Github address: github.com/wuapnjie/La… Welcome Star and PR

Hope this article can have what help to you, I will also continue to work hard ~