跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava

Android Framework 异常调用栈解析

综述由AI生成Android 异常调用栈分析涵盖广播重抛、Binder 同步通信异常及异步通信模拟同步场景下的异常表现。通过源码剖析 RuntimeException 封装机制、Parcel.readException 远程异常处理逻辑以及 SynchronousResultReceiver 的双次异步通信原理,揭示了跨进程异常回传规则与常见误判原因,为系统级崩溃定位提供技术依据。文章详细列举了可回传的 9 类异常类型,并提供了针对复杂调用栈的排查实战指南,帮助开发者准确识别根本原因。

女王发布于 2025/2/7更新于 2026/6/621 浏览
Android Framework 异常调用栈解析

Android Framework 异常调用栈解析

本文分析基于 Android 13 (T) 系统。异常是程序未按预设逻辑运行的一种提示,Java 中的异常输出通常包含一句提示语和其发生时的调用栈。多数情况下,这些提示是直接且清晰的。但如果我们将异常捕获后封装一下重新抛出,或者让它发生在跨进程通信的过程中,那么此时的调用栈信息将会变得复杂,甚至会干扰我们对最终原因的判断。以下将详解几种不同形式的异常调用栈。

1. 异常捕获后重新抛出

以下是剥离了时间、pid、tid 和 tag 后的输出示例:

*** FATAL EXCEPTION IN SYSTEM PROCESS: main
java.lang.RuntimeException: Error receiving broadcast Intent { act=android.intent.action.NEW_OUTGOING_CALL flg=0x11000010 (has extras) } in com.android.server.location.injector.SystemEmergencyHelper$1@42d2813
  at android.app.LoadedApk$ReceiverDispatcher$Args.lambda$getRunnable$0$android-app-LoadedApk$ReceiverDispatcher$Args(LoadedApk.java:1800)
  at android.app.LoadedApk$ReceiverDispatcher$Args$$ExternalSyntheticLambda0.run(Unknown Source:2)
  at android.os.Handler.handleCallback(Handler.java:942)
  at android.os.Handler.dispatchMessage(Handler.java:99)
  at android.os.Looper.loopOnce(Looper.java:201)
  at android.os.Looper.loop(Looper.java:288)
  at com.android.server.SystemServer.run(SystemServer.java:966)
  at com.android.server.SystemServer.main(SystemServer.java:651)
  at java.lang.reflect.Method.invoke(Native Method)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:920)
Caused by: java.lang.IllegalStateException: telephony service is null.
  at android.telephony.TelephonyManager.isEmergencyNumber(TelephonyManager.java:14136)
  at com.android.server.location.injector.SystemEmergencyHelper$1.onReceive(SystemEmergencyHelper.java:70)
  at android.app.LoadedApk$ReceiverDispatcher$Args.lambda$getRunnable$0$android-app-LoadedApk$ReceiverDispatcher$Args(LoadedApk.java:1790)
  ... 10 more

可以发现其中有两段不同的调用栈,由 "Caused by" 字段进行分隔。结合以下代码,我们可以分析出此异常的转换过程:广播处理会进入到 receiver.onReceive 中,其中发生了 "telephony service is null" 的 IllegalStateException。此异常向上抛出,最终被如下代码的 1791 行捕获。捕获之后的异常会在 1800 行进行重新封装,原始异常 e 将会作为第二个参数参与 RuntimeException 的构造(赋值给 cause 字段)。因此,这个 RuntimeException 是导致进程退出的直接原因,而原始异常 IllegalStateException 则是根本原因。

1781  try {
1782      ClassLoader cl = mReceiver.getClass().getClassLoader();
1783      intent.setExtrasClassLoader(cl);
1784      // TODO: determine at registration time if caller is
1785      // protecting themselves with signature permission
1786      intent.prepareToEnterProcess(ActivityThread.isProtectedBroadcast(intent),
1787              mContext.getAttributionSource());
1788      setExtrasClassLoader(cl);
1789      receiver.setPendingResult(this);
1790      receiver.onReceive(mContext, intent);
1791  } catch (Exception e) {
1792      if (mRegistered && ordered) {
1793          if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
1794                  "Finishing failed broadcast to " + mReceiver);
1795          sendFinished(mgr);
1796      }
1797      if (mInstrumentation == null ||
1798              !mInstrumentation.onException(mReceiver, e)) {
1799          Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
1800          throw new RuntimeException(
1801                  "Error receiving broadcast " + intent
1802                          + " in " + mReceiver, e);
1803      }
1804  }
306     public Throwable(String message, Throwable cause) {   //第二个参数赋值给 cause 字段
307         fillInStackTrace();
308         detailMessage = message;
309         this.cause = cause;
310     }

从调用栈的打印来看,它会首先将直接导致崩溃的异常调用栈打印出来,之后会递归地将 cause 的异常调用栈打印出来(因为 cause 也可能有自己的 cause)。

另外需要注意的是,IllegalStateException 的调用栈最下方有 "… 10 more" 的字样。它表示的其实就是 RuntimeException 的调用栈(除去最后一帧)。因为异常在向上抛出的过程中被捕获,因此捕获位置往上的调用栈是不变的。我们把这 10 帧补齐,IllegalStateException 的完整调用栈便如下所示:

Caused by: java.lang.IllegalStateException: telephony service is null.
  at android.telephony.TelephonyManager.isEmergencyNumber(TelephonyManager.java:14136)
  at com.android.server.location.injector.SystemEmergencyHelper$1.onReceive(SystemEmergencyHelper.java:70)
  at android.app.LoadedApk$ReceiverDispatcher$Args.lambda$getRunnable$0$android-app-LoadedApk$ReceiverDispatcher$Args(LoadedApk.java:1790)
  at android.app.LoadedApk$ReceiverDispatcher$Args$$ExternalSyntheticLambda0.run(Unknown Source:2)
  at android.os.Handler.handleCallback(Handler.java:942)
  at android.os.Handler.dispatchMessage(Handler.java:99)
  at android.os.Looper.loopOnce(Looper.java:201)
  at android.os.Looper.loop(Looper.java:288)
  at com.android.server.SystemServer.run(SystemServer.java:966)
  at com.android.server.SystemServer.main(SystemServer.java:651)
  at java.lang.reflect.Method.invoke(Native Method)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:920)

2. Binder 同步通信时发生的异常

以下是剥离了时间、pid、tid 和 tag 后的输出:

FATAL EXCEPTION: main
PID: 3264
java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.hashCode()' on a null object reference
  at android.os.Parcel.createExceptionOrNull(Parcel.java:3017)
  at android.os.Parcel.createException(Parcel.java:2995)
  at android.os.Parcel.readException(Parcel.java:2978)
  at android.os.Parcel.readException(Parcel.java:2920)
  at android.app.IActivityManager$Stub$Proxy.attachApplication(IActivityManager.java:5148)
  at android.app.ActivityThread.attach(ActivityThread.java:7644)
  at android.app.ActivityThread.main(ActivityThread.java:7943)
  at java.lang.reflect.Method.invoke(Native Method)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:942)
Caused by: android.os.RemoteException: Remote stack trace:
  at com.android.server.am.HostingRecord.getHostingTypeIdStatsd(HostingRecord.java:234)
  at com.android.server.am.ActivityManagerService.attachApplicationLocked(ActivityManagerService.java:5102)
  at com.android.server.am.ActivityManagerService.attachApplication(ActivityManagerService.java:5115)
  at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:2339)
  at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:2655)

这种调用栈中有 Parcel.readException 字样,且 "Caused by" 后面跟的是 "Remote stack trace",它们通常是由 Binder 同步通信时对端进程中的异常所导致。对端进程发生的异常拆分为 3 个部分,序列化地发回给本进程:

  • code:表示该异常的类型
  • msg:异常的具体描述
  • remoteStackTrace:异常发生时的调用栈

这 3 部分信息在本进程中组合成了两个 Exception 对象。一个由 code 和 msg 构造,如下 2978 行所示,它是造成进程退出的直接原因;另一个由 remoteStackTrace 构造,如下 2981 行所示,它是造成进程退出的根本原因(2983 行将它赋值给 e 的 cause)。

2972  public final void readException(int code, String msg) {
2973      String remoteStackTrace = null;
2974      final int remoteStackPayloadSize = readInt();
2975      if (remoteStackPayloadSize > 0) {
2976          remoteStackTrace = readString();
2977      }
2978      Exception e = createException(code, msg);
2979      // Attach remote stack trace if availalble
2980      if (remoteStackTrace != null) {
2981          RemoteException cause = new RemoteException(
2982                  "Remote stack trace:\n" + remoteStackTrace, null, false, false);
2983          ExceptionUtils.appendCause(e, cause);
2984      }
2985      SneakyThrow.sneakyThrow(e);
2986  }

回到上面这个例子,它真实的含义是:本 App 进程希望通过 attachApplication 接口和 system_server 进程通信,但是 system_server 在处理这个请求时,发生了 NullPointerException。System_server 将这个异常发回给 App 进程,最终导致了 App 进程的退出。

其实我觉得现有的调用栈输出是有瑕疵的。它将原本属于同一个异常的 msg 和 stackTrace 拆分开来,会给开发者带来困扰。按照正确的理解,上面的调用栈显示为如下格式会更加清晰:

android.os.RemoteException: Binder transaction failed
  at android.os.Parcel.createExceptionOrNull(Parcel.java:3017)
  at android.os.Parcel.createException(Parcel.java:2995)
  at android.os.Parcel.readException(Parcel.java:2978)
  at android.os.Parcel.readException(Parcel.java:2920)
  at android.app.IActivityManager$Stub$Proxy.attachApplication(IActivityManager.java:5148)
  at android.app.ActivityThread.attach(ActivityThread.java:7644)
  at android.app.ActivityThread.main(ActivityThread.java:7943)
  at java.lang.reflect.Method.invoke(Native Method)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:942)
Caused by: java.lang.NullPointerException in remote process: Attempt to invoke virtual method 'int java.lang.String.hashCode()' on a null object reference
  at com.android.server.am.HostingRecord.getHostingTypeIdStatsd(HostingRecord.java:234)
  at com.android.server.am.ActivityManagerService.attachApplicationLocked(ActivityManagerService.java:5102)
  at com.android.server.am.ActivityManagerService.attachApplication(ActivityManagerService.java:5115)
  at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:2339)
  at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:2655)

不过需要注意,并非对端进程处理 binder 通信时发生的任何异常都可以传回,只有如下这 9 类异常可以:

ExceptionCode
Parcelable Exceptions in BootClassLoaderEX_PARCELABLE
SecurityExceptionEX_SECURITY
BadParcelableExceptionEX_BAD_PARCELABLE
IllegalArgumentExceptionEX_ILLEGAL_ARGUMENT
NullPointerExceptionEX_NULL_POINTER
IllegalStateExceptionEX_ILLEGAL_STATE
NetworkOnMainThreadExceptionEX_NETWORK_MAIN_THREAD
UnsupportedOperationExceptionEX_UNSUPPORTED_OPERATION
ServiceSpecificExceptionEX_SERVICE_SPECIFIC

当对端进程将异常传回后,对端进程恢复正常。仔细思考这样设计也是很合理的。作为 Server 进程,它在什么时候执行,该执行些什么都不由自己掌控,而是由 Client 进程发起。因此抛出异常本质上与 Client 进程相关,让一个 Client 进程的行为导致 Server 进程退出显然是不合理的。此外,Server 进程可能关联着多个 Client,不能由于一个 Client 的错误行为而影响本可以正常获取服务的其他 Client。

除了上述 9 种异常以外,其余的异常将由对端进程的 JavaBBinder::onTransact 来处理,最终会通过 LOGE 将该异常输出。值得注意的是,异常中的 Exception 输出完后进程恢复,而 Error 则会导致进程退出。

410   jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
411       code, reinterpret_cast<jlong>(&data), reinterpret_cast<jlong>(reply), flags);
412
413   if (env->ExceptionCheck()) {
414       ScopedLocalRef<jthrowable> excep(env, env->ExceptionOccurred());
415       binder_report_exception(env, excep.get(),
416                               "*** Uncaught remote exception!  "
417                               "(Exceptions are not yet supported across processes.)");
418       res = JNI_FALSE;
419   }
*** Uncaught remote exception!  (Exceptions are not yet supported across processes.) 
java.lang.OutOfMemoryError: Failed to allocate a 280361534 byte allocation with 25165820 free bytes and 258MB until OOM, target footprint 29165820, growth limit 536870912
  at java.util.Arrays.copyOf(Arrays.java:3136)
  at java.util.Arrays.copyOf(Arrays.java:3106)
  at java.util.ArrayList.grow(ArrayList.java:275)
  at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:249)
  at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:241)
  at java.util.ArrayList.add(ArrayList.java:467)
  at android.os.Parcel.readStringList(Parcel.java:3093)
  at android.content.IntentFilter.<init>(IntentFilter.java:2377)
  at android.content.IntentFilter$1.createFromParcel(IntentFilter.java:2269)
  at android.content.IntentFilter$1.createFromParcel(IntentFilter.java:2267)
  at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:2241)
  at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:2669)
  at android.os.Binder.execTransactInternal(Binder.java:1221)
  at android.os.Binder.execTransact(Binder.java:1163)

3. Binder 异步通信时发生的异常

对于普通的异步通信,Client 进程发送完后就不会再管了,所以 Server 端在收到通信后处理时发生的异常不会回传。最终所有的异常都会交由 JavaBBinder::onTransact 进行处理,处理的原则和上面一样:Exception 输出完后进程恢复,Error 则会导致进程退出。

不过有一类 Binder 异步通信的异常非常隐晦,如果不了解内部原理基本无法理解。示例如下:

FATAL EXCEPTION: Thread-3
Process: com.android.systemui, PID: 31695
java.lang.RuntimeException: Error receiving broadcast Intent { act=android.bluetooth.device.action.BOND_STATE_CHANGED flg=0x10 (has extras) } in com.android.bluetooth.BluetoothManager$BluetoothBroadcastReceiver@c5b7352
  at android.app.LoadedApk$ReceiverDispatcher$Args.lambda$getRunnable$0$android-app-LoadedApk$ReceiverDispatcher$Args(LoadedApk.java:1920)
  at android.app.LoadedApk$ReceiverDispatcher$Args$$ExternalSyntheticLambda0.run(Unknown Source:2)
  at android.os.Handler.handleCallback(Handler.java:942)
  at android.os.Handler.dispatchMessage(Handler.java:99)
  at android.os.Looper.loopOnce(Looper.java:240)
  at android.os.Looper.loop(Looper.java:351)
  at android.os.HandlerThread.run(HandlerThread.java:67)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.os.Looper android.os.HandlerThread.getLooper()' on a null object reference
  at com.android.bluetooth.a2dp.A2dpService.getOrCreateStateMachine(A2dpService.java:2101)
  at com.android.bluetooth.a2dp.A2dpService.connect(A2dpService.java:515)
  at com.android.bluetooth.btservice.AdapterService.connectEnabledProfiles(AdapterService.java:1548)
  at com.android.bluetooth.btservice.AdapterService.connectAllEnabledProfiles(AdapterService.java:4962)
  at com.android.bluetooth.btservice.AdapterService$AdapterServiceBinder.connectAllEnabledProfiles(AdapterService.java:3076)
  at com.android.bluetooth.btservice.AdapterService$AdapterServiceBinder.connectAllEnabledProfiles(AdapterService.java:3057)
  at android.bluetooth.IBluetooth$Stub.onTransact(IBluetooth.java:1750)
  at android.os.Binder.execTransactInternal(Binder.java:1331)
  at android.os.Binder.execTransact(Binder.java:1268)

其实同步通信除了通过 Binder 同步模式实现,还可以通过两个 Binder 异步通信实现。而这也正是上面调用栈形成的原因。Systemui 进程接收到广播后,会执行相应广播的 onReceive 方法。此次广播处理会尝试连接蓝牙。而异常发生的关键点,就在如下代码中:

final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get();
service.connectAllEnabledProfiles(this, mAttributionSource, recv);
return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);

connectAllEnabledProfiles 是异步的 Binder 请求,它的通信对端是 com.android.bluetooth 进程。Systemui 发送完 connectAllEnabledProfiles 的异步请求后会继续往下执行,但是 awaitResultNoInterrupt 会将线程挂起,等待对端进程的回复。对端进程的回复同样是一个异步通信,这样程序便通过 SynchronousResultReceiver 和两次异步通信,模仿了同步通信的过程。

com.android.bluetooth 进程接收到异步请求后,会执行如下代码。如果程序没有异常,最终 receiver.send 会将返回值发回给 systemui 进程。但如果程序发生了 RuntimeException,receiver.propagateException 会将异常发回给 systemui。

public void connectAllEnabledProfiles(BluetoothDevice device,
        AttributionSource source, SynchronousResultReceiver receiver) {
    try {
        receiver.send(connectAllEnabledProfiles(device, source));
    } catch (RuntimeException e) {
        receiver.propagateException(e);
    }
}

发回给 systemui 的异常最终会在哪里抛出呢?答案是上面代码的 getValue 方法中:

public T getValue(T defaultValue) {
    if (mException != null) {
        throw mException;
    }
    if (mObject == null) {
        return defaultValue;
    }
    return mObject;
}

至此我们可以知道,上述调用栈中的 caused by 部分(截取如下)其实是 Binder 异步通信后对端进程(com.android.bluetooth)发生的异常。而整个调用栈中没有任何的 remote 字样,所以非常容易让人误以为是 systemui 进程中发生的异常。大家以后碰到这种调用栈时,一定要小心。

Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.os.Looper android.os.HandlerThread.getLooper()' on a null object reference
  at com.android.bluetooth.a2dp.A2dpService.getOrCreateStateMachine(A2dpService.java:2101)
  at com.android.bluetooth.a2dp.A2dpService.connect(A2dpService.java:515)
  at com.android.bluetooth.btservice.AdapterService.connectEnabledProfiles(AdapterService.java:1548)
  at com.android.bluetooth.btservice.AdapterService.connectAllEnabledProfiles(AdapterService.java:4962)
  at com.android.bluetooth.btservice.AdapterService$AdapterServiceBinder.connectAllEnabledProfiles(AdapterService.java:3076)
  at com.android.bluetooth.btservice.AdapterService$AdapterServiceBinder.connectAllEnabledProfiles(AdapterService.java:3057)
  at android.bluetooth.IBluetooth$Stub.onTransact(IBluetooth.java:1750)
  at android.os.Binder.execTransactInternal(Binder.java:1331)
  at android.os.Binder.execTransact(Binder.java:1268)

4. 异常排查实战指南

在实际开发中,面对复杂的异常调用栈,建议遵循以下步骤进行排查:

  1. 识别异常类型:首先查看异常堆栈的最顶层,确定是直接崩溃还是远程异常。如果是 RemoteException 且包含 Remote stack trace,需立即定位到对端进程。
  2. 追踪 Cause 链:利用 Caused by 字段向下追溯,找到根本原因。注意忽略中间层的包装异常(如 RuntimeException 包裹),关注最底层的业务逻辑错误。
  3. 检查跨进程边界:遇到 Binder 调用失败时,区分同步与异步场景。特别是使用 SynchronousResultReceiver 等机制模拟同步时,务必确认对端进程是否已正确捕获并回传异常。
  4. 验证权限与状态:许多异常(如 IllegalStateException)源于服务未初始化或权限不足。检查 Service 生命周期及 Manifest 配置。
  5. 日志关联分析:结合 Logcat 中的 Tag 和时间戳,对比 SystemServer 与 Client 进程的日志,确认异常发生的时间顺序。

5. 总结

Android 系统的异常处理机制涉及本地 JVM 层与 Native 层、进程间通信等多个层面。理解异常调用栈的生成规则,特别是跨进程异常的回传限制与封装方式,对于系统级问题的定位至关重要。开发者应熟悉常见异常类型的传播路径,避免被表面现象误导,从而高效解决系统稳定性问题。

目录

  1. Android Framework 异常调用栈解析
  2. 1. 异常捕获后重新抛出
  3. 2. Binder 同步通信时发生的异常
  4. 3. Binder 异步通信时发生的异常
  5. 4. 异常排查实战指南
  6. 5. 总结
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • AI 赋能 FPGA 开发:Vivado 配置与智能编程实战
  • Spring AI 实战:从零开发 IDEA 插件版 AI 代码助手
  • AIGC 在日常生活中的应用与挑战
  • Python 列表核心用法与操作指南
  • 本地代码上传 Gitee 实战:Git 配置与推送详解
  • C++ 入门:引用、内联函数与 C++11 新特性详解
  • 网络安全行业主流证书选择指南
  • OpenArm 开源协作机器人:从技术痛点到落地实践
  • Python 核心语法详解:测试脚本开发基础
  • GESP C++ 七级真题解析:矩阵移动
  • 贪心算法核心解析:原理、策略与 C++ 实战
  • 本地离线部署大模型 Ollama+AnythingLLM
  • 使用 Claude Code 与 GLM4.7 修复前端 Bug 的踩坑实录与反思
  • Neo4j 插件 APOC 安装及配置指南
  • 无人机目标检测中的自适应图像变焦与边界框变换
  • LLM 大模型学习指南:从原理到工程化应用实战
  • 大厂 AI 人才争夺战:供需失衡下的薪资与岗位要求分析
  • WhisperLiveKit 翻译引擎深度评测:NLLB 600M vs 1.3B 速度与质量对比
  • Python 数据分析相比 Excel 的优势与入门指南
  • VS Code Copilot 完整使用教程

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online