Showing DialogFragment while Application is on background - java

Case is after I got a response from my async task, I want to show a DialogFragment.
But if user put the application to background while the app still waiting for the response, on the moment the response came and .show DialogFragment it will crashed.
I've done immediate fix by try catching the .show, but the DialogFragment won't show after user return to the app.
Is there a clean way to let the application keep on showing DialogFragment while on background or on the next onResume ?
The only way I found while googling is using an ActivityDialog, but it will require much effort.
Edit : Eh I actually able to show it now with commitStateLoss ._.
from
customErrorDialog.show(((FragmentActivity) context).getSupportFragmentManager(), "TAG");
to
((FragmentActivity)context).getSupportFragmentManager().beginTransaction().add(customErrorDialog, "TAG").commitAllowingStateLoss();
idk if this is dangerous for some specific case though

You should use lifecycle-aware components to receive response.
For android-java projects simply use livedata. Base on the document:
LiveData is lifecycle-aware, meaning it respects the lifecycle of other app components, such as activities, fragments, or services. This awareness ensures LiveData only updates app component observers that are in an active lifecycle state. LiveData considers an observer, which is represented by the Observer class, to be in an active state if its lifecycle is in the STARTED or RESUMED state.
For android-kotlin projects, you have more options than java. You can still use livedata like Java. Other options are StateFlow, which is part of the flow and coroutines. Collecting them with repeatOnLifecycle. Base on document:
public class NameActivity extends AppCompatActivity {
private NameViewModel model;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Other code to setup the activity...
// Get the ViewModel.
model = new ViewModelProvider(this).get(NameViewModel.class);
// Create the observer which updates the UI.
final Observer<String> nameObserver = new Observer<String>() {
#Override
public void onChanged(#Nullable final String newName) {
// Update the UI, in this case, a TextView.
nameTextView.setText(newName);
}
};
// Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
model.getCurrentName().observe(this, nameObserver);
}
}
StateFlow and SharedFlow are Flow APIs that enable flows to optimally emit state updates and emit values to multiple consumers.
You can find this behavior in other observable classes like LiveData
class LatestNewsActivity : AppCompatActivity() {
private val latestNewsViewModel = // getViewModel()
override fun onCreate(savedInstanceState: Bundle?) {
...
// Start a coroutine in the lifecycle scope
lifecycleScope.launch {
// repeatOnLifecycle launches the block in a new coroutine every time the
// lifecycle is in the STARTED state (or above) and cancels it when it's STOPPED.
repeatOnLifecycle(Lifecycle.State.STARTED) {
// Trigger the flow and start listening for values.
// Note that this happens when lifecycle is STARTED and stops
// collecting when the lifecycle is STOPPED
latestNewsViewModel.uiState.collect { uiState ->
// New value received
when (uiState) {
is LatestNewsUiState.Success -> showFavoriteNews(uiState.news)
is LatestNewsUiState.Error -> showError(uiState.exception)
}
}
}
}
}

Related

Communicate between service and activity

I have a class in a service
MyClass m = new MyClass();
and inside my class I check if I have permission to overlay the view; if so, it's ok, otherwise I must start an activity
if (Settings.canDrawOverlays(mContext)) {
// draw over app
} else {
// start the activity
Intent i = new Intent(context,Calls.class);
context.startActivity(i);
}
When I start the activity I have a problem communicating between the class and the activity. I know how to use the interface but how can I register it in activity.
Some time I want to pass an object or data from the class to the activity or from the activity to the class... how can I do that?
I saw many examples in Stack Overflow about how to communicate between service and activity; they suggest to start the class from the activity but this does not work in my app because my class must be running all the time.
Perhaps you could use an event bus like mechanism where you can send or receive events through out your app, Though there are several libraries out there, I would recommend using Otto library for android.
Usage is pretty simple just register in your activity onCreate
Bus bus = new Bus();
bus.register(this);
For sending events
// example data to post
public class TestData {
public String message;
}
// post this data
bus.post(new TestData().message="Hello from the activity");
And subscribe to events like this
#Subscribe public void getMessage(TestData data) {
// TODO: React to the event somehow!
}
More info here
If you want to implement a communication pattern between a Service and an Activity, you should use a LocalBroadcastManager.
It will turn handy because, in case your Service is still on but your Activity
has been destroyed (very common situation), then the 'messagging' between the two will simply have no effect (no NPE or whatsoever will be thrown).
Step 1
Create a BroadcastReceiver in your Activity and define an ID / Filter
this.localBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Do what you have to do here if you receive data from the Service / Background Task
}
}
public static final IntentFilter SIGNAL_FILTER = new IntentFilter("com.you.yourapp.MY_SIGNAL")
Step 2
In your Activity register the broadcast in onResume() and unregister it in onPause().
#Override
protected void onResume() {
// Listen if a Service send me some data
LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(this.localBroadcastReceiver, SIGNAL_FILTER);
}
#Override
protected void onPause() {
// I'm going to the background / or being destroyed: no need to listen to anything anymore...
LocalBroadcastManager.getInstance(getApplicationContext()).unregisterReceiver(this.localBroadcastReceiver);
}
Your Activity is now ready to receive data from any other component in your Application.
If it's in the background, then there is no need to update the UI: in fact the Activity will not respond if in the background.
In the same way, if it's being garbage collected, the Receiver will be unregistered and the Activity will just not respond to anything.
If the Activity is resumed / restarted, onResume() will be triggered and the Receiver will be registered again.
Step 3
All you need to do right now, is send data from the Service.
Simply call
final Intent intent = new Intent();
intent.setAction(SomeActivity.SIGNAL_FILTER);
// put your data in intent
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
and your Activity will accordingly respond to the signal.
It's surprising how few people know about the LocalBroadcastManager and instead use some self-implemented callback / singleton pattern, which increases complexity and non-readability.
This pattern is built-in in Android, so you don't need external libraries. As for security, this ensures that your signals stay internal to your application: no data can therefore be read by other apps.
I similarly answered to another question here.

Should there be any logic on the activity class?

I was recently reading about design patterns and especially about low coupling and delegation.
I was wondering, whether there should be any logic on the Activity class or if it only serves the view.
E.g. I have an activity called BattleActivity and that is supposed to work as some kind of session between two players. A lot of Push Notifications happen there, also the class works as an Observer, so there is a lot of comminication going on there.
Right now I am trying to figure out what logic could I move to a separated object(and whether I should) and then just work with the activity.
Example of one of my methods on the activity:
private void postCastedSpell(final int spellId) {
Call call = StaticGlobalContainer.api.postSpellToBattle(Integer.parseInt(battleId), Integer.parseInt(MainActivity.CURRENT_USER_ID), spellId, 100);
call.enqueue(new Callback<User>() {
#Override
public void onResponse(Response<User> response, Retrofit retrofit) {
User user = response.body();
if (response.code() == 202) {
// 200
Log.i("Posting spell to battle", "Success");
Boolean affectedUserIsOpponent = isUserOpponent(user);
if (affectedUserIsOpponent && user.currentHp<1){
StaticGlobalContainer.battleOnResult(Constants.WON, getApplicationContext());
}else {
updateBattleLog(affectedUserIsOpponent, user, spellId);
}
// TODO: do something here
} else {
// 404 or the response cannot be converted to User.
Log.e("Posting spell to battle", "Error:" + response.errorBody());
}
}
#Override
public void onFailure(Throwable t) {
Log.i("HttpRequest-Post spell", "Failure");
}
});
}
It's not specifically bad to put a lot of logic in Activities, but you're right to try to keep it only view related things. If the app is relatively small, it might not be worth moving the logic out. Also, there is some overhead to using abstractions.
if your abstractions aren't supplying a significant benefit, you should avoid them
I try to keep any big data objects in a manager class, so given your example, it might worthwhile to create a Battle manager class to hold all the logic involved in it, like this postCastedSpell function. This way all the Battle information is self contained, and also can be used elsewhere in other activities.
Just keep in mind if you're use data manager classes and you want them to prompt some sort of interation with the UI, you'll have to use Callbacks or the Bus pattern since the Battle manager won't have access to your UI. For example, to call the postCastedSpell the call would look like:
BattleActivity:
BattleManager bm = BattleManager.getInstance(user1, user2);
onSpellClicked() {
bm.castSpell(spellId, user1, callback)
}
BasicCallback callback = new BasicCallback() {
#Override
onComplete() {
if (MyInfoFragment.this.isVisible()) {
[Update UI]
}
}
};
NOTE: When using callbacks like my example, when it finally gets called the activity may have already gone out of view and have been already garbage collected. So in the callback function you need to first make sure it is still visible before trying to modify the UI that possibly no longer exists.

Proper way to refresh multiple tab in backround thread

I have implemented dynamic tab navigation with fragments. Fragments are stored on the stack and when user change tab, new fragment should replace existing content to show new items.
But first, data must be refreshed (they will be downloading from rest webservice) and it takese some time, so there is also BusyFragment, indicating data loading process.
I implemented background refreshing data with AsyncTask and I have separate AsyncTask object that is called to refresh specific tabs, this way:
private void RefreshShopsMainMenuFragment(Fragment f)
{
if (refreshAsyncTaskShops != null && refreshAsyncTaskShops.getStatus() != AsyncTask.Status.FINISHED)
return;
final ListFragment fragmentToRefresh = (ListFragment) f;
refreshAsyncTaskShops = new AsyncTask<Void, Void, ArrayList<ListDataDef>>()
{
#Override
protected void onPreExecute()
{
ShowBusyFragment(true, Global.TAB_SHOPS); //for other tabs, other tab identifiers from Global
}
#Override
protected ArrayList<ListDataDef> doInBackground(Void... voids)
{
return ShopsTabDataProvider.GetNewData(); //For other tabs, different data provider call
}
#Override
protected void onPostExecute(ArrayList<ListDataDef> result)
{
fragmentToRefresh.mainData = result;
fragmentToRefresh.DisplayNewData();
ShowBusyFragment(false, Global.TAB_SHOPS); //for other tabs, different tab indentifier
}
};
refreshAsyncTaskShops.execute();
}
So, basically as shown above: to refresh shops tab, refreshAsyncTaskShops will be called, there are also other async task object, I marked lines where they are different.
Is there any more elegant solution, without declaring multiple AsyncTask objects? I'm not fully satisfied with my current implementation, because my all async tasks objects are basically the same.
Also, as you can see, there is no any queue so don't need to wait for first tab refreshing and to refresh another one. However, refreshing the same tab multiple times simultaneously should never happen so I added if condition to prevent the same task to be executed if previous one is not finished yet.

Executing thread only in one Android Activity

I have three java files in my Android project. Two are activities (MainActivity and GeoActivity) and one is a plain java file (PostHttp -> sends data to server via the HTTP POST)
I switch over to GeoActivity via a simple button on-click method. GeoActivity returns the co-ordinates of the current location in a TextView AND sends them to a remote server via the HTTP POST.
I have a Handler.class which executes sends the Post Message after a delay of 50s. Something like this below. The problem i have is that when i click the back button and switch over to MainActivity i can still see in LogCat the echoes receiving from the server that the data is still being sent. How can i stop that?
GeoActivity.class
public class GeoActivity extends Activity {
Location location;
private Handler mHandler = new Handler();
protected void onCreate(Bundle savedInstanceState) {
....
if(location != null){
mHandler.postDelayed(updateTask,0);
}
...
}
...
public Runnable updateTask = new Runnable(){
public void run(){
mlocListener.onLocationChanged(location);
//send coordinates with a delay of 50s
new PostHttp(getUDID(),latitude,longitude).execute();
mHandler.postDelayed(updateTask, 50000);
}
Try acting on the activity's life cycle.
For example:
#Override
protected void onStop() {
super.onStop(); // Always call the superclass method first
// Save the note's current draft, because the activity is stopping
// and we want to be sure the current note progress isn't lost.
ContentValues values = new ContentValues();
values.put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText());
values.put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle());
getContentResolver().update(
mUri, // The URI for the note to update.
values, // The map of column names and new values to apply to them.
null, // No SELECT criteria are used.
null // No WHERE columns are used.
);
}
This doesn't destroy the activity, it will reside in memory. However, you can always resume when needed.
Source:
Stopping and Restarting Android Activities

Prevent AsyncTask from running multiple times in bindView

I have a custom CursorAdapter that is using multiple AsyncTasks in its bindView method to load images into a grid.
When bindView runs my AsyncTasks get launched multiple times. This has the effect of pushing up my heap size and can cause Out of Memory errors.
What is the best approach to take, to get AsyncTasks to run just once?
You can cache the results of the Asynctask. There is a very useful project on Github you might want to look into.https://github.com/nostra13/Android-Universal-Image-Loader
I think you are looking for AsyncTaskLoader or its support package implementation. It handles everything for you.
An example of how to use it:
public SampleLoader extends AsyncTaskLoader<List<SampleItem>> {
// We hold a reference to the Loader’s data here.
private List<SampleItem> mData;
public SampleLoader(Context ctx) {
// Loaders may be used across multiple Activitys (assuming they aren't
// bound to the LoaderManager), so NEVER hold a reference to the context
// directly. Doing so will cause you to leak an entire Activity's context.
// The superclass constructor will store a reference to the Application
// Context instead, and can be retrieved with a call to getContext().
super(ctx);
}
/****************************************************/
/** (1) A task that performs the asynchronous load **/
/****************************************************/
#Override
public List<SampleItem> loadInBackground() {
// This method is called on a background thread and should generate a
// new set of data to be delivered back to the client.
List<SampleItem> data = new ArrayList<SampleItem>();
// TODO: Perform the query here and add the results to 'data'.
return data;
}
/********************************************************/
/** (2) Deliver the results to the registered listener **/
/********************************************************/
#Override
public void deliverResult(List<SampleItem> data) {
if (isReset()) {
// The Loader has been reset; ignore the result and invalidate the data.
releaseResources(data);
return;
}
// Hold a reference to the old data so it doesn't get garbag ecollected.
// We must protect it until the new data has been delivered.
List<SampleItem> oldData = mData;
mData = data;
if (isStarted()) {
// If the Loader is in a started state, deliver the results to the
// client. The superclass method does this for us.
super.deliverResult(data);
}
// Invalidate the old data as we don't need it any more.
if (oldData != null && oldData != data) {
releaseResources(oldData);
}
}
/*********************************************************/
/** (3) Implement the Loader’s state-dependent behavior **/
/*********************************************************/
#Override
protected void onStartLoading() {
if (mData != null) {
// Deliver any previously loaded data immediately.
deliverResult(mData);
}
// Begin monitoring the underlying data source.
if (mObserver == null) {
mObserver = new SampleObserver();
// TODO: register the observer
}
if (takeContentChanged() || mData == null) {
// When the observer detects a change, it should call onContentChanged()
// on the Loader, which will cause the next call to takeContentChanged()
// to return true. If this is ever the case (or if the current data is
// null), we force a new load.
forceLoad();
}
}
#Override
protected void onStopLoading() {
// The Loader is in a stopped state, so we should attempt to cancel the
// current load (if there is one).
cancelLoad();
// Note that we leave the observer as is. Loaders in a stopped state
// should still monitor the data source for changes so that the Loader
// will know to force a new load if it is ever started again.
}
#Override
protected void onReset() {
// Ensure the loader has been stopped.
onStopLoading();
// At this point we can release the resources associated with 'mData'.
if (mData != null) {
releaseResources(mData);
mData = null;
}
// The Loader is being reset, so we should stop monitoring for changes.
if (mObserver != null) {
// TODO: unregister the observer
mObserver = null;
}
}
#Override
public void onCanceled(List<SampleItem> data) {
// Attempt to cancel the current asynchronous load.
super.onCanceled(data);
// The load has been canceled, so we should release the resources
// associated with 'data'.
releaseResources(data);
}
private void releaseResources(List<SampleItem> data) {
// For a simple List, there is nothing to do. For something like a Cursor, we
// would close it in this method. All resources associated with the Loader
// should be released here.
}
/*********************************************************************/
/** (4) Observer which receives notifications when the data changes **/
/*********************************************************************/
// NOTE: Implementing an observer is outside the scope of this post (this example
// uses a made-up "SampleObserver" to illustrate when/where the observer should
// be initialized).
// The observer could be anything so long as it is able to detect content changes
// and report them to the loader with a call to onContentChanged(). For example,
// if you were writing a Loader which loads a list of all installed applications
// on the device, the observer could be a BroadcastReceiver that listens for the
// ACTION_PACKAGE_ADDED intent, and calls onContentChanged() on the particular
// Loader whenever the receiver detects that a new application has been installed.
// Please don’t hesitate to leave a comment if you still find this confusing! :)
private SampleObserver mObserver;
}
The source: androiddesignpatterns.com - Implementing Loaders (part 3)

Categories

Resources