putSerializable for Bundle OK but getSerializable depreciated (passing GregorianCalendar date) - android

So I'm using the following code to put values into a Bundle
val arguments = Bundle()
arguments.putSerializable(DATE_PICKER_DATE, viewModel.getFilterDate())
dialogFragment.arguments = arguments
getFilterDate() returns a GregorianCalendar.time.
This seems fine but when I unpack the bundle with
val givenDate = arguments.getSerializable(DATE_PICKER_DATE) as Date
It says getSerializable is depreciated.
Can live with this for now but would rather not use depreciates stuff.
Did a load of googling and the simplest way to do this seem to be to use a long to pass the date in milliseconds, which will work for this but does mean some extra date processing but wondered if there is a better alternative to pass complex objects, such as GregorianCalendar dates? Be good to have a nice generic solution. Did look at Parcelables and found https://developer.android.com/guide/components/activities/parcelables-and-bundles but it was not much help. Lacked example code that was useful to me. My search has led me here.
PS
I think the way to put the Date into bundle is
arguments.putLong(DATE_PICKER_DATE, viewModel.getFilterDate().time)
And to get it back to a date is
val givenDate = Date(arguments.getLong(DATE_PICKER_DATE))
Which I guess is a good solution this time but wondering if there is a more generic solution like Serialise.

Whenever you get a deprecated warning, look at the warning to see if there's any information about why it's deprecated and what you're supposed to use instead - or look at the documentation for the method (usually the same thing):
This method was deprecated in API level 33.
Use the type-safer getSerializable(java.lang.String, java.lang.Class) starting from Android Build.VERSION_CODES#TIRAMISU.
So basically, it's telling you to use a new getSerializable method instead, one where you explicitly provide the class of the object being deserialised. So if you're using the Date provided by GregorianCalendar#getTime, you'd do:
val givenDate = arguments.getSerializable(DATE_PICKER_DATE, Date::class.java)
Also note that this is an API 33 (Tiramisu) thing - that means it's not available on lower APIs (so you'd need to use the deprecated method for those, with an API check to work out which to use). It also means it's only just been deprecated, so it will stick around for a while yet - you have the option of just using it! It's your call, but it's unlikely to disappear for a few years.
The other option for this kind of thing is to use a Compat library that calls the relevant method depending on API, handling all the boilerplate for you. I can't see anything for this specific method (although I only checked BundleCompat to see if it was added) but maybe later - it's always worth a look for API-dependent methods though.

Related

Android: getIntent() is deprecated

My program consists of a MainActivity and two fragment activities. I need one fragment to take a String value from the user and pass it to the second fragment.
I am trying to wrap my head around how to do this. Since I am familiar with intents, I found this answer on another post and decided to try it out. Everything looks fine until I get to step 4, when I try to use Intent i = getIntent(); in my second fragment, Studio won't let me use it and says "getIntent(java.lang.String) is deprecated".
This doesn't make sense to me since I have used getIntent() in other programs without issue, and it is letting me use it in my MainActivity (step 2 from the other post) without screaming at me.
I know this can be done without using intents, but I can't figure it out and can't find any really thorough tutorials in order to do so. So I guess my questions are:
Can I make intents work for this purpose still? What should I do to get around this deprecation issue?
Any other advice, explanations, or links to "explain it like I'm 5" tutorials would be very helpful and welcome. I have Googled and read a few, but I am still not understanding this and am becoming increasingly frustrated. It seems like this should be a relatively simple concept.
It is too late for answer but still I am providing my answer for other persons. It is happen because Intent is basically work with an activity. And fragments are not activity, but attached to activity. So simply you need to do this:
Intent intent=getActivity().getIntent();
Having the same problem while passing Object from an Activity to a Java Class.
Here is what I did
This Activity sends data
Salary newSalary = new Salary();
Intent intent = new Intent(ViewData.this,Data.class);
intent.putExtra("SalaryObj", newSalary);
It recieves data(In Data.class)
Here I tried this but Android Studio says getIntent is deprecatedIntent intent = Intent.getIntent();
So What can I use in place of getIntent(), because all the solutions I find on Internet to this problem, uses getIntent().
EDIT:
I was playing around with this and found that I was trying to receive data in Java Class(Not an Activity). But when I used it in an Activity then it works fine. But it takes me to another question that how to send data from an Activity to Java Class(which is not an Activity).
On your onCreate
val bundle = intent.extras
if (bundle != null) {
idEmployee = bundle?.getString("idEmployee", "")
idClient = bundle?.getString("idClient", "")
listAvailable = bundle?.getStringArrayList("listAvailable") as ArrayList<String>
Log.i("list:", "$listAvailable" )
}
It means, that this method could not be supported in further releases. The method is still in the API for backward compability for an unspecified amount of time. Mostly it is dangerous to use deprecated methods or there is a better way to achieve this.
Like it is described here
In this case you should rather use: parseUri(String, int) to achieve this ( according to the android developer api).

Better way of upping my Minimum API Level?

So I just had to up my min API level to 11 for the Preference (addpreferenceresource was depreciated) - turns out 9-10 takes out like 50% of the market. So my question is, is it better to just suppress the warning to cater to the Gingerbread market or is there a different way to make my preference reference without using Preference Fragments?
I would implement both types (the one that works in 11+ and the one that works in 10-), then use conditional checks for them. This is written in quite a bit of detail in this answer.
Basically, you end up setting up OtherPreferencesActivity with PreferenceFragment, and then PreferencesActivity with the deprecated PreferenceActivity. (Your APK will not break by including this deprecated code, as long as you use a version check so that if/when it's removed in the future, it doesn't try to find it.)
if (Build.VERSION.SDK_INT < 11) {
startActivity(new Intent(this, PreferencesActivity.class);
} else {
startActivity(new Intent(this, OtherPreferencesActivity.class);
}
Keep in mind, you will want to have them use each others' methods as much as possible so that you don't end up duplicating code.
Last tip: #TargetApi(11) and #SuppressWarnings("deprecation") will come in handy here. Just be careful that you're not ignoring other deprecations by doing so.
"depreciated" doesn't mean you can't use it and will break your system if you run the code. It means that it is officially not recommended to use and this method maybe removed from the api. In the future but we don't know when. So I would say it is save to use in this case for now.

Robolectric Custom Shadow Object

OOTB, Robolectric does not support Locales that well. Therefore, if your app is dependent on locales (which a lot of apps are if they are i18n'nd properly) this can be a royal pain. Long story short, I created my own ShadowFooGeocoder and ShadowFooAddress that allow me to simulate the locale I want. They're basically re-implementations of the existing shadows.
However, when I bind my class as such: bindShadowClass(ShadowFooGeocoder.class), this works great. At runtime, the correct shadow is returned. The problem is that I want to set up the simulations on this object and I'm not sure how. shadowOf(instance) where instance is an injected GeoCoder returns ShadowGeoCoder. I've tried working directly with the ShadowWrangler, but that also returns a ShadowGeocoder.
How can I get at my shadowed class that I've bound through the bindShadowClass(...) call so I can set my expectations (simulations)?
Note: This is a repost of the same question on the Robolectric group here. I posted here because my success rate of getting anyone to answer questions on the group is fairly low. I'm hoping for a better result here.
What I've basically done here is extend ShadowGeocoder like this:
#SuppressWarnings({"UnusedDeclaration"})
#Implements(Geocoder.class)
public class ShadowFooBarGeocoder extends ShadowGeocoder {
// implementation stuff
}
Then I would bind it using the bindShadowClasss(...) and when I retreive the shadow via the static shadowOf(...) call I get back a "ShadowGeocoder" which is an instance of ShadowFooBarGeocoder. I then cast it to that type and perform whatever work I need to.

Why does calendar.isSet(field) change to true when calling many of the other methods?

I have a project where the isSet method of a Calendar would be very useful, but between clearing a field and reading the isSet flag for that field I need to call another method of the calendar that is affected by this issue.
Here's a sample to demonstrate what is going on:
Calendar cal = Calendar.getInstance();
Log.d(TAG, Boolean.toString(cal.isSet(Calendar.SECOND))); // true
cal.clear(Calendar.SECOND);
Log.d(TAG, Boolean.toString(cal.isSet(Calendar.SECOND))); // false
cal.getTimeInMillis();
// These other methods I've tried, and I'm sure many more, have the same affect on isSet:
// before(calendar), compareTo(calendar), get(field), add(field, int), set(field, int)
// Note: 'field' in above comment line refers to a field other than Calendar.SECOND
Log.d(TAG, Boolean.toString(cal.isSet(Calendar.SECOND))); // true
I've looked at the source for the Calendar class and I don't see what's causing this. Does anyone know why this is happening? Do I need to track the set fields separately?
Update:
It appears this issue may be Android-specific or perhaps a specific version of Java since a couple answerers said it works for them in standard Java. Just to be clear, the class I'm using is still java.util.Calendar. In case the info might help, I am building against the Android 2.2 platform (API 8).
Update 2:
According to someone in #android-dev on FreeNode and my subsequent research on Wikipedia, this is probably because Dalvik uses a subset of Harmony (a Java implementation) for its class library instead of Java. However, that then gets me wondering why Harmony changes the isSet behavior. Needless to say, this issue sounds like it's way too far upstream for there to be a fix in the Android SDK any time soon, so I'll need to settle for a workaround.
I'll leave the question open since it hasn't been properly answered yet.
Is it bug in Harmony as I suspect or intentional design? If intentional, what is the purpose and is there a way to use it as I'm wanting to use it?
Update 3:
I've posted the issue to the Android issue tracker, now to wait and see if anything happens from it. Please star it if this issue is a problem for you as well. I'm hoping Android has its own fork of Harmony and doesn't need to wait for the fix upstream.
https://code.google.com/p/android/issues/detail?id=16826
Also removed the java tag since this is clearly not a Java issue.
Works as expected for me
Working as expected for me..
Here is the code I have used
import java.util.Calendar;
public class Test {
/**
* #param args
*/
public static void main(String[] args) {
Calendar cal = Calendar.getInstance();
System.out.println(Boolean.toString(cal.isSet(Calendar.SECOND)));
cal.clear(Calendar.SECOND);
System.out.println(Boolean.toString(cal.isSet(Calendar.SECOND)));
cal.getTimeInMillis();
System.out.println(Boolean.toString(cal.isSet(Calendar.SECOND)));
}
}
And here is here is the output in console
true
false
false
Thanks,

Avoid Internal Getters/Setters

In the source code of Activity.java, I see some methods bellow :
public View findViewById(int id) {
return getWindow().findViewById(id);
}
and the definition of getWindow method:
public Window getWindow() {
return mWindow;
}
But as the following rules:
Avoid Internal Getters/Setters
In native languages like C++ it's
common practice to use getters (e.g. i
= getCount()) instead of accessing the field directly (i = mCount). This is
an excellent habit for C++, because
the compiler can usually inline the
access, and if you need to restrict or
debug field access you can add the
code at any time.
On Android, this is a bad idea.
Virtual method calls are expensive,
much more so than instance field
lookups. It's reasonable to follow
common object-oriented programming
practices and have getters and setters
in the public interface, but within a
class you should always access fields
directly.
Without a JIT, direct field access is
about 3x faster than invoking a
trivial getter. With the JIT (where
direct field access is as cheap as
accessing a local), direct field
access is about 7x faster than
invoking a trivial getter. This is
true in Froyo, but will improve in the
future when the JIT inlines getter
methods.
so I want to know why android developers not access this mWindow object directly? If the JIT of the current android versions cannot inline the access, getWindow().findViewById(id) will costs more time than mWindow.findViewById(id), and findViewById is a rather frequently used method.
First: you can't access it because it's private.
Why is it private?
As you said, accessing members directly is faster. On the other hand, you are invoking a method that isn't very fast as it will lookup for some view in the view hierarchy. So using a method instead of a direct access will incur in a small overhead in terms of percentage of the total time that it would take to perform that task.
Anyway, I believe that the reason for this is encapsulation.
You are invoking something you don't own (that is the Android SDK). So, you shouldn't make any assumptions of whats happening "in the other side". Simply use this method and expect that it will return the view you want (or null if it doesn't exists).
Maybe the next version of android will use a different method to lookup a view, not calling getWindow(). If you use this method, they (Google/Android) can simply mark the method as deprecated and "forward" your invocation to the newest implementation. If you were calling directly getWindow(), maybe you would be looking for something that is no longer placed in there.
You can't access the mWindow property directly - it's private.
And I wouldn't care about the speed of findViewById, since you only need to call it once for every view in your layout in your onCreate() method and store the views in members of your activity. You do call findViewById only once per view, don't you? ;-)
However, if you really care about these things, you could call getWindow() for yourself, store it into a local variable and call findViewById on it directly. I wouldn't recommend this because all your performance increasements here are not worth the time and anyway will be obsolete with future versions of the JIT.
If you do this I would be very interested in the amount of microseconds you saved. :-)
We have a reason to smile now...
The android documentation which says to avoid internal getters and setters will change soon, supposedly progruard was added to Gingerbread platform which does a fine job of inlining accessor's, please refer to "Avoid Internal Getters/Setters" is bad advice and these two SO posts.
https://stackoverflow.com/a/6716573/892055
https://stackoverflow.com/a/4930538/892055

Categories

Resources