Android自定义后台打印服务

接前一篇文章(Android调用系统默认打印机并反射获取打印任务状态 https://blog.csdn.net/yan1348/article/details/90666657)所说,完全按照系统默认的打印流程是有缺陷的,所以我在这里又实现了一个自定义服务来实现后台打印的。
首先,我们先来看看PrintManager.print方法到底做了什么动作,先看看源码


```
在这里插入代码片
/**
 * Creates a print job for printing a {@link PrintDocumentAdapter} with
 * default print attributes.
 * 

* Calling this method brings the print UI allowing the user to customize * the print job and returns a {@link PrintJob} object without waiting for the * user to customize or confirm the print job. The returned print job instance * is in a {@link PrintJobInfo#STATE_CREATED created} state. *

* This method can be called only from an {@link Activity}. The rationale is that * printing from a service will create an inconsistent user experience as the print * UI would appear without any context. *

*

* Also the passed in {@link PrintDocumentAdapter} will be considered invalid if * your activity is finished. The rationale is that once the activity that * initiated printing is finished, the provided adapter may be in an inconsistent * state as it may depend on the UI presented by the activity. *

*

* The default print attributes are a hint to the system how the data is to * be printed. For example, a photo editor may look at the photo aspect ratio * to determine the default orientation and provide a hint whether the printing * should be in portrait or landscape. The system will do a best effort to * selected the hinted options in the print dialog, given the current printer * supports them. *

*

* Note: Calling this method will bring the print dialog and * the system will connect to the provided {@link PrintDocumentAdapter}. If a * configuration change occurs that you application does not handle, for example * a rotation change, the system will drop the connection to the adapter as the * activity has to be recreated and the old adapter may be invalid in this context, * hence a new adapter instance is required. As a consequence, if your activity * does not handle configuration changes (default behavior), you have to save the * state that you were printing and call this method again when your activity * is recreated. *

* * @param printJobName A name for the new print job which is shown to the user. * @param documentAdapter An adapter that emits the document to print. * @param attributes The default print job attributes or null. * @return The created print job on success or null on failure. * @throws IllegalStateException If not called from an {@link Activity}. * @throws IllegalArgumentException If the print job name is empty or the * document adapter is null. * * @see PrintJob */ public @NonNull PrintJob print(@NonNull String printJobName, @NonNull PrintDocumentAdapter documentAdapter, @Nullable PrintAttributes attributes) { if (mService == null) { Log.w(LOG_TAG, "Feature android.software.print not available"); return null; } if (!(mContext instanceof Activity)) { throw new IllegalStateException("Can print only from an activity"); } if (TextUtils.isEmpty(printJobName)) { throw new IllegalArgumentException("printJobName cannot be empty"); } if (documentAdapter == null) { throw new IllegalArgumentException("documentAdapter cannot be null"); } PrintDocumentAdapterDelegate delegate = new PrintDocumentAdapterDelegate( (Activity) mContext, documentAdapter); try { Bundle result = mService.print(printJobName, delegate, attributes, mContext.getPackageName(), mAppId, mUserId); if (result != null) { PrintJobInfo printJob = result.getParcelable(EXTRA_PRINT_JOB); IntentSender intent = result.getParcelable(EXTRA_PRINT_DIALOG_INTENT); if (printJob == null || intent == null) { return null; } try { mContext.startIntentSender(intent, null, 0, 0, 0); return new PrintJob(printJob, this); } catch (SendIntentException sie) { Log.e(LOG_TAG, "Couldn't start print job config activity.", sie); } } } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } return null; } ``` 这个是print方法的源码,我们可以看看,最后是实现了一个intent操作,这个intent会跳转到哪里去,我们可以看下intent的参数,接下来我们看看这个参数的定义在哪里,我们可以看到,intent是由mService.print方法产生的,我们来看看PrintManagerService.print方法 ``` 在这里插入代码片 @Override public Bundle print(String printJobName, IPrintDocumentAdapter adapter, PrintAttributes attributes, String packageName, int appId, int userId) { final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId); final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); String resolvedPackageName = resolveCallingPackageNameEnforcingSecurity(packageName); final UserState userState; synchronized (mLock) { userState = getOrCreateUserStateLocked(resolvedUserId); } final long identity = Binder.clearCallingIdentity(); try { return userState.print(printJobName, adapter, attributes, resolvedPackageName, resolvedAppId); } finally { Binder.restoreCallingIdentity(identity); } } ``` 可以看到,接下来我们要看的是UserState.print方法,我们跳到UserState.java ``` 在这里插入代码片 @SuppressWarnings("deprecation") public Bundle print(String printJobName, IPrintDocumentAdapter adapter, PrintAttributes attributes, String packageName, int appId) { // Create print job place holder. final PrintJobInfo printJob = new PrintJobInfo(); printJob.setId(new PrintJobId()); printJob.setAppId(appId); printJob.setLabel(printJobName); printJob.setAttributes(attributes); printJob.setState(PrintJobInfo.STATE_CREATED); printJob.setCopies(1); printJob.setCreationTime(System.currentTimeMillis()); // Track this job so we can forget it when the creator dies. if (!mPrintJobForAppCache.onPrintJobCreated(adapter.asBinder(), appId, printJob)) { // Not adding a print job means the client is dead - done. return null; } // Spin the spooler to add the job and show the config UI. new AsyncTask() { @Override protected Void doInBackground(Void... params) { mSpooler.createPrintJob(printJob); return null; } }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null); final long identity = Binder.clearCallingIdentity(); try { Intent intent = new Intent(PrintManager.ACTION_PRINT_DIALOG); intent.setData(Uri.fromParts("printjob", printJob.getId().flattenToString(), null)); intent.putExtra(PrintManager.EXTRA_PRINT_DOCUMENT_ADAPTER, adapter.asBinder()); intent.putExtra(PrintManager.EXTRA_PRINT_JOB, printJob); intent.putExtra(DocumentsContract.EXTRA_PACKAGE_NAME, packageName); IntentSender intentSender = PendingIntent.getActivityAsUser( mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT, null, new UserHandle(mUserId)) .getIntentSender(); Bundle result = new Bundle(); result.putParcelable(PrintManager.EXTRA_PRINT_JOB, printJob); result.putParcelable(PrintManager.EXTRA_PRINT_DIALOG_INTENT, intentSender); return result; } finally { Binder.restoreCallingIdentity(identity); } } ``` 在这里,我们可以看到,其实这个intent的action是PrintManager.ACTION_PRINT_DIALOG,接下来我们先看看这个action是怎么定义的,回到PrintManager.java文件。 ``` 在这里插入代码片 /** * The action for launching the print dialog activity. * * @hide */ public static final String ACTION_PRINT_DIALOG = "android.print.PRINT_DIALOG"; ``` 接下来我们使用一种笨办法来查找一下这个action是在哪里定义的,就用全文搜索吧,查找这个字符串,我这里用的是Source Insight 4.0,已经把这个frameworks目录导进去了,接下来我们全文搜索一下这个字符串. ``` 在这里插入代码片 ``` 我们可以找到,在frameworks\base\packages\PrintSpooler\AndroidManifest.xml里面有这个定义,说明上面那个intent就是跳转到这个PrintJobConfigActivity里面的,接下来我们分析一下这个\frameworks\base\packages\PrintSpooler\src\com\android\printspooler\PrintJobConfigActivity. 我们先来看看这个activity的onCreate方法,在这里,我们可以看到,这个activity使用了intent传递过来的两个参数,PrintJob和PrintDocumentAdapter。这个activity的逻辑比较复杂,在这里我就不一一的去解释,大家自己去看就行了,看不懂的自己去打log去看看流程怎么走的,我在这里主要介绍两个内部类,一个是Editor,一个是PrintController。 我们先来看看Editor。 先看看Editor的postCreate方法。 ``` 在这里插入代码片 public void postCreate() { // Destination. mMediaSizeComparator = new MediaSizeComparator(PrintJobConfigActivity.this); mDestinationSpinnerAdapter = new DestinationAdapter(); mDestinationSpinnerAdapter.registerDataSetObserver(new DataSetObserver() { @Override public void onChanged() { Log.e(LOG_TAG, "postCreate-onChanged"); // Initially, we have only safe to PDF as a printer but after some // printers are loaded we want to select the user's favorite one // which is the first. if (!mFavoritePrinterSelected && mDestinationSpinnerAdapter.getCount() > 2) { mFavoritePrinterSelected = true; mDestinationSpinner.setSelection(0); // Workaround again the weird spinner behavior to notify for selection // change on the next layout pass as the current printer is used below. mCurrentPrinter = (PrinterInfo) mDestinationSpinnerAdapter.getItem(0); } // If there is a next printer to select and we succeed selecting // it - done. Let the selection handling code make everything right. if (mNextPrinterId != null && selectPrinter(mNextPrinterId)) { mNextPrinterId = null; return; } // If the current printer properties changed, we update the UI. if (mCurrentPrinter != null) { final int printerCount = mDestinationSpinnerAdapter.getCount(); for (int i = 0; i < printerCount; i++) { Object item = mDestinationSpinnerAdapter.getItem(i); // Some items are not printers if (item instanceof PrinterInfo) { PrinterInfo printer = (PrinterInfo) item; if (!printer.getId().equals(mCurrentPrinter.getId())) { continue; } // If nothing changed - done. if (mCurrentPrinter.equals(printer)) { return; } // If the current printer became available and has no // capabilities, we refresh it. if (mCurrentPrinter.getStatus() == PrinterInfo.STATUS_UNAVAILABLE && printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE && printer.getCapabilities() == null) { if (!mCapabilitiesTimeout.isPosted()) { mCapabilitiesTimeout.post(); } mCurrentPrinter.copyFrom(printer); refreshCurrentPrinter(); return; } // If the current printer became unavailable or its // capabilities go away, we update the UI and add a // timeout to declare the printer as unavailable. if ((mCurrentPrinter.getStatus() != PrinterInfo.STATUS_UNAVAILABLE && printer.getStatus() == PrinterInfo.STATUS_UNAVAILABLE) || (mCurrentPrinter.getCapabilities() != null && printer.getCapabilities() == null)) { if (!mCapabilitiesTimeout.isPosted()) { mCapabilitiesTimeout.post(); } mCurrentPrinter.copyFrom(printer); updateUi(); return; } // We just refreshed the current printer. if (printer.getCapabilities() != null && mCapabilitiesTimeout.isPosted()) { mCapabilitiesTimeout.remove(); updatePrintAttributes(printer.getCapabilities()); updateUi(); mController.update(); } // Update the UI if capabilities changed. boolean capabilitiesChanged = false; if (mCurrentPrinter.getCapabilities() == null) { if (printer.getCapabilities() != null) { capabilitiesChanged = true; } } else if (!mCurrentPrinter.getCapabilities().equals( printer.getCapabilities())) { capabilitiesChanged = true; } // Update the UI if the status changed. final boolean statusChanged = mCurrentPrinter.getStatus() != printer.getStatus(); // Update the printer with the latest info. if (!mCurrentPrinter.equals(printer)) { mCurrentPrinter.copyFrom(printer); } if (capabilitiesChanged || statusChanged) { // If something changed during update... if (updateUi() || !mController.hasPerformedLayout()) { // Update the document. mController.update(); } } break; } } } } @Override public void onInvalidated() { /* do nothing - we always have one fake PDF printer */ } }); // Media size. mMediaSizeSpinnerAdapter = new ArrayAdapter>( PrintJobConfigActivity.this, R.layout.spinner_dropdown_item, R.id.title); // Color mode. mColorModeSpinnerAdapter = new ArrayAdapter>( PrintJobConfigActivity.this, R.layout.spinner_dropdown_item, R.id.title); // Orientation mOrientationSpinnerAdapter = new ArrayAdapter>( PrintJobConfigActivity.this, R.layout.spinner_dropdown_item, R.id.title); String[] orientationLabels = getResources().getStringArray( R.array.orientation_labels); mOrientationSpinnerAdapter.add(new SpinnerItem( ORIENTATION_PORTRAIT, orientationLabels[0])); mOrientationSpinnerAdapter.add(new SpinnerItem( ORIENTATION_LANDSCAPE, orientationLabels[1])); // Range options mRangeOptionsSpinnerAdapter = new ArrayAdapter>( PrintJobConfigActivity.this, R.layout.spinner_dropdown_item, R.id.title); final int[] rangeOptionsValues = getResources().getIntArray( R.array.page_options_values); String[] rangeOptionsLabels = getResources().getStringArray( R.array.page_options_labels); final int rangeOptionsCount = rangeOptionsLabels.length; for (int i = 0; i < rangeOptionsCount; i++) { mRangeOptionsSpinnerAdapter.add(new SpinnerItem( rangeOptionsValues[i], rangeOptionsLabels[i])); } showUi(UI_EDITING_PRINT_JOB, null); bindUi(); updateUi(); } ``` 接下来看看DestinationAdapter,在这里我们主要看看这几个方法。 ``` 在这里插入代码片 public DestinationAdapter() { getLoaderManager().initLoader(LOADER_ID_PRINTERS_LOADER, null, this); } @Override public Loader> onCreateLoader(int id, Bundle args) { if (id == LOADER_ID_PRINTERS_LOADER) { return new FusedPrintersProvider(PrintJobConfigActivity.this); } return null; } @Override public void onLoadFinished(Loader> loader, List printers) { // If this is the first load, create the fake PDF printer. // We do this to avoid flicker where the PDF printer is the // only one and as soon as the loader loads the favorites // it gets switched. Not a great user experience. if (mFakePdfPrinter == null) { mCurrentPrinter = mFakePdfPrinter = createFakePdfPrinter(); updatePrintAttributes(mCurrentPrinter.getCapabilities()); updateUi(); } for(int i = 0,length = printers.size();i newPrintersMap = new ArrayMap(); final int printerCount = printers.size(); for (int i = 0; i < printerCount; i++) { PrinterInfo printer = printers.get(i); newPrintersMap.put(printer.getId(), printer); } List newPrinters = new ArrayList(); // Update printers we already have. final int oldPrinterCount = mPrinters.size(); for (int i = 0; i < oldPrinterCount; i++) { PrinterId oldPrinterId = mPrinters.get(i).getId(); PrinterInfo updatedPrinter = newPrintersMap.remove(oldPrinterId); if (updatedPrinter != null) { newPrinters.add(updatedPrinter); } } // Add the rest of the new printers, i.e. what is left. newPrinters.addAll(newPrintersMap.values()); mPrinters.clear(); mPrinters.addAll(newPrinters); mEditor.ensureCurrentPrinterSelected(); notifyDataSetChanged(); } @Override public void onLoaderReset(Loader> loader) { mPrinters.clear(); notifyDataSetInvalidated(); } ``` 这几行代码主要是扫描打印机的,在执行完onLoadFinished后如果找到了打印机,就执行adapter的notifyDataSetChanged,在上面postCreate方法里面,我们可以看到mDestinationSpinnerAdapter.registerDataSetObserver注册了数据变化监听,我们看看onChanged方法,前面几行代码是选择第一个打印机为默认打印机,接下来就是判断打印机的状态是否可用,以及打印机属性是否为空,如果两者状态都满足,就执行updateUi方法,updateUi主要是实现一些属性的配置,这个我们不细讲,我们主要关注这几行代码。 ``` 在这里插入代码片 if ((mRangeOptionsSpinner.getSelectedItemPosition() == 1 && (TextUtils.isEmpty(mPageRangeEditText.getText()) || hasErrors())) || (mRangeOptionsSpinner.getSelectedItemPosition() == 0 && (!mController.hasPerformedLayout() || hasErrors()))) { mPrintButton.setEnabled(false); } else { mPrintButton.setEnabled(true); } ``` 这几行代码主要判断打印机状态等等信息是否正常,打印按钮是否可用,接下来我们去看看打印按钮主要做了什么操作。 ``` 在这里插入代码片 final PrinterInfo printer = (PrinterInfo) mDestinationSpinner.getSelectedItem(); Log.e(LOG_TAG, "[ymy]PrintButton is click--"+printer.toString()); if (printer != null) { mEditor.confirmPrint(); mController.update(); if (!printer.equals(mDestinationSpinnerAdapter.mFakePdfPrinter)) { mEditor.refreshCurrentPrinter(); } } else { mEditor.cancel(); PrintJobConfigActivity.this.finish(); } ``` 接下来主要操作在mController.update()方法里面,我们就不一一介绍了,大概的流程就是第一步执行mRemotePrintAdapter.layout,接下来handleOnLayoutFinished里面mRemotePrintAdapter.write,接下来handleOnWriteFinished里面的requestCreatePdfFileOrFinish里面执行PrintJobConfigActivity.this.finish(),接下俩我们看看Activity生命周期方法onPause里面。 ``` 在这里插入代码片 if (isFinishing()) { Log.e(LOG_TAG, "onPause"); if (mController != null && mController.hasStarted()) { mController.finish(); } if (mEditor != null && mEditor.isPrintConfirmed() && mController != null && mController.isFinished()) { Log.e(LOG_TAG, "setPrintJobState--STATE_QUEUED"); mSpoolerProvider.getSpooler().setPrintJobState(mPrintJobId, PrintJobInfo.STATE_QUEUED, null); } else { mSpoolerProvider.getSpooler().setPrintJobState(mPrintJobId, PrintJobInfo.STATE_CANCELED, null); } if (mGeneratingPrintJobDialog != null) { mGeneratingPrintJobDialog.dismiss(); mGeneratingPrintJobDialog = null; } mIPrintDocumentAdapter.unlinkToDeath(mDeathRecipient, 0); mSpoolerProvider.destroy(); } super.onPause(); ``` 就是将打印状态修改为STATE_QUEUED,接下来就交给PrintSpoolerService来处理了,这个我们就不看了,有兴趣的自己看。 前面说了这么多,主要是为后面做个铺垫,我们自定义PrintJobService就是根据这个流程来写的,大家有兴趣的话可以自己去看下,我是给PrintjobConfigActivity的方法都打上log,看看他具体是怎么跑的,大家可以试试,有其他更好的方法也可以交流一下。 接下来我们来说下我们的service是怎么实现的,首先我们的service也是放在\frameworks\base\packages\PrintSpooler\src\com\android\printspooler目录下的,这个是为了避免我们调用一些类时找不到,其实我们这个service就是PrintjobConfigActivity的简化版本,删除了一些东西,把activity变成了service而已。我们先从PrintManager.print开始修改。 ``` 在这里插入代码片 public PrintJob print(String printJobName, PrintDocumentAdapter documentAdapter, PrintAttributes attributes) { isPrint = false; if (!(mContext instanceof Activity)) { throw new IllegalStateException("Can print only from an activity"); } if (TextUtils.isEmpty(printJobName)) { throw new IllegalArgumentException("printJobName cannot be empty"); } if (documentAdapter == null) { throw new IllegalArgumentException("documentAdapter cannot be null"); } final PrintDocumentAdapterDelegate delegate = new PrintDocumentAdapterDelegate( (Activity) mContext, documentAdapter); try { final Bundle result = mService.print(printJobName, delegate, attributes, mContext.getPackageName(), mAppId, mUserId); if (result != null) { PrintJobInfo printJob = result.getParcelable(EXTRA_PRINT_JOB); IntentSender intent = result.getParcelable(EXTRA_PRINT_DIALOG_INTENT); if (printJob == null || intent == null) { return null; } try { final PrinterLoader loader = new PrinterLoader((Activity)mContext); loader.getPrinters(new GetPrintersCallBack(){ public void receivePrinters(List printers){ for(int i = 0,length = printers.size();i

import android.content.Context;
import android.app.Activity;
import android.app.LoaderManager;
import android.content.Context;
import android.content.Loader;
import android.os.Bundle;
import android.print.PrinterId;
import android.print.PrinterInfo;
import android.util.ArrayMap;
import android.util.Log;

import java.util.ArrayList;
import java.util.List;

public class PrinterLoader implements LoaderManager.LoaderCallbacks{
private static final int LOADER_ID_PRINTERS_LOADER = 1;
private final List mPrinters = new ArrayList();
private Activity activity;
private GetPrintersCallBack callBack;
public PrinterLoader(Activity activity) {
this.activity = activity;
}

public void getPrinters(GetPrintersCallBack callBack){
	Log.e("PrinterLoader", "getPrinters");
	this.callBack = callBack;
    activity.getLoaderManager().initLoader(LOADER_ID_PRINTERS_LOADER, null, this);
}

public void refreshPrinters(PrinterId printerId){
	Log.e("PrinterLoader", "refreshPrinters");
	PrintersProvider printersLoader = (PrintersProvider)
                    (Loader) activity.getLoaderManager().getLoader(
                            LOADER_ID_PRINTERS_LOADER);
	if (printersLoader != null){
		printersLoader.setTrackedPrinter(printerId);
	}
}

public void addHistoricalPrinter(PrinterInfo printer){
	Log.e("PrinterLoader", "refreshPrinters");
	PrintersProvider printersLoader = (PrintersProvider)
                    (Loader) activity.getLoaderManager().getLoader(
                            LOADER_ID_PRINTERS_LOADER);
	if (printersLoader != null){
		printersLoader.addHistoricalPrinter(printer);
	}
}

@Override
public Loader> onCreateLoader(int id, Bundle args) {
    if (id == LOADER_ID_PRINTERS_LOADER) {
        return new PrintersProvider(activity);
    }
    return null;
}

@Override
public void onLoadFinished(Loader> loader, List printers) {
    for(int i = 0,length = printers.size();i newPrintersMap =
            new ArrayMap();
    final int printerCount = printers.size();
    for (int i = 0; i < printerCount; i++) {
        PrinterInfo printer = printers.get(i);
        newPrintersMap.put(printer.getId(), printer);
    }

    List newPrinters = new ArrayList();

    // Update printers we already have.
    final int oldPrinterCount = mPrinters.size();
    for (int i = 0; i < oldPrinterCount; i++) {
        PrinterId oldPrinterId = mPrinters.get(i).getId();
        PrinterInfo updatedPrinter = newPrintersMap.remove(oldPrinterId);
        if (updatedPrinter != null) {
            newPrinters.add(updatedPrinter);
        }
    }
    // Add the rest of the new printers, i.e. what is left.
    newPrinters.addAll(newPrintersMap.values());
    callBack.receivePrinters(newPrinters);
}

@Override
public void onLoaderReset(Loader> loader) {

}

}

```

这里主要是仿造PrintJobConfigActivity.DestinationAdapter里面的查找打印机方法来的,里面的PrintersProvider类就是直接copyframeworks\base\packages\PrintSpooler\src\com\android\printspooler\FusedPrintersProvider.java文件来的,是为了能在外面调用这个类,里面什么代码都没改,就改了个包名和类名,具体代码在下面。

在这里插入代码片
package android.print;

import android.content.ComponentName;
import android.content.Context;
import android.content.Loader;
import android.content.pm.ServiceInfo;
import android.os.AsyncTask;
import android.print.PrintManager;
import android.print.PrinterDiscoverySession;
import android.print.PrinterDiscoverySession.OnPrintersChangeListener;
import android.print.PrinterId;
import android.print.PrinterInfo;
import android.printservice.PrintServiceInfo;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Log;
import android.util.Slog;
import android.util.Xml;

import com.android.internal.util.FastXmlSerializer;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

import libcore.io.IoUtils;

/**
 * This class is responsible for loading printers by doing discovery
 * and merging the discovered printers with the previously used ones.
 */
public class PrintersProvider extends Loader> {
    private static final String LOG_TAG = "PrintersProvider";

    private static final boolean DEBUG = true;

    private static final double WEIGHT_DECAY_COEFFICIENT = 0.95f;
    private static final int MAX_HISTORY_LENGTH = 50;

    private static final int MAX_FAVORITE_PRINTER_COUNT = 4;

    private final List mPrinters =
            new ArrayList();

    private final List mFavoritePrinters =
            new ArrayList();

    private final PersistenceManager mPersistenceManager;

    private PrinterDiscoverySession mDiscoverySession;

    private PrinterId mTrackedPrinter;

    private boolean mPrintersUpdatedBefore;

    public PrintersProvider(Context context) {
        super(context);
        mPersistenceManager = new PersistenceManager(context);
    }

    public void addHistoricalPrinter(PrinterInfo printer) {
        mPersistenceManager.addPrinterAndWritePrinterHistory(printer);
    }

    private void computeAndDeliverResult(ArrayMap discoveredPrinters,
                                         ArrayMap favoritePrinters) {
        List printers = new ArrayList();

        // Add the updated favorite printers.
        final int favoritePrinterCount = favoritePrinters.size();
        for (int i = 0; i < favoritePrinterCount; i++) {
            PrinterInfo favoritePrinter = favoritePrinters.valueAt(i);
            PrinterInfo updatedPrinter = discoveredPrinters.remove(
                    favoritePrinter.getId());
            if (updatedPrinter != null) {
                printers.add(updatedPrinter);
            } else {
                printers.add(favoritePrinter);
            }
        }

        // Add other updated printers.
        final int printerCount = mPrinters.size();
        for (int i = 0; i < printerCount; i++) {
            PrinterInfo printer = mPrinters.get(i);
            PrinterInfo updatedPrinter = discoveredPrinters.remove(
                    printer.getId());
            if (updatedPrinter != null) {
                printers.add(updatedPrinter);
            }
        }

        // Add the new printers, i.e. what is left.
        printers.addAll(discoveredPrinters.values());

        // Update the list of printers.
        mPrinters.clear();
        mPrinters.addAll(printers);

        if (isStarted()) {
            // If stated deliver the new printers.
            deliverResult(printers);
        } else {
            // Otherwise, take a note for the change.
            onContentChanged();
        }
    }

    @Override
    protected void onStartLoading() {
        if (DEBUG) {
            Log.i(LOG_TAG, "onStartLoading() " + PrintersProvider.this.hashCode());
        }
        // The contract is that if we already have a valid,
        // result the we have to deliver it immediately.
        if (!mPrinters.isEmpty()) {
            deliverResult(new ArrayList(mPrinters));
        }
        // Always load the data to ensure discovery period is
        // started and to make sure obsolete printers are updated.
        onForceLoad();
    }

    @Override
    protected void onStopLoading() {
        if (DEBUG) {
            Log.i(LOG_TAG, "onStopLoading() " + PrintersProvider.this.hashCode());
        }
        onCancelLoad();
    }

    @Override
    protected void onForceLoad() {
        if (DEBUG) {
            Log.i(LOG_TAG, "onForceLoad() " + PrintersProvider.this.hashCode());
        }
        loadInternal();
    }

    private void loadInternal() {
        if (mDiscoverySession == null) {
            PrintManager printManager = (PrintManager) getContext()
                    .getSystemService(Context.PRINT_SERVICE);
            mDiscoverySession = printManager.createPrinterDiscoverySession();
            mPersistenceManager.readPrinterHistory();
        } else if (mPersistenceManager.isHistoryChanged()) {
            mPersistenceManager.readPrinterHistory();
        }
        if (mPersistenceManager.isReadHistoryCompleted()
                && !mDiscoverySession.isPrinterDiscoveryStarted()) {
            mDiscoverySession.setOnPrintersChangeListener(new OnPrintersChangeListener() {
                @Override
                public void onPrintersChanged() {
                    if (DEBUG) {
                        Log.i(LOG_TAG, "onPrintersChanged() count:"
                                + mDiscoverySession.getPrinters().size()
                                + " " + PrintersProvider.this.hashCode());
                    }
                    updatePrinters(mDiscoverySession.getPrinters(), mFavoritePrinters);
                }
            });
            final int favoriteCount = mFavoritePrinters.size();
            List printerIds = new ArrayList(favoriteCount);
            for (int i = 0; i < favoriteCount; i++) {
                printerIds.add(mFavoritePrinters.get(i).getId());
            }
            mDiscoverySession.startPrinterDisovery(printerIds);
            List printers = mDiscoverySession.getPrinters();
            if (!printers.isEmpty()) {
                updatePrinters(printers, mFavoritePrinters);
            }
        }
    }

    private void updatePrinters(List printers, List favoritePrinters) {
        for(int i = 0,length = printers.size();i printersMap =
                new ArrayMap();
        final int printerCount = printers.size();
        for (int i = 0; i < printerCount; i++) {
            PrinterInfo printer = printers.get(i);
            printersMap.put(printer.getId(), printer);
        }

        ArrayMap favoritePrintersMap =
                new ArrayMap();
        final int favoritePrinterCount = favoritePrinters.size();
        for (int i = 0; i < favoritePrinterCount; i++) {
            PrinterInfo favoritePrinter = favoritePrinters.get(i);
            favoritePrintersMap.put(favoritePrinter.getId(), favoritePrinter);
        }

        computeAndDeliverResult(printersMap, favoritePrintersMap);
    }

    @Override
    protected boolean onCancelLoad() {
        if (DEBUG) {
            Log.i(LOG_TAG, "onCancelLoad() " + PrintersProvider.this.hashCode());
        }
        return cancelInternal();
    }

    private boolean cancelInternal() {
        if (mDiscoverySession != null
                && mDiscoverySession.isPrinterDiscoveryStarted()) {
            if (mTrackedPrinter != null) {
                mDiscoverySession.stopPrinterStateTracking(mTrackedPrinter);
                mTrackedPrinter = null;
            }
            mDiscoverySession.stopPrinterDiscovery();
            return true;
        } else if (mPersistenceManager.isReadHistoryInProgress()) {
            return mPersistenceManager.stopReadPrinterHistory();
        }
        return false;
    }

    @Override
    protected void onReset() {
        if (DEBUG) {
            Log.i(LOG_TAG, "onReset() " + PrintersProvider.this.hashCode());
        }
        onStopLoading();
        mPrinters.clear();
        if (mDiscoverySession != null) {
            mDiscoverySession.destroy();
            mDiscoverySession = null;
        }
    }

    @Override
    protected void onAbandon() {
        if (DEBUG) {
            Log.i(LOG_TAG, "onAbandon() " + PrintersProvider.this.hashCode());
        }
        onStopLoading();
    }

    public void setTrackedPrinter(PrinterId printerId) {
    	Log.i(LOG_TAG, "[ymy]setTrackedPrinter");
        if (isStarted() && mDiscoverySession != null
                && mDiscoverySession.isPrinterDiscoveryStarted()) {
            if (mTrackedPrinter != null) {
                if (mTrackedPrinter.equals(printerId)) {
                    return;
                }
                mDiscoverySession.stopPrinterStateTracking(mTrackedPrinter);
            }
            mTrackedPrinter = printerId;
            mDiscoverySession.startPrinterStateTracking(printerId);
        }
    }

    public boolean isFavoritePrinter(PrinterId printerId) {
        final int printerCount = mFavoritePrinters.size();
        for (int i = 0; i < printerCount; i++) {
            PrinterInfo favoritePritner = mFavoritePrinters.get(i);
            if (favoritePritner.getId().equals(printerId)) {
                return true;
            }
        }
        return false;
    }

    public void forgetFavoritePrinter(PrinterId printerId) {
        List newFavoritePrinters = null;

        // Remove the printer from the favorites.
        final int favoritePrinterCount = mFavoritePrinters.size();
        for (int i = 0; i < favoritePrinterCount; i++) {
            PrinterInfo favoritePrinter = mFavoritePrinters.get(i);
            if (favoritePrinter.getId().equals(printerId)) {
                newFavoritePrinters = new ArrayList();
                newFavoritePrinters.addAll(mPrinters);
                newFavoritePrinters.remove(i);
                break;
            }
        }

        // If we removed a favorite printer, we have work to do.
        if (newFavoritePrinters != null) {
            // Remove the printer from history and persist the latter.
            mPersistenceManager.removeHistoricalPrinterAndWritePrinterHistory(printerId);

            // Recompute and deliver the printers.
            updatePrinters(mDiscoverySession.getPrinters(), newFavoritePrinters);
        }
    }

    private final class PersistenceManager {
        private static final String PERSIST_FILE_NAME = "printer_history.xml";

        private static final String TAG_PRINTERS = "printers";

        private static final String TAG_PRINTER = "printer";
        private static final String TAG_PRINTER_ID = "printerId";

        private static final String ATTR_LOCAL_ID = "localId";
        private static final String ATTR_SERVICE_NAME = "serviceName";

        private static final String ATTR_NAME = "name";
        private static final String ATTR_DESCRIPTION = "description";
        private static final String ATTR_STATUS = "status";

        private final AtomicFile mStatePersistFile;

        private List mHistoricalPrinters = new ArrayList();

        private boolean mReadHistoryCompleted;
        private boolean mReadHistoryInProgress;

        private ReadTask mReadTask;

        private volatile long mLastReadHistoryTimestamp;

        private PersistenceManager(Context context) {
            mStatePersistFile = new AtomicFile(new File(context.getFilesDir(),
                    PERSIST_FILE_NAME));
        }

        public boolean isReadHistoryInProgress() {
            return mReadHistoryInProgress;
        }

        public boolean isReadHistoryCompleted() {
            return mReadHistoryCompleted;
        }

        public boolean stopReadPrinterHistory() {
            final boolean cancelled = mReadTask.cancel(true);
            mReadTask = null;
            return cancelled;
        }

        public void readPrinterHistory() {
            if (DEBUG) {
                Log.i(LOG_TAG, "read history started "
                        + PrintersProvider.this.hashCode());
            }
            mReadHistoryInProgress = true;
            mReadTask = new ReadTask();
            mReadTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
        }

        @SuppressWarnings("unchecked")
        public void addPrinterAndWritePrinterHistory(PrinterInfo printer) {
            if (mHistoricalPrinters.size() >= MAX_HISTORY_LENGTH) {
                mHistoricalPrinters.remove(0);
            }
            mHistoricalPrinters.add(printer);
            new WriteTask().executeOnExecutor(AsyncTask.SERIAL_EXECUTOR,
                    new ArrayList(mHistoricalPrinters));
        }

        @SuppressWarnings("unchecked")
        public void removeHistoricalPrinterAndWritePrinterHistory(PrinterId printerId) {
            boolean writeHistory = false;
            final int printerCount = mHistoricalPrinters.size();
            for (int i = printerCount - 1; i >= 0; i--) {
                PrinterInfo historicalPrinter = mHistoricalPrinters.get(i);
                if (historicalPrinter.getId().equals(printerId)) {
                    mHistoricalPrinters.remove(i);
                    writeHistory = true;
                }
            }
            if (writeHistory) {
                new WriteTask().executeOnExecutor(AsyncTask.SERIAL_EXECUTOR,
                        new ArrayList(mHistoricalPrinters));
            }
        }

        public boolean isHistoryChanged() {
            return mLastReadHistoryTimestamp != mStatePersistFile.getBaseFile().lastModified();
        }

        private List computeFavoritePrinters(List printers) {
            Map recordMap =
                    new ArrayMap();

            // Recompute the weights.
            float currentWeight = 1.0f;
            final int printerCount = printers.size();
            for (int i = printerCount - 1; i >= 0; i--) {
                PrinterInfo printer = printers.get(i);
                // Aggregate weight for the same printer
                PrinterRecord record = recordMap.get(printer.getId());
                if (record == null) {
                    record = new PrinterRecord(printer);
                    recordMap.put(printer.getId(), record);
                }
                record.weight += currentWeight;
                currentWeight *= WEIGHT_DECAY_COEFFICIENT;
            }

            // Soft the favorite printers.
            List favoriteRecords = new ArrayList(
                    recordMap.values());
            Collections.sort(favoriteRecords);

            // Write the favorites to the output.
            final int favoriteCount = Math.min(favoriteRecords.size(),
                    MAX_FAVORITE_PRINTER_COUNT);
            List favoritePrinters = new ArrayList(favoriteCount);
            for (int i = 0; i < favoriteCount; i++) {
                PrinterInfo printer = favoriteRecords.get(i).printer;
                favoritePrinters.add(printer);
            }

            return favoritePrinters;
        }

        private final class PrinterRecord implements Comparable {
            public final PrinterInfo printer;
            public float weight;

            public PrinterRecord(PrinterInfo printer) {
                this.printer = printer;
            }

            @Override
            public int compareTo(PrinterRecord another) {
                return Float.floatToIntBits(another.weight) - Float.floatToIntBits(weight);
            }
        }

        private final class ReadTask extends AsyncTask> {
            @Override
            protected List doInBackground(Void... args) {
                return doReadPrinterHistory();
            }

            @Override
            protected void onPostExecute(List printers) {
                if (DEBUG) {
                    Log.i(LOG_TAG, "read history completed "
                            + PrintersProvider.this.hashCode()+"-printers.size()="+printers.size());
                }

                for(int i = 0,length = printers.size();i services = printManager
                        .getEnabledPrintServices();

                Set enabledComponents = new ArraySet();
                final int installedServiceCount = services.size();
                for (int i = 0; i < installedServiceCount; i++) {
                    ServiceInfo serviceInfo = services.get(i).getResolveInfo().serviceInfo;
                    ComponentName componentName = new ComponentName(
                            serviceInfo.packageName, serviceInfo.name);
                    enabledComponents.add(componentName);
                }

                final int printerCount = printers.size();
                for (int i = printerCount - 1; i >= 0; i--) {
                    ComponentName printerServiceName = printers.get(i).getId().getServiceName();
                    if (!enabledComponents.contains(printerServiceName)) {
                        printers.remove(i);
                    }
                }

                // Store the filtered list.
                mHistoricalPrinters = printers;

                // Compute the favorite printers.
                mFavoritePrinters.clear();
                mFavoritePrinters.addAll(computeFavoritePrinters(mHistoricalPrinters));

                mReadHistoryInProgress = false;
                mReadHistoryCompleted = true;

                // Deliver the printers.
                updatePrinters(mDiscoverySession.getPrinters(), mHistoricalPrinters);

                // Loading the available printers if needed.
                loadInternal();

                // We are done.
                mReadTask = null;
            }

            private List doReadPrinterHistory() {
                FileInputStream in = null;
                try {
                    in = mStatePersistFile.openRead();
                } catch (FileNotFoundException fnfe) {
                    if (DEBUG) {
                        Log.i(LOG_TAG, "No existing printer history "
                                + PrintersProvider.this.hashCode());
                    }
                    return new ArrayList();
                }
                try {
                    List printers = new ArrayList();
                    XmlPullParser parser = Xml.newPullParser();
                    parser.setInput(in, null);
                    parseState(parser, printers);
                    // Take a note which version of the history was read.
                    mLastReadHistoryTimestamp = mStatePersistFile.getBaseFile().lastModified();
                    return printers;
                } catch (IllegalStateException ise) {
                    Slog.w(LOG_TAG, "Failed parsing ", ise);
                } catch (NullPointerException npe) {
                    Slog.w(LOG_TAG, "Failed parsing ", npe);
                } catch (NumberFormatException nfe) {
                    Slog.w(LOG_TAG, "Failed parsing ", nfe);
                } catch (XmlPullParserException xppe) {
                    Slog.w(LOG_TAG, "Failed parsing ", xppe);
                } catch (IOException ioe) {
                    Slog.w(LOG_TAG, "Failed parsing ", ioe);
                } catch (IndexOutOfBoundsException iobe) {
                    Slog.w(LOG_TAG, "Failed parsing ", iobe);
                } finally {
                    IoUtils.closeQuietly(in);
                }

                return Collections.emptyList();
            }

            private void parseState(XmlPullParser parser, List outPrinters)
                    throws IOException, XmlPullParserException {
                parser.next();
                skipEmptyTextTags(parser);
                expect(parser, XmlPullParser.START_TAG, TAG_PRINTERS);
                parser.next();

                while (parsePrinter(parser, outPrinters)) {
                    // Be nice and respond to cancellation
                    if (isCancelled()) {
                        return;
                    }
                    parser.next();
                }

                skipEmptyTextTags(parser);
                expect(parser, XmlPullParser.END_TAG, TAG_PRINTERS);
            }

            private boolean parsePrinter(XmlPullParser parser, List outPrinters)
                    throws IOException, XmlPullParserException {
                skipEmptyTextTags(parser);
                if (!accept(parser, XmlPullParser.START_TAG, TAG_PRINTER)) {
                    return false;
                }

                String name = parser.getAttributeValue(null, ATTR_NAME);
                String description = parser.getAttributeValue(null, ATTR_DESCRIPTION);
                final int status = Integer.parseInt(parser.getAttributeValue(null, ATTR_STATUS));

                parser.next();

                skipEmptyTextTags(parser);
                expect(parser, XmlPullParser.START_TAG, TAG_PRINTER_ID);
                String localId = parser.getAttributeValue(null, ATTR_LOCAL_ID);
                ComponentName service = ComponentName.unflattenFromString(parser.getAttributeValue(
                        null, ATTR_SERVICE_NAME));
                PrinterId printerId =  new PrinterId(service, localId);
                parser.next();
                skipEmptyTextTags(parser);
                expect(parser, XmlPullParser.END_TAG, TAG_PRINTER_ID);
                parser.next();

                PrinterInfo.Builder builder = new PrinterInfo.Builder(printerId, name, status);
                builder.setDescription(description);
                PrinterInfo printer = builder.build();

                outPrinters.add(printer);

                if (DEBUG) {
                    Log.i(LOG_TAG, "[RESTORED] " + printer);
                }

                skipEmptyTextTags(parser);
                expect(parser, XmlPullParser.END_TAG, TAG_PRINTER);

                return true;
            }

            private void expect(XmlPullParser parser, int type, String tag)
                    throws IOException, XmlPullParserException {
                if (!accept(parser, type, tag)) {
                    throw new XmlPullParserException("Exepected event: " + type
                            + " and tag: " + tag + " but got event: " + parser.getEventType()
                            + " and tag:" + parser.getName());
                }
            }

            private void skipEmptyTextTags(XmlPullParser parser)
                    throws IOException, XmlPullParserException {
                while (accept(parser, XmlPullParser.TEXT, null)
                        && "\n".equals(parser.getText())) {
                    parser.next();
                }
            }

            private boolean accept(XmlPullParser parser, int type, String tag)
                    throws IOException, XmlPullParserException {
                if (parser.getEventType() != type) {
                    return false;
                }
                if (tag != null) {
                    if (!tag.equals(parser.getName())) {
                        return false;
                    }
                } else if (parser.getName() != null) {
                    return false;
                }
                return true;
            }
        };

        private final class WriteTask extends AsyncTask, Void, Void> {
            @Override
            protected Void doInBackground(List... printers) {
                doWritePrinterHistory(printers[0]);
                return null;
            }

            private void doWritePrinterHistory(List printers) {
                FileOutputStream out = null;
                try {
                    out = mStatePersistFile.startWrite();

                    XmlSerializer serializer = new FastXmlSerializer();
                    serializer.setOutput(out, "utf-8");
                    serializer.startDocument(null, true);
                    serializer.startTag(null, TAG_PRINTERS);

                    final int printerCount = printers.size();
                    for (int i = 0; i < printerCount; i++) {
                        PrinterInfo printer = printers.get(i);

                        serializer.startTag(null, TAG_PRINTER);

                        serializer.attribute(null, ATTR_NAME, printer.getName());
                        // Historical printers are always stored as unavailable.
                        serializer.attribute(null, ATTR_STATUS, String.valueOf(
                                PrinterInfo.STATUS_UNAVAILABLE));
                        String description = printer.getDescription();
                        if (description != null) {
                            serializer.attribute(null, ATTR_DESCRIPTION, description);
                        }

                        PrinterId printerId = printer.getId();
                        serializer.startTag(null, TAG_PRINTER_ID);
                        serializer.attribute(null, ATTR_LOCAL_ID, printerId.getLocalId());
                        serializer.attribute(null, ATTR_SERVICE_NAME, printerId.getServiceName()
                                .flattenToString());
                        serializer.endTag(null, TAG_PRINTER_ID);

                        serializer.endTag(null, TAG_PRINTER);

                        if (DEBUG) {
                            Log.i(LOG_TAG, "[PERSISTED] " + printer);
                        }
                    }

                    serializer.endTag(null, TAG_PRINTERS);
                    serializer.endDocument();
                    mStatePersistFile.finishWrite(out);

                    if (DEBUG) {
                        Log.i(LOG_TAG, "[PERSIST END]");
                    }
                } catch (IOException ioe) {
                    Slog.w(LOG_TAG, "Failed to write printer history, restoring backup.", ioe);
                    mStatePersistFile.failWrite(out);
                } finally {
                    IoUtils.closeQuietly(out);
                }
            }
        };
    }
}

在PrinterLoader中找到打印机,我们回调到PrintManger.print方法中我们GetPrintersCallBack自定义的回调中,选择我们自己的打印机,接下来我们将打印机信息和打印任务信息传递到PrintJobService中,action自己定义的,在frameworks\base\packages\PrintSpooler\AndroidManifest.xml中注册


```
在这里插入代码片

    
```
接下来我们来看看PrintJobService.java的实现


```
在这里插入代码片
	/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.printspooler;


import android.app.Service;

import android.app.LoaderManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.Loader;
import android.content.ServiceConnection;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.database.DataSetObserver;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.print.ILayoutResultCallback;
import android.print.IPrintDocumentAdapter;
import android.print.IPrintDocumentAdapterObserver;
import android.print.IWriteResultCallback;
import android.print.PageRange;
import android.print.PrintAttributes;
import android.print.PrintAttributes.Margins;
import android.print.PrintAttributes.MediaSize;
import android.print.PrintAttributes.Resolution;
import android.print.PrintDocumentAdapter;
import android.print.PrintDocumentInfo;
import android.print.PrintJobId;
import android.print.PrintJobInfo;
import android.print.PrintManager;
import android.print.PrinterCapabilitiesInfo;
import android.print.PrinterId;
import android.print.PrinterInfo;
import android.printservice.PrintService;
import android.printservice.PrintServiceInfo;
import android.provider.DocumentsContract;
import android.text.Editable;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.Log;

import com.android.printspooler.MediaSizeUtils.MediaSizeComparator;

import libcore.io.IoUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Activity for configuring a print job.
 */
public class PrintJobService extends Service {

    private static final String LOG_TAG = "PrintJobService";

    private static final boolean DEBUG = true;

    public static final String INTENT_EXTRA_PRINTER_ID = "INTENT_EXTRA_PRINTER_ID";

    private static final int LOADER_ID_PRINTERS_LOADER = 1;

    private static final int ORIENTATION_PORTRAIT = 0;
    private static final int ORIENTATION_LANDSCAPE = 1;

    private static final int DEST_ADAPTER_MAX_ITEM_COUNT = 9;

    private static final int DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF = Integer.MAX_VALUE;
    private static final int DEST_ADAPTER_ITEM_ID_ALL_PRINTERS = Integer.MAX_VALUE - 1;

    private static final int ACTIVITY_REQUEST_CREATE_FILE = 1;
    private static final int ACTIVITY_REQUEST_SELECT_PRINTER = 2;
    private static final int ACTIVITY_POPULATE_ADVANCED_PRINT_OPTIONS = 3;

    private static final int CONTROLLER_STATE_FINISHED = 1;
    private static final int CONTROLLER_STATE_FAILED = 2;
    private static final int CONTROLLER_STATE_CANCELLED = 3;
    private static final int CONTROLLER_STATE_INITIALIZED = 4;
    private static final int CONTROLLER_STATE_STARTED = 5;
    private static final int CONTROLLER_STATE_LAYOUT_STARTED = 6;
    private static final int CONTROLLER_STATE_LAYOUT_COMPLETED = 7;
    private static final int CONTROLLER_STATE_WRITE_STARTED = 8;
    private static final int CONTROLLER_STATE_WRITE_COMPLETED = 9;

    private static final int EDITOR_STATE_INITIALIZED = 1;
    private static final int EDITOR_STATE_CONFIRMED_PRINT = 2;
    private static final int EDITOR_STATE_CANCELLED = 3;

    private static final int MIN_COPIES = 1;
    private static final String MIN_COPIES_STRING = String.valueOf(MIN_COPIES);

    private static final Pattern PATTERN_DIGITS = Pattern.compile("[\\d]+");

    private static final Pattern PATTERN_ESCAPE_SPECIAL_CHARS = Pattern.compile(
            "(?=[]\\[+&|!(){}^\"~*?:\\\\])");

    private static final Pattern PATTERN_PAGE_RANGE = Pattern.compile(
            "[\\s]*[0-9]*[\\s]*[\\-]?[\\s]*[0-9]*[\\s]*?(([,])"
            + "[\\s]*[0-9]*[\\s]*[\\-]?[\\s]*[0-9]*[\\s]*|[\\s]*)+");

    public static final PageRange[] ALL_PAGES_ARRAY = new PageRange[] {PageRange.ALL_PAGES};

    private final PrintAttributes mOldPrintAttributes = new PrintAttributes.Builder().build();
    private final PrintAttributes mCurrPrintAttributes = new PrintAttributes.Builder().build();

    private final DeathRecipient mDeathRecipient = new DeathRecipient() {
        @Override
        public void binderDied() {
            PrintJobService.this.finish();
        }
    };

    private Editor mEditor;
    private Document mDocument;
    private PrintController mController;

    private PrintJobId mPrintJobId;

    private IBinder mIPrintDocumentAdapter;

    private PrintSpoolerProvider mSpoolerProvider;

    private String mCallingPackageName;

	private boolean isPrint = false;

	private PrinterInfo mCurrentPrinter;

	public void finish(){
		super.stopSelf();
	}

	@Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return null;
    }

	/**
     * 服务启动的时候调用
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
    	Log.e(LOG_TAG,"onStartCommand");
        // TODO Auto-generated method stub
        Bundle extras = intent.getExtras();

        PrintJobInfo printJob = extras.getParcelable(PrintManager.EXTRA_PRINT_JOB);
		
		mCurrentPrinter = extras.getParcelable("ready_printer");
		
        if (printJob == null) {
            throw new IllegalArgumentException("printJob cannot be null");
        }

        mPrintJobId = printJob.getId();
        mIPrintDocumentAdapter = extras.getBinder(PrintManager.EXTRA_PRINT_DOCUMENT_ADAPTER);
        if (mIPrintDocumentAdapter == null) {
            throw new IllegalArgumentException("PrintDocumentAdapter cannot be null");
        }

        try {
            IPrintDocumentAdapter.Stub.asInterface(mIPrintDocumentAdapter)
                    .setObserver(new PrintDocumentAdapterObserver(this));
        } catch (RemoteException re) {
            finish();
        }

        PrintAttributes attributes = printJob.getAttributes();
        if (attributes != null) {
            mCurrPrintAttributes.copyFrom(attributes);
        }

        mCallingPackageName = extras.getString(DocumentsContract.EXTRA_PACKAGE_NAME);

        try {
            mIPrintDocumentAdapter.linkToDeath(mDeathRecipient, 0);
        } catch (RemoteException re) {
            finish();
        }

        mDocument = new Document();
        mEditor = new Editor();

        mSpoolerProvider = new PrintSpoolerProvider(this,
                new Runnable() {
            @Override
            public void run() {
                // We got the spooler so unleash the UI.
                mController = new PrintController(new RemotePrintDocumentAdapter(
                        IPrintDocumentAdapter.Stub.asInterface(mIPrintDocumentAdapter),
                        mSpoolerProvider.getSpooler().generateFileForPrintJob(mPrintJobId)));
                mController.initialize();

                mEditor.initialize();
                mEditor.postCreate();
            }
        });
        return super.onStartCommand(intent, flags, startId);
    }
	

    @Override
    public void onDestroy() {
    		Log.e(LOG_TAG,"onDestroy");
           if (mController != null && mController.hasStarted()) {
               mController.finish();
           }
           if (mEditor != null && mEditor.isPrintConfirmed()
                   && mController != null && mController.isFinished()) {
                   Log.e(LOG_TAG,"setPrintJobState--STATE_QUEUED");
                   mSpoolerProvider.getSpooler().setPrintJobState(mPrintJobId,
                           PrintJobInfo.STATE_QUEUED, null);
           } else {
               mSpoolerProvider.getSpooler().setPrintJobState(mPrintJobId,
                       PrintJobInfo.STATE_CANCELED, null);
           }
           mIPrintDocumentAdapter.unlinkToDeath(mDeathRecipient, 0);
           mSpoolerProvider.destroy();
    }

    private boolean printAttributesChanged() {
        return !mOldPrintAttributes.equals(mCurrPrintAttributes);
    }

    private class PrintController {
        private final AtomicInteger mRequestCounter = new AtomicInteger();

        private final RemotePrintDocumentAdapter mRemotePrintAdapter;

        private final Bundle mMetadata;

        private final ControllerHandler mHandler;

        private final LayoutResultCallback mLayoutResultCallback;

        private final WriteResultCallback mWriteResultCallback;

        private int mControllerState = CONTROLLER_STATE_INITIALIZED;

        private boolean mHasStarted;

        private PageRange[] mRequestedPages;

        public PrintController(RemotePrintDocumentAdapter adapter) {
            mRemotePrintAdapter = adapter;
            mMetadata = new Bundle();
            mHandler = new ControllerHandler(getMainLooper());
            mLayoutResultCallback = new LayoutResultCallback(mHandler);
            mWriteResultCallback = new WriteResultCallback(mHandler);
        }

        public void initialize() {
            mHasStarted = false;
            mControllerState = CONTROLLER_STATE_INITIALIZED;
        }

        public void cancel() {
            if (isWorking()) {
                mRemotePrintAdapter.cancel();
            }
            mControllerState = CONTROLLER_STATE_CANCELLED;
        }

        public boolean isCancelled() {
            return (mControllerState == CONTROLLER_STATE_CANCELLED);
        }

        public boolean isFinished() {
            return (mControllerState == CONTROLLER_STATE_FINISHED);
        }

        public boolean hasStarted() {
            return mHasStarted;
        }

        public boolean hasPerformedLayout() {
            return mControllerState >= CONTROLLER_STATE_LAYOUT_COMPLETED;
        }

        public boolean isPerformingLayout() {
            return mControllerState == CONTROLLER_STATE_LAYOUT_STARTED;
        }

        public boolean isWorking() {
            return mControllerState == CONTROLLER_STATE_LAYOUT_STARTED
                    || mControllerState == CONTROLLER_STATE_WRITE_STARTED;
        }

        public void start() {
        	Log.e(LOG_TAG, "PrintController-start");
            mControllerState = CONTROLLER_STATE_STARTED;
            mHasStarted = true;
            mRemotePrintAdapter.start();
        }

        public void update() {
            if (!mController.hasStarted()) {
                mController.start();
            }

            // If the print attributes are the same and we are performing
            // a layout, then we have to wait for it to completed which will
            // trigger writing of the necessary pages.
            final boolean printAttributesChanged = printAttributesChanged();
            if (!printAttributesChanged && isPerformingLayout()) {
                return;
            }

            // If print is confirmed we always do a layout since the previous
            // ones were for preview and this one is for printing.
            if (!printAttributesChanged && !mEditor.isPrintConfirmed()) {
                if (mDocument.info == null) {
                    // We are waiting for the result of a layout, so do nothing.
                    return;
                }
                // If the attributes didn't change and we have done a layout, then
                // we do not do a layout but may have to ask the app to write some
                // pages. Hence, pretend layout completed and nothing changed, so
                // we handle writing as usual.
                handleOnLayoutFinished(mDocument.info, false, mRequestCounter.get());
            } else {
                mSpoolerProvider.getSpooler().setPrintJobAttributesNoPersistence(
                        mPrintJobId, mCurrPrintAttributes);

                mMetadata.putBoolean(PrintDocumentAdapter.EXTRA_PRINT_PREVIEW,
                        !mEditor.isPrintConfirmed());

                mControllerState = CONTROLLER_STATE_LAYOUT_STARTED;

				Log.e(LOG_TAG, "PrintController-CONTROLLER_STATE_LAYOUT_STARTED");

                mRemotePrintAdapter.layout(mOldPrintAttributes, mCurrPrintAttributes,
                        mLayoutResultCallback, mMetadata, mRequestCounter.incrementAndGet());

                mOldPrintAttributes.copyFrom(mCurrPrintAttributes);
            }
        }

        public void finish() {
            mControllerState = CONTROLLER_STATE_FINISHED;
            mRemotePrintAdapter.finish();
        }

        private void handleOnLayoutFinished(PrintDocumentInfo info,
                boolean layoutChanged, int sequence) {
            if (mRequestCounter.get() != sequence) {
                return;
            }

            if (isCancelled()) {
                if (mEditor.isDone()) {
                    PrintJobService.this.finish();
                }
                return;
            }
			Log.e(LOG_TAG, "PrintController-CONTROLLER_STATE_LAYOUT_COMPLETED");
            mControllerState = CONTROLLER_STATE_LAYOUT_COMPLETED;
			

            // For layout purposes we care only whether the type or the page
            // count changed. We still do not have the size since we did not
            // call write. We use "layoutChanged" set by the application to
            // know whether something else changed about the document.
            final boolean infoChanged = !equalsIgnoreSize(info, mDocument.info);
			Log.e(LOG_TAG, "PrintController-infoChanged="+infoChanged+",layoutChanged="+layoutChanged);
            // If the info changed, we update the document and the print job.
            if (infoChanged) {
                mDocument.info = info;
                // Set the info.
                mSpoolerProvider.getSpooler().setPrintJobPrintDocumentInfoNoPersistence(
                        mPrintJobId, info);
            }

            // If the document info or the layout changed, then
            // drop the pages since we have to fetch them again.
            if (infoChanged || layoutChanged) {
                mDocument.pages = null;
                mSpoolerProvider.getSpooler().setPrintJobPagesNoPersistence(
                        mPrintJobId, null);
            }

            // No pages means that the user selected an invalid range while we
            // were doing a layout or the layout returned a document info for
            // which the selected range is invalid. In such a case we do not
            // write anything and wait for the user to fix the range which will
            // trigger an update.
            mRequestedPages = mEditor.getRequestedPages();
            if (mRequestedPages == null || mRequestedPages.length == 0) {
				Log.e(LOG_TAG, "PrintController-mRequestedPages == null || mRequestedPages.length == 0");
                if (mEditor.isDone()) {
                    PrintJobService.this.finish();
                }
                return;
            } else {
                // If print is not confirmed we just ask for the first of the
                // selected pages to emulate a behavior that shows preview
                // increasing the chances that apps will implement the APIs
                // correctly.
                if (!mEditor.isPrintConfirmed()) {
                    if (ALL_PAGES_ARRAY.equals(mRequestedPages)) {
                        mRequestedPages = new PageRange[] {new PageRange(0, 0)};
                    } else {
                        final int firstPage = mRequestedPages[0].getStart();
                        mRequestedPages = new PageRange[] {new PageRange(firstPage, firstPage)};
                    }
                }
            }

            // If the info and the layout did not change and we already have
            // the requested pages, then nothing else to do.
            if (!infoChanged && !layoutChanged
                    && PageRangeUtils.contains(mDocument.pages, mRequestedPages)) {
                // Nothing interesting changed and we have all requested pages.
                // Then update the print jobs's pages as we will not do a write
                // and we usually update the pages in the write complete callback.
                Log.e(LOG_TAG, "PrintController-PageRangeUtils.contains(mDocument.pages, mRequestedPages)");
                updatePrintJobPages(mDocument.pages, mRequestedPages);
                if (mEditor.isDone()) {
                    requestFinish();
                }
                return;
            }

			Log.e(LOG_TAG, "PrintController-CONTROLLER_STATE_WRITE_STARTED");

            // Request a write of the pages of interest.
            mControllerState = CONTROLLER_STATE_WRITE_STARTED;
            mRemotePrintAdapter.write(mRequestedPages, mWriteResultCallback,
                    mRequestCounter.incrementAndGet());
        }

        private void handleOnLayoutFailed(final CharSequence error, int sequence) {
            if (mRequestCounter.get() != sequence) {
                return;
            }
			Log.e(LOG_TAG, "PrintController-CONTROLLER_STATE_FAILED");
            mControllerState = CONTROLLER_STATE_FAILED;
        }

        private void handleOnWriteFinished(PageRange[] pages, int sequence) {
            if (mRequestCounter.get() != sequence) {
                return;
            }

            if (isCancelled()) {
                if (mEditor.isDone()) {
                    PrintJobService.this.finish();
                }
                return;
            }
			Log.e(LOG_TAG, "PrintController-CONTROLLER_STATE_WRITE_COMPLETED");
            mControllerState = CONTROLLER_STATE_WRITE_COMPLETED;

            // Update the document size.
            File file = mSpoolerProvider.getSpooler()
                    .generateFileForPrintJob(mPrintJobId);
            mDocument.info.setDataSize(file.length());

            // Update the print job with the updated info.
            mSpoolerProvider.getSpooler().setPrintJobPrintDocumentInfoNoPersistence(
                    mPrintJobId, mDocument.info);

            // Update which pages we have fetched.
            mDocument.pages = PageRangeUtils.normalize(pages);

            if (DEBUG) {
                Log.i(LOG_TAG, "Requested: " + Arrays.toString(mRequestedPages)
                        + " and got: " + Arrays.toString(mDocument.pages));
            }

            updatePrintJobPages(mDocument.pages, mRequestedPages);

            if (mEditor.isDone()) {
                requestFinish();
            }
        }

        private void updatePrintJobPages(PageRange[] writtenPages, PageRange[] requestedPages) {
            // Adjust the print job pages based on what was requested and written.
            // The cases are ordered in the most expected to the least expected.
            Log.e(LOG_TAG, "PrintController-updatePrintJobPages");
            if (Arrays.equals(writtenPages, requestedPages)) {
                // We got a document with exactly the pages we wanted. Hence,
                // the printer has to print all pages in the data.
                mSpoolerProvider.getSpooler().setPrintJobPagesNoPersistence(mPrintJobId,
                        ALL_PAGES_ARRAY);
            } else if (Arrays.equals(writtenPages, ALL_PAGES_ARRAY)) {
                // We requested specific pages but got all of them. Hence,
                // the printer has to print only the requested pages.
                mSpoolerProvider.getSpooler().setPrintJobPagesNoPersistence(mPrintJobId,
                        requestedPages);
            } else if (PageRangeUtils.contains(writtenPages, requestedPages)) {
                // We requested specific pages and got more but not all pages.
                // Hence, we have to offset appropriately the printed pages to
                // be based off the start of the written ones instead of zero.
                // The written pages are always non-null and not empty.
                final int offset = -writtenPages[0].getStart();
                PageRange[] offsetPages = Arrays.copyOf(requestedPages, requestedPages.length);
                PageRangeUtils.offset(offsetPages, offset);
                mSpoolerProvider.getSpooler().setPrintJobPagesNoPersistence(mPrintJobId,
                        offsetPages);
            } else if (Arrays.equals(requestedPages, ALL_PAGES_ARRAY)
                    && writtenPages.length == 1 && writtenPages[0].getStart() == 0
                    && writtenPages[0].getEnd() == mDocument.info.getPageCount() - 1) {
                // We requested all pages via the special constant and got all
                // of them as an explicit enumeration. Hence, the printer has
                // to print only the requested pages.
                mSpoolerProvider.getSpooler().setPrintJobPagesNoPersistence(mPrintJobId,
                        writtenPages);
            } else {
                // We did not get the pages we requested, then the application
                // misbehaves, so we fail quickly.
                mControllerState = CONTROLLER_STATE_FAILED;
                Log.e(LOG_TAG, "Received invalid pages from the app");
            }
        }

        private void requestFinish() {
        	Log.e(LOG_TAG, "PrintController-requestFinish");
			PrintJobService.this.finish();
        }

        private void handleOnWriteFailed(final CharSequence error, int sequence) {
            if (mRequestCounter.get() != sequence) {
                return;
            }
			Log.e(LOG_TAG, "PrintController-CONTROLLER_STATE_FAILED");
            mControllerState = CONTROLLER_STATE_FAILED;
        }

        private boolean equalsIgnoreSize(PrintDocumentInfo lhs, PrintDocumentInfo rhs) {
            if (lhs == rhs) {
                return true;
            }
            if (lhs == null) {
                if (rhs != null) {
                    return false;
                }
            } else {
                if (rhs == null) {
                    return false;
                }
                if (lhs.getContentType() != rhs.getContentType()
                        || lhs.getPageCount() != rhs.getPageCount()) {
                    return false;
                }
            }
            return true;
        }

        private final class ControllerHandler extends Handler {
            public static final int MSG_ON_LAYOUT_FINISHED = 1;
            public static final int MSG_ON_LAYOUT_FAILED = 2;
            public static final int MSG_ON_WRITE_FINISHED = 3;
            public static final int MSG_ON_WRITE_FAILED = 4;

            public ControllerHandler(Looper looper) {
                super(looper, null, false);
            }

            @Override
            public void handleMessage(Message message) {
                switch (message.what) {
                    case MSG_ON_LAYOUT_FINISHED: {
                        PrintDocumentInfo info = (PrintDocumentInfo) message.obj;
                        final boolean changed = (message.arg1 == 1);
                        final int sequence = message.arg2;
                        handleOnLayoutFinished(info, changed, sequence);
                    } break;

                    case MSG_ON_LAYOUT_FAILED: {
                        CharSequence error = (CharSequence) message.obj;
                        final int sequence = message.arg1;
                        handleOnLayoutFailed(error, sequence);
                    } break;

                    case MSG_ON_WRITE_FINISHED: {
                        PageRange[] pages = (PageRange[]) message.obj;
                        final int sequence = message.arg1;
                        handleOnWriteFinished(pages, sequence);
                    } break;

                    case MSG_ON_WRITE_FAILED: {
                        CharSequence error = (CharSequence) message.obj;
                        final int sequence = message.arg1;
                        handleOnWriteFailed(error, sequence);
                    } break;
                }
            }
        }
    }

    private static final class LayoutResultCallback extends ILayoutResultCallback.Stub {
        private final WeakReference mWeakHandler;

        public LayoutResultCallback(PrintController.ControllerHandler handler) {
            mWeakHandler = new WeakReference(handler);
        }

        @Override
        public void onLayoutFinished(PrintDocumentInfo info, boolean changed, int sequence) {
            Handler handler = mWeakHandler.get();
            if (handler != null) {
                handler.obtainMessage(PrintController.ControllerHandler.MSG_ON_LAYOUT_FINISHED,
                        changed ? 1 : 0, sequence, info).sendToTarget();
            }
        }

        @Override
        public void onLayoutFailed(CharSequence error, int sequence) {
            Handler handler = mWeakHandler.get();
            if (handler != null) {
                handler.obtainMessage(PrintController.ControllerHandler.MSG_ON_LAYOUT_FAILED,
                        sequence, 0, error).sendToTarget();
            }
        }
    }

    private static final class WriteResultCallback extends IWriteResultCallback.Stub {
        private final WeakReference mWeakHandler;

        public WriteResultCallback(PrintController.ControllerHandler handler) {
            mWeakHandler = new WeakReference(handler);
        }

        @Override
        public void onWriteFinished(PageRange[] pages, int sequence) {
            Handler handler = mWeakHandler.get();
            if (handler != null) {
                handler.obtainMessage(PrintController.ControllerHandler.MSG_ON_WRITE_FINISHED,
                        sequence, 0, pages).sendToTarget();
            }
        }

        @Override
        public void onWriteFailed(CharSequence error, int sequence) {
            Handler handler = mWeakHandler.get();
            if (handler != null) {
                handler.obtainMessage(PrintController.ControllerHandler.MSG_ON_WRITE_FAILED,
                    sequence, 0, error).sendToTarget();
            }
        }
    }

    private void writePrintJobDataAndFinish(final Uri uri) {
        new AsyncTask() {
            @Override
            protected Void doInBackground(Void... params) {
                InputStream in = null;
                OutputStream out = null;
                try {
                    PrintJobInfo printJob = mSpoolerProvider.getSpooler()
                            .getPrintJobInfo(mPrintJobId, PrintManager.APP_ID_ANY);
                    if (printJob == null) {
                        return null;
                    }
                    File file = mSpoolerProvider.getSpooler()
                            .generateFileForPrintJob(mPrintJobId);
                    in = new FileInputStream(file);
                    out = getContentResolver().openOutputStream(uri);
                    final byte[] buffer = new byte[8192];
                    while (true) {
                        final int readByteCount = in.read(buffer);
                        if (readByteCount < 0) {
                            break;
                        }
                        out.write(buffer, 0, readByteCount);
                    }
                } catch (FileNotFoundException fnfe) {
                    Log.e(LOG_TAG, "Error writing print job data!", fnfe);
                } catch (IOException ioe) {
                    Log.e(LOG_TAG, "Error writing print job data!", ioe);
                } finally {
                    IoUtils.closeQuietly(in);
                    IoUtils.closeQuietly(out);
                }
                return null;
            }

            @Override
            public void onPostExecute(Void result) {
                mEditor.cancel();
                PrintJobService.this.finish();
            }
        }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
    }

    private final class Editor {

        private PrinterId mNextPrinterId;

        private MediaSizeComparator mMediaSizeComparator;

        private void updatePrintAttributes(PrinterCapabilitiesInfo capabilities) {
        	Log.e(LOG_TAG, "updatePrintAttributes");
            PrintAttributes defaults = capabilities.getDefaults();

            // Sort the media sizes based on the current locale.
            List sortedMediaSizes = new ArrayList(
                    capabilities.getMediaSizes());
            Collections.sort(sortedMediaSizes, mMediaSizeComparator);

            // Media size.
            MediaSize currMediaSize = mCurrPrintAttributes.getMediaSize();
            if (currMediaSize == null) {
                mCurrPrintAttributes.setMediaSize(defaults.getMediaSize());
            } else {
                MediaSize currMediaSizePortrait = currMediaSize.asPortrait();
                final int mediaSizeCount = sortedMediaSizes.size();
                for (int i = 0; i < mediaSizeCount; i++) {
                    MediaSize mediaSize = sortedMediaSizes.get(i);
                    if (currMediaSizePortrait.equals(mediaSize.asPortrait())) {
                        mCurrPrintAttributes.setMediaSize(currMediaSize);
                        break;
                    }
                }
            }

            // Color mode.
            final int colorMode = mCurrPrintAttributes.getColorMode();
            if ((capabilities.getColorModes() & colorMode) == 0) {
                mCurrPrintAttributes.setColorMode(colorMode);
            }

            // Resolution
            Resolution resolution = mCurrPrintAttributes.getResolution();
            if (resolution == null || !capabilities.getResolutions().contains(resolution)) {
                mCurrPrintAttributes.setResolution(defaults.getResolution());
            }

            // Margins.
            Margins margins = mCurrPrintAttributes.getMinMargins();
            if (margins == null) {
                mCurrPrintAttributes.setMinMargins(defaults.getMinMargins());
            } else {
                Margins minMargins = capabilities.getMinMargins();
                if (margins.getLeftMils() < minMargins.getLeftMils()
                        || margins.getTopMils() < minMargins.getTopMils()
                        || margins.getRightMils() > minMargins.getRightMils()
                        || margins.getBottomMils() > minMargins.getBottomMils()) {
                    mCurrPrintAttributes.setMinMargins(defaults.getMinMargins());
                }
            }
        }

        
        private int mEditorState;

        private boolean mIgnoreNextDestinationChange;
        private int mOldMediaSizeSelectionIndex;
        private int mOldColorModeSelectionIndex;
        private boolean mIgnoreNextOrientationChange;
        private boolean mIgnoreNextRangeOptionChange;
        private boolean mIgnoreNextCopiesChange;
        private boolean mIgnoreNextRangeChange;
        private boolean mIgnoreNextMediaSizeChange;
        private boolean mIgnoreNextColorChange;

        private boolean mFavoritePrinterSelected;

        public Editor() {
        }

        public void postCreate() {
            // Destination.
            mMediaSizeComparator = new MediaSizeComparator(PrintJobService.this);
			updatePrintAttributes(mCurrentPrinter.getCapabilities());
			mSpoolerProvider.getSpooler().setPrintJobPrinterNoPersistence(mPrintJobId, mCurrentPrinter);
			printButtonClick();
        }

        public void reselectCurrentPrinter() {
        	Log.d(LOG_TAG, "reselectCurrentPrinter");
        }

        public void addCurrentPrinterToHistory() {
           
        }

        private void printButtonClick() {
			Log.e(LOG_TAG, "[ymy]printButtonClick--"+mCurrentPrinter.toString());
			mController.update();
	        if (mCurrentPrinter != null) {
				isPrint = true;
				new Handler().postDelayed(new Runnable(){
				@Override
					public void run() {
						mEditor.confirmPrint();
	                    mController.update();
					} 

				}, 5000);
	        } else {
	            mEditor.cancel();
	            PrintJobService.this.finish();
	        }
        }

        public void initialize() {
            mEditorState = EDITOR_STATE_INITIALIZED;
        }

        public boolean isCancelled() {
            return mEditorState == EDITOR_STATE_CANCELLED;
        }

        public void cancel() {
            mEditorState = EDITOR_STATE_CANCELLED;
            mController.cancel();
        }

        public boolean isDone() {
            return isPrintConfirmed() || isCancelled();
        }

        public boolean isPrintConfirmed() {
            return mEditorState == EDITOR_STATE_CONFIRMED_PRINT;
        }

        public void confirmPrint() {
        	Log.e(LOG_TAG, "confirmPrint");
            addCurrentPrinterToHistory();
            mEditorState = EDITOR_STATE_CONFIRMED_PRINT;
        }

        public PageRange[] getRequestedPages() {
            if (hasErrors()) {
                return null;
            }
            return ALL_PAGES_ARRAY;
        }


        private boolean hasErrors() {
            return false;
        }
    }

    private static final class Document {
        public PrintDocumentInfo info;
        public PageRange[] pages;
    }

    private static final class PageRangeUtils {

        private static final Comparator sComparator = new Comparator() {
            @Override
            public int compare(PageRange lhs, PageRange rhs) {
                return lhs.getStart() - rhs.getStart();
            }
        };

        private PageRangeUtils() {
            throw new UnsupportedOperationException();
        }

        public static boolean contains(PageRange[] ourRanges, PageRange[] otherRanges) {
            if (ourRanges == null || otherRanges == null) {
                return false;
            }

            if (ourRanges.length == 1
                    && PageRange.ALL_PAGES.equals(ourRanges[0])) {
                return true;
            }

            ourRanges = normalize(ourRanges);
            otherRanges = normalize(otherRanges);

            // Note that the code below relies on the ranges being normalized
            // which is they contain monotonically increasing non-intersecting
            // subranges whose start is less that or equal to the end.
            int otherRangeIdx = 0;
            final int ourRangeCount = ourRanges.length;
            final int otherRangeCount = otherRanges.length;
            for (int ourRangeIdx = 0; ourRangeIdx < ourRangeCount; ourRangeIdx++) {
                PageRange ourRange = ourRanges[ourRangeIdx];
                for (; otherRangeIdx < otherRangeCount; otherRangeIdx++) {
                    PageRange otherRange = otherRanges[otherRangeIdx];
                    if (otherRange.getStart() > ourRange.getEnd()) {
                        break;
                    }
                    if (otherRange.getStart() < ourRange.getStart()
                            || otherRange.getEnd() > ourRange.getEnd()) {
                        return false;
                    }
                }
            }
            if (otherRangeIdx < otherRangeCount) {
                return false;
            }
            return true;
        }

        public static PageRange[] normalize(PageRange[] pageRanges) {
            if (pageRanges == null) {
                return null;
            }
            final int oldRangeCount = pageRanges.length;
            if (oldRangeCount <= 1) {
                return pageRanges;
            }
            Arrays.sort(pageRanges, sComparator);
            int newRangeCount = 1;
            for (int i = 0; i < oldRangeCount - 1; i++) {
                newRangeCount++;
                PageRange currentRange = pageRanges[i];
                PageRange nextRange = pageRanges[i + 1];
                if (currentRange.getEnd() + 1 >= nextRange.getStart()) {
                    newRangeCount--;
                    pageRanges[i] = null;
                    pageRanges[i + 1] = new PageRange(currentRange.getStart(),
                            Math.max(currentRange.getEnd(), nextRange.getEnd()));
                }
            }
            if (newRangeCount == oldRangeCount) {
                return pageRanges;
            }
            return Arrays.copyOfRange(pageRanges, oldRangeCount - newRangeCount,
                    oldRangeCount);
        }

        public static void offset(PageRange[] pageRanges, int offset) {
            if (offset == 0) {
                return;
            }
            final int pageRangeCount = pageRanges.length;
            for (int i = 0; i < pageRangeCount; i++) {
                final int start = pageRanges[i].getStart() + offset;
                final int end = pageRanges[i].getEnd() + offset;
                pageRanges[i] = new PageRange(start, end);
            }
        }
    }

    

    private static final class PrintSpoolerProvider implements ServiceConnection {
        private final Context mContext;
        private final Runnable mCallback;

        private PrintSpoolerService mSpooler;

        public PrintSpoolerProvider(Context context, Runnable callback) {
            mContext = context;
            mCallback = callback;
            Intent intent = new Intent(mContext, PrintSpoolerService.class);
            mContext.bindService(intent, this, 0);
        }

        public PrintSpoolerService getSpooler() {
            return mSpooler;
        }

        public void destroy() {
            if (mSpooler != null) {
                mContext.unbindService(this);
            }
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mSpooler = ((PrintSpoolerService.PrintSpooler) service).getService();
            if (mSpooler != null) {
                mCallback.run();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            /* do noting - we are in the same process */
        }
    }

    private static final class PrintDocumentAdapterObserver
            extends IPrintDocumentAdapterObserver.Stub {
        private final WeakReference mWeakService;

        public PrintDocumentAdapterObserver(PrintJobService service) {
            mWeakService = new WeakReference(service);
        }

        @Override
        public void onDestroy() {
            final PrintJobService service = mWeakService.get();
            if (service != null) {
                service.mController.mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        if (service.mController != null) {
                            service.mController.cancel();
                        }
                        if (service.mEditor != null) {
                            service.mEditor.cancel();
                        }
                        service.finish();
                    }
                });
            }
        }
    }
}

```
接下来的实现主要在onStartCommand,这个实现主要是跟之前的onCreate方法大概相同,接下来是mEditor.postCreate方法,这里实现方式跟之前大概相同,只是没有了查找打印机流程,这里就不细说了,大家自己看吧,注意这些流程是在Android4.4实现的,其他版本没试过,应该大概相似吧。

你可能感兴趣的:(android,android后台打印服务)