Method work right when deploy via Debugger but same method not working when APK is exported

Viewed 106

im getting this error when deploy a APK maked in Xamarin.Android (From Logcat of my device)

>*android.runtime.JavaProxyThrowable: System.NullReferenceException: Object reference not set to an instance of an object
  at MyApp.InvoiceActivity.SendJson (System.String Json, System.String Route, System.String Token) [0x00053] in <f72bff8dbb074291a532e12425c8737a>:0 
  at MyApp.InvoiceActivity.OnCreate (Android.OS.Bundle bundle) [0x0004a] in <f72bff8dbb074291a532e12425c8737a>:0 
  at Android.App.Activity.n_OnCreate_Landroid_os_Bundle_ (System.IntPtr jnienv, System.IntPtr native__this, System.IntPtr native_savedInstanceState) [0x0000f] in <0181fdcaff164e668b24279eb4fb429f>:0 
  at (wrapper dynamic-method) Android.Runtime.DynamicMethodNameCounter.1(intptr,intptr,intptr)
    at crc64df67992c1ed0ef67.InvoiceActivity.n_onCreate(Native Method)
    at crc64df67992c1ed0ef67.InvoiceActivity.onCreate(Unknown Source:0)
    at android.app.Activity.performCreate(Activity.java:7136)
    at android.app.Activity.performCreate(Activity.java:7127)
    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2893)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
    at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
    at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
    at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loop(Looper.java:193)
    at android.app.ActivityThread.main(ActivityThread.java:6669)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)*

This error says that my problem start in sendJson() method, this is the snippet of this method

public string SendJson(string Json, string Route = "MyRoute", string Token = "MyToken")
{
     try
     {
         using var client = new WebClient();
         client.Headers[HttpRequestHeader.ContentType] = "application/json";
         client.Headers[HttpRequestHeader.Authorization] = "Token token=" + Token;

         string response = client.UploadString(Route, "POST", Json);
         return response;
     }
     catch (WebException ex)
     {
          var response = new StreamReader(ex.Response.GetResponseStream()).ReadToEnd();

          return response;
     }
} 

But, this error only happens when the app is instaled and executed from the exported APK. When i use the Visual Studio Debugger, the method work well.

I tried to put the method in a isolated class or inside the Activity (even put part of the method inside onCreate() method), change level access, but nothing works.

EDIT

Thanks to all for answer and give me some ideas about this problem. After making some test i discover that the problem ocurr when "Optimize code" is enabled in Compilation section on Project properties. In Xamarin.Android, to export a signed APK you need first set project in "Release" mode to make that and, of course, this option is enabled by default ther. Im not sure if this is a bug of compilator or is just my fault, so im gonna keep investigating :). Anyways, thanks so much again for the help.

2 Answers

At the end the problem was the same: app works in debug meanwhile in release (specifically in exported apk) the app doesn't work. After some digging i found that this happens because AndroidManifest by debug have all the permissions. Before export the apk we need to set explicitly the permissions needed through (In my case) VisualStudio. For example, in my application a internet connection is required to perform some API calls and file access to store returned data (Data persistence on file or Database), that means we need ACCESS_NETWORK_STATE and WRITE_EXTERNAL_STORAGE in our 'real' AndroidManifest, so setted permissions on AndroidManifest and... app still not working.

Taking a part of Saamer answer in API call code throws exception: Cant reach server. Basicaly the app can't connect to server for some reason. At the beggining not was clear, swaping to RestSharp from WebClient throws code 0 (None or not connection). With that in mind found in Xamarin documentation the answer: Permissions supported and allowed by default (https://docs.microsoft.com/en-us/xamarin/essentials/permissions?context=xamarin%2Fandroid&tabs=android). So, ACCESS_NETWORK_STATE and WRITE_EXTERNAL_STORAGE need to be requested because there not allowed by default in our AndroidManifest. I know is a terrible mistake, but this was mi first application, so maybe this can help to others who facing a similar situation. Thanks for help.

Analysing the source of the bug:

According to the LogCat, you're getting the error because of some code that is in the SendJson function. But the only code that can cause the issue is this single line:

var response = new StreamReader(ex.Response.GetResponseStream()).ReadToEnd();

That is because the rest of the code has error handling implemented with the help of the try. However, the line above doesn't have any try-catch to catch an exception raised by the code.

Fixing the issue:

The proper fix is to prevent the error happening in the first place.

  • You are using the using statement with your httpclient incorrectly. You might want to stick with a static httpclient instead, as shown in this Official Microsoft Documentation.
  • It seems like you are facing this issue because you are not using asynchronous calls, so your client is turning out to be null.
  • You try-catch seems to catch that error, however your "GetResponseStream() is also null. And so your app tried to ReadToEnd() for a null object. And that causes your crash. So get rid of that and instead just use a generic exception catch (Exception ex) so that you can just return the error message you get. Here's what you could do:
static HttpClient client = new HttpClient();
public Task<string> SendJsonAsync(string Json, string Route = "MyRoute", string Token = "MyToken")
{
     try
     {
         client.Headers[HttpRequestHeader.ContentType] = "application/json";
         client.Headers[HttpRequestHeader.Authorization] = "Token token=" + Token;

         string response = await client.UploadStringAsync(Route, "POST", Json);
         return response;
     }
     catch (Exception ex)
     {
          var response = ex.Message;
          return response;
     }
} 
Related