我尝试基于Android PrintServer API实现自己的打印机(意味着打印服务器)。此打印服务器提供静态单一打印机,其任务是通过http上载将打印作业上载到网页。
我在Sony Z2上使用Marshmallow。为了测试,我使用电子邮件应用程序并从子菜单“打印”。
原则上我已经管理了所有业务逻辑并且它可以工作,所以至少我使用了正确的方法。但是,当我按下Android打印对话框中的“打印”按钮(带有打印机徽标)时,整个打印服务器崩溃。如果我等到对话框呈现所有预览然后按下按钮,一切都很好。
我不知道问题是否清楚,因此我制作了一部小电影(1,8MB),你可以在这里找到:
https://c.geniusbytes.com/index.php/s/leJrSGRlxBSSCux
为了减少这个问题,我删除了所有转移代码,并得出了仍然导致崩溃的最小版本,见下文。
有人能看出问题是什么吗?在我的手机上还有PDF刻录机PrintServer(索尼!?)。使用相同的过程不会崩溃。而是“打印”行为,即选择存储PDF的目的地,等待呈现预览然后弹出。
重要的是要提到需要更大的文档才能打印。否则,通过按下打印按钮可以快速渲染预览以导致崩溃。
以下是PrintServer实现的代码:
public class UploadPrintService extends PrintService {
private static final String LOG_TAG = "UploadPrintService";
////////////////////////////////////////////////
//
// service methods
//
/////////////////////////////////////////////////
@Override
public void onCreate() {
super.onCreate();
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
protected void onConnected() {
Log.i(LOG_TAG, "#onConnected()");
}
@Override
protected void onDisconnected() {
Log.i(LOG_TAG, "#onDisconnected()");
}
@Override
protected PrinterDiscoverySession onCreatePrinterDiscoverySession() {
Log.i(LOG_TAG, "#onCreatePrinterDiscoverySession()");
return new UploadPrintDiscoverySession(this);
}
@Override
protected void onRequestCancelPrintJob(PrintJob printJob) {
// standard cancel
if ((printJob.isStarted() && (!printJob.isCancelled()))) {
printJob.cancel();
}
}
@Override
protected void onPrintJobQueued(final PrintJob printJob) {
Log.i(LOG_TAG, "#onPrintJobQueued");
printJob.start();
}
}
并且PrinterDiscoverySession以某种方式实现,以便它始终返回相同的打印机。见这里:
public class UploadPrintDiscoverySession extends PrinterDiscoverySession {
private static final String LOG_TAG = UploadPrintDiscoverySession.class.getSimpleName();
private PrintService printService = null;
private static final String PRINTER_ID = "my.uploadprinter.id";
public UploadPrintDiscoverySession(PrintService printService) {
super();
Log.i(LOG_TAG, "new discovery session!");
this.printService = printService;
}
@Override
public void onStartPrinterDiscovery(List<PrinterId> list) {
Log.i(LOG_TAG, "#onStartPrinterDiscovery");
addUploadPrinter();
}
@Override
public void onStopPrinterDiscovery() {
Log.i(LOG_TAG, "#onStopPrinterDiscovery");
}
@Override
public void onValidatePrinters(List<PrinterId> list) {
Log.i(LOG_TAG, "#onValidatePrinters");
addUploadPrinter();
}
@Override
public void onStartPrinterStateTracking(PrinterId printerId) {
Log.i(LOG_TAG, "#onStartPrinterStateTracking");
addUploadPrinter();
}
@Override
public void onStopPrinterStateTracking(PrinterId printerId) {
Log.i(LOG_TAG, "#onStopPrinterStateTracking");
}
@Override
public void onDestroy() {
Log.i(LOG_TAG, "#onDestroy");
}
private void addUploadPrinter() {
Log.i(LOG_TAG, "#addUploadPrinter");
PrinterId uploadPrinterId = printService.generatePrinterId(PRINTER_ID);
PrinterInfo.Builder printerInfoBuilder = new PrinterInfo.Builder(uploadPrinterId,
"Mighty Printer", PrinterInfo.STATUS_IDLE);
PrinterCapabilitiesInfo.Builder capBuilder =
new PrinterCapabilitiesInfo.Builder(uploadPrinterId);
capBuilder.addMediaSize(PrintAttributes.MediaSize.ISO_A4, true);
capBuilder.addMediaSize(PrintAttributes.MediaSize.ISO_A3, false);
capBuilder.addResolution(new PrintAttributes.Resolution(
"Default", "default resolution", 600, 600), true);
capBuilder.setColorModes(PrintAttributes.COLOR_MODE_COLOR
| PrintAttributes.COLOR_MODE_MONOCHROME,
PrintAttributes.COLOR_MODE_COLOR);
printerInfoBuilder.setCapabilities(capBuilder.build());
Log.i(LOG_TAG, "add *the* printer to the list...");
List<PrinterInfo> discoveredPrinterList = new ArrayList<PrinterInfo>();
discoveredPrinterList.add(printerInfoBuilder.build());
this.addPrinters(discoveredPrinterList);
}
}
感谢Logcat-hint,我已经想知道为什么我没有看到错误但是当我禁用默认激活的过滤器时,我看到以下错误:
09-05 20:06:05.523 27568-27568/? E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.android.printspooler, PID: 27568
java.lang.IllegalArgumentException: end cannot be less than zero.
at android.print.PageRange.<init>(PageRange.java:50)
at com.android.printspooler.util.PageRangeUtils.asAbsoluteRange(PageRangeUtils.java:199)
at com.android.printspooler.ui.PrintActivity$DocumentTransformer.computePagesToShred(PrintActivity.java:2620)
at com.android.printspooler.ui.PrintActivity$DocumentTransformer.<init>(PrintActivity.java:2496)
at com.android.printspooler.ui.PrintActivity.transformDocumentAndFinish(PrintActivity.java:1733)
at com.android.printspooler.ui.PrintActivity.requestCreatePdfFileOrFinish(PrintActivity.java:967)
at com.android.printspooler.ui.PrintActivity.onUpdateCompleted(PrintActivity.java:492)
at com.android.printspooler.model.RemotePrintDocument.notifyUpdateCompleted(RemotePrintDocument.java:385)
at com.android.printspooler.model.RemotePrintDocument.access$900(RemotePrintDocument.java:55)
at com.android.printspooler.model.RemotePrintDocument$1.onDone(RemotePrintDocument.java:117)
at com.android.printspooler.model.RemotePrintDocument$WriteCommand.handleOnWriteFinished(RemotePrintDocument.java:1000)
at com.android.printspooler.model.RemotePrintDocument$WriteCommand.access$2200(RemotePrintDocument.java:867)
at com.android.printspooler.model.RemotePrintDocument$WriteCommand$WriteHandler.handleMessage(RemotePrintDocument.java:1061)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:234)
at android.app.ActivityThread.main(ActivityThread.java:5526)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
0
后来我发现了一些行:
09-05 20:06:06.956 1005-3292/? I/WindowState: WIN DEATH: Window{ff8499c u0 com.android.printspooler/com.android.printspooler.ui.PrintActivity}
09-05 20:06:06.956 1005-3325/? W/UserState: Not stopping printer state tracking - session destroyed
09-05 20:06:06.956 1005-3924/? D/GraphicsStats: Buffer count: 7
09-05 20:06:06.957 1005-1005/? D/RemotePrintSpooler: Error clearing print spooler client
android.os.DeadObjectException
at android.os.BinderProxy.transactNative(Native Method)
at android.os.BinderProxy.transact(Binder.java:503)
at android.print.IPrintSpooler$Stub$Proxy.setClient(IPrintSpooler.java:358)
at com.android.server.print.RemotePrintSpooler.clearClientLocked(RemotePrintSpooler.java:424)
at com.android.server.print.RemotePrintSpooler.access$400(RemotePrintSpooler.java:53)
at com.android.server.print.RemotePrintSpooler$MyServiceConnection.onServiceDisconnected(RemotePrintSpooler.java:456)
at android.app.LoadedApk$ServiceDispatcher.doDeath(LoadedApk.java:1232)
at android.app.LoadedApk$ServiceDispatcher$RunConnection.run(LoadedApk.java:1246)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:234)
at com.android.server.SystemServer.run(SystemServer.java:302)
at com.android.server.SystemServer.main(SystemServer.java:174)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
09-05 20:06:06.958 26358-26358/com.geniusbytes.uploadprintservice I/UploadPrintDiscoverySession: #onStopPrinterStateTracking
09-05 20:06:06.958 26358-26358/com.geniusbytes.uploadprintservice I/UploadPrintDiscoverySession: #onStopPrinterDiscovery
09-05 20:06:06.958 26358-26358/com.geniusbytes.uploadprintservice I/UploadPrintDiscoverySession: #onDestroy
09-05 20:06:06.958 26358-26358/com.geniusbytes.uploadprintservice I/UploadPrintService: #onDisconnected()
09-05 20:06:07.006 1005-3886/? I/ActivityManager: Process com.android.printspooler (pid 27568) has died
09-05 20:06:07.006 1005-3886/? W/ActivityManager: Scheduling restart of crashed service com.android.printspooler/.model.PrintSpoolerService in 1000ms
09-05 20:06:07.188 1005-1237/? W/AppOps: Finishing op nesting under-run: uid 1000 pkg android code 24 time=1462541465446 duration=2311 nesting=0
09-05 20:06:08.024 1005-1185/? I/ActivityManager: Start proc 5263:com.android.printspooler/u0a159 for service com.android.printspooler/.model.PrintSpoolerService
09-05 20:06:09.953 391-3149/? D/audio_hw_primary: out_standby: enter: stream (0xb5bf7240) usecase(0: deep-buffer-playback)