一 简介

从Android4.4开始,系统加入了打印相关的API,可以通过系统打印服务实现打印。对于需要使用打印功能的APP可参考官方教程接入打印服务。

这不是本文的内容,本文介绍打印机厂商如何使自己的打印机接入android,即编写自己的打印插件接入android打印服务。且仅介绍接入部分,与打印机如何连接不在本文范围之内。

系统打印服务框架代码位于android.printservice包中。系统并没有实现具体打印功能,需要打印机厂商制作插件接入系统打印服务之后,自行实现。本文基于Android API Reference和以下两个github上的开源项目研究而来。两个参考项目如下:

通过实验,初步实现了系统打印服务的接入(添加打印机)和模拟打印(将要打印的文件输出)。

二 主要类介绍PrintDocument 表示待打印文件,里面存放有文件的大小等信息和文件内容。

PrinterDiscoverySession 用于发现打印机,整个发现打印机和打印机状态更新的过程在该类里进行。

PrintJob 代表一个打印任务。

PrintService 接入系统打印的关键Service。

PrinterDiscoverySession 由 PrintService 创建,通过 onCreatePrinterDiscoverySession() 函数返回给系统。

PrintJob 由需要打印的APP创建,发送给 PrintService 。

PrintDocument 存放在 PrintJob 里面,被一同发过来。

三 打印服务插件的工作流程

1 打印机发现过程

当用户在设置里开启你的打印服务插件和进入系统打印服务界面时,系统会调用 PrinterDiscoverySession 里的 onStartPrinterDiscovery(List priorityList) 函数,通知你的插件查找打印机。具体查找方式需要自己实现,可能是查找USB接口,可能是搜索网络。系统只管结果,你通过调用其父类的 addPrinters() 方法将打印机添加进去。打印机是放在List数组里传入。

当用户离开上述打印插件的界面时,系统会调用 onStopPrinterDiscovery() 函数,表示插件可以停止寻找打印机了。

另外,在自定义的 addPrintersActivity 中,系统不会自动触发打印机寻找过程,需要自行处理。

2 打印机选择过程

当用户通过一些有打印功能的APP调用系统打印服务时,如果选择了你的插件的打印机,那么系统会调用 PrinterDiscoverySession 里的 onStartPrinterStateTracking(PrinterId printerId) 方法。这里系统主要希望得到打印机的 PrinterCapabilitiesInfo 和状态,里面包括打印机支持的纸张大小,以及色彩等详细功能参数。

比如:如果没有addMediaSize(PrintAttributes.MediaSize.ISO_A4, false),那么用户就不能选择A4大小进行打印。后面的false表示是否设为默认值。

打印机有STATUS_BUSY、STATUS_IDLE、STATUS_UNAVAILABLE三种状态,只有打印机处于STATUS_IDLE时,系统才允许使用该打印机。

打印机参数直接体现在系统打印服务界面,只可以选择支持的参数,比如选择纸张的大小为A4。

同样,当用户离开该界面或者选择其他打印机时,系统会调用 onStopPrinterStateTracking(PrinterId printerId) 函数,来告诉插件不用再提供打印机的信息了。

3 打印过程

当用户在刚刚的系统打印服务界面点击右上角的打印按钮时,系统会调用打印机所属的 PrintService 里的 onPrintJobQueued(PrintJob printJob) 方法,插件需要处理该 PrintJob 。首先需要通过 PrintJob.isQueued() 判断,该PrintJob是否准备好打印,返回true代表可以打印。然后可以通过 PrintJob.getDocument() 获得要打印的文档,这里面的数据可以通过 PrintDocument.getData() 读取。开始打印的时候,调用PrintJob.start()标记开始状态。当打印成功时,调用 PrintJob.complete() 标记打印成功。或者打印失败时,调用 PrintJob.fail( String) 标记失败。

注意:一定要对PrintJob进行状态标记,包括开始或者成功失败。如果什么都不标记,系统会一直在任务栏提示该任务打印中,并且该打印机不可打印其他任务,处于准备中。如果任务结束不标记成功或者失败,一段时间之后,系统会自动将该任务标记为失败,并且打印机状态自动变为不可用。

四 系统打印服务输出的数据

通过编写DEMO测试,发现android系统打印服务输出的数据是pdf 1.4的格式,无论文件内容是照片还是文档,都会统一转换为pdf 1.4。

五 打印服务插件初步编写

1 打印服务插件的声明

一个打印服务和其他任何服务一样,需要在AndroidManifest.xml里声明。但是它还必须处理action为android.printservice.PrintService的Intent。这个intent声明失败会导致系统忽略该打印服务。另外,一个打印服务必须请求android.permission.BIND_PRINT_SERVICE权限,来保证只有系统能绑定(bind)它。声明这个失败会导致系统忽略这个打印服务。

一个打印服务可通过自定义设置页面(setting activity)进行配置,该activity提供自定义设置功能。还有一个添加打印机的activity可以手动添加打印机,供应商名称等等。系统负责在适当的时候启动设置和添加打印机的activities。

一个打印服务在声明的时候,要在mainfest里提供一条 android:name=”android.printservice” 的 meta-data,这是指定上述activities的方式。

AndroidManifest.xml文件如下:```<?xml  version="1.0" encoding="utf-8"?>

package="com.example.testprintservice">

android:allowBackup="true"

android:icon="@mipmap/ic_launcher"

android:label="@string/app_name"

android:supportsRtl="true"

android:theme="@style/AppTheme">

android:name=".MyPrintService"

android:permission="android.permission.BIND_PRINT_SERVICE">

android:name="android.printservice"

android:resource="@xml/printservice" />

android:name=".SettingsActivity"

android:exported="true"

android:label="@string/settings_activity_label" />

android:name=".AddPrintersActivity"

android:exported="true"

android:label="@string/add_pritners_activity_label">

android:name=".AdvancedPrintOptionsActivity"

android:label="@string/advanced_print_options_activity_label"

android:exported="true">

```

android:resource="@xml/printservice"对应的文件为printservice.xml。

这里面指定的settingsActivity在打印插件开启界面右上角的菜单里,用于配置插件。

addPrintersActivity除了在打印插件开启界面的菜单里,在打印文件时添加打印机里也会被触发,这个activity用来自定义添加打印机。

advancedPrintOptionsActivity则是在打印文件的界面上点击更多箭头里出现的MORE OPTIONS选项触发,这个activity用配置打印机的跟多信息。当然这是可选的操作,也可以没有这个activity。

printservice.xml文件内容如下所示:<?xml  version="1.0" encoding="utf-8"?>

android:vendor="example"

android:settingsActivity="com.example.testprintservice.SettingsActivity"

android:addPrintersActivity="com.example.testprintservice.AddPrintersActivity"

android:advancedPrintOptionsActivity="com.example.testprintservice.AdvancedPrintOptionsActivity"

>

2 PrintService实现类编写

在这里的 onPrintJobQueued 方法中,直接将需要打印的数据输出为文件。存放在APP根目录里的files文件夹。package com.example.testprintservice;

import android.app.Service;

import android.content.Intent;

import android.os.IBinder;

import android.os.ParcelFileDescriptor;

import android.print.PrintJobInfo;

import android.printservice.PrintDocument;

import android.printservice.PrintJob;

import android.printservice.PrintService;

import android.printservice.PrinterDiscoverySession;

import android.util.Log;

import java.io.File;

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

import java.io.IOException;

import java.nio.channels.FileChannel;

public class MyPrintService extends PrintService {

private static final String TAG = "MyPrintService";

@Override

protected PrinterDiscoverySession onCreatePrinterDiscoverySession() {

Log.d(TAG, "onCreatePrinterDiscoverySession()");

return new MyPrintDiscoverySession(this);

}

@Override

protected void onRequestCancelPrintJob(PrintJob printJob) {

Log.d(TAG, "onRequestCancelPrintJob()");

printJob.cancel();

}

@Override

protected void onPrintJobQueued(PrintJob printJob) {

Log.d(TAG, "onPrintJobQueued()");

PrintJobInfo printjobinfo = printJob.getInfo();

PrintDocument printdocument = printJob.getDocument();

if (printJob.isQueued()) {

return;

}

printJob.start();

String filename = "docu.pdf";

File outfile = new File(this.getFilesDir(), filename);

outfile.delete();

FileInputStream file = new ParcelFileDescriptor.AutoCloseInputStream(printdocument.getData());

//创建一个长度为1024的内存空间

byte[] bbuf = new byte[1024];

//用于保存实际读取的字节数

int hasRead = 0;

//使用循环来重复读取数据

try {

FileOutputStream outStream = new FileOutputStream(outfile);

while ((hasRead = file.read(bbuf)) > 0) {

//将字节数组转换为字符串输出

//System.out.print(new String(bbuf, 0, hasRead));

outStream.write(bbuf);

}

outStream.close();

} catch (IOException e) {

e.printStackTrace();

}finally {

//关闭文件输出流,放在finally块里更安全

try {

file.close();

} catch (IOException e) {

e.printStackTrace();

}

}

printJob.complete();

}

}

3 PrinterDiscoverySession实现类编写package com.example.testprintservice;

import android.print.PrintAttributes;

import android.print.PrinterCapabilitiesInfo;

import android.print.PrinterId;

import android.print.PrinterInfo;

import android.printservice.PrinterDiscoverySession;

import android.util.Log;

import java.util.ArrayList;

import java.util.List;

/**

* Created by bboxh on 2016/3/14.

*/

public class MyPrintDiscoverySession extends PrinterDiscoverySession {

private static final String TAG = "MyPrintDiscoverySession";

private final MyPrintService myPrintService;

public MyPrintDiscoverySession(MyPrintService myPrintService) {

Log.d(TAG, "MyPrintDiscoverySession()");

this.myPrintService = myPrintService;

}

@Override

public void onStartPrinterDiscovery(List priorityList) {

Log.d(TAG, "onStartPrinterDiscovery()");

List printers = this.getPrinters();

String name = "printer1";

PrinterInfo myprinter = new PrinterInfo

.Builder(myPrintService.generatePrinterId(name), name, PrinterInfo.STATUS_IDLE)

.build();

printers.add(myprinter);

addPrinters(printers);

}

@Override

public void onStopPrinterDiscovery() {

Log.d(TAG, "onStopPrinterDiscovery()");

}

/**

* 确定这些打印机存在

* @param printerIds

*/

@Override

public void onValidatePrinters(List printerIds) {

Log.d(TAG, "onValidatePrinters()");

}

/**

* 选择打印机时调用该方法更新打印机的状态,能力

* @param printerId

*/

@Override

public void onStartPrinterStateTracking(PrinterId printerId) {

Log.d(TAG, "onStartPrinterStateTracking()");

PrinterInfo printer = findPrinterInfo(printerId);

if (printer != null) {

PrinterCapabilitiesInfo capabilities =

new PrinterCapabilitiesInfo.Builder(printerId)

.setMinMargins(new PrintAttributes.Margins(200, 200, 200, 200))

.addMediaSize(PrintAttributes.MediaSize.ISO_A4, true)

//.addMediaSize(PrintAttributes.MediaSize.ISO_A5, false)

.addResolution(new PrintAttributes.Resolution("R1", "200x200", 200, 200), false)

.addResolution(new PrintAttributes.Resolution("R2", "300x300", 300, 300), true)

.setColorModes(PrintAttributes.COLOR_MODE_COLOR

| PrintAttributes.COLOR_MODE_MONOCHROME,

PrintAttributes.COLOR_MODE_MONOCHROME)

.build();

printer = new PrinterInfo.Builder(printer)

.setCapabilities(capabilities)

.setStatus(PrinterInfo.STATUS_IDLE)

//        .setDescription("fake print 1!")

.build();

List printers = new ArrayList();

printers.add(printer);

addPrinters(printers);

}

}

@Override

public void onStopPrinterStateTracking(PrinterId printerId) {

Log.d(TAG, "onStopPrinterStateTracking()");

}

@Override

public void onDestroy() {

Log.d(TAG, "onDestroy()");

}

private PrinterInfo findPrinterInfo(PrinterId printerId) {

List printers = getPrinters();

final int printerCount = getPrinters().size();

for (int i = 0; i

PrinterInfo printer = printers.get(i);

if (printer.getId().equals(printerId)) {

return printer;

}

}

return null;

}

}

六 总结

学习了该部分知识之后,已经可以初步从系统打印服务接入打印机,并取得要打印的文件。之后根据使用情况,适时地跟进细节即可。

android 打印机服务,Android系统打印服务插件printservice开发相关推荐

  1. 计算机无法启动打印服务,win10系统打印服务无法启动的设置教程

    win10系统使用久了,好多网友反馈说win10系统打印服务无法启动的问题,非常不方便.有什么办法可以永久解决win10系统打印服务无法启动的问题,面对win10系统打印服务无法启动的图文步骤非常简单 ...

  2. 路由器 android 打印机,谷歌关闭云打印服务,安卓和Chrome办公用户要慌了

    原标题:谷歌关闭云打印服务,安卓和Chrome办公用户要慌了 不少外资企业用谷歌的云打印服务来办公,不过最近有一个坏消息来了,据可靠消息,谷歌云打印服务将于2021年1月1日开始正式终止,包括Andr ...

  3. android打印服务apk,hp打印服务插件安卓下载

    HP Print Service插件是一款很实用的打印机插件.APK在特定的安卓设备中更新HP Print插件.此APK本身不提供任何打印功能: 您将需要HP提供的其他组件,才能使用此打印机进行打印. ...

  4. windows mobile 服务自动停止_打印服务print Splooer自动停止怎么办?

    打印文件的时候,print Splooer会自动停止,每次都要手动启动后才能打印,要不然就不能打印,print Splooer是打印服务,打印文件的话就需要print Splooer服务的支持,有没有 ...

  5. 计算机开启telnet服务,Win7系统telnet服务怎么开启?Win7电脑开启telnet服务的方法...

    telnet是一个远程登录与管理工具,由于telnet服务我们平时使用的较少,因此不知道打开它是很正常的.那么Win7系统telnet服务怎么开启?下面装机之家分享一下Win7电脑开启telnet服务 ...

  6. 【大话QT之四】ctkPlugin插件系统实现项目插件式开发

    插件式开发体会: 自开始写[大话QT]系列就开始接触渲染客户端的开发,说是开发不如更多的说是维护以及重构,在接手这块的东西之前自己还有点犹豫,因为之前我一直认为客户端嘛,没什么技术含量,总是想做比较有 ...

  7. ctkPlugin插件系统实现项目插件式开发

    插件式开发体会: 自开始写[大话QT]系列就开始接触渲染客户端的开发,说是开发不如更多的说是维护以及重构,在接手这块的东西之前自己还有点犹豫,因为之前我一直认为客户端嘛,没什么技术含量,总是想做比较有 ...

  8. linux中启动不了服务,Linux系统xinetd服务启动不了

    JavaScript高阶函数 所谓高阶函数(higher-order function) 就是操作函数的函数,它接收一个或多个函数作为参数,并返回一个新函数. 下面的例子接收两个函数f()和g(),并 ...

  9. Android8打印功能,[翻译]Android 8.1 默认打印服务的中间人攻击漏洞披露

    漏洞概述 Android 8.1引入了默认打印服务.默认打印服务,基于Mopria联盟在谷歌应用商店发布的免费应用,Mopria Print Service.因其缺乏验证,所以会导致中间人攻击并被拦截 ...

最新文章

  1. TensorFlow数据归一化
  2. sql输出表中重复数据
  3. Java什么时候该加void_平时容易忽视的地方之一:java在抽取方法时,什么时候该用void...
  4. 哪些钱借了可以不还?
  5. 工业互联网白皮书_发布|《工业互联网平台安全白皮书(2020)》发布
  6. java非法字符检测_Java Web 一些特殊字符的过滤(appscan检查的安全问题)
  7. [Android]Android四大组件之Service总结
  8. 字符编码(1)——Unicode,utf-8
  9. 为什么我们需要Pulsar?
  10. android6.0加内存卡,Android 6.0 TF卡 手机扩容的新选择
  11. window上mySQL以及其可视化工具的安装
  12. ANSI SQL标准和准则
  13. windows 控制台cmd乱码的解决办法
  14. 响应式餐饮牛杂小吃类网站源码 dedecms织梦模板 (带手机端)
  15. 人人网主页登录_人人网回归了!原来的账号还能登录,你的“黑历史”都在呢...
  16. MapReduce实现kmeans算法
  17. 申请德国农工大学计算机案例,【2014】德州农工大学:腼腆男的春天
  18. C盘扩容(步骤记录)
  19. 杰克·伦敦: 一块牛排
  20. 联想小新Air15-2021安装elementaryOS5.1(基于ubuntu18.04)

热门文章

  1. c语言酒店管理系统流程,c语言的酒店管理系统方案.doc
  2. MailUtil-Java发送邮件工具类
  3. IE浏览器无法显示背景颜色问题
  4. 2963. 宝石机关
  5. 如何进行UI界面设计
  6. 00后小码农的人生感慨 —— 2021年度总结
  7. Linux启动hbase的shell命令出现警告_Linux 系统故障修复和修复技巧
  8. 实战Windows下编译Opencc 1.0.5
  9. 神经网络中的降维和升维方法 (tensorflow pytorch)
  10. 开源数据库HAWQ,架构调研