zoukankan      html  css  js  c++  java
  • Android随笔之——用shell脚本模拟用户按键、触摸操作

      之前写过两篇关于Android中模拟用户操作的博客(其实用一篇是转载的),现在就来讲讲用shell脚本来模拟用户按键操作。本次的目标是用shell脚本打开微信并在其搜索框中搜索相关内容。

      本文的模拟功能主要是用adb的input命令来实现,如果你adb的环境变量配置正确的话,在cmd中输入 adb shell input 就可以看见input的用法了。

    usage: input ...
    input text //输入文字(中文不支持)
    input keyevent //keyevent按键
    input [touchscreen|touchpad|touchnavigation] tap <x> <y>//点击屏幕
    input [touchscreen|touchpad|touchnavigation] swipe <x1> <y1> <x2> <y2> //屏幕滑动 
    input trackball press //滚球已经不用了
    input trackball roll //滚球已经不用了
    input rotationevent 0 1->90 2->180 3->270> //顺时针旋转

    下面直接上安卓用户操作的代码,就一个MainActivity而已,UI、Mainfest都不用配置(可能需要root权限)

      1 package com.lsj.adb;
      2 
      3 import java.io.DataOutputStream;
      4 
      5 import android.app.Activity;
      6 import android.os.Bundle;
      7 import android.view.Menu;
      8 
      9 public class MainActivity extends Activity {
     10 
     11     private String[] search = {
     12             "input keyevent 3",// 返回到主界面,数值与按键的对应关系可查阅KeyEvent
     13             "sleep 1",// 等待1秒
     14             "am start -n com.tencent.mm/com.tencent.mm.ui.LauncherUI",// 打开微信的启动界面,am命令的用法可自行百度、Google
     15             "sleep 3",// 等待3秒
     16             "am start -n com.tencent.mm/com.tencent.mm.plugin.search.ui.SearchUI",// 打开微信的搜索
     17             "input text 123",// 像搜索框中输入123,但是input不支持中文,蛋疼,而且这边没做输入法处理,默认会自动弹出输入法
     18     };
     19 
     20     @Override
     21     protected void onCreate(Bundle savedInstanceState) {
     22         super.onCreate(savedInstanceState);
     23         setContentView(R.layout.activity_main);
     24         //如果input text中有中文,可以将中文转成unicode进行input,没有测试,只是觉得这个思路是可行的
     25         search[5] = chineseToUnicode(search[5]);
     26         execShell(search);
     27     }
     28 
     29     /**
     30      * 执行Shell命令
     31      * 
     32      * @param commands
     33      *            要执行的命令数组
     34      */
     35     public void execShell(String[] commands) {
     36         // 获取Runtime对象
     37         Runtime runtime = Runtime.getRuntime();
     38 
     39         DataOutputStream os = null;
     40         try {
     41             // 获取root权限,这里大量申请root权限会导致应用卡死,可以把Runtime和Process放在Application中初始化
     42             Process process = runtime.exec("su");
     43             os = new DataOutputStream(process.getOutputStream());
     44             for (String command : commands) {
     45                 if (command == null) {
     46                     continue;
     47                 }
     48 
     49                 // donnot use os.writeBytes(commmand), avoid chinese charset
     50                 // error
     51                 os.write(command.getBytes());
     52                 os.writeBytes("
    ");
     53                 os.flush();
     54             }
     55             os.writeBytes("exit
    ");
     56             os.flush();
     57             process.waitFor();
     58         } catch (Exception e) {
     59             e.printStackTrace();
     60         }
     61     }
     62     
     63     /**
     64      * 把中文转成Unicode码
     65      * @param str
     66      * @return
     67      */
     68     public String chineseToUnicode(String str){
     69         String result="";
     70         for (int i = 0; i < str.length(); i++){
     71             int chr1 = (char) str.charAt(i);
     72             if(chr1>=19968&&chr1<=171941){//汉字范围 u4e00-u9fa5 (中文)
     73                 result+="\u" + Integer.toHexString(chr1);
     74             }else{
     75                 result+=str.charAt(i);
     76             }
     77         }
     78         return result;
     79     }
     80 
     81     /**
     82      * 判断是否为中文字符
     83      * @param c
     84      * @return
     85      */
     86     public  boolean isChinese(char c) {
     87         Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
     88         if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
     89                 || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
     90                 || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
     91                 || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION
     92                 || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION
     93                 || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) {
     94             return true;
     95         }
     96         return false;
     97     }
     98 
     99 
    100 }

    效果图:

      模拟用户打开微信,并进行搜索就这么完成了。其实这里用shell命令模拟用户操作还是有些问题的,比如说控件长按(sendevent),好难理解,而且需要跟其中传递的控件坐标参数应该要跟屏幕分辨率联系起来,实际应用范围不是很广泛。PS:那种大量需要重复操作的除外,如:自动化测试,游戏刷图(PS:腾讯的仙剑手游把我大仙剑毁了啊,麻花腾你妹啊,你全家都是麻花腾)。

      最后,其实可以参考下按键精灵,这款应用做的还不错,除了不给root权限就崩外....

    补充:以上模拟用户操作的代码在交互不频繁的情况下是完全没有问题的,但是如果使用频繁的话,会发生多次申请root权限,导致系统卡死的现象,后面在google上找到了一个开源项目,可以解决这个问题(它使用的是单例模式),代码如下:

      1 /**
      2  * 类名 RootContext.java 说明 获取root权限 创建日期 2012-8-21 作者 LiWenLong Email
      3  * lendylongli@gmail.com 更新时间 $Date$ 最后更新者 $Author$
      4  */
      5 public class RootContext {
      6     private static RootContext instance = null;
      7     private static Object mLock = new Object();
      8     String mShell;
      9     OutputStream o;
     10     Process p;
     11 
     12     private RootContext(String cmd) throws Exception {
     13         this.mShell = cmd;
     14         init();
     15     }
     16 
     17     public static RootContext getInstance() {
     18         if (instance != null) {
     19             return instance;
     20         }
     21         synchronized (mLock) {
     22             try {
     23                 instance = new RootContext("su");
     24             } catch (Exception e) {
     25                 while (true)
     26                     try {
     27                         instance = new RootContext("/system/xbin/su");
     28                     } catch (Exception e2) {
     29                         try {
     30                             instance = new RootContext("/system/bin/su");
     31                         } catch (Exception e3) {
     32                             e3.printStackTrace();
     33                         }
     34                     }
     35             }
     36             return instance;
     37         }
     38     }
     39 
     40     private void init() throws Exception {
     41         if ((this.p != null) && (this.o != null)) {
     42             this.o.flush();
     43             this.o.close();
     44             this.p.destroy();
     45         }
     46         this.p = Runtime.getRuntime().exec(this.mShell);
     47         this.o = this.p.getOutputStream();
     48         system("LD_LIBRARY_PATH=/vendor/lib:/system/lib ");
     49     }
     50 
     51     private void system(String cmd) {
     52         try {
     53             this.o.write((cmd + "
    ").getBytes("ASCII"));
     54             return;
     55         } catch (Exception e) {
     56             while (true)
     57                 try {
     58                     init();
     59                 } catch (Exception e1) {
     60                     e1.printStackTrace();
     61                 }
     62         }
     63     }
     64 
     65     public void runCommand(String cmd) {
     66         system(cmd);
     67     }
     68 
     69     /**
     70      * 判断是否已经root了
     71      * */
     72     public static boolean hasRootAccess(Context ctx) {
     73         final StringBuilder res = new StringBuilder();
     74         try {
     75             if (runCommandAsRoot(ctx, "exit 0", res) == 0)
     76                 return true;
     77         } catch (Exception e) {
     78         }
     79         return false;
     80     }
     81 
     82     /**
     83      * 以root的权限运行命令
     84      * */
     85     public static int runCommandAsRoot(Context ctx, String script,
     86             StringBuilder res) {
     87         final File file = new File(ctx.getCacheDir(), "secopt.sh");
     88         final ScriptRunner runner = new ScriptRunner(file, script, res);
     89         runner.start();
     90         try {
     91             runner.join(40000);
     92             if (runner.isAlive()) {
     93                 runner.interrupt();
     94                 runner.join(150);
     95                 runner.destroy();
     96                 runner.join(50);
     97             }
     98         } catch (InterruptedException ex) {
     99         }
    100         return runner.exitcode;
    101     }
    102 
    103     private static final class ScriptRunner extends Thread {
    104         private final File file;
    105         private final String script;
    106         private final StringBuilder res;
    107         public int exitcode = -1;
    108         private Process exec;
    109 
    110         public ScriptRunner(File file, String script, StringBuilder res) {
    111             this.file = file;
    112             this.script = script;
    113             this.res = res;
    114         }
    115 
    116         @Override
    117         public void run() {
    118             try {
    119                 file.createNewFile();
    120                 final String abspath = file.getAbsolutePath();
    121                 Runtime.getRuntime().exec("chmod 777 " + abspath).waitFor();
    122                 final OutputStreamWriter out = new OutputStreamWriter(
    123                         new FileOutputStream(file));
    124                 if (new File("/system/bin/sh").exists()) {
    125                     out.write("#!/system/bin/sh
    ");
    126                 }
    127                 out.write(script);
    128                 if (!script.endsWith("
    "))
    129                     out.write("
    ");
    130                 out.write("exit
    ");
    131                 out.flush();
    132                 out.close();
    133 
    134                 exec = Runtime.getRuntime().exec("su");
    135                 DataOutputStream os = new DataOutputStream(
    136                         exec.getOutputStream());
    137                 os.writeBytes(abspath);
    138                 os.flush();
    139                 os.close();
    140 
    141                 InputStreamReader r = new InputStreamReader(
    142                         exec.getInputStream());
    143                 final char buf[] = new char[1024];
    144                 int read = 0;
    145                 while ((read = r.read(buf)) != -1) {
    146                     if (res != null)
    147                         res.append(buf, 0, read);
    148                 }
    149 
    150                 r = new InputStreamReader(exec.getErrorStream());
    151                 read = 0;
    152                 while ((read = r.read(buf)) != -1) {
    153                     if (res != null)
    154                         res.append(buf, 0, read);
    155                 }
    156 
    157                 if (exec != null)
    158                     this.exitcode = exec.waitFor();
    159             } catch (InterruptedException ex) {
    160                 if (res != null)
    161                     res.append("
    Operation timed-out");
    162             } catch (Exception ex) {
    163                 if (res != null)
    164                     res.append("
    " + ex);
    165             } finally {
    166                 destroy();
    167             }
    168         }
    169 
    170         public synchronized void destroy() {
    171             if (exec != null)
    172                 exec.destroy();
    173             exec = null;
    174         }
    175     }
    176 }
    View Code

    作者:登天路

    转载请说明出处:http://www.cnblogs.com/travellife/

  • 相关阅读:
    lua 计算字符串字符个数“中文字算一个字符”
    C API
    词汇
    LUA 创建文件和文件夹
    lua lfs库
    Unity3d gameObject
    Unity3d Time
    Unity3d Vector3
    Unity3d transform
    从Oracle数据库中的本地命名文件tnsnames.ora来看服务别名、服务名和实例名的区别。
  • 原文地址:https://www.cnblogs.com/travellife/p/4108208.html
Copyright © 2011-2022 走看看