Search code examples
androidvisual-studioxamarinxamarin.android

Xamarin - "java.lang.RuntimeException: Unable to instantiate service" on PrintService Project


I'm trying to make work the PrintService class in Xamarin PCL Android Project and I make my PrintService to appear in printing options but in the moment of activating it or find printers program stops and Visual Studio shows the next error:

07-11 18:56:32.802 E/AndroidRuntime( 6088): java.lang.RuntimeException: Unable to instantiate service PrintService.Test.TransferPrintService: java.lang.ClassNotFoundException: Didn't find class "PrintService.Test.TransferPrintService" on path: DexPathList[[zip file "/data/app/PrintService.Test-dAvKeFP8zI2zBEHqn5yyqA==/base.apk"],nativeLibraryDirectories=[/data/app/PrintService.Test-dAvKeFP8zI2zBEHqn5yyqA==/lib/x86, /system/fake-libs, /data/app/PrintService.Test-dAvKeFP8zI2zBEHqn5yyqA==/base.apk!/lib/x86, /system/lib, /vendor/lib]]

I have read many posts here, on Google, etc, but haven't find a working solution.

I'm working on Visual Studio 2017 15.7.4, JDK 8, Xamarin 4.10.10.2.

Any idea of solving this error?

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="PrintService.Test" android:installLocation="auto" android:versionCode="1" android:versionName="1.0">
    <uses-sdk android:minSdkVersion="19" />
    <application android:label="@string/app_name" android:allowBackup="false" android:icon="@mipmap/ic_launcher">
        <service android:name=".TransferPrintService" android:permission="android.permission.BIND_PRINT_SERVICE">
            <intent-filter>
                <action android:name="android.printservice.PrintService" />
            </intent-filter>
            <meta-data android:name="android.printservice" android:resource="@xml/transfer_print_service" />
        </service>
    </application>
</manifest>

MainActivity.cs

namespace PrintServiceTest2
{
    using Android.App;
    using Android.Widget;
    using Android.OS;
    using Android.Support.V7.App;
    using Android.Content;

    [Activity(Label = "Test Print Service",
        Theme = "@style/AppTheme", MainLauncher = true,
        Name = "PrintService.Test.MainActivity")]
    public class MainActivity : AppCompatActivity
    {
        public static Toast mainToast;
        public static Intent myIntent;

        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);

            // Set our view from the "main" layout resource
            SetContentView(Resource.Layout.activity_main);

            mainToast = Toast.MakeText(this, "Service", ToastLength.Long);
            myIntent = new Intent("android.printservice.PrintService");

            //var intent = new Intent(this, typeof(TransferPrintService));
            //StartService(intent);
        }

        public static void ShowToast(string text)
        {
            mainToast.SetText(text);
            mainToast.Show();
        }
    }
}

TransferPrintService.cs

namespace PrintServiceTest2
{
    using Android.Print;
    using Android.PrintServices;
    using System.Collections.Generic;

    public class TransferPrintService : PrintService
    {
        private object mFirstFakePrinter;
        private object mSecondFakePrinter;

        public TransferPrintService()
        {
            MainActivity.ShowToast("TransferPrintService");
        }

        public override void OnCreate()
        {
            mFirstFakePrinter = new PrinterInfo.Builder(GeneratePrinterId("Printer 1"),
                "SHGH-21344", PrinterStatus.Idle).Build();
            mSecondFakePrinter = new PrinterInfo.Builder(GeneratePrinterId("Printer 2"),
                    "OPPPP-09434", PrinterStatus.Idle).Build();
            MainActivity.ShowToast("OnCreate");
            base.OnCreate();
        }

        protected override void OnPrintJobQueued(Android.PrintServices.PrintJob printJob)
        {
            printJob.Start();
            printJob.Complete();
        }

        protected override PrinterDiscoverySession OnCreatePrinterDiscoverySession()
        {
            MainActivity.ShowToast("OnCreatePrinterDiscoverySession");
            return new TransferPrinterDiscoverySession(this);
        }

        protected override void OnRequestCancelPrintJob(Android.PrintServices.PrintJob printJob)
        {
        }

        protected override void OnConnected()
        {
            MainActivity.ShowToast("OnConnected");
            base.OnConnected();
        }

        protected override void OnDisconnected()
        {
            MainActivity.ShowToast("OnDisconnected");
            base.OnDisconnected();
        }

        public override void OnDestroy()
        {
            MainActivity.ShowToast("OnDestroy");
            base.OnDestroy();
        }
    }

    public class TransferPrinterDiscoverySession : PrinterDiscoverySession
    {
        private readonly TransferPrintService printService;
        private const string PRINTER = "Transfer Printer";

        public TransferPrinterDiscoverySession(TransferPrintService printService)
        {
            this.printService = printService;
        }

        public override void OnStartPrinterDiscovery(IList<PrinterId> priorityList)
        {
            PrinterId id = printService.GeneratePrinterId(PRINTER);
            PrinterInfo.Builder builder =
                    new PrinterInfo.Builder(id, PRINTER, PrinterStatus.Idle);
            PrinterInfo info = builder.Build();
            List<PrinterInfo> infos = new List<PrinterInfo>
            {
                info
            };
            AddPrinters(infos);
        }

        public override void OnStopPrinterDiscovery()
        {
        }

        public override void OnValidatePrinters(IList<PrinterId> printerIds)
        {
            MainActivity.mainToast.Show();
        }

        public override void OnStartPrinterStateTracking(PrinterId printerId)
        {
            PrinterInfo.Builder builder = new PrinterInfo.Builder(printerId, PRINTER, PrinterStatus.Idle);
            PrinterCapabilitiesInfo.Builder capBuilder =
                    new PrinterCapabilitiesInfo.Builder(printerId);

            capBuilder.AddMediaSize(PrintAttributes.MediaSize.IsoA4, true);
            capBuilder.AddResolution(new PrintAttributes.Resolution(
                    "Default", "Default", 360, 360), true);
            capBuilder.SetColorModes(3, 2);
            capBuilder.SetMinMargins(PrintAttributes.Margins.NoMargins);

            PrinterCapabilitiesInfo caps = capBuilder.Build();
            builder.SetCapabilities(caps);
            PrinterInfo info = builder.Build();
            List<PrinterInfo> infos = new List<PrinterInfo>
            {
                info
            };
            AddPrinters(infos);
        }

        public override void OnStopPrinterStateTracking(PrinterId printerId)
        {
            MainActivity.mainToast.Show();
        }

        public override void OnDestroy()
        {
        }
    }
}

Solution

  • Either match your exported Xamarin.Android JNI/Java Wrapper Class (AWC) to your manual manifest entries by using the Name parameter on the Service attribute:

    [Service(Name = "PrintService.Test.TransferPrintService")]
    public class TransferPrintService : PrintService
    {
    

    Or do not manually edit your manifest and let the Xamarin build do it:

    [Service(Permission = "android.permission.BIND_PRINT_SERVICE")]
    [IntentFilter(new String[] { "android.printservice.PrintService" })]
    public class TransferPrintService : PrintService
    {
       ~~~
    }