You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Java开发:如何绕过打印机选择直接打印?

问题描述

我正在开发一款基于Android Java的POS应用,需要实现点击按钮直接打印文档的功能。目前我会把Base64格式的文档转成PDF后打印,但每次都得通过Android界面选择打印机。想请教:

  • 已知目标打印机的IP地址和端口,能不能实现直接打印,跳过选择打印机的步骤?
  • 如果可以的话,能不能同样实现无需选择直接打印到蓝牙或USB打印机?

当前代码

主类调用代码

public void printPdfNormalPrinter (String base64 ){
    PDFPrinter.printPDFFromBase64(this.cordova.getContext(), base64);
}

PDFPrinter.java 完整代码

import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.print.PageRange;
import android.print.PrintAttributes;
import android.print.PrintDocumentAdapter;
import android.print.PrintDocumentInfo;
import android.print.PrintManager;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Base64;

import android.util.Base64InputStream;
public class PDFPrinter {

    public static void printPDFFromBase64(Context context, String base64PDF) {
        byte[] decodedBytes = android.util.Base64.decode(base64PDF, 0);

        File pdfFile = createTempPDFFile(context, decodedBytes);

        if (pdfFile != null) {
            printPDF(context, pdfFile);
        }
    }

    private static File createTempPDFFile(Context context, byte[] pdfBytes) {
        File pdfFile = null;
        FileOutputStream fos = null;
        try {
            File storageDir = context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS);
            pdfFile = File.createTempFile("temp", ".pdf", storageDir);

            fos = new FileOutputStream(pdfFile);
            fos.write(pdfBytes);
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return pdfFile;
    }

    private static void printPDF(Context context, File pdfFile) {
        PrintManager printManager = (PrintManager) context.getSystemService(Context.PRINT_SERVICE);

        String jobName = context.getString(R.string.app_name) + " Document";

        PrintAttributes attributes = new PrintAttributes.Builder()
                .setMediaSize(PrintAttributes.MediaSize.ISO_A4)
                .setMinMargins(PrintAttributes.Margins.NO_MARGINS)
                .build();

        printManager.print(jobName, new PDFPrintDocumentAdapter(context, pdfFile.getAbsolutePath()), attributes);
    }

    private static class PDFPrintDocumentAdapter extends PrintDocumentAdapter {
        private Context context;
        private String filePath;

        public PDFPrintDocumentAdapter(Context context, String filePath) {
            this.context = context;
            this.filePath = filePath;
        }

        @Override
        public void onWrite(PageRange[] pages, ParcelFileDescriptor destination, CancellationSignal cancellationSignal, WriteResultCallback callback) {
            try {
                FileInputStream fis = new FileInputStream(filePath);
                FileOutputStream fos = new FileOutputStream(destination.getFileDescriptor());

                byte[] buffer = new byte[1024];
                int length;
                while ((length = fis.read(buffer)) > 0) {
                    fos.write(buffer, 0, length);
                }

                fis.close();
                fos.close();

                callback.onWriteFinished(new PageRange[]{PageRange.ALL_PAGES});
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes, CancellationSignal cancellationSignal, LayoutResultCallback callback, Bundle extras) {
            if (cancellationSignal.isCanceled()) {
                callback.onLayoutCancelled();
                return;
            }

            PrintDocumentInfo pdi = new PrintDocumentInfo.Builder("pdf_document.pdf")
                    .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
                    .build();

            callback.onLayoutFinished(pdi, true);
        }
    }
}
解决方案

1. 网络打印机(IP+端口)直接打印

Android系统自带PrintManager不支持直接指定IP打印机,需通过Socket直接发送打印指令实现。多数网络打印机支持PDF直接打印(如HP、EPSON部分型号),部分型号需将PDF转成打印机支持的指令集(如ESC/POS)。

实现代码

在PDFPrinter类中添加静态方法:

public static void printPDFToNetworkPrinter(String printerIp, int port, File pdfFile) {
    Socket socket = null;
    OutputStream outputStream = null;
    FileInputStream fis = null;
    try {
        socket = new Socket(printerIp, port);
        outputStream = socket.getOutputStream();
        fis = new FileInputStream(pdfFile);

        byte[] buffer = new byte[4096];
        int bytesRead;
        while ((bytesRead = fis.read(buffer)) != -1) {
            outputStream.write(buffer, 0, bytesRead);
            outputStream.flush();
        }
    } catch (IOException e) {
        e.printStackTrace();
        // 可在此添加打印失败提示逻辑
    } finally {
        try {
            if (fis != null) fis.close();
            if (outputStream != null) outputStream.close();
            if (socket != null) socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

调用方式:

public void printPdfToNetworkPrinter(String base64, String printerIp, int port) {
    byte[] decodedBytes = android.util.Base64.decode(base64, 0);
    File pdfFile = PDFPrinter.createTempPDFFile(this.cordova.getContext(), decodedBytes);
    if (pdfFile != null) {
        PDFPrinter.printPDFToNetworkPrinter(printerIp, port, pdfFile);
    }
}

2. 蓝牙打印机直接打印

蓝牙打印需先配对设备,通过BluetoothSocket连接后发送打印数据。ESC/POS蓝牙打印机通常需将PDF转成ESC/POS指令(可使用第三方库如escpos-coffee),高端型号支持直接打印PDF。

实现代码

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import java.util.UUID;

public static void printPDFToBluetoothPrinter(String deviceAddress, File pdfFile) {
    BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) {
        // 蓝牙未开启,提示用户开启
        return;
    }

    BluetoothDevice device = bluetoothAdapter.getRemoteDevice(deviceAddress);
    BluetoothSocket socket = null;
    OutputStream outputStream = null;
    FileInputStream fis = null;
    final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");

    try {
        socket = device.createRfcommSocketToServiceRecord(SPP_UUID);
        socket.connect();
        outputStream = socket.getOutputStream();
        fis = new FileInputStream(pdfFile);

        byte[] buffer = new byte[4096];
        int bytesRead;
        while ((bytesRead = fis.read(buffer)) != -1) {
            outputStream.write(buffer, 0, bytesRead);
            outputStream.flush();
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if (fis != null) fis.close();
            if (outputStream != null) outputStream.close();
            if (socket != null) socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

3. USB打印机直接打印

USB打印需使用AndroidUsbManagerAPI,获取设备权限后通过UsbDeviceConnection发送数据。同样需根据打印机型号决定是直接发送PDF还是转成ESC/POS指令。

实现代码

import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbManager;
import android.app.PendingIntent;
import android.content.Intent;
import java.util.HashMap;

public static void printPDFToUsbPrinter(Context context, File pdfFile) {
    UsbManager usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
    HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();

    // 替换为你的打印机Vendor ID和Product ID
    UsbDevice targetPrinter = null;
    for (UsbDevice device : deviceList.values()) {
        if (device.getVendorId() == 0xXXXX && device.getProductId() == 0xXXXX) {
            targetPrinter = device;
            break;
        }
    }

    if (targetPrinter == null) {
        // 未找到目标打印机
        return;
    }

    // 请求USB权限
    if (!usbManager.hasPermission(targetPrinter)) {
        PendingIntent permissionIntent = PendingIntent.getBroadcast(context, 0, new Intent("com.example.USB_PERMISSION"), 0);
        usbManager.requestPermission(targetPrinter, permissionIntent);
        return;
    }

    UsbDeviceConnection connection = usbManager.openDevice(targetPrinter);
    if (connection == null) {
        return;
    }

    // 获取批量输出端点
    UsbEndpoint outEndpoint = null;
    for (int i = 0; i < targetPrinter.getInterfaceCount(); i++) {
        UsbInterface usbInterface = targetPrinter.getInterface(i);
        for (int j = 0; j < usbInterface.getEndpointCount(); j++) {
            UsbEndpoint endpoint = usbInterface.getEndpoint(j);
            if (endpoint.getDirection() == UsbEndpoint.DIRECTION_OUT && endpoint.getType() == UsbEndpoint.TYPE_BULK) {
                outEndpoint = endpoint;
                break;
            }
        }
        if (outEndpoint != null) {
            connection.claimInterface(usbInterface, true);
            break;
        }
    }

    if (outEndpoint == null) {
        connection.close();
        return;
    }

    FileInputStream fis = null;
    try {
        fis = new FileInputStream(pdfFile);
        byte[] buffer = new byte[4096];
        int bytesRead;
        while ((bytesRead = fis.read(buffer)) != -1) {
            connection.bulkTransfer(outEndpoint, buffer, bytesRead, 5000);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if (fis != null) fis.close();
            connection.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

权限配置(AndroidManifest.xml)

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.USB_PERMISSION" />
<uses-feature android:name="android.hardware.usb.host" />

注意事项

  • 网络打印机需确保设备与打印机在同一局域网,端口正确(默认9100)
  • 蓝牙打印机需提前配对,且设备蓝牙处于开启状态
  • 部分打印机不支持直接打印PDF,需将PDF转换为打印机支持的指令集,可借助第三方库完成转换

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 00:47:02