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
相关产品推荐
相关产品推荐

