蓝牙通讯数据传输,蓝牙UUID,BluetoothAdapter;adb input 模拟系统输入,adb logcat:抓包,抓日志,蓝牙通讯,adb命令实现

本文涉及的产品
数据传输服务DTS,同步至DuckDB 3个月
简介: 蓝牙通讯数据传输,蓝牙UUID,BluetoothAdapter;adb input 模拟系统输入,adb logcat:抓包,抓日志,蓝牙通讯,adb命令实现

adb 命令 模拟滑动



input text
input keyevent
input tap
input swipe


  1. keyevent指的是android对应的keycode,比如home键的keycode=3,back键的keycode=4.


具体:  Android Keycode详解_小小攻城师的博客-CSDN博客_android keycode


举例:模拟home按键:adb shell input keyevent 3


  1. 关于tap的话,他模拟的是touch屏幕的事件,只需给出x、y坐标即可。


此x、y坐标对应的是真实的屏幕分辨率,所以要根据具体手机具体看,比如你想点击屏幕(x, y) = (250, 250)位置:


adb shell input tap 250 250


  1. 关于swipe同tap是一样的,只是他是模拟滑动的事件,给出起点和终点的坐标即可。例如从屏幕(250, 250), 到屏幕(300, 300)即

adb shell input swipe 250 250 300 300


还可以使用:android MotionEvent.obtain模拟事件


public static final int ACTION_DOWN = 0;
public static final int ACTION_HOVER_ENTER = 9;
public static final int ACTION_HOVER_EXIT = 10;
public static final int ACTION_HOVER_MOVE = 7;
public static final int ACTION_MOVE = 2;
复制代码
final long downTime = SystemClock.uptimeMillis();
                final MotionEvent downEvent = MotionEvent.obtain(
                        downTime, downTime, MotionEvent.ACTION_DOWN, 200, 200, 0);
                final MotionEvent upEvent = MotionEvent.obtain(
                        downTime, SystemClock.uptimeMillis() + 10, MotionEvent.ACTION_UP, 200, 200, 0);
                if (mDocView != null) {
                    mDocView.onTouchEvent(downEvent);
                    mDocView.onTouchEvent(upEvent);
                }
                downEvent.recycle();
                upEvent.recycle();

蓝牙相关知识



UUID是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的,那么怎么才能生成这样的一串数字呢?下面就讲讲怎么生成这个数字。


BluetoothAdapter



ef8003f2569e47e5b8ddabca43ee322f_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.png


BluetoothAdapter——本地设备,对蓝牙操作首先就需要有一个BluetoothAdapter实例。常用的几个方法如下:


cancelDiscovery()——取消本地蓝牙设备的搜索操作,如果本地设备正在进行搜索,那么调用该方法后将停止搜索操作。


Disable()——关闭蓝牙设备。


Enable()——打开蓝牙设备。相信大家都有过打开蓝牙的经历,一般情况下都会弹出一个窗口,说正在请求打开蓝牙设备


getAddress()——获取蓝牙设备的MAC地址。


GetDefaultAdapter()——获取本地的蓝牙设备


getName()——获取本地蓝牙的名称


getRemoteDevice(String address)——根据远程设备的MAC地址来获取远程设备


startDiscovery()——蓝牙设备开始搜索周边设备


BuletoothDevice——远程设备。


// 获取本地的蓝牙适配器实例
             BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
             if(adapter!=null)
             {
                 if(!adapter.isEnabled())
                 {
                     //通过这个方法来请求打开我们的蓝牙设备
                     Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                     startActivity(intent);
                 }
             }
             else
             {
                 System.out.println("本地设备驱动异常!");
             }

配对一般都是发现设备后,由我们人工来选择配对。可以通过方法来获得配对的设备:



//通过getBondedDevices方法来获取已经与本设备配对的设备
                 Set<BluetoothDevice> device= adapter.getBondedDevices();
                 if(device.size()>0)
                 {
                     for(Iterator iterator=device.iterator();iterator.hasNext();)
                     {
                         BluetoothDevice bluetoothDevice=(BluetoothDevice)iterator.next();
                         System.out.println(bluetoothDevice.getAddress());
                     }
                 }

数据交换,首先是进行连接,在进行数据传输:



连接:


/**
     * 客户端主动发起连接,去连接服务端
     *
     * @param serviceDevice 服务端的蓝牙设备
     */
    public void connect(BluetoothDevice serviceDevice) {
        if (mTargThread != null) {
            mTargThread.cancle();
        }
        if (mDataThread != null) {
            mDataThread.cancle();
        }
        mTargThread = new BlueClientThread(serviceDevice, mSocketHandler);
        mTargThread.start();
    }
//另一个服务线程
    public BlueClientThread(BluetoothDevice serviceDevice, Handler handler) {
        super(handler);
        mServiceDevice = serviceDevice;
    }
    @Override
    public void run() {
        super.run();
        if (!isRunning) return;
        try {
            sendMessage(BlueSocketStatus.CONNECTIONING);
            mBlueSocket = mServiceDevice.createRfcommSocketToServiceRecord(UUID_ANDROID_DEVICE);
            mBlueSocket.connect();
            sendMessage(BlueSocketStatus.ACCEPTED);
        } catch (IOException e) {
            sendMessage(BlueSocketStatus.DISCONNECTION);
        }
    }
//关键sendmessage
    public void sendMessage(BlueSocketStatus status) {
        if (mHandler != null && isRunning)
            mHandler.obtainMessage(status.ordinal()).sendToTarget();//ordinal  :序数
    }
    public void sendMessage(BlueSocketStatus status, Object object) {
        if (mHandler != null && isRunning)
            mHandler.obtainMessage(status.ordinal(), object).sendToTarget();
//获取message并发送
    }

关键就是:


sendMessage(BlueSocketStatus.CONNECTIONING);
mBlueSocket = mServiceDevice.createRfcommSocketToServiceRecord(UUID_ANDROID_DEVICE);
mBlueSocket.connect();

1e7f22eebe5344159df15faa7b51c558_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.png

进行连接:


8a17acc0d6614d0ab533c06141e83267_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.png


接下来就是发送数据:


private ConcurrentLinkedQueue<IMessage> mQueue = new ConcurrentLinkedQueue<>();
    public synchronized boolean write(IMessage message) {
        message.toString();
        if (mNowStatus == BlueSocketBaseThread.BlueSocketStatus.CONNEDTIONED) {
            synchronized (BluetoothSppHelper.class) {
                if (mNowStatus == BlueSocketBaseThread.BlueSocketStatus.CONNEDTIONED) {
                    mQueue.add(message);
                    mDataThread.startQueue();
                    return true;
                }
                return false;
            }
        }
        return false;
    }

补充1:ConcurrentLinkedQueue:Java并发编程;


在并发编程中我们有时候需要使用线程安全的队列。如果我们要实现一个线程安全的队列有两种实现方式


  1. 一种是使用阻塞算法
  2. 另一种是使用非阻塞算法。
  • 使用阻塞算法的队列可以用一个锁(入队和出队用同一把锁)或两个锁(入队和出队用不同的锁)等方式来实现
  • 非阻塞的实现方式则可以使用循环CAS的方式来实现,下面我们一起来研究下Doug Lea是如何使用非阻塞的方式来实现线程安全队列ConcurrentLinkedQueue的。


ConcurrentLinkedQueue是一个基于链接节点的无界线程安全队列,它采用先进先出的规则对节点进行排序,当我们添加一个元素的时候,它会添加到队列的尾部,当我们获取一个元素时,它会返回队列头部的元素。它采用了“wait-free”算法来实现,该算法在Michael & Scott算法上进行了一些修改。


补充2:createRfcommSocketToServiceRecord,listenUsingRfcommWithServiceRecord


服务端


通过调用BluetoothAdapter的listenUsingRfcommWithServiceRecord(String, UUID)方法来获取BluetoothServerSocket(UUID用于客户端与服务端之间的配对)


BluetoothServerSocket serverSocket = mAdapter.listenUsingRfcommWithServiceRecord(serverSocketName,UUID);
serverSocket.accept();

客户端:UUID 是 通用唯一识别码(Universally Unique Identifier)


调用BluetoothService的createRfcommSocketToServiceRecord(UUID)方法获取BluetoothSocket(该UUID应该同于服务端的UUID)。


调用BluetoothSocket的connect()方法(该方法为block方法),如果UUID同服务端的UUID匹配,并且连接被服务端accept,则connect()方法返回。


BluetoothServerSocket和BluetoothSocket两个类来建立Server端和Client端,还需要使用到一些关于流(Stream)的知识。


BluetoothServerSocket——服务端(监听端、监听器、接受请求的一端)


Accept()——阻塞宿主线程,直至收到客户端请求。返回BluetoothSocket对象。由于这个


Accept(int timeout)——阻塞宿主线程,直至收到客户端请求或等待时间超过timeout。返回BluetoothSocket对象。


Close()——关闭BluetoothServerSocket监听器。


可以看到,Accept方法是一个阻塞方法,所以在进行开发的时候,一般都需要用到多线程的知识。JAVA的多线程知识,可以在JAVA的JDK帮助文档中查看,就单纯的应用来说还是比较简单的。


BluetoothSocket——客户端(请求端)


Close()——关闭BluetoothSocket请求端。


Connect()——主动向服务端(监听端)发起连接请求。


如果一个设备需要和两个或多个设备连接时,就需要作为一个server来传输,服务器端套接字在接受(accepted) 一个客户发来的BluetoothSocket连接请求时作出相应的响应。服务器socket将监听进入的连接请求,一旦连接被接受,将产生一个BluetoothSocket。


创建一个Server


使用BluetoothAdapter类的listenUsingRfcommWithServiceRecord方法来新建一个ServerSocket。在listenUsingRfcommWithServiceRecord中有一个参数叫做UUID,UUID(Universally Unique Identifier)是一个128位的字符串ID,被用于唯一标识我们的蓝牙服务。你可以使用web上的任何一款UUID产生器为你的程序获取一个UUID,然后使用fromString(String)初始化一个UUID。

  

使用ServerSocket实例的accept方法进行监听,当监听到带有我们初始化的UUID参数的连接请求后作出响应,连接成功后返回一个BluetoothSocket对象。连接完成后,调用close方法关闭该Socket监听。


// Bluetooth的ServerSocket包装类
     class BluetoothServer {
         public BluetoothServer() throws IOException {
         }
         // 要建立一个ServerSocket对象,需要使用adapter.listenUsingRfcommWithServiceRecord方法
 // UUID可以在网上去申请
         private BluetoothServerSocket serverSocket = adapter.listenUsingRfcommWithServiceRecord("myServerSocket",
                         UUID.fromString("84D1319C-FBAF-644C-901A-8F091F25AF04"));
         BluetoothSocket socket = serverSocket.accept();
         void m() throws IOException {
             if (socket != null) {
                 InputStream inputStream = socket.getInputStream();
                 int read = -1;
                 final byte[] bytes = new byte[1024];
                 for (; (read = inputStream.read(bytes)) > -1;) {
                     final int count = read;
                     Thread _start = new Thread(new Runnable() {
                         @Override
                         public void run() { 
                             // TODO Auto-generated method stub
                             StringBuilder sb = new StringBuilder();
                             for (int i = 0; i < count; i++) {
                                 if (i > 0) {
                                     sb.append(' ');
                                 }
                                 String _s = Integer.toHexString(bytes[i] & 0xFF);
                                 if (_s.length() < 2) {
                                     sb.append('0');
                                 }
                                 sb.append(_s);
                             }
                             System.out.println(sb.toString());
                         }
                     });
                     _start.start();
                 }
             }
         }
     }

创建一个Client   



创建一个Client端,首先需要我们使用BluetoothDevice的实例的createRfcommSocketToServiceRecord方法来创建一个BluetoothSocket实例。在创建的时候,需要给createRfcommSocketToServiceRecord方法传入我们服务端的UUID值。然后使用BluetoothSocket实例的Connect方法对Server端进行连接请求,当连接成功后,Client端和Server端的传输通道就被打开。最后在连接完成后使用该实例的close方法来关闭这个连接。


class BluetoothClient {
         BluetoothDevice device = null;
         //通过构造函数来传入一个BluetoothDevice实例
         public BluetoothClient(BluetoothDevice device) {
             this.device = device;
         }
         BluetoothSocket socket = null;
         void connetServer() throws IOException {
             Thread _clientThread = new Thread(new Runnable() {
                 public void run() {
                     try {
                         //通过BluetoothDevice实例的createRfcommSocketToServiceRecord方法可以返回一个带有UUID的BluetoothSocket实例
                         socket = device.createRfcommSocketToServiceRecord(UUID.fromString("84D1319C-FBAF-644C-901A-8F091F25AF04"));
                     } catch (IOException e1) { 
                         // TODO Auto-generated catch block
                         e1.printStackTrace();
                     }
                     try {
                         socket.connect();
                     } catch (IOException e1) { 
                         // TODO Auto-generated catch block
                         e1.printStackTrace();
                     }
                     if (socket != null) {
                         try {
                             socket.close();
                         } catch (Exception e) {
                             // TODO: handle exception
                         }
                     }
                 }
             });
             _clientThread.start();
         }
     }

getInputStream()——获得一个可读的流,该流在连接不成功的情况下依旧可以获得,但是对其操作的话就会报IOException的异常。需要从外部获取的数据都从该流中获取。


getOutputStrem()——获得一个可写的流,该流在连接不成功的情况下依旧可以获得,但是对其操作的话就会报IOException的异常。需要往外部传输的数据都可以写到该流中传输出去。


数据传输的大致流程如下:



首先,分别通过getInputStream()和getOutputStream()获得管理数据传输的InputStream和OutputStream。


然后,开辟一个线程专门用于数据的读或写。这是非常重要的,因为read(byte[])和write(byte[])方法都是阻塞调用。read(byte[])从输入流(InputStream)中读取数据。write(byte[])将数据写入到OutputStream流中去,这个方法一般不会阻塞,但当远程设备的中间缓冲区已满而对方没有及时地调用read(byte[])时将会一直阻塞。所以,新开辟的线程中的主循环将一直用于从InputStream中读取数据。


还要补充一点,由于蓝牙设备是系统设备,所以需要有相应的权限支持。在AndroidManifest.xml文件中添加上权限。


<uses-permission android:name="android.permission.BLUETOOTH"></uses-permission>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"></uses-permission>

蓝牙通讯数据传输



蓝牙通讯用android 的api获取看牙设备进行连接,之后进行数据传输,建立各线程;设置监听,进行数据接受监听和状态监听;


数据传输都是以 流的方式进行;


不管是图片,视频还是直接的文字,都是转化成byte,再用数据流进行传输;


图片视频是以文件的形式,再转成byte,再加上头尾校验位,长度等等,再用流进行读写,流已经是Android给我们弄好的了;

 

private File mContent;   
FileInputStream fio = new FileInputStream(mContent);
private String mExtend;
    @Override
    public byte[] creatHeader() {
        byte[] extend = mExtend.getBytes();
        byte[] length = TypeUtils.longToBytes(getLength());
        byte[] header = new byte[10 + extend.length];
        header[0] = HEADER;                                         //魔数
        header[1] = getType();                                     //类型
        System.arraycopy(length, 0, header, 2, length.length);     //长度
        System.arraycopy(extend, 0, header, 10, extend.length);     //扩展信息
        return header;
    }
    private File mContent;
    public void writeContent(OutputStream outputStream) throws IOException {
        outputStream.write(creatHeader());
        outputStream.write(0xA);
        outputStream.write(0xD);
        FileInputStream fio = new FileInputStream(mContent);
        byte[] buffer = new byte[64 * 1024];
        int length = 0;
        while ((length = fio.read(buffer)) >= 0) {
            outputStream.write(buffer, 0, length);
            outputStream.flush();
        }
    }

string,int 等形式就是直接转成byte,再加上头尾校验位,长度等等,再用流进行读写;

  outputStream.write(creatHeader());

private String mExtend;
    @Override
    public byte[] creatHeader() {
        byte[] extend = mExtend.getBytes();
        byte[] length = TypeUtils.longToBytes(getLength());
        byte[] header = new byte[10 + extend.length];
        header[0] = HEADER;                                         //魔数
        header[1] = getType();                                     //类型
        System.arraycopy(length, 0, header, 2, length.length);     //长度
        System.arraycopy(extend, 0, header, 10, extend.length);     //扩展信息
        return header;
    }
   public void writeContent(OutputStream outputStream) throws IOException {
        outputStream.write(creatHeader());
        outputStream.write(0xA);
        outputStream.write(0xD);
        outputStream.write(contentByte);
    }

adb logcat:抓包,抓日志



adb logcat | find  "com.itep.bluetoothsocketservice" C:/hehe.txt

adb logcat 仅仅是输出应用的log信息;而不是android的系统日志;


adb input 模拟系统输入



KEYCODE_CALL 进入拨号盘 5

KEYCODE_ENDCALL 挂机键 6

KEYCODE_HOME 按键Home 3

KEYCODE_MENU 菜单键 82

KEYCODE_BACK 返回键 4

KEYCODE_SEARCH 搜索键 84

KEYCODE_CAMERA 拍照键 27

KEYCODE_FOCUS 拍照对焦键 80

KEYCODE_POWER 电源键 26

KEYCODE_NOTIFICATION 通知键 83

KEYCODE_MUTE 话筒静音键 91

KEYCODE_VOLUME_MUTE 扬声器静音键 164

KEYCODE_VOLUME_UP 音量增加键 24

KEYCODE_VOLUME_DOWN 音量减小键 25


控制键


KEYCODE_ENTER 回车键 66

KEYCODE_ESCAPE ESC键 111

KEYCODE_DPAD_CENTER 导航键 确定键 23

KEYCODE_DPAD_UP 导航键 向上 19

KEYCODE_DPAD_DOWN 导航键 向下 20

KEYCODE_DPAD_LEFT 导航键 向左 21

KEYCODE_DPAD_RIGHT 导航键 向右 22

KEYCODE_MOVE_HOME 光标移动到开始键 122

KEYCODE_MOVE_END 光标移动到末尾键 123

KEYCODE_PAGE_UP 向上翻页键 92

KEYCODE_PAGE_DOWN 向下翻页键 93

KEYCODE_DEL 退格键 67

KEYCODE_FORWARD_DEL 删除键 112

KEYCODE_INSERT 插入键 124

KEYCODE_TAB Tab键 61

KEYCODE_NUM_LOCK 小键盘锁 143

KEYCODE_CAPS_LOCK 大写锁定键 115

KEYCODE_BREAK Break/Pause键 121

KEYCODE_SCROLL_LOCK 滚动锁定键 116

KEYCODE_ZOOM_IN 放大键 168

KEYCODE_ZOOM_OUT 缩小键 169

利用命令“adb shell input keyevent <键值>”可以实现自动化。例如“adb shell input keyevent 3”就可以按下Home键。]

执行返回:adb shell input keyevent 4

执行灭屏亮屏:adb shell input keyevent 26

执行解锁屏幕:adb shell input keyevent 82


3 HOME 键
4 返回键
5 打开拨号应用
6 挂断电话
24 增加音量
25 降低音量
26 电源键
27 拍照(需要在相机应用里)
64 打开浏览器
82 菜单键
85 播放/暂停
86 停止播放
87 播放下一首
88 播放上一首
122 移动光标到行首或列表顶部
123 移动光标到行末或列表底部
126 恢复播放
127 暂停播放
164 静音
176 打开系统设置
187 切换应用
207 打开联系人
208 打开日历
209 打开音乐
210 打开计算器
220 降低屏幕亮度
221 提高屏幕亮度
223 系统休眠
224 点亮屏幕
231 打开语音助手
276 如果没有 wakelock 则让系统休眠


蓝牙通讯和adb命令实现手机之间互相操作:adb命令24 是音量增加。


c1730f86b6d5402d8d1cb245f9c7175a_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.jpg


4b80f5f9a5a04c7fa53d8a36ba08ff28_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.jpg

虚拟按键通过AccessibilityService进行控制,实际就是一个service,实现button,监听系统暴露的Api:


d37adbb7cb344334adc6fa60035094e1_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.jpg

fe93fbc1de6c46f28d1a2e8be64582f3_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.jpg

eeb65b3a581541ceb8a67c06d4ef1164_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.jpg

源码就是在github上,自己拉下;


相关实践学习
自建数据库迁移到云数据库
本场景将引导您将网站的自建数据库平滑迁移至云数据库RDS。通过使用RDS,您可以获得稳定、可靠和安全的企业级数据库服务,可以更加专注于发展核心业务,无需过多担心数据库的管理和维护。
Sqoop 企业级大数据迁移方案实战
Sqoop是一个用于在Hadoop和关系数据库服务器之间传输数据的工具。它用于从关系数据库(如MySQL,Oracle)导入数据到Hadoop HDFS,并从Hadoop文件系统导出到关系数据库。 本课程主要讲解了Sqoop的设计思想及原理、部署安装及配置、详细具体的使用方法技巧与实操案例、企业级任务管理等。结合日常工作实践,培养解决实际问题的能力。本课程由黑马程序员提供。
相关文章
|
9月前
|
监控 Shell Linux
Android调试终极指南:ADB安装+多设备连接+ANR日志抓取全流程解析,覆盖环境变量配置/多设备调试/ANR日志分析全流程,附Win/Mac/Linux三平台解决方案
ADB(Android Debug Bridge)是安卓开发中的重要工具,用于连接电脑与安卓设备,实现文件传输、应用管理、日志抓取等功能。本文介绍了 ADB 的基本概念、安装配置及常用命令。包括:1) 基本命令如 `adb version` 和 `adb devices`;2) 权限操作如 `adb root` 和 `adb shell`;3) APK 操作如安装、卸载应用;4) 文件传输如 `adb push` 和 `adb pull`;5) 日志记录如 `adb logcat`;6) 系统信息获取如屏幕截图和录屏。通过这些功能,用户可高效调试和管理安卓设备。
|
监控 Linux 开发者
如何在 Linux 中优雅的使用 head 命令,用来看日志简直溜的不行
`head` 命令是 Linux 系统中一个非常实用的工具,用于快速查看文件的开头部分内容。本文介绍了 `head` 命令的基本用法、高级用法、实际应用案例及注意事项,帮助用户高效处理文件和日志,提升工作效率。
304 7
|
Ubuntu Linux Apache
在Linux中,如何使用logrotate命令管理日志文件?
在Linux中,如何使用logrotate命令管理日志文件?
|
存储 监控 安全
|
监控 Linux 测试技术
Linux系统命令与网络,磁盘和日志监控总结
Linux系统命令与网络,磁盘和日志监控总结
205 0
|
监控 Linux 测试技术
Linux系统命令与网络,磁盘和日志监控三
Linux系统命令与网络,磁盘和日志监控三
193 0
|
存储 监控 安全
深入探索Linux的journalctl命令:系统日志的利器
**journalctl 深入解析:Linux 系统日志的强大工具** journalctl 是 Linux 中用于查询和管理 systemd 日志的命令行工具,与 systemd-journald 配合收集广泛的信息,包括内核消息和服务日志。它提供实时追踪、过滤、导出等功能,如 `-f` 实时监控,`-u` 过滤特定服务日志,`-k` 显示内核消息,`--since` 和 `--until` 选择时间范围。在实际应用中,结合权限管理、日志空间控制和有效过滤,journalctl 成为系统管理员诊断和优化系统的得力助手。
|
关系型数据库 MySQL Linux
探索Linux下的`db_log_verify`命令(假设)
探索Linux下假设的`db_log_verify`命令,用于验证数据库日志文件的完整性、顺序性和一致性。功能包括检查文件存在、完整性(通过校验和)、顺序及内容一致性。提供的简单Bash脚本示例演示了文件存在和MD5校验。适用场景包括定期验证、备份验证和故障恢复。注意权限、错误处理、测试、兼容性及文档编写。
|
SQL Oracle 关系型数据库
探索 Linux 命令 `db_archive`:Oracle 数据库归档日志的工具
探索 Linux 中的 `db_archive`,实际与 Oracle 数据库归档日志管理相关。在 Oracle 中,归档日志用于恢复,当在线重做日志满时自动归档。管理员可使用 SQL*Plus 查看归档模式,通过 `RMAN` 进行备份和恢复操作。管理归档日志需谨慎,避免数据丢失。了解归档管理对 Oracle 管理员至关重要,确保故障时能快速恢复数据库。