You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android:如何绕过系统预览直接打印已连接手机的打印机的PDF文件?

这个问题我之前也帮开发者处理过,Android自带的打印框架默认会触发预览界面,要跳过它直接打印确实需要一些额外配置,还要注意系统权限和设备兼容性问题。下面是具体的实现方案和要点:

核心思路

Android的PrintManager本身没有提供直接跳过预览的开关,但我们可以通过指定目标打印机+配置完整的打印属性,尽可能让系统直接提交打印任务,减少用户交互。另外要注意,系统出于安全限制,大部分非系统应用无法做到完全无交互的静默打印,部分设备可能仍会弹出确认对话框,这是无法绕过的系统机制。

具体代码实现

1. 构建PDF文件的打印适配器

首先需要自定义PrintDocumentAdapter来读取本地PDF文件的内容,供打印框架使用:

private fun createPdfPrintAdapter(context: Context, pdfFilePath: String): PrintDocumentAdapter {
    val pdfFile = File(pdfFilePath)
    
    return object : PrintDocumentAdapter() {
        override fun onLayout(
            oldAttributes: PrintAttributes?,
            newAttributes: PrintAttributes,
            cancellationSignal: CancellationSignal?,
            callback: LayoutResultCallback?,
            extras: Bundle?
        ) {
            // 描述打印文档的基本信息
            val docInfo = PrintDocumentInfo.Builder("my_document.pdf")
                .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
                .setPageCount(PrintDocumentInfo.PAGE_COUNT_UNKNOWN) // 若知道PDF页数可填具体数值
                .build()
            callback?.onLayoutFinished(docInfo, true)
        }

        override fun onWrite(
            pages: Array<out PageRange>?,
            destination: ParcelFileDescriptor?,
            cancellationSignal: CancellationSignal?,
            callback: WriteResultCallback?
        ) {
            try {
                // 将PDF文件内容写入打印目标流
                val inputStream = FileInputStream(pdfFile)
                val outputStream = FileOutputStream(destination?.fileDescriptor)
                val buffer = ByteArray(4096)
                var bytesRead: Int
                
                while (inputStream.read(buffer).also { bytesRead = it } != -1) {
                    outputStream.write(buffer, 0, bytesRead)
                }
                
                callback?.onWriteFinished(arrayOf(PageRange.ALL_PAGES))
            } catch (e: Exception) {
                callback?.onWriteFailed("打印失败:${e.message}")
            } finally {
                // 实际开发中记得关闭所有打开的流
                inputStream.close()
                outputStream.close()
            }
        }
    }
}

2. 获取打印机并提交无预览打印任务

接下来获取可用打印机,配置打印参数,提交打印任务:

fun printPdfDirectly(context: Context, pdfPath: String, printJobName: String) {
    val printManager = context.getSystemService(Context.PRINT_SERVICE) as PrintManager

    // 获取当前可用的打印机列表(API 21+可用)
    val availablePrinters = printManager.getPrintServices(PrintManager.FLAG_PRINT_SERVICES_AVAILABLE)
    if (availablePrinters.isEmpty()) {
        Toast.makeText(context, "未找到可用打印机", Toast.LENGTH_SHORT).show()
        return
    }

    // 选择目标打印机(这里选第一个可用的,也可以保存用户选择的默认打印机)
    val targetPrinter = availablePrinters.first()

    // 配置打印属性:纸张大小、分辨率、颜色模式等
    val printAttributes = PrintAttributes.Builder()
        .setMediaSize(PrintAttributes.MediaSize.ISO_A4)
        .setResolution(PrintAttributes.Resolution("default_res", "默认分辨率", 300, 300))
        .setColorMode(PrintAttributes.COLOR_MODE_COLOR)
        .build()

    // 创建打印适配器
    val pdfPrintAdapter = createPdfPrintAdapter(context, pdfPath)

    // 提交打印任务,传入配置好的PrintAttributes
    printManager.print(printJobName, pdfPrintAdapter, printAttributes)

    // 可选:监听打印任务状态
    val printJob = printManager.printJobs.firstOrNull { it.label == printJobName }
    printJob?.addStateChangeListener { job ->
        when (job.state) {
            PrintJob.STATE_COMPLETED -> Toast.makeText(context, "打印完成", Toast.LENGTH_SHORT).show()
            PrintJob.STATE_FAILED -> Toast.makeText(context, "打印失败", Toast.LENGTH_SHORT).show()
            PrintJob.STATE_CANCELED -> Toast.makeText(context, "打印已取消", Toast.LENGTH_SHORT).show()
        }
    }
}
注意事项和兼容性说明
  • 权限要求:在AndroidManifest.xml中添加必要权限:
    <!-- 网络打印机需要网络权限 -->
    <uses-permission android:name="android.permission.INTERNET" />
    <!-- 部分蓝牙打印机可能需要位置权限(API 23+) -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30" />
    
  • 系统版本限制getPrintServices方法从API 21(Android 5.0)开始支持,若需兼容更低版本,需要做版本判断并降级处理。
  • 静默打印限制:Android系统出于安全考虑,非系统应用无法实现完全无交互的静默打印,部分设备可能仍会弹出打印确认对话框,这是系统的强制机制,无法绕过。
  • 替代方案:如果必须实现完全无交互打印,可以考虑使用打印机厂商提供的专属SDK(如HP、Brother等的打印SDK),这类SDK通常支持直接提交打印任务,无需经过系统预览流程。

内容的提问来源于stack exchange,提问作者PhongBM

火山引擎 最新活动