网站首页 > 技术文章 正文
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:
- 优点:支持日志级别和过滤,能够长时间记录日志,适合应用和系统的全面调试。
- 缺点:由于依赖环形缓冲区和内核日志机制,处理速度可能较慢,且在缓冲区满时可能丢失日志。
猜你喜欢
- 2024-09-29 图文教程:PC利用adb工具通过CMD命令控制手机动作(备忘笔记)
- 2024-09-29 ADB 调试手机的三种方式(USB、WLAN、WIFI)
- 2024-09-29 打怪升级看这里,adb命令大全等着你
- 2024-09-29 App专项测试(3)-常见的ADB命令(下)
- 2024-09-29 软件测试系列:移动端安卓APP测试必备之ADB命令 (一)
- 2024-09-29 软件测试学习笔记丨App端测试——adb shell相关命令
- 2024-09-29 App专项测试(2)-常见的ADB命令(上)
- 2024-09-29 番外篇——ADB命令总结 adb命令干什么用的
- 2024-09-29 「Android」Android常规adb命令 android adb命令
- 2024-09-29 泽众云真机-ADB调试功能已上线! 泽众安全科技有限公司
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- oraclesql优化 (66)
- 类的加载机制 (75)
- feignclient (62)
- 一致性hash算法 (71)
- dockfile (66)
- 锁机制 (57)
- javaresponse (60)
- 查看hive版本 (59)
- phpworkerman (57)
- spark算子 (58)
- vue双向绑定的原理 (68)
- springbootget请求 (58)
- docker网络三种模式 (67)
- spring控制反转 (71)
- data:image/jpeg (69)
- base64 (69)
- java分页 (64)
- kibanadocker (60)
- qabstracttablemodel (62)
- java生成pdf文件 (69)
- deletelater (62)
- com.aspose.words (58)
- android.mk (62)
- qopengl (73)
- epoch_millis (61)
本文暂时没有评论,来添加一个吧(●'◡'●)