I am building an android application that uses google maps to overlay items. The latitude and longitude for the items I get from a MySQL database. To do this I connect to a php script using HTTP in Async Task. My code for displaying items on the map is in the onPostExecute() method of Async Task.
Everything works fine but when I for example rotate the phone all my overlayed items disappear. How can I resolve this issue?
Should overlaying the items happen in the main thread? If so, I need to somehow pass the information from the async taks to the main thread, which I have looked into but have not been able to get that working. If somebody knows a good and right way to do this, I would really appreciate the help.
OnPostExecute it is invoked in the main thread!
Your problem is that when you rotate your phone
Android restarts the running Activity (onDestroy() is called, followed by onCreate()). The restart behavior is designed to help your application adapt to new configurations by automatically reloading your application with alternative resources that match the new device configuration.
.http://developer.android.com/guide/topics/resources/runtime-changes.html
Async task is your thread where onpost method is default attach with main UI thread its not Async problem you just need to handle onConfigurationChanged method, better to post some code.
Use the onSaveInstanceState(Bundle outstate) and onRestoreInstanceState(Bundle savedInstanceState) of activity class.
As mentioned android restarts your activity when you change rotation. So solution to this is in onSaveInstenceState you should store you overlay items data and in onRestoreInstanceState you should recreate overlay items based on saved data
create a method named initUi() and call it in onConfigurationChanged and onCreate methods like this:
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
try {
setContentView(...);
initUi();
} catch (Exception e) {
}
}
and
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.act_document);
initUi();
try {
} catch (Exception e) {
}
As said by the other answers you can solve your problem using onSaveInstanceState(Bundle outstate) and onRestoreInstanceState(Bundle savedInstanceState) or even disabling orientation changes in the device.
Lets see one by one:
-Disabling orientation changes, may result in a pour user experience, so I would not favour it.
-Using onSaveInstanceState(Bundle outstate) and onRestoreInstanceState(Bundle savedInstanceState) you have two main oprions ...
Option 1:
You can save only basic information about items being displayed (i.e. if it's a map, you could save map center location and zoom) and then retrieve again from the database all the overlays information. That's very simple but it may bacome very slow if you have several hundred of overlay items to retrieve, resulting again in a pour user experience.
Option 2:
You could use parcelable to extend you overlay Item so you could save all the overlay items during onSaveInstanceState(Bundle outstate), and restore them without reloading from database. I've used this for with several thousand of items, and it works quite well.
You can find parcelable information in here: Google and and example in here: Android – Parcel data to pass between Activities using Parcelable classes
Good luck
Related
I'am having troubles with Googles MapView Activity.
The reason for my problem:
My application relies on many asynchronous events. When I start an Activity containing the Map, I've a template mechanism which adds and initializes the Map and relays all activity callbacks like onCreate(), onResume() to the map. All works fine!
But when this activity is restored from background my template mechanism is not yet ready to create the dynamic layout in onCreate(), hence I can't relay the lifecycle callbacks which are needed for the Map. In worst case, the Map is created after onResume().
This simplified code is for understanding the issue:
public class MapActivity{
List<Templates> templates;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (app.isInit())) {
initActivity();
}
templates.onCreate();
}
// init after templates are loaded
private void initActivity() {
templates = templateEngine.renderSomeTemplates()
}
#Override
protected void onResume() {
super.onResume();
templates.onResume()
}
public void onEventMainThread(InitAppEvent event) {
initActivity()
}
}
So my question.
Is there any way to add a google MapView programmatically during runtime without passing onCreate() and so on to it or do some lazy init?
Ok, I figured it out.
Apparently it's impossible to live without the callbacks as described in the docs:
http://developer.android.com/reference/com/google/android/gms/maps/MapView.html
So I found a trick to force my activity to start all over again, as soon as my dependencies (templates) are ready.
If anyone is looking for a neat way to restart an activity: there is recreate()
http://developer.android.com/reference/android/app/Activity.html#recreate()
It basically finishes the activity and repeats the normal lifecycle from the beginning.
Yes its is possible, take a look at the samples in the SDK
I have use the v4 support lib for FragmentTabHost
The requirement is that when I am switching tab one to another & another one, that is calling
onCreateView() & onActivityCreated() every time.
That's why my code performance is slow.
So, any other solutions? how to increase performance in fragment tab?
Sounds like a design smell.
Redesign your code so that heavy work is done asynchronously. Fragments should be able to be built quickly. If there is any large processing that needs to be done to in order for a Fragment to display useful information, that work should be done beforehand or asynchronously after the Fragment is created and the Fragment should be notified to update its content when the work is complete.
First thing which you should take care of is to watch about calculations / loading a big set of data should be places on a different worker thread than main UI thread. The best option to do that (in my opinion) is to use AsyncTask. You can use something like this in your Fragment :
private class LoadData extends AsyncTask<Void, Void, Void>{
#Override
protected void onPreExecute(){
super.onPreExecute();
// this is the place where you can show
// progressbar for example to indicate the user
// that there is something which is happening/loading in the background
}
#Override
protected void doInBackground(Void... params){
// that's the place where you should do
// 'the heavy' process which should run on background thread
}
#Override
protected void onPostExecute(Void result){
super.onPostExecute();
// you should update your UI here.
// For example set your listview's adapter
// changes button states, set text to textview and etc.
}
}
This is the way you can make your tabs work faster.Hope this will help you! : )
I found a solution for that. I inserted all websevices & database transaction code in on create. because oncreate in not calling every time untill the ondestroy not call. & the other one solution is also available we can use
fragment.show();
& fragment.hide(); method
As an addition to Android-Developer: if you already are using AsyncTask, remember that even when you use multiple AsyncTask's, they are executed in the background, but all sequentially! If you want more threads to handle your tasks, check out this post, which perfectly explains how to achieve that! Running multiple AsyncTasks at the same time -- not possible?
I started reading around about the activity life cycle callbacks and saving state and there are quite a few things I don't understand - I'm writing an android app but I want to ask more general questions than how to do it specifically for the few activities etc I have at the moment, I would like to have a better overall view of how this works!
There are two groups of methods I have seen being used (I have seen one or two others but don't want to confuse myself even further...)
onPause, onResume etc,
and then the onSaveInstanceState ones.
What is the difference between them and the circumstances we should be looking to use them? I have seen some questions where a poster is using one of the normal life cycle callbacks, and is told to use onSaveInstanceState instead, so when should we be implementing onPause rather than onSaveInstanceState and so on. Some posts mentioned about methods being used for transient state only, could someone expand on that?
I have seen state being used to mean slightly different things - UI/View state and Activity state, what is the difference between the two?
I am also a bit unsure with what they mean by state, when we are saving state what kind of things are we saving exactly, could anyone give some quick examples (I don't mean actual code)? The android developer guides say that the android system automatically takes care of some of this, so what should we be concerned with? Bundle objects used by onCreate and onSaveInstanceState only store simple values, so what about more complex objects and arrays.
Thanks
protected void onPause ()
protected void onSaveInstanceState (Bundle outState)
Just by looking at it, onSaveInstanceState has an Bundle you can put your things you need to save in it. And get it back in onCreate(Bundle) or onRestoreInstanceState(Bundle);
Some important lines in the document:
This method is called before an activity may be killed so that when it
comes back some time in the future it can restore its state. Do not
confuse this method with activity lifecycle callbacks such as
onPause(), which is always called when an activity is being placed in
the background or on its way to destruction, or onStop() which is
called before destruction.
Android can destroy your activity or even kill your process at any given time (not likely when it is visible to the user though :-)). When the user navigates back to the activity, the data/info that was shown on the screen before he or she left it should be shown again.
The onSaveInstanceState callback allows you to do this.
Most of the Views already do this for you automatically. E.g. the current text in an EditText, the current scroll position of a ListView, etc. are all automatically saved for you.
However, there are some things that are not automatically saved for you. E.g. the current text in a TextView, the (changed) background drawable of a particular View.
Say, you show an error message after a user action fails. The error message is then shown in a TextField and this TextField's background becomes red (i'm just making this up here :-)). When the user leaves the activity while this error is shown (e.g. presses Home button), the activity is destroyed, the error message and the red background won't be shown again when the user comes back to the activity.
This is where onSaveInstanceState comes to the rescue.
You can save a String in there that containts the error message. Then when the activity is re-created, the Bundle savedInstanceState of the onCreate is not null and you can query it for the error message. If this message is not null/empty, call setText on the TextView for the error message and make that TextView's background red.
try to use this code to save state
#Override
protected void onSaveInstanceState(Bundle outState) {
State s = new State(yourTextView.getText().toString());
outState.putSerializable(State.STATE, s);
super.onSaveInstanceState(outState);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
State s = (State) savedInstanceState.getSerializable(State.STATE);
yourTextView.setText(s.getYourTextViewText());
}
I'm making a simple drawing application.
I want to be able to save the user's drawing on the screen when the device orientation changes. This only happens in the main activity.
I read that if the orientation changes, then the activity is destroyed and recreated again (onCreate(Bundle created) being called).
I'm not sure if it means that it should also call onSavedInstanceState(Bundle bundle), because in my app it is only called if another activity takes the focus on top of my main activity, but not when rotating to landscape/portrait.
I'm simply looking for a way to save an existing bitmap and pass it to the main activity when the orientation changes. How can I do it if my onSaveInstanceState never gets called?
Also, since Bitmap implements parceable already I used it.
Here's the code from the main activity:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// some more activity code...
if (savedInstanceState != null) {
bitmap = savedInstanceState.getParcelable("bitmap");
Log.d("STATE-RESTORE", "bitmap created");
paintBoard.setBitmapBackground(bitmap, false);
Log.d("RESTORING...", "onRestoreInstanceState()");
} else {
Log.d("SavedInstanceState", "null");
}
}
// Never called when I change orientation on my device
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
bitmap = Bitmap.createBitmap(paintBoard.getBitmap());
outState.putParcelable("bitmap", bitmap);
Log.d("STATE-SAVE", "onSaveInstanceState()");
}
Any help will be appreciated.
EDIT :
I removed this line from the AndroidManifest.xml file:
android:configChanges="orientation"
and now onSaveInstanceState() does get called when I change orientation on the device.
You should read this article completely.
...it might not be possible for you to completely restore your
activity state with the Bundle that the system saves for you with the
onSaveInstanceState() callback—it is not designed to carry large
objects (such as bitmaps) and the data within it must be serialized
then deserialized, which can consume a lot of memory and make the
configuration change slow. In such a situation, you can alleviate the
burden of reinitializing your activity by retaining a stateful Object
when your activity is restarted due to a configuration change.
To retain an object during a runtime configuration change:
Override the onRetainNonConfigurationInstance() method to return the
object you would like to retain.
When your activity is created again,
call getLastNonConfigurationInstance() to recover your object.
I'm going through the Android tutorials from TheNewBoston https://thenewboston.com/
In the 4th section, Travis walked us through making a Camera application which can set a picture as wallpaper as well. The problem with the vanishing bitmap on rotation wasn't resolved however, he only offered the android:configChanges="orientation" solution, which blocks the screen rotation temporarily.
Using the information from this page, I added
if (savedInstanceState != null) {
bmp = savedInstanceState.getParcelable("bitmap");
iv.setImageBitmap(bmp);
}
at the end of the onCreate
and added this override
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable("bitmap", bmp);
}
in the class.
This solved the problem of the vanishing bitmap.
An onConfigurationChanged() method would probably be the simplest solution to your problem.
It saves the apps current state between orientation switches
This link has more information about it.
Now, you can store your data in view models and keep them across configuration changes.
Save your bitmap in onPause() or onDestroy() because i think onSavedInstanceState() is never called by the android sytem when switching orientation.
If I press home and come back to my app a little later I will find that the state has been preserved perfectly. For some reason however if I lock the phone and then unlock it, my app has been returned to the original state bar a few things here and there. When I looked into the logs I found that onCreate had been called while the phone was in a locked state. Because locking the phone is quite an off hand thing to do, having your game reset every time you do so is not desirable to the user. How can this be avoided at least for a longer period of time than a few seconds after locking the phone?
This is how Android OS works, it decides by it's own when to destroy your view. To avoid loosing this information there is a Method that can be reimplemented in your activity
#Override
public void onSaveInstanceState(Bundle outState){
iGameContent.saveGame(outState);
}
Save all your needed data into outState, and in the onCreate method, check if its a new instance or saved instance, like this:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.game);
if (savedInstanceState!=null){
iGameContent.loadGame(savedInstanceState);
}else{
// Normal initialization
}
}
An example of the save/load to a Bundle is the following:
public void loadGame(Bundle aBundle){
iBadsHit = aBundle.getInt("iBadsHits",0);
}
public void saveGame(Bundle aBundle){
aBundle.putInt("iBadsHit", iBadsHit);
}
If your log is showing that onCreate has been called then that means your apps process was killed.
Do you know the Android Activity Lifecycle? If not, read up on it here: Android Activities
The behavior on screen lock could vary from one device to other. Some events could cause the destruction of the app. You can try to handle some of this events to avoid this situation specifying it on the AndroidManifest.xml:
android:configChanges="keyboardHidden|orientation"
These two are the most problematic in screen lock. Yo can find more information on the last chapter of this nvidia document