从 入口代码中可以看出,我们要先选一个Process,也就是下面这个界面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N10nofbZ-1631089219217)(https://user-gold-cdn.xitu.io/2018/4/14/162c4b744805ad02?imageView2/0/w/1280/h/960/ignore-error/1)]
#### Window选择
之后会在Background执行`LayoutInspectorAction.GetClientWindowsTask`,这个Task会获取当前活跃的ClientWindow(也就是Android中的Window),如果超过一个的话,会出现对话框让我们选择,这里就不贴图了,反正大家都用过。
#### Capture View
选择了Window之后就会在Background执行`LayoutInspectorCaptureTask`,这个Task会获取到需要显示的View Hierarchy,View Properties以及一张BufferedImage(选择Window的截图),这些信息全部以二进制的信息储存在.li文件中
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SD0W7UqN-1631089219219)(https://user-gold-cdn.xitu.io/2018/4/14/162c4b7468522e27?imageView2/0/w/1280/h/960/ignore-error/1)]
#### 显示
然后Layout Inspector自定义了一个FileEditor以支持.li文件的显示,也就是我们能看到View Tree和Properties Table的主界面。具体显示细节可参考`LayoutInspectorContext`类
#### Android SDK中的响应
上面介绍了Layout Inspector在插件端的简单流程,它想Android端要了Window信息,View的信息,相关代码都在`HandleViewDebug`类,下面是这个类的一些结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nHzr6L3S-1631089219220)(https://user-gold-cdn.xitu.io/2018/4/14/162c4b7469b2a9b1?imageView2/0/w/1280/h/960/ignore-error/1)]
也就是说服务端发出了一些命令的包,那作为客户端的Android是在哪里作出响应的呢?经过我的代码查找,我在Android SDK中发现了一个`DdmHandleViewDebug`类和`ViewDebug`类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N2CziDZx-1631089219222)(https://user-gold-cdn.xitu.io/2018/4/14/162c4b746cb98937?imageView2/0/w/1280/h/960/ignore-error/1)]
从两个类的structure中就可以看出,Android端是在`ViewDebug`这个类获取各种信息的,具体代码就不分析了,大家感兴趣可以研究研究。
同时,这个类中有一个注解,叫`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 { …… }
查看这个注解用的地方,可以发现,所有Layout Inspector中显示的Properties都被标注了这个注解。
![image](https://user-gold-cdn.xitu.io/2018/4/14/162c4b7472b79633?imageView2/0/w/1280/h/960/ignore-error/1)
通过这个注解我们可以给一些自定义的View暴露出想要显示的属性。
### 扩展Layout Inspector
经过上面的对Layout Inspector的分析,我们已经足够了解它并可以对其做扩展了。Layout Inspector只能查看View Hierarchy和Properties,它完全可以做更多的事情。
在`HandleViewDebug`类中有一个方法`invokeMethod`,这个方法可以做到调用View的相关方法,目前只支持primitive arguments的方法,很可惜,意味着我们不能改变TextView的text。
![image](https://user-gold-cdn.xitu.io/2018/4/14/162c4b7489d1d0db?imageView2/0/w/1280/h/960/ignore-error/1)
触发的方法在Android SDK的`ViewDebug`的`invokeViewMethod`方法中,可以看到是通过反射实现的,view post出去的
/** * 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 result = new AtomicReference(); final AtomicReference exception = new AtomicReference();
view.post(new Runnable() {
@Override
public void run() {
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();
}
接下来就好办了,核心方法Idea和Android SDK都为我们提供好了,我们只需要构建我们的插件UI,写入View的相关方法即可。
由于我们需要对View的Property进行操作,由于负责显示View Properties的控件是私有的,所以这里我通过反射获取了实例,并为其添加了一个双击鼠标事件。
private var propertyTable: PTable
init { val editorReflect = Reflect.on(layoutInspectorEditor)
val context = editorReflect.get(“myContext”)
propertyTable = context.propertiesTable … }
… fun hook() {
propertyTable.addMouseListener(object : MouseAdapter() { … } }
双击过后就是显示一个Popup,不同的类型显示不同的Popup。
##### 不支持动画的普通属性
![image](https://user-gold-cdn.xitu.io/2018/4/14/162c4b748d2c0e81?imageView2/0/w/1280/h/960/ignore-error/1)
##### 支持动画的属性
![image](https://user-gold-cdn.xitu.io/2018/4/14/162c4b748ebfdc23?imageView2/0/w/1280/h/960/ignore-error/1)
##### 颜色属性
![image](https://user-gold-cdn.xitu.io/2018/4/14/162c4b749581a798?imageView2/0/w/1280/h/960/ignore-error/1)
##### Enum类型的属性
![image](https://user-gold-cdn.xitu.io/2018/4/14/162c4b7497e1b1d7?imageView2/0/w/1280/h/960/ignore-error/1)
##### Bitwise类型的属性
![image](https://user-gold-cdn.xitu.io/2018/4/14/162c4b74a2507959?imageView2/0/w/1280/h/960/ignore-error/1)
##### 自定义的属性
可以支持自定义View的自定义的属性无疑是最棒的,实现起来也很简单,在介绍`ViewDebug`类时,介绍了`ExportedProperty`注解,我们只需要在自定义的View中运用这个注解就可以了,并设置好setXXX()方法,一个简单例子,注意这个category一定要为custom,插件才会做出响应,属性名中带有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 {
最后
为了方便有学习需要的朋友,我把资料都整理成了视频教程(实际上比预期多花了不少精力)
CodeChina开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》
当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。
- 无论你现在水平怎么样一定要 持续学习 没有鸡汤,别人看起来的毫不费力,其实费了很大力,这四个字就是我的建议!!
- 我希望每一个努力生活的IT工程师,都会得到自己想要的,因为我们很辛苦,我们应得的。
当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。
无论你现在水平怎么样一定要 持续学习 没有鸡汤,别人看起来的毫不费力,其实费了很大力,没有人能随随便便成功。
的IT工程师,都会得到自己想要的,因为我们很辛苦,我们应得的。
当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。
无论你现在水平怎么样一定要 持续学习 没有鸡汤,别人看起来的毫不费力,其实费了很大力,没有人能随随便便成功。
加油,共勉。
|