I has a view that is create by
this.vehicleListUI = new GameUIVehicleList();
this.vehicleListUI.attachGameEngine(myGameEngine);
This work fin unto the android destroy and restore the view.
The Android can recreate the view by use the empty constructor,
but my problem is for this GameUIVehicleList will work I most also have the myGameEngine
object set to the GameUIVehicleList
The myGameEngine is not Serializable and can't be this to ( Is a part of UI )
Exist this a way to restore my View fully by reattach the myGameEngine object
to a create view.
This will also be good for enable a lot of "new GameUIVehicleList()" into a XML layout file.
EDIT:
I don't know if this is to some help but way I has this problem from begin is
I use a android.support.v4.app.FragmentActivity and this keep to destroy and recreate
my View object. I can also fix this by override some method in android.support.v4.app.FragmentActivity that is invoke while FragmentActivity is recreate/crate a fragment and for this set the need value from my Activity that hold the need data.
Destroying and re-creation of Activity (and views) usually happens on configuration change (most often screen orientation change). You may want to check if this article helps and onRetainNonconfigurationInstance()/setRetainInstance() would not be the cure for your problems.
If myGameEngine is not Serializable or Parcelable, I do not believe it is possible to do this reliably.
However if it is ok, for myGameEngine to sometimes not be recovered you can extend the Application class with your own custom MyApplication class which contains the variable myGameEngine then in your onCreate you can get the myGameEngine that saved in your application context.
MyApplication app = (MyApplication) this.getApplicationContext();
this.vehicleListUI.attachGameEngine(app.myGameEngine);
Related
I have an object which I know is persist, because it performs it's behavior. It is an extension of RecyclerView.Adapter with SelectionTracker inside, which I am connecting to my RecyclerView only once. After I rotate my screen, Activity recreates itself and all member fields turn to null, including field with extension of RecyclerView.Adapter. But it is still somewhere there, because RecyclerView still works and I can select and deselect items.
How can I reacquire a reference to this object?
I don't want to serialize or parcealize it, because it is in memory and working.
Also I wonder, under which circumstances Android will dispose objects in such a situation?
You can solve this in 2 ways:
Tell Android that your app will handle the configuration change itself by adding android:configChanges="..." to your manifest. In this case, when the screen orientation changes, Android will not kill and recreate your Activity, it will simply call onConfigurationChange() on the existing instance and you can manage it yourself.
Use onRetainNonConfigurationInstance() and getLastNonConfigurationInstance(). Upon an orientation change, Android will call onRetainNonConfigurationInstance() just before it kills the current Activity. In this case, implement the method and return this (the instance of the current Activity) from the method. Android will then kill the Activity and create a new instance. In onCreate(), you should call getLastNonConfigurationInstance(). If this returns a non-null value, then this will be a reference to the Activity that got killed. You can now copy any data from member variables in the old instance to member variables in the new instance. In this way, you can take "migrate" anything necessary from the old instance of the Activity to the new instance.
While I was coding, I wanted to use findViewById method to find a view that cant access in the current view but can be accessed via the MainActivity. So two options came to my mind. One is creating a static method from that object in the MainActivity class and access the static object. The second method is to create a static object form MainActivity class itself(this) and access the findViewById method by calling the static object. Please answer the method I should use.
And apart from that, it got me thinking that whether an Android developer should come across this type of scenario or whether I have done some improper coding to access findViewById method in MainActivity while I was in a different view.
You can take a look at the code in the below repo.
https://github.com/chrish2015/ExpenseTrackerLatest
Thanks
If you are inside a class that is neither a Context nor an Activity and you need to use a method which exists inside the activity or context, then simply pass the activity as a parameter to that class and take an instance to that activity inside your class.
public class MyAdapter extends ArrayAdapter { // this is not activity
private Activity mActivity; // activity is a member of this class.
public MyAdapter(Activity activity, List<String> data) {
mActivity = activity;
}
public View getView(...) {
// if you need to use findViewById:
View view = mActivity.findViewById(R.id.some_id);
}
}
Don't use any of your two methods.
I might be misunderstanding your first sentence, but just to be sure, are you asking for a way to access a View that exists in the MainActivity, while you're inside of a Fragment?
If that's what you're asking, then yes, as an Android Developer, there will definitely be moments where we come across this scenario. However, the solution is definitely NOT by making your Views or Context static.
This is one of the easiest ways to cause bugs to appear throughout your app, with a very high chance to cause memory leaks too. Here's an Article from Google talking about memory leaks related to keeping a reference to a Context: https://android-developers.googleblog.com/2009/01/avoiding-memory-leaks.html
Rather than your two options, there are better solutions that developers typically use.
First of all, keep in mind that you should NOT be directly accessing any Views from outside of your current layout... meaning, that if you're in a second Activity, you don't directly access Views from the first Activity, or if you're in a Fragment, you don't directly access Views that belong to it's FragmentActivity.
Instead, you let the Activity or Fragment handle it's own Views.
So for example, if you're in another Activity and you want to update some data in the previous Activity, you can take advantage of an Activity's startActivityForResult() and onActivityResult() to obtain the data necessary to update the Activity immediately upon returning to the app.
For Fragments, there's actually a tutorial from the Android Documentation that describes a very good way to communicate between other Fragments: https://developer.android.com/training/basics/fragments/communicating
This method is to use interfaces as a callbacks, so another Fragment or the Activity will be able to receive data and update it's Views within it's own layout.
So for your case, if you're using Fragments and an Activity, you can easily have your fragments and activities communicate to each other in a safer and more reliable way.
Also, make sure you read up more on static and it's effects on your code, especially the side effects on Android components. Do not carelessly use static without considering some of the effects it might cause, because that would cause an endless amount of trouble to your code.
I need to understand how to use MvxCachingFragmentCompatActivity. I have asked this question before previous question, but I got a piece of example code, which is helpful but not what I needed. What I need is an understanding of how to use it.
First of all I have one activity and all my views are fragments.
My big assumption here is that using MvxCachingFragmentCompatActivity will enable me to restore my application navigation hierarchy if my activity is torn down and needs to be restored. Can someone confirm if this is correct.
If this is correct how do I use it. For example
Do I need to implement Save and Restore state in the view models? Is
there anything else the developer needs to do?
What does the MvxFragmentAttribute parameter IsCacheableFragment
actually do as regards caching fragments?
What performs the action of recreating my fragment hierarchy when an
activity is restored?
It would be great if there was some documentation around this.
I need to know this as my Activity is being torn down and then restored after I use another Activity for a camera feature. When the Activity restores itself the ViewModel for my fragments are null.Also I am finding Close(this) does not work in my view model. I'm sure I am not doing everything I need to do to make this work, but I need guidance on how it is supposed to be used.
Any help would be appreciated, maybe someone from the MvvmCross team. I'm really stuck here. I would prefer a description of the behaviour rather than point to a sample, but both would be great.
[Updated]
So I built a debug version of the V4 and V7 MvvmCross libraries and set about debugging. As far as I can tell as long as you add the following attributes to your fragment class this should set about caching your fragments.
[MvxFragment(typeof(MainActivityViewModel), Resource.Id.contentFrame, AddToBackStack = true, IsCacheableFragment = true)]
[Register("com.dummynamespace.MyFragment")]
Note the lowercase namespace is important, your class name can be mixed case.
However I am still seeing problems after my activity is destroyed and re-created. In my case I am actually seeing my activity destroyed and recreated more than once in quick succession. One example is that I cannot close the view after the activity destroyed and recreated. This seems to be due to the fact that the code in GetFragmentInfoByTag (MvxCachingFragmentCompatActivity class) is returning the wrong information needed to close the view. The close functionality needs the ContentId from the returned IMvxCachedFragmentInfo, however this is returning it as 0. Also the AddToBackStack property is set to false. Below I have listed what is returned in the fragment info
AddToBackStack = false
CacheFragment = true
CachedFragment = null
ContentId = 0
FragmentType = This is set to the correct fragment type
Tag = This is set to the corresponding view model for the fragment
Before the activity is destroyed and recreated the the fragment info is correct.
I am using MvvmCross 4.2.3. Has anyone else experience this?
Update 02/03/2017
I found out that my activity was being destroyed and recreated not due to memory but due to the camera orientation. We found it only failed when we held the camera in landscape mode.
The issue regarding the ContentId being set to 0 was caused by my app not being able to resolve the IMvxJsonConverter implemenation. This occurs when the MvvmCross Json plugin is not installed. Also you have to add the following to your App.cs file so it can be registered
Mvx.RegisterType<IMvxJsonConverter, MvxJsonConverter>();
If this is not done, then the Try.Resolve fails and the code that uses it is skipped over. Sometimes it is done silently other times it outputs a log. IT would seem to me that this should probably be fatal if you expect your app to survive the activoty being torn down and reconstructed.
Also one the MvvmCross Json plugin is installed you have to implement the Save and Restore state pattern in your view models save-Restore
Update new problem 08/03/2017
I am testing the restore of every view in my app. I am doing this by allowing the orientation to be changed which destroys my MvxCachingFragmentCompatActivity and then re-creates it.
When the activity is destroyed my fragment is also destroyed. At this point I tidy up my view model to ensure it will be free'd up and will not cause a memory leak.
However I have hit a problem where when OnCreate is called. It seems to do two things
Get a view model from the MvxFragmentExtensions OnCreate method by
calling into the view model cache
Then calls RestoreViewModelsFromBundle
The problem is that the call to MvxFragmentExtensions OnCreate (1) calls into the view model cache and returns a view model which has not been Started e.g Start() called on it, but this is used to set the DataContext.
After RestoreViewModelsFromBundle (2) is called the DataContext is not set again event though it has gone through the Constructor->Init->RestoreState->Start set up. So I now have a view model which is not setup properly and so my view does not work.
When I took out my code to tidy the view models, I got a bit further as the cached view model set by (1) now had the correct data. But I am hitting other problems because it is attempting to create a new view model due to the call to RestoreViewModelsFromBundle (2). As a short term fix is there anyway I can force the view model created as part of the restore process to be set as the ViewModel
Can someone from the MvvmCross team please help out with some information as to what is happening here and why?
I would like to know if there is a method to Save a Custom Class while rotating in an Android app.
I want to save all instanced object called of the MatrixGame class...someone can help me?
Thanks
Since the Fragment lifecycle is independent - when you're using a Fragment you can set it so it doesn't get destroyed upon configuration changes.
As you noticed, the Activity class gets destroyed and re-created when you rotate the app (or apply other configuration changes), if you want to persist the Activity state you can use sqlite and save whatever you need in the onPause() method. Then in the onCreate() method check the DB for last known state.
If you want to avoid saving the state "forever" (meaning, the user turns off the app and tomorrow when she turns it back on - you want to start fresh and not load the last known state) you can add a timestamp and set a threshold which, if passed, the data is considered stale and gets disregarded.
Two comments:
As #saprvade wrote, Fragments gets destroyed by default, in order to prevent it you should call: setRetainInstance(true)
In case you want to implement #saprvade's other suggestion (have the activity ignore rotation changes) you can see the following explanation of how to implement: https://stackoverflow.com/a/456918/1057429
You should serialize your object. You can implement Parcelable or implement Serializable (Parcelable is several times faster). Then you will be able to put it in a Bundle in onSaveInstanceState and restore it in onCreate or onRestoreInstanceState.
Also, you can serialize your object to String, e.g. json string.
Another option would be to store your object in a database or a file. It depends on your needs.
If you don't want to recreate this object on screen rotation you can change the lifecycle of your Activity by adding a configuration change flag in AndroidManifest.xml. If we are talking about a Fragment, you can call setRetainInstance(true) to avoid fragment recreation on rotation.
This is a stylistic question more than an actual "how can this be done," but the basic situation is this: I have an Activity MyActivity which contains a MapFragment, as well as a List of Renderers which are my own class that takes care of displaying some data. The Renderers also have ViewPagers which get their content views from yet another class, let's call it ViewPagerTab. Sometimes, something happens in some of these ViewPagerTabs that necessitates the update of the map in the top level Activity. There are, as I see it, a few approaches:
1) Both my Renderers and my ViewPagerTabs contain a reference to the context. If I cast the context as MyActivity I can access its map parameter.
2) By using the reference to the context, I can call getSupportFragmentManager().findFragmentById(R.id.map)).getMap() on it and get the map that way.
3) I can pass the map down from the Activity to the Renderers to the ViewPagerTabs as they are created so the map is accessible in each as a class variable.
4) Use a BroadcastReceiver in my Activity and send a message to it when the map needs updating from my ViewPagerTab.
Have I missed anything? What's the best/cleanest way of doing this?
This lesson may give you some ideas:
Communicating with other Fragments
Basically, the idea is to define an interface in a subunit such as a Fragment, then implement it in the parent Activity. Then, actually call the methods in the interface in the Fragment.
Another alternative is to create a class that extends Application. There, you can "share and declare" a number of non-context specific variables (like a glorified container, but where you don't have to create multiple instances of, or do look ups).
Requires some setup in your manifest but then all your activities can call MyApp app = (MyApp) this.getApplication(); (or in fragments, via the onAttach activity's .getApplication() )
The standard way is to define a listener interface, but I've found this to be cumbersome. Otto is a really nice alternative that you should at least look into before making your decision.
I think this is a bit over my head but what about parcel.I think it wouldn't work because of the dynamic nature of your data however it is one way to communicate between activities.