jamesmontemagno / InAppBillingPlugin

Cross-platform In App Billing Plugin for .NET
MIT License
651 stars 152 forks source link

NRE on ConnectAsync() call #20

Closed guardrex closed 7 years ago

guardrex commented 7 years ago

@jamesmontemagno ... Thanks for producing this plugin.

I think it's working on iOS. It worked with a hardwired iPhone (sandbox). ~I'm waiting on feedback from a Test Flight tester to see if the published release build works on iOS.~ [EDIT] Yes, works fine on iOS in Test Flight.

On the Android side, I'm getting an NRE on the ConnectAsync() line:

capture3

Idk how to debug it. Can you provide guidance on what to do next?

Bug

Version Number of Plugin: 1.1.0.23-beta Device Tested On: Alcatel A460T (PIXI3_4TF) Simulator Tested On: N/A

Crash Report

java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:644)
    at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.reflect.InvocationTargetException
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:515)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:828)
    ... 2 more
Caused by: android.runtime.JavaProxyThrowable: System.NullReferenceException: Object reference not set to an instance of an object
  at Plugin.InAppBilling.InAppBillingImplementation+InAppBillingServiceConnection.ConnectAsync () [0x00037] in <1ecca8595de5416485857bb67e2ec090>:0 
  at Plugin.InAppBilling.InAppBillingImplementation.ConnectAsync () [0x00000] in <1ecca8595de5416485857bb67e2ec090>:0 
  at OkayKidO.PurchaseRecallAlertsPage+<<-ctor>b__7_1>d.MoveNext () [0x00038] in <9ba60b7d2d04489bb8fa7b24e508a624>:0 
--- End of stack trace from previous location where exception was thrown ---
  at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in <4320a1443daa4b43887537c6c14e77d7>:0 
  at System.Runtime.CompilerServices.AsyncMethodBuilderCore.<ThrowAsync>m__0 (System.Object state) [0x00000] in <4320a1443daa4b43887537c6c14e77d7>:0 
  at Android.App.SyncContext+<Post>c__AnonStorey0.<>m__0 () [0x00000] in <c7e72ae5f1bb4848b22a1b9d2ab62bc1>:0 
  at Java.Lang.Thread+RunnableImplementor.Run () [0x0000b] in <c7e72ae5f1bb4848b22a1b9d2ab62bc1>:0 
  at Java.Lang.IRunnableInvoker.n_Run (System.IntPtr jnienv, System.IntPtr native__this) [0x00009] in <c7e72ae5f1bb4848b22a1b9d2ab62bc1>:0 
  at (wrapper dynamic-method) System.Object:c20c090a-8004-49ef-9b1a-a7360f44a814 (intptr,intptr)
    at mono.java.lang.RunnableImplementor.n_run(Native Method)
    at mono.java.lang.RunnableImplementor.run(RunnableImplementor.java:30)
    at android.os.Handler.handleCallback(Handler.java:808)
    at android.os.Handler.dispatchMessage(Handler.java:103)
    at android.os.Looper.loop(Looper.java:193)
    at android.app.ActivityThread.main(ActivityThread.java:5335)

capture

capture1

MainActivity.cs

using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using Plugin.CurrentActivity;
using Plugin.InAppBilling;
using System;
using System.Globalization;
using Xamarin.Forms;

namespace OkayKidO.Droid
{
    [Activity(Label = "@string/app_name", Icon = "@drawable/icon", Theme = "@style/MainTheme", ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation, ScreenOrientation = ScreenOrientation.Portrait )]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
        public static string Version { get; set; }
        public static string Build { get; set; }

        protected override void OnCreate(Bundle bundle)
        {
            TabLayoutResource = Resource.Layout.Tabbar;
            ToolbarResource = Resource.Layout.Toolbar;

            base.OnCreate(bundle);

            Forms.Init(this, bundle);

            Version = PackageManager.GetPackageInfo(PackageName, 0).VersionName;
            Build = Convert.ToString(PackageManager.GetPackageInfo(PackageName, 0).VersionCode, CultureInfo.InvariantCulture);

            LoadApplication(new App());
        }

        public override void OnLowMemory()
        {
            GC.Collect();
        }

        protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
        {
            base.OnActivityResult(requestCode, resultCode, data);
            InAppBillingImplementation.HandleActivityResult(requestCode, resultCode, data);
        }

        public void OnActivityCreated(Activity activity, Bundle savedInstanceState)
        {
            CrossCurrentActivity.Current.Activity = activity;
        }

        public void OnActivityResumed(Activity activity)
        {
            CrossCurrentActivity.Current.Activity = activity;
        }

        public void OnActivityStarted(Activity activity)
        {
            CrossCurrentActivity.Current.Activity = activity;
        }
    }
}

packages.config

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="ExifLib.PCL" version="1.0.1" targetFramework="monoandroid70" />
  <package id="Newtonsoft.Json" version="9.0.1" targetFramework="monoandroid70" />
  <package id="Plugin.CurrentActivity" version="1.0.1" targetFramework="monoandroid70" />
  <package id="Plugin.InAppBilling" version="1.1.0.23-beta" targetFramework="monoandroid70" />
  <package id="Xamarin.Android.Support.Animated.Vector.Drawable" version="24.2.1" targetFramework="monoandroid70" />
  <package id="Xamarin.Android.Support.Compat" version="24.2.1" targetFramework="monoandroid70" />
  <package id="Xamarin.Android.Support.Core.UI" version="24.2.1" targetFramework="monoandroid70" />
  <package id="Xamarin.Android.Support.Core.Utils" version="24.2.1" targetFramework="monoandroid70" />
  <package id="Xamarin.Android.Support.Design" version="24.2.1" targetFramework="monoandroid70" />
  <package id="Xamarin.Android.Support.Fragment" version="24.2.1" targetFramework="monoandroid70" />
  <package id="Xamarin.Android.Support.Media.Compat" version="24.2.1" targetFramework="monoandroid70" />
  <package id="Xamarin.Android.Support.Transition" version="24.2.1" targetFramework="monoandroid70" />
  <package id="Xamarin.Android.Support.v4" version="24.2.1" targetFramework="monoandroid70" />
  <package id="Xamarin.Android.Support.v7.AppCompat" version="24.2.1" targetFramework="monoandroid70" />
  <package id="Xamarin.Android.Support.v7.CardView" version="24.2.1" targetFramework="monoandroid71" />
  <package id="Xamarin.Android.Support.v7.MediaRouter" version="24.2.1" targetFramework="monoandroid70" />
  <package id="Xamarin.Android.Support.v7.Palette" version="24.2.1" targetFramework="monoandroid70" />
  <package id="Xamarin.Android.Support.v7.RecyclerView" version="24.2.1" targetFramework="monoandroid70" />
  <package id="Xamarin.Android.Support.Vector.Drawable" version="24.2.1" targetFramework="monoandroid70" />
  <package id="Xamarin.Build.Download" version="0.4.2" targetFramework="monoandroid70" />
  <package id="Xamarin.Forms" version="2.3.3.193" targetFramework="monoandroid70" />
</packages>

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="auto" android:versionName="1.1" package="com.seattlecreations.okaykido" android:versionCode="10">
    <uses-sdk android:minSdkVersion="15" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.MICROPHONE" />
    <uses-permission android:name="com.android.vending.BILLING" />
    <application android:label="Okay Kid-O" android:icon="@drawable/icon" />
</manifest>
jamesmontemagno commented 7 years ago

Have you already uploaded a version to google play and setup your in app purchases? Additionally, does your device have google play services on it?

jamesmontemagno commented 7 years ago

I just re-validated yet again and it is working great in all 3 of my apps :)

guardrex commented 7 years ago

Yes, I did upload it to alpha. Yes, this is using my own Android phone (using my personal account setup in Play as a tester and not the account associated with the publish). It does have Google Play Services. Yes, the in-app purchase is setup and it is active.

capture

[EDIT] Let me also add that I only set the in-app purchase up today ... the same day it's failing. In spite of the Play Dev Console saying it's "Active," is there a delay for Google to approve it before the alpha build will run an in-app purchase using it?

guardrex commented 7 years ago

Would it be appropriate for me to take your Droid project source into the Droid project of my solution and try to log exactly where the NRE is thrown?

jamesmontemagno commented 7 years ago

And you have added your self in as Alpha users? I have mine setup as a closed beta with just myself and my test account setup.

You can easily clone this repo and add in both the Android and Vending Library. That helps diagnose the issue.

guardrex commented 7 years ago

Yes, I believe so ...

capture

... then clicking "Edit" ...

capture1

... and I'm getting the correct alpha build as a tester on the phone itself from Play.

You can easily clone this repo and add in both the Android and Vending Library. That helps diagnose the issue.

I guess if nothing else comes to mind that's my next step. There's no approval wait period for this to work, right? I mean when it says "Active," it should be active, correct? Otherwise, I'll try to figure out where the NRE is within the code and report back.

guardrex commented 7 years ago

@jamesmontemagno I thought that I would merely need to ...

  1. Compile the Vending and Abstractions projects
  2. Reference the Vending and Abstractions assemblies in the Android project
  3. Drop the InAppBillingImplementation.cs file from the Plugin.InAppBilling.Android folder (set to "compile") into the Android project

... and it would provide more info on the location of the NRE:

capture

capture1

App compiles and runs. When I hit the crash on connection [on execution of ConnectAsync()], it throws ...

System.NotImplementedException: This functionality is not implemented in the portable version of this assembly. You should reference the NuGet package from your main application project in order to reference the platform-specific implementation.

... but it is ... I thought. I didn't change the package reference in the portable app. The assemblies were added originally via the NuGet Package Manager, and they're still there.

capture3

I haven't implemented the source correctly in the project (apparently). Can you give me a tip?

Here's the full Android crash report with the current setup as :point_up: ...

java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:644)
    at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.reflect.InvocationTargetException
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:515)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:828)
    ... 2 more
Caused by: android.runtime.JavaProxyThrowable: System.NotImplementedException: This functionality is not implemented in the portable version of this assembly.  You should reference the NuGet package from your main application project in order to reference the platform-specific implementation.
  at Plugin.InAppBilling.CrossInAppBilling.get_Current () [0x00012] in <767bbde7fe2e479b838c7ba1e851a212>:0 
  at OkayKidO.PurchaseRecallAlertsPage+<<-ctor>b__7_1>d.MoveNext () [0x00033] in <85423db001bc474fb5d31505a188319f>:0 
--- End of stack trace from previous location where exception was thrown ---
  at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in <e932c69eedd443a3b6e636adcc7a67bb>:0 
  at System.Runtime.CompilerServices.AsyncMethodBuilderCore.<ThrowAsync>m__0 (System.Object state) [0x00000] in <e932c69eedd443a3b6e636adcc7a67bb>:0 
  at Android.App.SyncContext+<Post>c__AnonStorey0.<>m__0 () [0x00000] in <b2d3b0cc07df46cf87ae954e55965136>:0 
  at Java.Lang.Thread+RunnableImplementor.Run () [0x0000b] in <b2d3b0cc07df46cf87ae954e55965136>:0 
  at Java.Lang.IRunnableInvoker.n_Run (System.IntPtr jnienv, System.IntPtr native__this) [0x00009] in <b2d3b0cc07df46cf87ae954e55965136>:0 
  at (wrapper dynamic-method) System.Object:3587df43-6a83-48a5-adbd-6259e35b5b7a (intptr,intptr)
    at mono.java.lang.RunnableImplementor.n_run(Native Method)
    at mono.java.lang.RunnableImplementor.run(RunnableImplementor.java:30)
    at android.os.Handler.handleCallback(Handler.java:808)
    at android.os.Handler.dispatchMessage(Handler.java:103)
    at android.os.Looper.loop(Looper.java:193)
    at android.app.ActivityThread.main(ActivityThread.java:5335)
jamesmontemagno commented 7 years ago

So, remove the NuGets from your app. Manually reference the .csproj for: Abstractions, Android, VendingLibrary, and the Normal InAppBilling PCL.

Add: PCL: Abstractions + InAppBillingPCL Android: Abstractions + Android + VendingLibrary.

jamesmontemagno commented 7 years ago

???? Are you signing with your production key???? you have to.

guardrex commented 7 years ago

Yes, I'm signing the way I always do when I "Distribute" ... Is that not correct?

capture

jamesmontemagno commented 7 years ago

Yeah, just in debug mode you also need to sign it with your keystore. You need to add information into your csproj file like this: https://github.com/Azure-Samples/MyDriving/blob/master/src/MobileApps/MyDriving/MyDriving.Android/MyDriving.Android.csproj#L31

guardrex commented 7 years ago

sign it with your keystore

Ah! That's likely the problem. I think I thought that it was :sparkles: auto-magically :sparkles: added. I re-read that section of your README, and I see five important words there, "you must manually add this."

I'm coming back to this later today, so I'll come back with the result soon. Thanks again for your help.

guardrex commented 7 years ago

@jamesmontemagno

I did get the Debug keystore bits setup, but that didn't affect the result. I dropped the plugin source into the app as you advised.

It's throwing the NRE on this line ...

if (Context.PackageManager.QueryIntentServices(serviceIntent, 0).Any())

Soooo ... no service matches the com.android.vending.billing.InAppBillingService.BIND intent?

Can you interpret the meaning and what I might do next?

jamesmontemagno commented 7 years ago

if there is a null reference.. which is null? COntext? PackageManager? serviceIntent? What is stack trace? put in gist.github.com

guardrex commented 7 years ago

My bad ... been a long day here and brain is a little fried.

There isn't really enough to sweat a Gist ...

System.NullReferenceException: Object reference not set to an instance of an object.
03-01 21:39:42.821 I/mono-stdout( 1760):   at Plugin.InAppBilling.InAppBillingImplementation+InAppBillingServiceConnection.ConnectAsync () [0x00052] in C:\Users\<USER>\Desktop\InAppBillingPlugin-master\src\Plugin.InAppBilling.Android\InAppBillingImplementation.cs:523 

That :point_up: is Debug under emulation. On the phone, it's similar :point_down:

System.NullReferenceException: Object reference not set to an instance of an object
    at
Plugin.InAppBilling.InAppBillingImplementation+InAppBillingServiceConnection.ConnectAsync() [0x00004f] in
<57d71d104348443fac3b391cca4a68ac>:0

It's the Context which is null.

I see that coming in at ...

Activity Context => CrossCurrentActivity.Current.Activity;

[EDIT] The Plugin.CurrentActivity package is in two spots now: It's in my Android project references and it's also in the Plugin.InAppBilling.Android project that I added to the solution.

[EDIT] I'll dig into Plugin.CurrentActivity tomorrow.

jamesmontemagno commented 7 years ago

You absolutely can not use in app billing on emulators, it doesn't work you have to use it on a device.

Did you delete the MainApplication class that gets added in? That is what sets it.

guardrex commented 7 years ago

I thought that was the case. The exception is occurring on the phone tho.

I did delete it but moved it all into MainActivity.cs ...

namespace OkayKidO.Droid
{
    [Activity(Label = "@string/app_name", Icon = "@drawable/icon", Theme = "@style/MainTheme", ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation, ScreenOrientation = ScreenOrientation.Portrait )]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
        public static string Version { get; set; }
        public static string Build { get; set; }

        protected override void OnCreate(Bundle bundle)
        {
            TabLayoutResource = Resource.Layout.Tabbar;
            ToolbarResource = Resource.Layout.Toolbar;

            base.OnCreate(bundle);

            Forms.Init(this, bundle);

            Version = PackageManager.GetPackageInfo(PackageName, 0).VersionName;
            Build = Convert.ToString(PackageManager.GetPackageInfo(PackageName, 0).VersionCode, CultureInfo.InvariantCulture);

            LoadApplication(new App());
        }

        public override void OnLowMemory()
        {
            GC.Collect();
        }

        protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
        {
            base.OnActivityResult(requestCode, resultCode, data);
            InAppBillingImplementation.HandleActivityResult(requestCode, resultCode, data);
        }

        public void OnActivityCreated(Activity activity, Bundle savedInstanceState)
        {
            CrossCurrentActivity.Current.Activity = activity;
        }

        public void OnActivityResumed(Activity activity)
        {
            CrossCurrentActivity.Current.Activity = activity;
        }

        public void OnActivityStarted(Activity activity)
        {
            CrossCurrentActivity.Current.Activity = activity;
        }
    }
}
guardrex commented 7 years ago

:grin: I have a feel'in you're about to say that that was a bad move. lol

jamesmontemagno commented 7 years ago

:) most likely, does that code get called? those just look like methods you added in, not from the system.

You could have done on start and on resume maybe...

or add this class: https://github.com/jamesmontemagno/CurrentActivityPlugin/blob/master/files/MainApplication.cs.pp

jamesmontemagno commented 7 years ago

I will try to add some better error messages into the next release for sure :)

guardrex commented 7 years ago

The exception below explains why I tried to merge the bits from MainApplication.cs into my MainActivity.cs (and blew it :boom:).

When the file is added, the app won't compile with the following exception ...

Severity    Code    Description Project File    Line    Suppression State
Error       The "GenerateJavaStubs" task failed unexpectedly.
System.InvalidOperationException: Application cannot have both a type with an [Application] attribute and an [assembly:Application] attribute.
   at Xamarin.Android.Tasks.ManifestDocument.CreateApplicationElement(XElement manifest, String applicationClass, List`1 subclasses, List`1 selectedWhitelistAssemblies)
   at Xamarin.Android.Tasks.ManifestDocument.Merge(List`1 subclasses, List`1 selectedWhitelistAssemblies, String applicationClass, Boolean embed, String bundledWearApplicationName, IEnumerable`1 mergedManifestDocuments)
   at Xamarin.Android.Tasks.GenerateJavaStubs.Run(DirectoryAssemblyResolver res)
   at Xamarin.Android.Tasks.GenerateJavaStubs.Execute()
   at Microsoft.Build.BackEnd.TaskExecutionHost.Microsoft.Build.BackEnd.ITaskExecutionHost.Execute()
   at Microsoft.Build.BackEnd.TaskBuilder.<ExecuteInstantiatedTask>d__26.MoveNext() OkayKidO.Droid  C:\Program Files (x86)\MSBuild\Xamarin\Android\Xamarin.Android.Common.targets   1687    

I have an [assembly:Application] attribute in AssemblyInfo.cs ...

#if DEBUG
[assembly: Application(Debuggable=true)]
#else
[assembly: Application(Debuggable=false)]
#endif
jamesmontemagno commented 7 years ago

You can't have those in there, put them in the Application Class

guardrex commented 7 years ago

:tada: :balloon: :tada: :balloon: :tada: :balloon: :tada: :balloon: WORKS! :balloon: :tada: :tada: :balloon: :tada: :balloon: :tada: :balloon:

I see where I went horribly wrong there. Thanks so much for bearing with me! Thanks again for making the plugin available.

klint01 commented 7 years ago

@GuardRex Where did you move the debuggable code snippet? I realized I have the same code in my AssemblyInfo.cs and removing may resolve my issues as well. Thanks!

guardrex commented 7 years ago

@klint01 Sure ... just take the stock MainApplication.cs that's created for you when the packages are added and place the compiler directive #if-#else-#endif for the Debuggable at the top of that. :point_down:

My huge mistake was to try and solve it the other way by shoehorning the MainApplication bits into my MainActivity class. At this point, I can only hope that James at least got a little chuckle out of it. :grinning:

namespace MyApplication.Droid
{
#if DEBUG
    [Application(Debuggable=true)]
#else
    [Application(Debuggable=false)]
#endif
    public class MainApplication : Application, Application.IActivityLifecycleCallbacks
    {
        ...
    }
}
klint01 commented 7 years ago

@GuardRex Thank you! I believe this has been the contributing factor to my issues as well. Hoping to test it out in the next day.

umairali612 commented 7 years ago

Thanks mate. Was stuck on this. Was adding compiler directive #if-#else-#endif in assemblyInfo.cs. Xamarin should update its documentation on this page: https://developer.xamarin.com/guides/android/deployment,_testing,_and_metrics/publishing_an_application/part_1_-_preparing_an_application_for_release/

guardrex commented 7 years ago

@umairali612 I posted a note on that page with "something is missing" and linking this issue to that topic. Hopefully, they'll surface it there or somewhere that helps more folks.