Xamarin LocationManager.RequestSingleUpdate always throws IllegalArgumentException - android

For some reason whenever I implement ILocationListener and implement the necessary interface methods this call always crashes on me:
LocMgr.RequestSingleUpdate(provider, this, null);
It throws java.lang.IllegalArgumentException: invalid listener: null although it's obviously impossible for this to be null. Does anyone have any suggestions on how to deal with this?

There were two issues here.
I needed to inherit from Java.Lang.Object
I needed to make sure I wasn't providing new implementations of Dispose or Handle.
Once I removed these the function call worked as expected.

Related

Why does ?. is inevitable when calling a method although instance isn't nullable?

I have to following variable declaration:
var baseItemList: MutableList<BaseDataItem>? = null
when writing the line:
baseDataItemsList?.get(position).getObjectTypeNum()
I'm getting an error saying that:
Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type BaseDataItem?
but, get method doesn't return a BaseDataItem?, only a BaseDataItem since the BaseDataItem inside the brackets is without a question mark.
Can someone explain me this error, and why i have to add this question mark?
Looking at this code:
baseDataItemsList?.get(position)?.getObjectTypeNum()
The call ?.get(position) returns the position if baseDataItemsList is not null, but otherwise returns null. So even though baseDataItemsList.get() would return a non-nullable BaseDataItem (only possible to call if baseDataItemsList is not nullable), the null-safe baseDataItemsList?.get() call returns a nullable BaseDataItem?, where the null condition indicates that baseDataItemsList is null. So you must use ?.getObjectTypeNum() to account for this.
Side note: in my opinion combining var with a mutable collection is often a code smell, because you're making something mutable in two different ways, which makes it more error-prone to work with.
Make use of Kotlins scope functions, for example the let scope to avoid that warning:
baseDataItemsList?.let { baseDataItemList ->
baseDataItemList.get(position).getObjectTypeNum()
}
That way you assert that baseDataItemList cannot be null inside the let scope. If you want to read more about that topic, take a look into the documentation

LifecycleObserver produce exception with methods that use newer APIs

My ViewModel class implements LifecycleObserver.
When I call fragment.lifecycle.addObserver(this) it produces exception.
Caused by: java.lang.IllegalArgumentException: The observer class has some methods that use newer APIs which are not available in the current OS version. Lifecycles cannot access even other methods so you should make sure that your observer classes only access framework classes that are available in your min API level OR use lifecycle:compiler annotation processor.
Strange, that firstly it was working fine, but not long ago this exception has appeared. I've found, that audioFocusRequest is cause of this bug.
private val audioFocusRequest by lazy {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
.setOnAudioFocusChangeListener(this)
.build() else throw RuntimeException("Can't be done for Android API lower than 26")
}
Does anybody know how it can be fixed?
UPD
Tried to use annotationProcessor "androidx.lifecycle:lifecycle-compiler:$lifecycle_version", but got compilation error:
(decided to paste screenshot, because whole logs are quite big)
UPD 2
At the end I've decided to delete audioFocusRequest field and to use old deprecated method - requestAudioFocus(OnAudioFocusChangeListener l, int streamType, int durationHint) instead of recommended requestAudioFocus(#NonNull AudioFocusRequest focusRequest)
It helped me to make code working again, so it can be solution. But I didn't find answer - why this problem had appeared. It strange because code used to be working before.
So problem has been solved but question still stays unanswered
Try to use kapt "androidx.lifecycle:lifecycle-compiler:2.0.0"
The class which implements LifecycleObserver has some method, which has parameters with type that only exist for higher APIs.
Your variables (i guess) and function parameters must exist on all APIs even function is not called (maybe this is requirement for classes who implement LifecycleObserver).
A possible solution is to change function parameter type to Any (kotlin) or Object (Java) and cast it to appropriate type inside function.
I have to remove this set method on SpinnerView: lifecycleOwner = viewLifecycleOwner
I was able to fix this by moving the offending methods into another class, but still called from my LifecycleObserver. After reading the error message again:
Caused by: java.lang.IllegalArgumentException: The observer class has some methods that use newer APIs which are not available in the current OS version. Lifecycles cannot access even other methods so you should make sure that your observer classes only access framework classes that are available in your min API level OR use lifecycle:compiler annotation processor.
It seems as though no methods or objects are allowed in the class extending LifecycleObserver if they don't exist in the device's OS, even if they are wrapped in an SDK version check and never accessed.

[MissingMethodException: No constructor found for Xamarin.Forms.Maps.Android.MapRenderer::.ctor(System.IntPtr, Android.Runtime.JniHandleOwnership)]

I have implemented a Google Map in my Xamarin forms app but am getting the above exception when navigating away from the page before the map has completed loading the current location.
This is probably the same issue previously raised but unanswered here.
From my research I believe the issue is related to leaky abstration answer given in this separate question: MonoDroid: Error when calling constructor of custom view - TwoDScrollView
However I do not have enough knowledge of Java or Android development to know how to resolve this issue. I am hoping that someone can explain where and how I can handle this exception when it occurs. Basically what I believe I need to achieve is catching the exception and handle it in the Droid project, but where?
These are the key exception messages that I am getting.
Message: [NotSupportedException: Unable to activate instance of type Xamarin.Forms.Maps.Android.MapRenderer from native handle 0xbef7ad5c (key_handle 0xd4608e7).]
Message: [MissingMethodException: No constructor found for Xamarin.Forms.Maps.Android.MapRenderer::.ctor(System.IntPtr, Android.Runtime.JniHandleOwnership)]
Message: [Exception of type 'Java.Interop.JavaLocationException' was thrown.]
However I do not have enough knowledge of Java or Android development to know how to resolve this issue. I am hoping that someone can explain where and how I can handle this exception when it occurs.
I think the reason is when you are navigating the pages, the Dispose method of Xamarin.Forms.Maps.Android.MapRenderer is called before the loading of the rest of the map. ACW need to create a new instance of MapRenderer but failed to create a new one because MapRenderer has no constructor method of (IntPtr, JniHandleOwnership).
If you refer to Premature Dispose() Calls you can find following statement:
If the subclass does contain an (IntPtr, JniHandleOwnership) constructor, then a new instance of the type will be created.
So the workaround for this exception is to create a subclass(Let's say MyMapRenderer) for Xamarin.Forms.Maps.Android.MapRenderer,which has a constructor with two arguments: (IntPtr, JniHandleOwnership), and use MyMapRenderer for map rendering:
In PCL create a custom control for Xamarin.Forms.Map.Map and use MyMap instead in your project:
public class MyMap:Map{}
Create a Custom MapRenderer in Droid project,which has a (IntPtr, JniHandleOwnership) contructor:
[assembly:ExportRenderer(typeof(MyMap),typeof(MyMapRenderer))]
namespace YourNameSpace.Droid
{
public class MyMapRenderer:MapRenderer
{
public MyMapRenderer(IntPtr handle, JniHandleOwnership transfer) { }
}
}
For details about create a custom MapRenderer, please refer to Custom a Map.
Updated version for answer from Elvis. Constuctor should look like as below.
public MyMapRenderer(System.IntPtr handle, JniHandleOwnership transfer): base(Android.App.Application.Context) { }
And additionally most probably you will also an empty constuctor like below. Otherwise first instance creation fails. Also for Proguard it is important.
public MyMapRenderer(Context context) : base(context)
{
}

Unit testing: NoSuchMethodError while calling Notification.setLatestEventInfo()

Feel free to improve the title I'm a little uncreative in this special case.
I am implementing a unit test for checking notifications, this is hardly possible I know, but I want to check how far I can automatism it.
I cannot test this simple line of code:
Notification test = new NotificationCompat.Builder(context).build();
The reason is stupid and simple in one. This code here will been executed internally:
public Notification build(Builder b, BuilderExtender extender) {
Notification result = b.mNotification;
result.setLatestEventInfo(b.mContext, b.mContentTitle,
b.mContentText, b.mContentIntent);
[...]
I'm getting this exception:
Caused by: java.lang.NoSuchMethodError: android.app.Notification.setLatestEventInfo(Landroid/content/Context;Ljava/lang/CharSequence;Ljava/lang/CharSequence;Landroid/app/PendingIntent;)V
at android.support.v4.app.NotificationCompat$NotificationCompatImplBase.build(NotificationCompat.java:479)
at android.support.v4.app.NotificationCompat$Builder.build(NotificationCompat.java:1561)
It is not hard to guess that the Google guys call here a method which was removed (or more properly annotated with #hide) in the Android Marshmallow SDK. I have verified it that call is missing the the newest documentation, but it was introduced in API 1 AFIK.
How can I work around that?
Things I tried and got stuck:
Overriding the callback, and mock that method without invoking that call:
I got it managed to get that Class<?> with the callback, but with which method I can override a hole method? I mean I need to patch the call it I cannot just mock it.
Injecting that call, but how? I can just override it and not adding it.
Suppressing the call with:
PowerMockito.spy(Notification.class);
PowerMockito.suppress(PowerMockito.method(Notification.class, "setLatestEventInfo", Context.class, CharSequence.class, CharSequence.class, PendingIntent.class));
Does not work ether since I try to kick a non existing method.
Change the target SDK for this test, but how can I do it?
The solution is easier than expected. I missed that by default Build.VERSION.SDK_INT has the value 0 since it cannot read the real value. So that support library calls it on just that platforms where this method exists.
With the help of this answer. I just had to add this code:
setFinalStatic(Build.VERSION.class.getDeclaredField("SDK_INT"), 23);
And my the codes works.
Well it still crashes somewhere else, but the notification is created. Wohoo!
And the actual function:
public static void setFinalStatic(Field field, Object newValue) throws IllegalAccessException, NoSuchFieldException
{
field.setAccessible(true);
// remove final modifier from field
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, newValue);
}

Is it possible to bypass constructors when instantiating objects in Android

Do Android have any way to instantiate objects without calling any of its constructors?
In Java, Sun have sun.reflect.ReflectionFactory.getReflectionFactory().newConstructorForSerialization(), in .Net we have System.Runtime.Serialization.FormatterServices.GetUninitializedObject() but I was not able to find anything like that in the Android platform.
After looking into Android source code we found a way to achieve this by using ObjectInputStream#newInstance() static method.
private Object newInstanceSkippingConstructor(final Class clazz) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
Method newInstance = ObjectInputStream.class.getDeclaredMethod("newInstance", Class.class, Class.class);
newInstance.setAccessible(true);
return newInstance.invoke(null, clazz, Object.class);
}
You can do this from native code with the JNI AllocObject function. See the JNI Spec. Calling out to native code is going to be more expensive than calling a no-op constructor, but probably cheaper than calling a constructor that throws an exception.
I don't know if there's another way to do it. Nothing is leaping out at me.
I don't believe so, however the Android platform does contain the Java Reflection API in java.lang.reflect.* so anything that is possible using the Java Reflection API is possible in Android
I found out a library that can handle OBJ init with constructor bypass
http://objenesis.org/index.html
And they have different methods for different APIs.
There is a slight difference in their API 17 and 18+ impl as in the current suggested answer. Also Android N+ is using something called Unsafe that should be implemented by new JVMs http://mishadoff.com/blog/java-magic-part-4-sun-dot-misc-dot-unsafe/

Categories

Resources