上篇文章提到了如何解锁 Android 设备,文章结尾提到了 adb 相关的内容,所以花几分钟时间说下 Macaca 是如何封装 adb 的。
adb - Android Debug Bridge
, 是 Android 平台开发和测试需要用到的最基本工具,当你安装完 ANDROID SDK 时它就已经存在了。
更详细的介绍和命令用法请到 command-line/adb。
macaca-adb 就是 Macaca 对 adb 的 Node.js 封装。通过查看源码可以发现,它对push pull forward install ...
等一些常用方法都有包装。
用 startApp
这个 api 为例,细述一下:
调用的方法如下,上篇文章已经提及过,将需要的 activity
和 package name
通过参数传进去:
startApp
方法会接收到需要的参数,然后拼接成为最终要执行的命令行执行
ADB.prototype.startApp = function() {
var args = Array.prototype.slice.call(arguments);
var options = args.shift();
var activity = options.activity;
var pkg = options.package;
var shell = `am start -S -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -f 0x10200000 -n ${pkg}/${activity}`;
args.unshift(shell);
var promise = new Promise((resolve, reject) => {
this.getApiLevel((err, apiLevel) => {
if (parseInt(apiLevel, 10) > 15) {
args[0] = `${args[0]} -S`;
}
this.shell.apply(this, args).then(data => {
resolve(data);
}).catch(err => {
reject(`exec ${cmd} error with: ${err}`);
});
});
});
if (args.length > 1) {
var cb = args[1];
return promise.then(data => {
cb.call(this, null, data);
}).catch(err => {
cb.call(this, `exec ${cmd} error with: ${err}`);
});
} else {
return promise;
}
};
执行的方式也很简单,直接使用 Node.js 提供的子进程运行,并且拿到执行结果透传即可:
_.exec = function(cmd, opts) {
return new Promise(function(resolve, reject) {
childProcess.exec(cmd, _.merge({
maxBuffer: 1024 * 512,
wrapArgs: false
}, opts || {}), function(err, stdout, stderr) {
if (err) {
return reject(err);
}
resolve(_.trim(stdout));
});
});
};
其他的方法也都是使用同样的方式封装的,封装的过程中需要注意必要错误一定要抛出,避免用户遇到问题无法排查,或者无法被外围捕捉,需要容错的地方也要给出一定的提示。
解答下大家对 -f 0x10200000
启动参数的疑问。
先看下为什么会出现这个参数,这里的含义是 launchFlags
,通过查看 Android SDK 提供的源码 Intent.java
:
/**
* If set, this activity will become the start of a new task on this
* history stack. A task (from the activity that started it to the
* next task activity) defines an atomic group of activities that the
* user can move to. Tasks can be moved to the foreground and background;
* all of the activities inside of a particular task always remain in
* the same order. See
* <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
* Stack</a> for more information about tasks.
*
* <p>This flag is generally used by activities that want
* to present a "launcher" style behavior: they give the user a list of
* separate things that can be done, which otherwise run completely
* independently of the activity launching them.
*
* <p>When using this flag, if a task is already running for the activity
* you are now starting, then a new activity will not be started; instead,
* the current task will simply be brought to the front of the screen with
* the state it was last in. See {@link #FLAG_ACTIVITY_MULTIPLE_TASK} for a flag
* to disable this behavior.
*
* <p>This flag can not be used when the caller is requesting a result from
* the activity being launched.
*/
public static final int FLAG_ACTIVITY_NEW_TASK = 0x10000000;
/**
* If set, and this activity is either being started in a new task or
* bringing to the top an existing task, then it will be launched as
* the front door of the task. This will result in the application of
* any affinities needed to have that task in the proper state (either
* moving activities to or from it), or simply resetting that task to
* its initial state if needed.
*/
public static final int FLAG_ACTIVITY_RESET_TASK_IF_NEEDED = 0x00200000;
ok,打开你的浏览器 console 试试吧,这里相当于两个枚举值的或操作。
0x10200000 == Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
注:本例使用 API-19,不代表所有 API 版本。
欢迎讨论,互相学习。
微博: http://weibo.com/xudafeng
Github: https://github.com/xudafeng