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权限)
package com.lsj.adb; import java.io.DataOutputStream; import android.app.Activity;
import android.os.Bundle;
import android.view.Menu; public class MainActivity extends Activity { private String[] search = {
"input keyevent 3",// 返回到主界面,数值与按键的对应关系可查阅KeyEvent
"sleep 1",// 等待1秒
"am start -n com.tencent.mm/com.tencent.mm.ui.LauncherUI",// 打开微信的启动界面,am命令的用法可自行百度、Google
"sleep 3",// 等待3秒
"am start -n com.tencent.mm/com.tencent.mm.plugin.search.ui.SearchUI",// 打开微信的搜索
"input text 123",// 像搜索框中输入123,但是input不支持中文,蛋疼,而且这边没做输入法处理,默认会自动弹出输入法
}; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//如果input text中有中文,可以将中文转成unicode进行input,没有测试,只是觉得这个思路是可行的
search[5] = chineseToUnicode(search[5]);
execShell(search);
} /**
* 执行Shell命令
*
* @param commands
* 要执行的命令数组
*/
public void execShell(String[] commands) {
// 获取Runtime对象
Runtime runtime = Runtime.getRuntime(); DataOutputStream os = null;
try {
// 获取root权限,这里大量申请root权限会导致应用卡死,可以把Runtime和Process放在Application中初始化
Process process = runtime.exec("su");
os = new DataOutputStream(process.getOutputStream());
for (String command : commands) {
if (command == null) {
continue;
} // donnot use os.writeBytes(commmand), avoid chinese charset
// error
os.write(command.getBytes());
os.writeBytes("\n");
os.flush();
}
os.writeBytes("exit\n");
os.flush();
process.waitFor();
} catch (Exception e) {
e.printStackTrace();
}
} /**
* 把中文转成Unicode码
* @param str
* @return
*/
public String chineseToUnicode(String str){
String result="";
for (int i = 0; i < str.length(); i++){
int chr1 = (char) str.charAt(i);
if(chr1>=19968&&chr1<=171941){//汉字范围 \u4e00-\u9fa5 (中文)
result+="\\u" + Integer.toHexString(chr1);
}else{
result+=str.charAt(i);
}
}
return result;
} /**
* 判断是否为中文字符
* @param c
* @return
*/
public boolean isChinese(char c) {
Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
|| ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
|| ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
|| ub == Character.UnicodeBlock.GENERAL_PUNCTUATION
|| ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION
|| ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) {
return true;
}
return false;
} }
效果图:
模拟用户打开微信,并进行搜索就这么完成了。其实这里用shell命令模拟用户操作还是有些问题的,比如说控件长按(sendevent),好难理解,而且需要跟其中传递的控件坐标参数应该要跟屏幕分辨率联系起来,实际应用范围不是很广泛。PS:那种大量需要重复操作的除外,如:自动化测试,游戏刷图(PS:腾讯的仙剑手游把我大仙剑毁了啊,麻花腾你妹啊,你全家都是麻花腾)。
最后,其实可以参考下按键精灵,这款应用做的还不错,除了不给root权限就崩外....
补充:以上模拟用户操作的代码在交互不频繁的情况下是完全没有问题的,但是如果使用频繁的话,会发生多次申请root权限,导致系统卡死的现象,后面在google上找到了一个开源项目,可以解决这个问题(它使用的是单例模式),代码如下:
/**
* 类名 RootContext.java 说明 获取root权限 创建日期 2012-8-21 作者 LiWenLong Email
* lendylongli@gmail.com 更新时间 $Date$ 最后更新者 $Author$
*/
public class RootContext {
private static RootContext instance = null;
private static Object mLock = new Object();
String mShell;
OutputStream o;
Process p; private RootContext(String cmd) throws Exception {
this.mShell = cmd;
init();
} public static RootContext getInstance() {
if (instance != null) {
return instance;
}
synchronized (mLock) {
try {
instance = new RootContext("su");
} catch (Exception e) {
while (true)
try {
instance = new RootContext("/system/xbin/su");
} catch (Exception e2) {
try {
instance = new RootContext("/system/bin/su");
} catch (Exception e3) {
e3.printStackTrace();
}
}
}
return instance;
}
} private void init() throws Exception {
if ((this.p != null) && (this.o != null)) {
this.o.flush();
this.o.close();
this.p.destroy();
}
this.p = Runtime.getRuntime().exec(this.mShell);
this.o = this.p.getOutputStream();
system("LD_LIBRARY_PATH=/vendor/lib:/system/lib ");
} private void system(String cmd) {
try {
this.o.write((cmd + "\n").getBytes("ASCII"));
return;
} catch (Exception e) {
while (true)
try {
init();
} catch (Exception e1) {
e1.printStackTrace();
}
}
} public void runCommand(String cmd) {
system(cmd);
} /**
* 判断是否已经root了
* */
public static boolean hasRootAccess(Context ctx) {
final StringBuilder res = new StringBuilder();
try {
if (runCommandAsRoot(ctx, "exit 0", res) == 0)
return true;
} catch (Exception e) {
}
return false;
} /**
* 以root的权限运行命令
* */
public static int runCommandAsRoot(Context ctx, String script,
StringBuilder res) {
final File file = new File(ctx.getCacheDir(), "secopt.sh");
final ScriptRunner runner = new ScriptRunner(file, script, res);
runner.start();
try {
runner.join(40000);
if (runner.isAlive()) {
runner.interrupt();
runner.join(150);
runner.destroy();
runner.join(50);
}
} catch (InterruptedException ex) {
}
return runner.exitcode;
} private static final class ScriptRunner extends Thread {
private final File file;
private final String script;
private final StringBuilder res;
public int exitcode = -1;
private Process exec; public ScriptRunner(File file, String script, StringBuilder res) {
this.file = file;
this.script = script;
this.res = res;
} @Override
public void run() {
try {
file.createNewFile();
final String abspath = file.getAbsolutePath();
Runtime.getRuntime().exec("chmod 777 " + abspath).waitFor();
final OutputStreamWriter out = new OutputStreamWriter(
new FileOutputStream(file));
if (new File("/system/bin/sh").exists()) {
out.write("#!/system/bin/sh\n");
}
out.write(script);
if (!script.endsWith("\n"))
out.write("\n");
out.write("exit\n");
out.flush();
out.close(); exec = Runtime.getRuntime().exec("su");
DataOutputStream os = new DataOutputStream(
exec.getOutputStream());
os.writeBytes(abspath);
os.flush();
os.close(); InputStreamReader r = new InputStreamReader(
exec.getInputStream());
final char buf[] = new char[1024];
int read = 0;
while ((read = r.read(buf)) != -1) {
if (res != null)
res.append(buf, 0, read);
} r = new InputStreamReader(exec.getErrorStream());
read = 0;
while ((read = r.read(buf)) != -1) {
if (res != null)
res.append(buf, 0, read);
} if (exec != null)
this.exitcode = exec.waitFor();
} catch (InterruptedException ex) {
if (res != null)
res.append("\nOperation timed-out");
} catch (Exception ex) {
if (res != null)
res.append("\n" + ex);
} finally {
destroy();
}
} public synchronized void destroy() {
if (exec != null)
exec.destroy();
exec = null;
}
}
}
作者:登天路
转载请说明出处:http://www.cnblogs.com/travellife/
Android随笔之——用shell脚本模拟用户按键、触摸操作的更多相关文章
- Android手机上,利用bat脚本模拟用户操作
………… 那么你就可以来看看这篇帖子了. 言归正传 利用bat脚本模拟用户操作,需要用到两点: ①就是adb命令了,adb命令可以用来模拟用户在手机上的操作 ②bat语言,就是批处理语言,主要用来进行 ...
- win7 cmd终端连接android手机运行adb shell脚本命令
win7 cmd终端连接android手机运行adb shell脚本命令 (2013-03-22 20:13:57) 转载▼ 标签: android it shell 连接 linux 分类: 嵌入式 ...
- Shell脚本中执行sql语句操作mysql的5种方法【转】
对于自动化运维,诸如备份恢复之类的,DBA经常需要将SQL语句封装到shell脚本.本文描述了在Linux环境下mysql数据库中,shell脚本下调用sql语句的几种方法,供大家参考.对于脚本输出的 ...
- 可显示Android设备选择列表,并进入指定Android设备Console的Shell脚本
如果PC上连接多部Android设备(包括Android模拟器),在进入Console时还需要使用adb -s deviceid shell.比较麻烦,本文为此编写了一个Shell脚本文件(需要在Li ...
- Shell 脚本模拟 milter 实现黑白名单及关键词过滤
程序执行流程:1. 开始接受邮件.2. 检查发件人是否在黑名单内,如果是拒绝接受;否则继续3. 检查发件人是否在白名单内,如果是接收邮件;否则继续4. 对邮件进行关键字过滤,如果邮件中包含被过滤的关键 ...
- [Android]Recovery调用外部Shell脚本,Shell脚本使用ui_print方法
busybox_bin=/sbin/busybox # 获取PIPE get_outfd(){ | $busybox_bin grep -q 'pipe'; then else local all_p ...
- 使用shell脚本添加用户
该文演示如何使用shell脚本完成添加用户,首先进行一个判断,如果用户存在,提示该用户已经存在,否则进行添加新的用户. 示例代码如下: #!/bin/bash grep_user() { R=`gre ...
- shell脚本连接、读写、操作mysql数据库实例
本文介绍了如何在shell中读写mysql数据库.主要介绍了如何在shell 中连接mysql数据库,如何在shell中创建数据库,创建表,插入csv文件,读取mysql数据库,导出mysql数据库为 ...
- Shell脚本中执行sql语句操作mysql
对于自动化运维,诸如备份恢复之类的,DBA经常需要将SQL语句封装到shell脚本.本文描述了在Linux环境下mysql数据库中,shell脚本下调用sql语句的几种方法,供大家参考.对于脚本输出的 ...
随机推荐
- mysql安装流程
一.配置MySQL数据库 1.解压绿色版mysql,如下图 二.安装服务 1.运行cmd(管理员版本,否则没有权限),如下图 2.运行命令mysqld –install安装服务,如下图: 如果不需要m ...
- PHP:函数赋参数默认初值
函数的默认参数 允许定义函数时,指定参数的默认值,如: function js_location($url, $alert='', $top=''){ if($alert=='' && ...
- zabbix监控报错zabbix server is not running解决方法
问题描述: 布置好zabbix监控,正常运行.但是重启了虚拟机服务器之后,页面出现如下的报错,而且设置的报信息也是失效的:
- Windows多线程多任务设计初步(转)
Windows多线程多任务设计初步 [前言:]当前流行的Windows操作系统,它能同时运行几个程序(独立运行的程序又称之为进程),对于同一个程序,它又可以分成若干个独立的执行流,我们称之为线程,线程 ...
- 2016/11/16 周三 <Web SQL Database基本使用方法(入门) >
<!doctype html> <html> <head> <meta charset="UTF-8"> <title> ...
- TaintDroid剖析之Native方法级污点跟踪分析
1.Native方法的污点传播 在前两篇文章中我们详细分析了TaintDroid对DVM栈帧的修改,以及它是如何在修改之后的栈帧中实现DVM变量级污点跟踪的.现在我们继续分析其第二个粒度的污点跟踪—— ...
- Expert 诊断优化系列------------------冤枉磁盘了
现在很多用户被数据库的慢的问题所困扰,又苦于花钱请一个专业的DBA成本太高.软件维护人员对数据库的了解又不是那么深入,所以导致问题迟迟不能解决,或只能暂时解决不能得到根治.开发人员解决数据问题基本又是 ...
- 软件工程(QLGY2015)博客点评总结
目录 第一次作业(2015.5.9) 第二次作业(2015.5.21) 第三次作业(2015.6.11) 2015上半年软工助教总结 第一次作业(2015.5.9) 存在主要问题 1)书写这种练习博客 ...
- OpenGL快问快答
OpenGL快问快答 本文内容主要来自对(http://www.opengl.org/wiki/FAQ)的翻译,随机加入了本人的观点.与原文相比,章节未必完整,含义未必雷同,顺序未必一致.仅供参考. ...
- C语言 · 冒泡排序
for(int k=0;k<N;k++) { for(int j=k+1;j<N;j++){ if(a[k]>a[j]){ int t = a[k]; a ...