Android LinkedHashMap.eldest() method is NOT available - android

I see the LinkedHashMap.eldest() method when attaching the source but cannot use it.
Any idea why it isn't available?

It is a hidden method - invokable using reflection, but isn't part of the public API and therefore likely to change between versions of android
https://android.googlesource.com/platform/libcore/+/a47f800/luni/src/main/java/java/util/LinkedHashMap.java line 169 the #hide annotation stops it from being visible to the public

Because there no such method in this class and any super type:
LinkedHashMap
HashMap
AbstractMap

Related

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.

Has MatrixCursor implementation within Android P changed?

Recently I have observed a large number of crashes for an app that I maintain when the Android P developer preview is used.
Diving (deep) into the project's code, I have found the problem method to be the following:
public static <T> T get(MatrixCursor cursor, int column) {
try {
cursor.moveToFirst();
Method get = MatrixCursor.class.getDeclaredMethod("get", int.class);
get.setAccessible(true);
return (T) get.invoke(cursor, column);
} catch (Exception e) {
throw new IllegalArgumentException("Android has changed the implementation of MatrixCursor?!");
}
}
From what I understand, this code is used to retrieve a custom object from the MatrixCursor directly, rather than a primitive type, byte array or String. There has previously been a private method within MatrixCursor that performs this internally, and it is this method that we access through reflection.
Needless to say, there's a number of issues with this approach. As far as I am aware, reflection to access private APIs is a feature that Android advises heavily against. Nevertheless, until the Android P preview, this seems to have been working as expected.
This leads me to raise the following questions:
Has MatrixCursor's implementation changed or is reflection totally deprecated as of Android P?
Sadly, I am not 100% clued up on what alternatives I have to avoid this issue. Any suggestions for that are greatly appreciated, is there a Cursor that can be used to store custom objects?
Yes, something has changed.
No, the underlying implementation of MatrixCursor has likely not changed.
What has changed is that Android P is introducing restrictions on non-public members of SDK classes. Attempting to use private fields or methods on SDK classes (whether by direct invocation, reflection, or JNI) will result in a crash.
If you run the code in question on a device running P and look at the logcat output, you should see a message similar to this:
Accessing hidden field Landroid/os/Message;->flags:I (light greylist, JNI)
I highly encourage you to fully read the linked documentation on these restrictions for the full context and for more information on how you can handle it.
One option (which you should do ASAP if needed!) is to file a bug so the Android team knows that this is a method you use and does not have a public alternative. If you do this before the release of Android P, there is a much better likelihood that the team will either create a public alternative for this method or allow you to continue to access that method in P.

Android unit test, super deprecated, replacement?

The android unit testing documents say to test with junit like this
public SpinnerActivityTest() {
super("com.android.example.spinner", SpinnerActivity.class);
} // end of SpinnerActivityTest constructor definition
http://developer.android.com/tools/testing/activity_test.html#InstallCompletedTestApp
Their example uses Android 2.1 though.
My app is using Android 4.2 although is backwards compatible.
What has the super class been replaced with? How should my constructor be written
You will need to use the call:
super (SpinnerActivity.class);
It's specified in the Android reference docs.
ActivityInstrumentationTestCase2(String pkg, Class<T> activityClass)
This constructor was deprecated in API level 8. use ActivityInstrumentationTestCase2(Class) instead
Use this instead.
ActivityInstrumentationTestCase2(Class<T> activityClass)
Creates an ActivityInstrumentationTestCase2.
Parameters
activityClass The activity to test. This must be a class in the instrumentation targetPackage specified in the AndroidManifest.xml
The API level 24 recommendations are to use the Testing Support Library. The InstrumentationRegistry.getTargetContext() method is probably what you want if you were using getContext().

Using Java reflection vs checking Build.VERSION.SDK_INT

Why should I bother using reflection as discussed here, if I can simply test Android version from Build.VERSION.SDK_INT and conditionally run functions not available on lower API versions?
That article discussed how to get method ID, handle exceptions, etc, which seems more complicated than simply using:
if(Build.VERSION.SDK_INT>=11){
// some Honeycomb code
// example: findViewById(R.id.root).setSystemUiVisibility(View.STATUS_BAR_HIDDEN);
}
This code works fine for me on various devices (2.2 / 3.2 / etc).
Thanks
Your proposal won't work (without reflection) when running on an older Android system, if the code hidden in "// some Honeycomb code" uses Class or Method names that only exist in the Honeycomb API. The root of the problem is that all classes referenced from code are loaded when the class is loaded. You need to use reflection to delay resolution of the code containing Honeycomb references until run-time.
Specifically, if you have a class:
class MyUseOfFeatures {
public void doSomething() {
if (TestIfPhoneHasFancyHoneycombFeature()) {
Object example = android.util.JsonReader(); // JsonReader is new in 3.0
}
}
Then when the JVM (er, DVM?) loads the bytecode for this class it will try and resolve the android.util.JsonReader name when the class is loaded (presumably when your application is loaded).
If you only rely on some behavior of Honeycomb (not any any new classes, methods or fields), then you'd be fine to just test the build number.

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