Android中如何实现adb向应用发送指令并返回
suiw9 2024-12-18 17:02 44 浏览 0 评论
1 ADB发送命令给应用
1.1 发送自定义广播给系统或应用
adb shell am broadcast 是用于向 Android 系统发送广播的命令。通过这个命令,开发者可以发送自定义广播给系统或应用,触发应用中的广播接收器(BroadcastReceiver)
命令解析
命令的格式如下:
adb shell am broadcast -a com.example.broadcast.MY_ACTION --es "key" "value"
各个部分的解释如下:
- adb shell:进入设备的命令行环境,执行后续的 shell 命令。
- am broadcast:am 是 Android Activity Manager 的缩写,它管理 Android 的核心系统组件(如 Activity、Service、Broadcast等)。broadcast 表示通过 am 命令来发送广播。
- -a com.example.broadcast.MY_ACTION:-a 代表广播的动作(action),这也是应用监听广播的主要方式。这里 com.example.broadcast.MY_ACTION 是一个自定义的广播动作名。
- --es "key" "value":--es 表示添加额外的数据,key 是数据的键,value 是对应的值。在这个例子中,key 是 "userQuestion",value 是用户的问题。
实现原理
发送广播:
- 当运行这个命令时,ADB 通过 USB、Wi-Fi 或其他通信方式与 Android 设备进行通信,并在设备上执行 am broadcast 命令。
- am broadcast 代表通过 Android 的 Activity Manager 来发送一个广播,广播的 action 是由 -a 后指定的 com.example.broadcast.MY_ACTION。
应用的监听机制:
- Android 应用可以通过注册一个 BroadcastReceiver 来监听特定的广播 action。如果应用注册了监听 com.example.broadcast.MY_ACTION 的广播接收器,系统在接收到该广播时就会自动将其传递给应用。
广播接收器的注册方式有两种:
- 静态注册:在 AndroidManifest.xml 中声明接收器。
- 动态注册:在运行时通过代码调用 registerReceiver() 方法注册。
- 例如,在 AndroidManifest.xml 中声明一个广播接收器:
- <receiver android:name=".MyBroadcastReceiver"> <intent-filter> <action android:name="com.example.broadcast.MY_ACTION" /> </intent-filter> </receiver>
- 或在代码中动态注册:
- IntentFilter filter = new IntentFilter("com.example.broadcast.MY_ACTION"); registerReceiver(myBroadcastReceiver, filter);
传递数据:
- 当广播被接收时,广播中的额外数据(如 "userQuestion": "value")也会通过 Intent 对象传递给接收器。应用可以在 onReceive() 方法中获取这些数据,并根据具体逻辑处理它们。
- 例如,在 BroadcastReceiver 的 onReceive 方法中:
- @Override public void onReceive(Context context, Intent intent) { if ("com.example.broadcast.MY_ACTION".equals(intent.getAction())) { String userQuestion = intent.getStringExtra("key"); // 处理用户问题 } }
系统调度广播:
- Android 系统会根据广播的 action 将广播调度给所有已经注册了相关接收器的应用。
- 如果广播接收器是静态注册的,系统会启动相关应用的进程来接收广播。
- 如果是动态注册的,只有当应用正在运行时,系统才会将广播传递给它。
应用场景
- 测试广播接收器是否正常工作。
- 向应用发送特定指令(如更新数据、触发特定操作)。
- 在应用开发过程中,用于模拟特定场景下广播的发送。
1.2 通过 ADB 接收到应用的返回信息
通过Logcat捕获日志输出
一种常见的方式是通过应用在接收到广播后的日志输出来获取应用的执行结果。你可以在应用中接收到广播后,使用 Log 类记录相关信息,然后通过 adb logcat 命令捕获这些日志。
步骤:
- 应用端广播接收器:在应用中接收到广播后,输出日志。
- @Override public void onReceive(Context context, Intent intent) { if ("com.example.broadcast.MY_ACTION".equals(intent.getAction())) { String userQuestion = intent.getStringExtra("key"); // 执行相应的操作 Log.d("MyBroadcastReceiver", "Received broadcast: " + userQuestion); } }
- 通过ADB发送广播:
- adb shell am broadcast -a com.example.broadcast.MY_ACTION --es "key" "value"
- 使用Logcat查看应用的返回结果:
- adb logcat | grep MyBroadcastReceiver
- 通过 adb logcat 过滤与应用相关的日志信息,查看应用的响应输出。
通过广播回传结果(结果接收机制)
ADB 发送的广播可以通过 --receiver-permission 参数指定接收器的权限,同时接收器可以通过 setResultData() 等方法返回数据。
步骤:
- 应用端广播接收器:在接收器的 onReceive() 中设置返回数据。
- @Override public void onReceive(Context context, Intent intent) { if ("com.example.broadcast.MY_ACTION".equals(intent.getAction())) { String userQuestion = intent.getStringExtra("key"); // 设置返回数据 setResultData("Received question: " + userQuestion); } }
- 通过ADB发送广播并等待返回:
- adb shell am broadcast -a com.example.broadcast.MY_ACTION --es "key" "value" --receiver-permission android.permission.BROADCAST_STICKY
- 返回结果:如果广播接收器调用了 setResultData(),则 ADB 会返回相应的结果数据。你可以在终端上看到如下返回:
- Broadcast completed: result=0, data="Received question: value"
通过文件共享传递结果
你可以通过应用将结果写入文件,然后通过 ADB 命令将文件从设备中导出读取。
步骤:
- 应用端接收广播并将结果写入文件:
- @Override public void onReceive(Context context, Intent intent) { if ("com.example.broadcast.MY_ACTION".equals(intent.getAction())) { String userQuestion = intent.getStringExtra("key"); // 将数据写入文件 try { File file = new File(context.getExternalFilesDir(null), "result.txt"); FileWriter writer = new FileWriter(file); writer.write("Received question: " + userQuestion); writer.close(); } catch (IOException e) { e.printStackTrace(); } } }
- 通过ADB发送广播:
- adb shell am broadcast -a com.example.broadcast.MY_ACTION --es "key" "value"
- 通过ADB拉取文件:
- adb pull /sdcard/Android/data/com.example/files/result.txt .
- 通过 adb pull 命令将结果文件拉取到本地进行查看。
通过Intent Service回传结果
如果你希望通过广播触发某个后台服务,并由该服务处理并回传结果,可以使用 IntentService 来处理逻辑,最后通过 ResultReceiver 回传数据。
步骤:
- 在应用端实现 IntentService 并使用 ResultReceiver:
- public class MyIntentService extends IntentService { public MyIntentService() { super("MyIntentService"); } @Override protected void onHandleIntent(Intent intent) { String userQuestion = intent.getStringExtra("key"); ResultReceiver receiver = intent.getParcelableExtra("receiver"); // 模拟处理 Bundle bundle = new Bundle(); bundle.putString("result", "Processed question: " + userQuestion); receiver.send(0, bundle); } }
- 在广播接收器中启动 IntentService 并设置 ResultReceiver:
- @Override public void onReceive(Context context, Intent intent) { if ("com.example.broadcast.MY_ACTION".equals(intent.getAction())) { Intent serviceIntent = new Intent(context, MyIntentService.class); serviceIntent.putExtra("key", intent.getStringExtra("key")); serviceIntent.putExtra("receiver", new ResultReceiver(new Handler()) { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { String result = resultData.getString("result"); Log.d("MyIntentService", result); } }); context.startService(serviceIntent); } }
- 通过ADB发送广播:
- adb shell am broadcast -a com.example.broadcast.MY_ACTION --es "key" "value"
- 通过 Logcat 查看IntentService的返回结果:
- adb logcat | grep MyIntentService
通过Content Provider回传结果
应用可以通过 ContentProvider 提供数据访问接口,ADB 可以通过 content 命令与 ContentProvider 交互,读取应用产生的结果数据
步骤:
- 应用实现 ContentProvider 并在接收广播后插入数据:
- @Override public void onReceive(Context context, Intent intent) { if ("com.example.broadcast.MY_ACTION".equals(intent.getAction())) { String userQuestion = intent.getStringExtra("key"); // 插入结果到ContentProvider ContentValues values = new ContentValues(); values.put("result", "Processed question: " + userQuestion); context.getContentResolver().insert(MyContentProvider.CONTENT_URI, values); } }
- 通过ADB发送广播:
- adb shell am broadcast -a com.example.broadcast.MY_ACTION --es "key" "value"
- 通过ADB查询 ContentProvider 获取结果:
- adb shell content query --uri content://com.example.provider/results
- 你可以通过 adb shell content 命令查询应用的 ContentProvider,获取应用插入的结果数据。
2 通过ADB使用Socket或HTTP的方式与应用通信
2.1 通过 Socket 方式与应用通信
原理:
- 应用需要在本地启动一个 Socket Server,监听某个端口,等待接收来自客户端的指令。
- 然后,通过 ADB 使用端口转发(Port Forwarding),使得开发者可以在本地通过 Socket 客户端发送指令到应用,并接收返回结果。
步骤:
应用端:创建一个 Socket 服务端
应用需要在某个端口上监听客户端连接,并处理收到的消息。你可以使用 Java 的 ServerSocket 来实现。
import java.io.*;
import java.net.*;
public class SocketServer extends Thread {
private ServerSocket serverSocket;
public SocketServer(int port) throws IOException {
serverSocket = new ServerSocket(port);
}
public void run() {
while (true) {
try {
Socket server = serverSocket.accept();
BufferedReader in = new BufferedReader(new InputStreamReader(server.getInputStream()));
PrintWriter out = new PrintWriter(server.getOutputStream(), true);
String clientInput;
while ((clientInput = in.readLine()) != null) {
System.out.println("Received: " + clientInput);
// 返回结果给客户端
out.println("Processed: " + clientInput);
}
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
你可以在应用的某个 Activity 或 Service 中启动这个 SocketServer,例如监听 localhost:12345。
ADB 端口转发
使用 ADB 将设备上的 Socket 端口转发到本地,方便你在本地通过 Socket 客户端与应用通信。
adb forward tcp:12345 tcp:12345
该命令会将本地的 12345 端口映射到设备上的 12345 端口。
在本地通过 Socket 客户端发送指令
你可以使用任意的 Socket 客户端工具(如 Python、Java 等)来连接设备,并发送消息。以下是 Python 的一个简单示例:
import socket
# 连接到localhost:12345 (已通过ADB转发到设备)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('127.0.0.1', 12345))
# 发送消息
sock.sendall(b'Hello from client\n')
# 接收应用返回的消息
response = sock.recv(1024)
print("Received from server: ", response.decode())
sock.close()
在这个过程中,消息会被通过 ADB 转发给设备上的应用的 Socket 服务,应用处理后会将结果返回给客户端。
2.2 通过 HTTP 方式与应用通信
原理:
- 应用可以作为一个 HTTP 服务器,监听某个端口,等待 HTTP 请求。
- 使用 ADB 端口转发将设备上的 HTTP 端口映射到本地,用户可以通过浏览器、cURL、Postman 或任何 HTTP 客户端向应用发送请求,并接收返回结果。
步骤:
应用端:创建一个 HTTP 服务
应用需要实现一个 HTTP 服务器,可以使用 Android 中的 NanoHTTPD 库等轻量级 HTTP 服务器工具,来监听 HTTP 请求并处理数据。
首先,将 NanoHTTPD 库添加到项目中:
implementation 'org.nanohttpd:nanohttpd:2.3.1'
然后,实现一个简单的 HTTP 服务器:
import fi.iki.elonen.NanoHTTPD;
public class MyHTTPServer extends NanoHTTPD {
public MyHTTPServer(int port) {
super(port);
}
@Override
public Response serve(IHTTPSession session) {
String msg = "<html><body><h1>Hello from HTTP Server</h1>\n";
String clientInput = session.getParms().get("input");
msg += "<p>Received input: " + clientInput + "</p>";
return newFixedLengthResponse(msg);
}
}
在应用的某个 Activity 或 Service 中启动 HTTP 服务器:
MyHTTPServer server = new MyHTTPServer(8080);
server.start();
ADB 端口转发
使用 ADB 将设备上的 HTTP 端口映射到本地端口:
adb forward tcp:8080 tcp:8080
这个命令会将本地的 8080 端口映射到设备的 8080 端口。
通过 HTTP 客户端发送请求
现在,你可以在本地通过浏览器、cURL、Postman 或其他 HTTP 客户端工具向应用的 HTTP 服务器发送请求。
- 通过浏览器:
在浏览器中访问 http://127.0.0.1:8080?input=HelloFromClient,浏览器会显示来自应用的响应。 - 通过 cURL:
你也可以使用 cURL 发送请求: - curl "http://127.0.0.1:8080?input=HelloFromClient"
应用将处理请求,并返回包含输入信息的响应:
<html>
<body>
<h1>Hello from HTTP Server</h1>
<p>Received input: HelloFromClient</p>
</body>
</html>
这两种方式可以实现通过 ADB 与应用进行交互,适用于不同的场景,具体选择哪种方式取决于需求和使用的技术栈。
3 接收应用的返回
System.out.println() / System.err.println():输出是直接流到当前的 ADB shell 会话或 IDE 调试控制台中,通常只在当前会话期间有效。如果通过 ADB 执行命令运行应用,你可以直接在 ADB 控制台中看到 System.out 的输出。
Log.d() / Log.i() 等:通过 adb logcat 查看,并可以通过日志级别过滤和保存。可以随时在设备中查看历史日志,甚至可以将设备中的日志导出到文件中。
3.1 System.out.println()` 的实现原理
System.out.println() 是 Java 标准库中提供的一种基础方法,用于向标准输出(stdout)写入信息。在 Android 系统中,这个标准输出通常被重定向到了设备的控制台输出,也就是连接设备的 ADB shell。其具体工作流程如下:
- 标准输出流(stdout):
- 在 Java 中,System.out 是 PrintStream 的一个实例,它包装了 UNIX 标准输出流(stdout)。
- 当你调用 System.out.println() 方法时,它将信息格式化为字符串,然后通过 PrintStream 将这些字符串写入 stdout。
- 输出重定向:
- 在 Android 设备上运行的应用是在一个 Linux-based 系统上的,每个应用都是一个独立的 Linux 进程。
- 这些进程的 stdout 和 stderr 通常被重定向到了 /dev/null(一个丢弃所有写入数据的设备),但当通过 ADB 连接时,stdout 和 stderr 会被 ADB 捕获并重定向到 ADB 的控制台。
- 即时性:
- 由于 stdout 输出的重定向,通过 ADB 运行的应用会将 System.out.println() 的输出直接显示在开发者的终端或 IDE 的控制台上。
- 这个过程非常快速,因为它几乎没有任何中间处理,直接通过系统的 I/O 操作进行数据传输。
3.2 Logcat 的实现原理
与 System.out.println() 直接操作标准输出流不同,Logcat 是 Android 特有的一个复杂的日志系统,设计用来收集和查看系统以及应用程序的各种日志信息。它的工作原理如下:
- 日志消息的产生:
- 在 Android 应用中,开发者通过调用 Log 类(如 Log.d(), Log.i(), 等)来记录日志。
- 这些方法最终会调用 Android 的 native 日志接口,该接口封装了向日志设备(如 /dev/log 或 /dev/logger)的写操作。
- 内核中的日志驱动:
- Android 操作系统内核包含一个日志驱动,负责管理日志设备。
- 当应用通过 Log 类写日志时,日志消息被发送到内核的日志驱动,日志驱动将这些消息存储在一个或多个环形缓冲区中。每种类型的日志(如 “main”, “system”)都有自己的环形缓冲区。
- 缓冲区和管理:
- 每个环形缓冲区都有固定的大小,当新的日志写入时,如果缓冲区已满,旧的日志将被新的日志覆盖。
- 这些环形缓冲区是用户空间和内核空间之间的桥梁,用户空间的应用(或 adb logcat 命令)可以查询这些缓冲区来读取日志。
- 日志的检索:
- 开发者通常使用 adb logcat 命令来读取和监控日志。
- adb logcat 命令实际上是连接到这些环形缓冲区,根据指定的过滤条件(如日志级别、标签等)输出日志信息。
3.稳定性和性能差异
- System.out.println():
- 优点:快速、直接,适合即时调试信息的输出。
- 缺点:不适合长期日志记录,输出内容可能会在设备断开时丢失,不支持日志级别和过滤。
- Logcat:
- 优点:支持日志级别和过滤,能够长时间记录日志,适合应用和系统的全面调试。
- 缺点:由于依赖环形缓冲区和内核日志机制,处理速度可能较慢,且在缓冲区满时可能丢失日志。
相关推荐
- 俄罗斯的 HTTPS 也要被废了?(俄罗斯网站关闭)
-
发布该推文的ScottHelme是一名黑客,SecurityHeaders和ReportUri的创始人、Pluralsight作者、BBC常驻黑客。他表示,CAs现在似乎正在停止为俄罗斯域名颁发...
- 如何强制所有流量使用 HTTPS一网上用户
-
如何强制所有流量使用HTTPS一网上用户使用.htaccess强制流量到https的最常见方法可能是使用.htaccess重定向请求。.htaccess是一个简单的文本文件,简称为“.h...
- https和http的区别(https和http有何区别)
-
“HTTPS和HTTP都是数据传输的应用层协议,区别在于HTTPS比HTTP安全”。区别在哪里,我们接着往下看:...
- 快码住!带你十分钟搞懂HTTP与HTTPS协议及请求的区别
-
什么是协议?网络协议是计算机之间为了实现网络通信从而达成的一种“约定”或“规则”,正是因为这个“规则”的存在,不同厂商的生产设备、及不同操作系统组成的计算机之间,才可以实现通信。简单来说,计算机与网络...
- 简述HTTPS工作原理(简述https原理,以及与http的区别)
-
https是在http协议的基础上加了一层SSL(由网景公司开发),加密由ssl实现,它的目的是为用户提供对网站服务器的身份认证(需要CA),以至于保护交换数据的隐私和完整性,原理如图示。1、客户端发...
- 21、HTTPS 有几次握手和挥手?HTTPS 的原理什么是(高薪 常问)
-
HTTPS是3次握手和4次挥手,和HTTP是一样的。HTTPS的原理...
- 一次安全可靠的通信——HTTPS原理
-
为什么HTTPS协议就比HTTP安全呢?一次安全可靠的通信应该包含什么东西呢,这篇文章我会尝试讲清楚这些细节。Alice与Bob的通信...
- 为什么有的网站没有使用https(为什么有的网站点不开)
-
有的网站没有使用HTTPS的原因可能涉及多个方面,以下是.com、.top域名的一些见解:服务器性能限制:HTTPS使用公钥加密和私钥解密技术,这要求服务器具备足够的计算能力来处理加解密操作。如果服务...
- HTTPS是什么?加密原理和证书。SSL/TLS握手过程
-
秘钥的产生过程非对称加密...
- 图解HTTPS「转」(图解http 完整版 彩色版 pdf)
-
我们都知道HTTPS能够加密信息,以免敏感信息被第三方获取。所以很多银行网站或电子邮箱等等安全级别较高的服务都会采用HTTPS协议。...
- HTTP 和 HTTPS 有何不同?一文带你全面了解
-
随着互联网时代的高速发展,Web服务器和客户端之间的安全通信需求也越来越高。HTTP和HTTPS是两种广泛使用的Web通信协议。本文将介绍HTTP和HTTPS的区别,并探讨为什么HTTPS已成为We...
- HTTP与HTTPS的区别,详细介绍(http与https有什么区别)
-
HTTP与HTTPS介绍超文本传输协议HTTP协议被用于在Web浏览器和网站服务器之间传递信息,HTTP协议以明文方式发送内容,不提供任何方式的数据加密,如果攻击者截取了Web浏览器和网站服务器之间的...
- 一文让你轻松掌握 HTTPS(https详解)
-
一文让你轻松掌握HTTPS原文作者:UC国际研发泽原写在最前:欢迎你来到“UC国际技术”公众号,我们将为大家提供与客户端、服务端、算法、测试、数据、前端等相关的高质量技术文章,不限于原创与翻译。...
- 如何在Spring Boot应用程序上启用HTTPS?
-
HTTPS是HTTP的安全版本,旨在提供传输层安全性(TLS)[安全套接字层(SSL)的后继产品],这是地址栏中的挂锁图标,用于在Web服务器和浏览器之间建立加密连接。HTTPS加密每个数据包以安全方...
- 一文彻底搞明白Http以及Https(http0)
-
早期以信息发布为主的Web1.0时代,HTTP已可以满足绝大部分需要。证书费用、服务器的计算资源都比较昂贵,作为HTTP安全扩展的HTTPS,通常只应用在登录、交易等少数环境中。但随着越来越多的重要...
你 发表评论:
欢迎- 一周热门
-
-
Linux:Ubuntu22.04上安装python3.11,简单易上手
-
宝马阿布达比分公司推出独特M4升级套件,整套升级约在20万
-
MATLAB中图片保存的五种方法(一)(matlab中保存图片命令)
-
别再傻傻搞不清楚Workstation Player和Workstation Pro的区别了
-
Linux上使用tinyproxy快速搭建HTTP/HTTPS代理器
-
如何提取、修改、强刷A卡bios a卡刷bios工具
-
Element Plus 的 Dialog 组件实现点击遮罩层不关闭对话框
-
日本组合“岚”将于2020年12月31日停止团体活动
-
SpringCloud OpenFeign 使用 okhttp 发送 HTTP 请求与 HTTP/2 探索
-
tinymce 号称富文本编辑器世界第一,大家同意么?
-
- 最近发表
- 标签列表
-
- dialog.js (57)
- importnew (44)
- windows93网页版 (44)
- yii2框架的优缺点 (45)
- tinyeditor (45)
- qt5.5 (60)
- windowsserver2016镜像下载 (52)
- okhttputils (51)
- android-gif-drawable (53)
- 时间轴插件 (56)
- docker systemd (65)
- slider.js (47)
- android webview缓存 (46)
- pagination.js (59)
- loadjs (62)
- openssl1.0.2 (48)
- velocity模板引擎 (48)
- pcre library (47)
- zabbix微信报警脚本 (63)
- jnetpcap (49)
- pdfrenderer (43)
- fastutil (48)
- uinavigationcontroller (53)
- bitbucket.org (44)
- python websocket-client (47)