当前位置:   article > 正文

ADB-使用JAVA动态代理和反射技术自动拼接ADB命令_utils.adb

utils.adb

ADB-使用JAVA动态代理和反射技术自动拼接ADB命令

1.在测试安卓app时,经常会用到adb命令,使用一些脚本比如卸载,做一些简单的性能统计,和运行monkey等等等,每次需要手写adb命令,造成一些不常用的adb命令使用起来还需要在参考,并且还要自己手动的拼接一些参数,造成了效率低,本文借用java动态代理和反射技术动态的实现了adb拼接命令,只需要手动创建一个接口就可以实现,方便快捷,易于扩展
2.环境:JAVA 1.8及以上版本
3.架构
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210125100456234.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzE4ODMwMTU5,size_16,color_FFFFFF,t_70)4:源码

父接口:

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;
    }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

实现接口

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;
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57

关键核心类:生成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();
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

生成AdbCode接口

public interface AdbCode extends DynamicSplicing<String> {

    @Override
    default String separator() {
        return " ";
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

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";
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

其他命令

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;
    }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

使用工厂封装:

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;
    }
}


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73

补充:keycode有很多key,为了方便使用,使用java的枚举实现
keycode接口:

/**
 * @author zly
 * @version 1.0
 * @date 2021/1/22 12:10
 */
public interface Keycode {
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

具体实现类

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;

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

5.测试

在这里插入图片描述

见上图,想怎么拼就怎么拼,想要生成其他的命令,只需要创建个接口,然后写上方法名和需不需要参数,然后使用特性default默认实现接口就OK了,不需要手动拼接的直接用void,需要手动拼接的返回个String和实现拼接效果,不想使用门面封装的方法,也可以自己实现接口类交由代理类或直接实现,剩下执行adb的工具类这里就不展现了。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/335643
推荐阅读
相关标签
  

闽ICP备14008679号