In CommonsWare eBook Android v3.6 pg 270, he discusses handling threads with rotation. He offers a solution where you create an inner static class to hold your state, call detach in the workflow and then attach again during screen rotation.
The problem is that each rotation will destroy the Activity and recreate it in Android, thus when you come back your thread may reference the destroyed activity giving you an exception for accessing an object set for collection.
I tried this in Mono Android and was unable to get it to work, I got an exception every single time. My question, hopefully Jonathan Pryer reads this is, how can I make this work in Mono Android? I have asked this twice on the forums with no results. So I am taking it to StackOverflow. I wanted to post the actualy code but I didn't want to violate CommonsWare licensing. So please take a look at the example in the book.
What was the exception? What's the adb logcat output?
The C# equivalent is the-same-but-different from the Java source. For example, here's the "rotation-aware" version of the default Mono for Android sample project:
[Activity (Label = "Scratch.PreserveCount", MainLauncher = true)]
public class Activity1 : Activity
{
CountInfo Info;
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
// Set our view from the "main" layout resource
SetContentView (Resource.Layout.Main);
// Get our button from the layout resource,
// and attach an event to it
Button button = FindViewById<Button> (Resource.Id.myButton);
button.Click += delegate {
SetButtonCount (button, Info.Count++);
};
Info = (CountInfo) LastNonConfigurationInstance;
if (Info == null) {
Info = new CountInfo {
Count = 1,
};
} else {
SetButtonCount (button, Info.Count);
}
}
void SetButtonCount (Button button, int count)
{
button.Text = string.Format ("{0} clicks!", Info.Count);
}
public override Java.Lang.Object OnRetainNonConfigurationInstance ()
{
return Info;
}
class CountInfo : Java.Lang.Object {
public int Count;
}
}
It's the same basic approach as the Java sample: the Activity.OnRetainNonConfigurationInstance() method is invoked by Android before the Activity is disposed, so we return our "state" from that method. The Activity.LastNonConfigurationInstance property will return null the first time it's invoked, otherwise it will return the last value returned from OnRetainNonConfigurationInstance(). The only missing piece of the puzzle is that the "state" object (CountInfo here) must inherit from Java.Lang.Object as we're passing the instance to Java.
Related
I am trying to update my app to handle configuration changes (especially screen turning) manually.
I have some questions about what happens when changes happen during a Thread execution.
I have created an abstract class I call ThreadTask which uses Threads and Handlers to the main thread's looper to send updates to the main thread. This is my implementation of AsyncTask but with threads, I prefer this to using AsyncTask because I have more control over it.
It also has two methods to register an observer to the above events, it uses this interface:
public interface TaskObserver {
void pre_execute();
void on_progress(ProgressData progress);
void finished(Object result);
void cancelled(Object result);
}
The abstract members that the subclass must implement are :
abstract Object do_in_background();
and some concrete members are:
synchronized void add_observer(TaskObserver observer){
_observers.add(observer);
}
synchronized void remove_observer(TaskObserver observer){
_observers.remove(observer);
}
synchronized void post_execute(Object result) {
int observers = _observers.size();
for (int idx = 0; idx < observers; idx++) {
_observers.get(idx).finished(result);
}
}
///plus all the other methods of the interface
So when I implement a concrete class it would go something like this:
public class MyThreadTask extends ThreadTask{
#Override
Object do_in_background() {
progress.primary_max = 5;
for (int cur = 0 ; cur < 5 ; cur++) {
progress.primary = cur;
publish();
Thread.Sleep(3000);
}
}
}
and I updated the activity that calls this like so:
static final string TAG ="my_main_activity";
MyDataFragment _data; //this is a datafragment, a fragment with retaininstancestate , and a public `MyThreadTask task` variable
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (_data == null) {
_data = (MyDataFragment)getFragmentManager().findFragmentByTag(TAG + "_data");
if (_data == null) {
_data = new MyDataFragment();
getFragmentManager().beginTransaction().add(_data, TAG + "_data").commit();
}
}
if (_data.task != null) {
_data.task.register(this);
}
}
#Override
protected void onDestroy() {
super.onDestroy();
if (_data.task != null) {
_data.task.remove(this);
}
}
this makes sure that I always have a reference to the correct thread
When I wish to start the task I do it like so:
button.setOnClickListener((v) -> {
if (_data.task != null) return; //to make sure only one thread starts at a time
_data.task = new MyThreadTask();
_data.task.register(this);
_data.task.start(); //this is not thread's start, it is mine, it does other things too
})
and when the thread finishes it calls void finished(Object result) which I handle like so:
void finished(Object result) {
try {
//get result;
} finally {
_data.task.remove(this);
_data.task = null;
}
}
here are my questions:
a) is declaring my observer methods as synchronized necessary? I did it just to make sure , but when the activity is destroyed and then recreated, does it happen on the same thread? is there a chance for example that a progress_update may happen while an observer is being removed during onDestroy?
b) what will happen if a thread finishes and calls post_execute(which is important) during a configuration change? will the update be lost?
c) If indeed the update is lost because it currently has no observers, is there a way, either in my implementation or a different one, to handle the above?
Thanks in advance for any help you can provide
The preferred way to keep a background task alive through a configuration change is by hosting it in a retained fragment. The same instance of the fragment will persist through the configuration change. When the fragment is paused, check the activity's isChangingConfigurations and cancel the task only if it's false.
I don't know if this is documented anywhere, but it seems that the entire configuration change is posted to the main thread so that nothing else can run between pausing the old activity and resuming the new one. If you were using an AsyncTask in a retained fragment, you would be assured that its onPostExecute could not run during the configuration change. With your approach, the task could easily complete when there is no observer.
Asynctask does not handle configuration changes that well. I think, instead of Asynctask you should use AsynctaskLoader which can handle the config changes easily and it behaves within the life cycle of activities/fragments.
When you run AsyncTask and if the android system kills your activity/fragment(during config changes or memory conservation) then your doInBackground() method still keeps on running in the background and this can lead to undesirable results.
Therefore, instead of using AsyncTask you can use AsynctaskLoader or if you are populating data from SQLite then you can use CursorLoader.
References:
Guideline to choose among AsyncTaskLoader and AsyncTask to be used in Fragment
https://developer.android.com/reference/android/content/AsyncTaskLoader.html
According to this https://github.com/MvvmCross/MvvmCross/wiki/View-Model-Lifecycle, we should implement save state and reload state to handle tombstoning.
But what happens if we have a value parameter in the Init method ?
example:
public StockDetailViewModel
{
int stockId;
...
void Init(int stockIdAsIntent)
{
stockId = stockIdAsIntent
}
protected override void ReloadFromBundle(IMvxBundle state)
{
id = int.Parse(state.Data["id"]);
}
protected override void SaveStateToBundle(IMvxBundle bundle)
{
bundle.Data["id"] = stockId;
}
async void Start()
{
stockModel = StockService.Get(stockId);
}
...
}
Now let say we navigate to a child view model called StockFormViewModel.
Doing this the SaveState method is called.
Now we open the Form, do our business, then call this.Close(this);
If we follow CIRS, first the Init will be called, and then the ReloadState.
But the Init method will fail because we are coming from a child view model, and no intent was given as argument, giving no chance to the ReloadState method to kick in and restore the stockId.
I'm pretty sure I'm doing something wrong here, but I cannot put my finger on it...
It turns out that I was mixing MVVMCross navigation with parent intent navigation (due to old refactoring of base class).
It makes perfectly sense that these won't work together.
I switch to full MVVMCross navigation, and with this, there are two scenarios:
If the view model is still in cache (mvvmcross has a VM cache), it gets the VM from there, and so, when you call close on the "child", none of the initialization methods are called (Init, Reload or Start).
If the view model is not in the cache, it will reconstruct the VM and call ReloadState instead of Init.
I have a weird / unique situation with my ListView. This is the scenario:
I'm making use of the MVP design pattern. As the Activity starts, it raises an event to notify the presenter to fetch some data from a web service. The web service call is an Async call. Once the web service Completed event is raised, I take the result and push it into a property (which is of type Array) that resides within my View / Activity.
Everything I mentioned works just fine, but as soon as the device is rotated, some interesting developments take place.
The async call resumes as normal and provides the property (Array) with a value. So nothing wrong there... (And yes there is data in the collection) I then set the ListView Adapter and call the notifyDataSetChanged, but nothing happens. The UI is not updated or anything?? If I re-enter the Activity the data is visible again ??
I even tried calling invalidateViews and invalidate on the ListView - this didn't do anything.
Could someone please assist me in this matter?
Many thanks in advance!
[Update]
I would like to stress the fact that I am making use of C# (Xamarin) and not Java (:sigh: - yes I know). Furthermore, I am not making use of the ASyncTask class, instead I'm making use of the async methods created within the proxy classes generated by Visual Studio. Pretty straight forward, but this is the code that populates the ListView - the property is set from the presenter
Presenter
Where View is of type IContactsView
protected override void OnCollectData(System.Collections.IEnumerable data, Type typeOfData)
{
if (data != null && typeOfData != null && typeOfData.Equals(typeof(UserContact)))
{
this.View.UserInformationCollection = data.Cast<UserContact>().ToArray();
}
}
Activity
The activity implements IContactsView
public UserContact[] UserInformationCollection
{
get
{
return this._userInformationCollection;
}
set
{
this.RunOnUiThread(() =>
{
this._userInformationCollection = value;
ListView listview = this.FindViewById<ListView>(Resource.Id.userLV);
if (listview != null)
{
UserContact[] subsidiesList = this.GetIndexedContacts(this._userInformationCollection);
listview.Adapter = new ContactsAdapter(this, subsidiesList.ToList());
((ContactsAdapter)listview.Adapter).NotifyDataSetChanged();
}
});
}
}
[/Update]
Found a much better solution! So please ignore the static variable idea!
Activity:
Override the OnRetainNonConfigurationInstance and return the presenter
public override Java.Lang.Object OnRetainNonConfigurationInstance()
{
return this._presenter;
}
Within the OnCreate check the LastNonConfigurationInstance and get the presenter - if it isn't null:
protected override void OnCreate(Bundle bundle)
{
...
if (this.LastNonConfigurationInstance != null)
{
this._presenter = this.LastNonConfigurationInstance as ContactsPresenter;
this._presenter.RefreshView(this);
}
else
{
// create a new presenter
this._presenter = new ContactsPresenter(this);
}
...
}
So maybe, you saw what I did in the previous code sample? Yes, I send the new instance of the activity to the presenter - have a look at the RefreshView
Presenter:
So within my base presenter I have the following method:
public class Presenter<T> : Java.Lang.Object, IPresenter where T : IView
{
/// <param name="view">The view.</param>
public void RefreshView(T view)
{
this.View = view;
}
}
The above code helps my presenter say with the creation of new activities - so when it returns data after the async call it will have the latest and greatest instance of the activity!
Hope this helps!
Kind regards,
Got it working by doing the following:
declare a static variable of the activity:
private static ContactsActivity _cachedActivity = null;
Overrode the OnResume within the activity and set the variable:
protected override void OnResume()
{
base.OnResume();
_cachedActivity = this;
}
Override the OnCreate within the activity and set the variable:
protected override void OnCreate(Bundle bundle)
{
...
_cachedActivity = this;
...
}
Lastly I changed the property mentioned earlier:
public USBUserContact[] UserInformationCollection
{
get
{
return this._userInformationCollection;
}
set
{
_cachedActivity.RunOnUiThread(() =>
{
_cachedActivity._userInformationCollection = value;
ListView listview = _cachedActivity.FindViewById<ListView>(Resource.Id.userLV);
if (listview != null)
{
UserContact[] subsidiesList = _cachedActivity.GetIndexedContacts(_cachedActivity._userInformationCollection);
listview.Adapter = new ContactsAdapter(_cachedActivity, subsidiesList.ToList());
((ContactsAdapter)listview.Adapter).NotifyDataSetChanged();
}
});
}
}
Kind regards,
I just switched over from iPhone to Android and am looking for something similar to where in the iPhone SDK, when a class finishes a certain task, it calls delegate methods in objects set as it's delegates.
I don't need too many details. I went through the docs and didn't find anything (the closest I got was "broadcast intents" which seem more like iOS notifications).
Even if someone can point me to the correct documentation, it would be great.
Thanks!
Never mind... found the answer here :)
http://www.javaworld.com/javaworld/javatips/jw-javatip10.html
Pasting from the article so as to preserve it:
Developers conversant in the event-driven programming model of MS-Windows and the X Window System are accustomed to passing function pointers that are invoked (that is, "called back") when something happens. Java's object-oriented model does not currently support method pointers, and thus seems to preclude using this comfortable mechanism. But all is not lost!
Java's support of interfaces provides a mechanism by which we can get the equivalent of callbacks. The trick is to define a simple interface that declares the method we wish to be invoked.
For example, suppose we want to be notified when an event happens. We can define an interface:
public interface InterestingEvent
{
// This is just a regular method so it can return something or
// take arguments if you like.
public void interestingEvent ();
}
This gives us a grip on any objects of classes that implement the interface. So, we need not concern ourselves with any other extraneous type information. This is much nicer than hacking trampoline C functions that use the data field of widgets to hold an object pointer when using C++ code with Motif.
The class that will signal the event needs to expect objects that implement the InterestingEvent interface and then invoke the interestingEvent() method as appropriate.
public class EventNotifier
{
private InterestingEvent ie;
private boolean somethingHappened;
public EventNotifier (InterestingEvent event)
{
// Save the event object for later use.
ie = event;
// Nothing to report yet.
somethingHappened = false;
}
//...
public void doWork ()
{
// Check the predicate, which is set elsewhere.
if (somethingHappened)
{
// Signal the even by invoking the interface's method.
ie.interestingEvent ();
}
//...
}
// ...
}
In that example, I used the somethingHappened predicate to track whether or not the event should be triggered. In many instances, the very fact that the method was called is enough to warrant signaling the interestingEvent().
The code that wishes to receive the event notification must implement the InterestingEvent interface and just pass a reference to itself to the event notifier.
public class CallMe implements InterestingEvent
{
private EventNotifier en;
public CallMe ()
{
// Create the event notifier and pass ourself to it.
en = new EventNotifier (this);
}
// Define the actual handler for the event.
public void interestingEvent ()
{
// Wow! Something really interesting must have occurred!
// Do something...
}
//...
}
That's all there is to it. I hope use this simple Java idiom will make your transition to Java a bit less jittery.
The pendant for kotlin.
Define your interface: In my example I scan a credit card with an external library.
interface ScanIOInterface {
fun onScannedCreditCard(creditCard: CreditCard)
}
Create a class where you can register your Activity / Fragment.
class ScanIOScanner {
var scannerInterface: ScanIOInterface? = null
fun startScanningCreditCard() {
val creditCard = Library.whichScanCreditCard() //returns CreditCard model
scannerInterface?.onScannedCreditCard(creditCard)
}
}
Implement the interface in your Activity / Fragment.
class YourClassActivity extends AppCompatActivity, ScanIOInterface {
//called when credit card was scanned
override fun onScannedCreditCard(creditCard: CreditCard) {
//do stuff with the credit card information
}
//call scanIOScanner to register your interface
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val scanIOScanner = ScanIOScanner()
scanIOScanner.scannerInterface = this
}
}
CreditCard is a model and could be define however you like. In my case it includes brand, digits, expiry date ...
After that you can call scanIOScanner.startScanningCreditCard() wherever you like.
The main content of this video tutorial is to show how to use interfaces to delegate methods / data exchange between different Fragments and activities, but it is great example to learn how delegate pattern can be implemented in Java for Android.
Java callback is not the same thing like ios delegate, in ios you can use a callback almost the same way like in Android. In Android there is startActivityForResult that can help you to implement the tasks for what ios delegate is used.
I believe ListAdapter is a example of delegation pattern in Android.
Kotlin's official Delegation pattern:
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}
class Derived(b: Base) : Base by b
fun main() {
val b = BaseImpl(10)
Derived(b).print()
}
See: https://kotlinlang.org/docs/delegation.html
I download some data from internet in background thread (I use AsyncTask) and display a progress dialog while downloading. Orientation changes, Activity is restarted and then my AsyncTask is completed - I want to dismiss the progess dialog and start a new Activity. But calling dismissDialog sometimes throws an exception (probably because the Activity was destroyed and new Activity hasn't been started yet).
What is the best way to handle this kind of problem (updating UI from background thread that works even if user changes orientation)? Did someone from Google provide some "official solution"?
Step #1: Make your AsyncTask a static nested class, or an entirely separate class, just not an inner (non-static nested) class.
Step #2: Have the AsyncTask hold onto the Activity via a data member, set via the constructor and a setter.
Step #3: When creating the AsyncTask, supply the current Activity to the constructor.
Step #4: In onRetainNonConfigurationInstance(), return the AsyncTask, after detaching it from the original, now-going-away activity.
Step #5: In onCreate(), if getLastNonConfigurationInstance() is not null, cast it to your AsyncTask class and call your setter to associate your new activity with the task.
Step #6: Do not refer to the activity data member from doInBackground().
If you follow the above recipe, it will all work. onProgressUpdate() and onPostExecute() are suspended between the start of onRetainNonConfigurationInstance() and the end of the subsequent onCreate().
Here is a sample project demonstrating the technique.
Another approach is to ditch the AsyncTask and move your work into an IntentService. This is particularly useful if the work to be done may be long and should go on regardless of what the user does in terms of activities (e.g., downloading a large file). You can use an ordered broadcast Intent to either have the activity respond to the work being done (if it is still in the foreground) or raise a Notification to let the user know if the work has been done. Here is a blog post with more on this pattern.
The accepted answer was very helpful, but it doesn't have a progress dialog.
Fortunately for you, reader, I have created an extremely comprehensive and working example of an AsyncTask with a progress dialog!
Rotation works, and the dialog survives.
You can cancel the task and dialog by pressing the back button (if you want this behaviour).
It uses fragments.
The layout of the fragment underneath the activity changes properly when the device rotates.
I've toiled for a week to find a solution to this dilemma without resorting to editing the manifest file. The assumptions for this solution are:
You always need to use a progress dialog
Only one task is performed at a time
You need the task to persist when the phone is rotated and the progress dialog to be automatically dismisses.
Implementation
You will need to copy the two files found at the bottom of this post into your workspace. Just make sure that:
All your Activitys should extend BaseActivity
In onCreate(), super.onCreate() should be called after you initialize any members that need to be accessed by your ASyncTasks. Also, override getContentViewId() to provide the form layout id.
Override onCreateDialog() like usual to create dialogs managed by the activity.
See code below for a sample static inner class to make your AsyncTasks. You can store your result in mResult to access later.
final static class MyTask extends SuperAsyncTask<Void, Void, Void> {
public OpenDatabaseTask(BaseActivity activity) {
super(activity, MY_DIALOG_ID); // change your dialog ID here...
// and your dialog will be managed automatically!
}
#Override
protected Void doInBackground(Void... params) {
// your task code
return null;
}
#Override
public boolean onAfterExecute() {
// your after execute code
}
}
And finally, to launch your new task:
mCurrentTask = new MyTask(this);
((MyTask) mCurrentTask).execute();
That's it! I hope this robust solution will help someone.
BaseActivity.java (organize imports yourself)
protected abstract int getContentViewId();
public abstract class BaseActivity extends Activity {
protected SuperAsyncTask<?, ?, ?> mCurrentTask;
public HashMap<Integer, Boolean> mDialogMap = new HashMap<Integer, Boolean>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getContentViewId());
mCurrentTask = (SuperAsyncTask<?, ?, ?>) getLastNonConfigurationInstance();
if (mCurrentTask != null) {
mCurrentTask.attach(this);
if (mDialogMap.get((Integer) mCurrentTask.dialogId) != null
&& mDialogMap.get((Integer) mCurrentTask.dialogId)) {
mCurrentTask.postExecution();
}
}
}
#Override
protected void onPrepareDialog(int id, Dialog dialog) {
super.onPrepareDialog(id, dialog);
mDialogMap.put(id, true);
}
#Override
public Object onRetainNonConfigurationInstance() {
if (mCurrentTask != null) {
mCurrentTask.detach();
if (mDialogMap.get((Integer) mCurrentTask.dialogId) != null
&& mDialogMap.get((Integer) mCurrentTask.dialogId)) {
return mCurrentTask;
}
}
return super.onRetainNonConfigurationInstance();
}
public void cleanupTask() {
if (mCurrentTask != null) {
mCurrentTask = null;
System.gc();
}
}
}
SuperAsyncTask.java
public abstract class SuperAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
protected BaseActivity mActivity = null;
protected Result mResult;
public int dialogId = -1;
protected abstract void onAfterExecute();
public SuperAsyncTask(BaseActivity activity, int dialogId) {
super();
this.dialogId = dialogId;
attach(activity);
}
#Override
protected void onPreExecute() {
super.onPreExecute();
mActivity.showDialog(dialogId); // go polymorphism!
}
protected void onPostExecute(Result result) {
super.onPostExecute(result);
mResult = result;
if (mActivity != null &&
mActivity.mDialogMap.get((Integer) dialogId) != null
&& mActivity.mDialogMap.get((Integer) dialogId)) {
postExecution();
}
};
public void attach(BaseActivity activity) {
this.mActivity = activity;
}
public void detach() {
this.mActivity = null;
}
public synchronized boolean postExecution() {
Boolean dialogExists = mActivity.mDialogMap.get((Integer) dialogId);
if (dialogExists != null || dialogExists) {
onAfterExecute();
cleanUp();
}
public boolean cleanUp() {
mActivity.removeDialog(dialogId);
mActivity.mDialogMap.remove((Integer) dialogId);
mActivity.cleanupTask();
detach();
return true;
}
}
Did someone from Google provide some "official solution"?
Yes.
The solution is more of an application architecture proposal rather that just some code.
They proposed 3 design patterns that allows an application to work in-sync with a server, regardless of the application state (it will work even if the user finishes the app, the user changes screen, the app gets terminated, every other possible state where a background data operation could be interrumpted, this covers it)
The proposal is explained in the Android REST client applications speech during Google I/O 2010 by Virgil Dobjanschi. It is 1 hour long, but it is extremely worth watching.
The basis of it is abstracting network operations to a Service that works independently to any Activity in the application. If you're working with databases, the use of ContentResolver and Cursor would give you an out-of-the-box Observer pattern that is convenient to update UI without any aditional logic, once you updated your local database with the fetched remote data. Any other after-operation code would be run via a callback passed to the Service (I use a ResultReceiver subclass for this).
Anyway, my explanation is actually pretty vague, you should definititely watch the speech.
While Mark's (CommonsWare) answer does indeed work for orientation changes, it fails if the Activity is destroyed directly (like in the case of a phone call).
You can handle the orientation changes AND the rare destroyed Activity events by using an Application object to reference your ASyncTask.
There's an excellent explanation of the problem and the solution here:
Credit goes completely to Ryan for figuring this one out.
After 4 years Google solved the problem just calling setRetainInstance(true) in Activity onCreate. It will preserve your activity instance during device rotation. I have also a simple solution for older Android.
you should call all activity actions using activity handler. So if you are in some thread you should create a Runnable and posted using Activitie's Handler. Otherwise your app will crash sometimes with fatal exception.
This is my solution: https://github.com/Gotchamoh/Android-AsyncTask-ProgressDialog
Basically the steps are:
I use onSaveInstanceState to save the task if it is still
processing.
In onCreate I get the task if it was saved.
In onPause I discard the ProgressDialog if it is shown.
In onResume I show the ProgressDialog if the task is still
processing.