赞
踩
1.在测试安卓app时,经常会用到adb命令,使用一些脚本比如卸载,做一些简单的性能统计,和运行monkey等等等,每次需要手写adb命令,造成一些不常用的adb命令使用起来还需要在参考,并且还要自己手动的拼接一些参数,造成了效率低,本文借用java动态代理和反射技术动态的实现了adb拼接命令,只需要手动创建一个接口就可以实现,方便快捷,易于扩展
2.环境:JAVA 1.8及以上版本
3.架构图
父接口:
public interface DynamicSplicing<T> { default T getResult() { return null; } default DynamicSplicing<T> join(Object... code) { return null; } default T init() { return null; } default T clear() { return null; } default Object separator() { return null; } /** * 忽略需要拼接的方面名称,如果配置了全局,这里不需要配置 * * @return */ default List<String> ignoreMethonName() { return null; } /** * 忽略全局拼接方法名称 * * @return */ default boolean ignoreGlobalMethonName() { return false; } }
实现接口
public abstract class DynamicSplicingImpl<V> implements DynamicSplicing<V>, InvocationHandler { protected DynamicSplicing<V> dynamicSplicing; protected final static List<String> ROOT_IGNORE_METHODS = new ArrayList<>(); static { for (Method method : DynamicSplicing.class.getMethods()) { ROOT_IGNORE_METHODS.add(method.getName()); } for (Method method : Object.class.getMethods()) { ROOT_IGNORE_METHODS.add(method.getName()); } } public <T extends DynamicSplicing<V>> T getInstance(T dynamicSplicing) { this.dynamicSplicing = dynamicSplicing; T t = (T) Proxy.newProxyInstance( this.dynamicSplicing.getClass().getClassLoader(), this.dynamicSplicing.getClass().getInterfaces(), this ); join(this.dynamicSplicing.init()); return t; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (ROOT_IGNORE_METHODS.contains(method.getName())) { try { return method.invoke(this, args); } catch (IllegalArgumentException e) { return method.invoke(this.dynamicSplicing, args); } } if (!this.dynamicSplicing.ignoreGlobalMethonName() && (this.dynamicSplicing.ignoreMethonName() == null || !this.dynamicSplicing.ignoreMethonName().contains(method.getName())) ) { join(method.getName()); } // 这种可变长的参数在经过代理后,直接是new Object[]{args},所以需要进行处理 if (args != null && args[0] instanceof Object[]) { args = (Object[]) args[0]; } Object o = method.invoke(this.dynamicSplicing, args); // 这里是处理某个参数需要子类自己拼接,所以当返回值不是void时,交由子类自己处理 if (method.getReturnType() != Void.TYPE) { join(o); } else { join(args); } return o; } }
关键核心类:生成String代理类
package com.zly.testcenter.utils.DynamicSplicing; import org.apache.commons.lang3.StringUtils; /** * @author zly * @version 1.0 * @date 2021/1/26 15:23 */ @SuppressWarnings("all") public class StringDynamicSplicingProxy extends DynamicSplicingImpl<String> { protected final StringBuilder sb = new StringBuilder(); @Override public String getResult() { return sb.toString().trim(); } @Override public StringDynamicSplicingProxy join(Object... code) { if (code == null) return this; this.sb.append(this.separator()); this.sb.append(StringUtils.join(code, this.separator())); return this; } // // @Override // public String init() { // return this.dynamicSplicing.init(); // } @Override public String clear() { String code = sb.toString(); sb.delete(0, sb.length()); sb.append(this.dynamicSplicing.init()); return code; } @Override public String separator() { return this.dynamicSplicing.separator().toString(); } @Override public String toString() { return getResult(); } }
生成AdbCode接口
public interface AdbCode extends DynamicSplicing<String> {
@Override
default String separator() {
return " ";
}
}
Input命令
package com.zly.testcenter.utils.adb.shell; import com.zly.testcenter.utils.adb.AdbCode; import com.zly.testcenter.utils.adb.Keycode; /** * @author zly * @version 1.0 * @date 2021/1/23 21:10 */ public interface Input extends AdbCode { default String init() { return "adb shell input"; } default void keyevent(Keycode keycode) { } ; default void tap(int x, int y) { } /** * @param x * @param y * @param endX * @param endY * @param time 单位:毫秒 */ default void swip(int x, int y, int endX, int endY, int time) { } default void swip(int x, int y, int endX, int endY) { } default String demo(int x, int y) { return "sssssss"; } }
其他命令
package com.zly.testcenter.utils.adb; /** * @author zly * @version 1.0 * @date 2021/1/25 11:15 */ public interface NoShell extends AdbCode{ @Override default String init() { return "adb"; } default String install(String filePath){ return "-r "+filePath; } }
使用工厂封装:
package com.zly.testcenter.utils.adb; import com.zly.testcenter.utils.DynamicSplicing.StringDynamicSplicingProxy; import com.zly.testcenter.utils.adb.shell.Input; import java.util.HashMap; import java.util.Map; /** * 使用门面模式封装调用代理的步骤 * * @author zly * @version 1.0 * @date 2021/1/24 14:14 */ public class AdbCodeFactory { // 使用threadLocal单例模式进行缓存 // 其实访问adb本身速度不会很快,其实这里增加的速度并不能为整体质的性能提升 private static final ThreadLocal<Map<Class<? extends AdbCode>, ? super AdbCode>> threadLocal = new ThreadLocal<Map<Class<? extends AdbCode>, ? super AdbCode>>() { @Override protected Map<Class<? extends AdbCode>, ? super AdbCode> initialValue() { return new HashMap<>(); } }; public static Input getInput() { return getInstance(new Input() { }); } public static NoShell getNoShell() { return getInstance(new NoShell() { }); } /** * @param obj * @param <T> * @return obj的代理实例 */ @SuppressWarnings("all") private static <T extends AdbCode> T getInstance(T obj) { Map<Class<? extends AdbCode>, ? super AdbCode> map = threadLocal.get(); T value = (T) map.get(obj.getClass()); if (value != null) { value.clear(); return value; } // 这里是想通过动态代理生成接口 // T instance = (T) mapInstance.get(clazz); // if (instance == null) { // instance = (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, // new InvocationHandler() { // @Override // public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 这里无法使用method.invoke,因为无法传入实例,生成这个接口的实例是通过动态代理生成的 // 但是调用method.invoke又需要一个实例,这就尴尬了,在需要返回值并且是实现父接口的相同方法 // 直接使用java8 default 生成的时,想要获取返回内容,就必须是这个接口的实例,所以这里 // 暂时放弃使用动态代理的方式生成类 // return null; // } // }); // mapInstance.put(clazz, instance); // } T result = new StringDynamicSplicingProxy().getInstance(obj); map.put(obj.getClass(), result); return result; } }
补充:keycode有很多key,为了方便使用,使用java的枚举实现
keycode接口:
/**
* @author zly
* @version 1.0
* @date 2021/1/22 12:10
*/
public interface Keycode {
}
具体实现类
package com.zly.testcenter.utils.adb.shell.Keyevent.keycode; import com.zly.testcenter.utils.adb.Keycode; /** * @author zly * @version 1.0 * @date 2021/1/22 12:00 */ public enum KeycodePhoneEnum implements Keycode { //拨号键 KEYCODE_CALL, //挂机键 KEYCODE_ENDCALL, // 按键Home KEYCODE_HOME, // 菜单键 KEYCODE_MENU, // 返回键 KEYCODE_BACK, // 搜索键 KEYCODE_SEARCH, // 拍照键 KEYCODE_CAMERA, // 拍照对焦键 KEYCODE_FOCUS, // 电源键 KEYCODE_POWER, // 通知键 KEYCODE_NOTIFICATION, // 话筒静音键 KEYCODE_MUTE, // 扬声器静音键 KEYCODE_VOLUME_MUTE, // 音量增加键 KEYCODE_VOLUME_UP, // 音量减小键 KEYCODE_VOLUME_DOWN; }
见上图,想怎么拼就怎么拼,想要生成其他的命令,只需要创建个接口,然后写上方法名和需不需要参数,然后使用特性default默认实现接口就OK了,不需要手动拼接的直接用void,需要手动拼接的返回个String和实现拼接效果,不想使用门面封装的方法,也可以自己实现接口类交由代理类或直接实现,剩下执行adb的工具类这里就不展现了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。