I'm using the next library ReactiveNetwork to listen the changes on the network to detect when the network is disconnected, but I've implemented this on the BaseActivity but is not doing nothing.
Have I something wrong? Let me know.
public class BaseActivity extends AppCompatActivity {
private Disposable networkDisposable;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
connectivityMonitorized();
safelyDispose(networkDisposable);
}
private void safelyDispose(Disposable... disposables) {
for (Disposable subscription : disposables) {
if (subscription != null && !subscription.isDisposed()) {
subscription.dispose();
}
}
}
#SuppressLint("CheckResult")
public void connectivityMonitorized(){
networkDisposable = ReactiveNetwork
.observeNetworkConnectivity(this)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(connectivity -> {
if (connectivity.state() == NetworkInfo.State.DISCONNECTED || connectivity.state() == NetworkInfo.State.DISCONNECTING) {
//TODO DIALOG SHOWING DISCONNECTED
}
});
}
}
I'm using this BaseActivity to extends in other activities.
Thanks
You are disposing the networkDisposable as soon as subscribed ideally it should be disposed on the activity destroy that's why you are not getting any network updates move the safelyDispose() to on destroy
#Override
protected void onDestroy() {
super.onDestroy();
safelyDispose(networkDisposable);
}
Related
My app launches a SplashActivity followed by a MainActivity. I run branch.initSession in the SplashActivity but it's taking about 1.5 seconds to return to the listener which delays the launch of the MainActivity. I would like to reduce this time.
My ideas are:
run branch.initSession in the MainActivity instead.
run branch.initSession in SplashActivity, launch MainActivity, then pass the branch to the MainActivity using an eventbus for processing.
Does anyone have any recommendations on how to solve this issue?
Cheers, Duane.
Amruta from Branch here.
By default, Branch will delay the install call only for up to 1.5 seconds. We delay the install call in order to capture the install referrer string passed through Google Play, which increases attribution and deferred deep linking accuracy. We do not delay any other call, and the install call only occurs the first time a user opens your app.
If we receive the referrer string before 1.5 seconds, we will immediately fire the call, meaning this delay is up to 1.5 seconds, but not guaranteed to take that long.
If you’d like to optimize the first install call, simply paste the following code in your Application class, and we will not delay the first install call.
public final class CustomApplicationClass {
#Override
public void onCreate() {
super.onCreate();
// initialize the Branch object
Branch.setPlayStoreReferrerCheckTimeout(0);
Branch.getAutoInstance(this);
}
}
My solution was:
If first launch, wait for the parameters in the Splash Activity
If not first launch, pass the Branch parameters on to the Application which would pass the parameters onto any listener to process. If there is no listener, then it can try to process it in the Splash Activity or save it for later.
I'm not sure if this is good practice, but I thought it'll be simpler than adding a Event Bus library. Would be happy to hear some feedback.
The Application class:
public class MyApp extends Application {
private Listener branchListener = null;
public interface Listener {
void onBranchLinkReceived(JSONObject params, BranchError error);
}
public void registerBranchListener(Listener listener) {
branchListener = listener;
}
public void unregisterBranchListener() {
branchListener = null;
}
public boolean branchLinkReceived(JSONObject params, BranchError error) {
if (branchListener != null) {
branchListener.onBranchLinkReceived(params, error);
return true;
}
return false;
}
#Override
public void onCreate() {
Branch.getAutoInstance(this);
...
}
}
In the Splash Activity:
public class SplashActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
Branch branch = Branch.getInstance();
branch.initSession(new Branch.BranchReferralInitListener(){
#Override
public void onInitFinished(JSONObject params, BranchError error) {
if (error == null) {
if (firstLaunch) {
processParamsInSplashActivity(params);
} else {
boolean isProcessed = ((MyApp) getApplication()).branchLinkReceived(params, error);
}
}
}, this.getIntent().getData(), this);
}
}
}
And in the MainActivity:
public class MainActivity extends AppCompatActivity implements MyApp.Listener {
#Override
protected void onCreate(Bundle savedInstanceState) {
((MyApp) getApplication()).registerBranchListener(this);
...
}
#Override
protected void onDestroy() {
((MyApp) getApplication()).unregisterBranchListener();
super.onDestroy();
}
#Override
public void onBranchLinkReceived(JSONObject params, BranchError error) {
Log.d("MainActivity", "Branch link received: " + params);
}
}
I'd like to make an initialization request when my app starts up. I then want to use the response in my MainActivity. I don't want to make that request in the Activity and then deal with the Activity lifecycle when the phone gets rotated.
So I was thinking of deriving from Application and making the request there. But what's the best way to send the response data to the my launcher Activity?
Is there a "best practice" solution here?
You could try using a library like Event Bus in order to receive the data inside your activity once your request task is complete. By doing this you wouldn't have to worry about where the call is made from or if your activity is rotated or recreated.
If the data is specificly for your MainActivity I would recommend having the request be triggered from there for the sake of keeping things coupled.
If you're looking for best practices, you shouldn't extend an Application class for this.
There is many ways to persist your request state on screen rotation.
Consider to use a retained Fragment. This approach is deeply discussed:
Understanding Fragment's setRetainInstance(boolean)
Further understanding setRetainInstance(true)
All you need to do is this:
1. Fragment class
public class RequestFragment extends Fragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// This will be a guarantee that request is sent only once,
// because fragment won't be recreated on screen rotation:
setRetainInstance(true);
// Pereform sending request here.
}
}
2. Activity class
public class MainActivity extends AppCompatActivity {
private final static TAG_FRAGMENT = "persistent_fragment";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fm = getSupportFragmentManager();
// Create fragment if it doesn't exist yet.
if (fm.findFragmentByTag(TAG_FRAGMENT) == null) {
fm.beginTransaction()
.add(new RequestFragment(), TAG_FRAGMENT)
.commit();
}
}
}
But if you strongly decided to perform the request in Application onCreate() method, you have to implement an observable object that responds to activity which is subscribed to it, because you can't access an Activity from the Application class.
You can try this:
1. ResponseObservable class
public class ResponseObservale {
private MainActivity activity;
private Response response;
public void sendRequest() {
// perform your async request here.
}
/*
* Consider this method as a point where the response is delivered.
* It can be done in onPostExecute of AsyncTask or somewhere else,
* depending on your implementation.
*/
public void onResponse(Response response) {
this.response = response;
publishResponse();
}
public void onActivityCreated(MainActivity activity) {
this.activity = activity;
if (response != null) {
publishResponse();
}
}
private void publishResponse() {
if (activity != null) {
activity.obtainResponse(response);
}
}
public void onActivityDestroy() {
activity = null;
}
}
2. Application class
public class MyApplication extends Application {
private ResponseObservable observable;
#Override
public void onCreate() {
super.onCreate();
observable = new ResponseObservable();
observable.sendRequest();
}
public ResponseObservable getObservable() {
return observable;
}
}
3. Activity class
public class MainActivity extends AppCompatActivity {
private ResponseObserbale observable;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyApplication app = (MyApplication) getApplicationContext();
observable = app.getObservable();
observable.onActivityCreated(this);
}
public void obtainResponse(Response response) {
// Manage your response here.
}
#Override
protected void onDestroy() {
observable.onActivityDestroy();
}
}
Don't forget to declare your Application class in AndroidManifest.xml:
<application
android:name="com.yournamespace.appname.MyApplication"
android:icon="#drawable/icon"
android:label="#string/app_name">
I've been using AsyncTasks for a while however, I've recently encountered a scenario where I'm unsure of how to handle correctly. Since I thought it would be a somewhat common scenario I decided to ask the question here.
So, I'm trying to use an AsyncTask to make a simple call to sign a user in to the app. After the call completes, if it succeeds, the user should be taken to another activity. This logic is simple. The problem arrises when the user navigates away from the app before the sign in call returns. In such a case, what should I do in onPostExecute()?
What I've seen some apps do is they continue with the call anyways, as long as the activity is still around, and will launch the next activity. However this creates a weird experience where the user navigates away from the app, then several seconds later, the app just pops back up in their face. Of course, I would like to avoid doing this.
Update
Example code:
public class ExampleActivity extends Activity {
private boolean mIsPaused;
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
Button btnSignIn = (Button) findViewById(R.id.btn_sign_in);
btnSignIn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
new SignInTask(ExampleActivity.this).execute();
}
});
...
}
#Override
protected void onPause() {
super.onPause();
mIsPaused = true;
}
#Override
protected void onResume() {
super.onResume();
mIsPaused = false;
}
private boolean isPaused() {
return mIsPaused;
}
...
private static class SignInTask extends AsyncTask<Void, Void, SomeResult> {
private final WeakReference<ExampleActivity> mAct;
public SignInTask(ExampleActivity act) {
mAct = new WeakReference<ExampleActivity>(act);
}
#Override
protected SomeResult doInBackground(Void... params) {
return mApi.signIn(creds);
}
#Override
protected void onPostExecute(SomeResult result) {
if (result.getCode() == OK) {
ExampleActivity act = mAct.get();
if (act != null) {
if (act.isPaused()) {
// do something
} else {
startActivity(new Intent(act, NextActivity.class));
}
} else {
// do something
}
}
}
}
}
made your AsyncTask class as static inner class.
Pretty interesting problem... Going with what you've started by using booleans, you could save the response the Activity receives to the SharedPreferences in the event it is paused, or continue processing normally if it is not. If the Activity later resumes (or is recreated), check whether or not there is a saved response and handle accordingly. I was thinking something along the lines of:
import org.json.JSONObject;
import android.app.Activity;
import android.os.Bundle;
public class TaskActivity extends Activity {
private static final String KEY_RESPONSE_JSON = "returned_response";
private boolean paused = false;
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
// don't setup here, wait for onPostResume() to figure out what to do
}
#Override
public void onPostResume(){
super.onPostResume();
paused = false;
if(isSavedResponseAvailable()) processResponse(getSavedResponse());
else setup();
}
#Override
public void onPause(){
paused = true;
super.onPause();
}
private void setup(){
// normal setup
}
public void onReceiveResponse(JSONObject response){
if(paused) setSavedResponse(response);
else processResponse(response);
}
private void processResponse(JSONObject response){
// Continue with processing as if they never left
getSharedPreferences(this.getClass().getName(), 0).edit().clear().commit(); // Clear everything so re-entering won't parse old data
}
private boolean isSavedResponseAvailable(){
return getSavedResponse() != null;
}
private JSONObject getSavedResponse(){
try{
return new JSONObject(getSharedPreferences(this.getClass().getName(), 0).getString(KEY_RESPONSE_JSON, ""));
}
catch(Exception e){ }
return null;
}
private void setSavedResponse(JSONObject response){
getSharedPreferences(this.getClass().getName(), 0).edit().putString(KEY_RESPONSE_JSON, response.toString()).commit();
}
}
Clearly that's assuming your response from the task is JSON, but there's no reason you couldn't extend that to save the data individually and rebuild the necessary response object from the saved preference data.
As far as clean approaches go, though... I give this about a 3/10, but I can't think of anything better (well, other than making the TaskActivity abstract and forcing implementations to override setup(), processResponse(), isResponseAvailable(), getSavedResponse(), and setSavedResponse(), but that would only be mildly better for like a 4/10)
I would suggest putting a try/catch statement in the post execute - as far as I know what would happen in this situation is that you would get some kind of Window Manager exception.
What I would STRONGLY recommend, however, is stopping any async tasks (with the cancel method) on the onPause method, meaning that you won't interrupt them.
http://developer.android.com/reference/android/os/AsyncTask.html#cancel(boolean)
public final boolean cancel (boolean mayInterruptIfRunning)
Added in API level 3
Attempts to cancel execution of this task. This attempt will fail if the task has already completed, already been cancelled, or could not be cancelled for some other reason. If successful, and this task has not started when cancel is called, this task should never run. If the task has already started, then the mayInterruptIfRunning parameter determines whether the thread executing this task should be interrupted in an attempt to stop the task.
Calling this method will result in onCancelled(Object) being invoked on the UI thread after doInBackground(Object[]) returns. Calling this method guarantees that onPostExecute(Object) is never invoked. After invoking this method, you should check the value returned by isCancelled() periodically from doInBackground(Object[]) to finish the task as early as possible.
Parameters
mayInterruptIfRunning true if the thread executing this task should be interrupted; otherwise, in-progress tasks are allowed to complete.
Returns
false if the task could not be cancelled, typically because it has already completed normally; true otherwise
See Also
isCancelled()
onCancelled(Object)
boolean isRunning; //set it to true in onResume, and false in onStop
boolean isWaiting; // set it to true in onPostExecute, if "isRunning" is false
check in onResume whether isWaiting is true, if yes, take user to another screen.
Use the cancel() of AsynchTask class onBackPress() of Activty class
public class ExampleActivity extends Activity {
private boolean mIsPaused;
SignInTask singleTaskObj;
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
Button btnSignIn = (Button) findViewById(R.id.btn_sign_in);
btnSignIn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
singleTaskObj = new SignInTask(ExampleActivity.this).execute();
}
});
...
}
#Override
protected void onPause() {
super.onPause();
mIsPaused = true;
}
#Override
protected void onResume() {
super.onResume();
mIsPaused = false;
}
protected void onBackPressed()
{
singleTaskObj.cancel();
}
private boolean isPaused() {
return mIsPaused;
}
...
private static class SignInTask extends AsyncTask<Void, Void, SomeResult> {
private final WeakReference<ExampleActivity> mAct;
public SignInTask(ExampleActivity act) {
mAct = new WeakReference<ExampleActivity>(act);
}
#Override
protected SomeResult doInBackground(Void... params) {
return mApi.signIn(creds);
}
#Override
protected void onPostExecute(SomeResult result) {
if (result.getCode() == OK) {
ExampleActivity act = mAct.get();
if (act != null) {
if (act.isPaused()) {
// do something
} else {
startActivity(new Intent(act, NextActivity.class));
}
} else {
// do something
}
}
}
}
}
Problem
The idea is very simple. Whenever an user comes back to my app from the Recents I want to show a simple dialog prompting with the password.
I know how to prompt the dialog with password, but my problem is how do I understand that the user has entered my app from the recents. If I put the prompt in the onResume in every activity, then it will get triggered everytime even if the user doesn't enter from the Recents menu.
There are lots of activities and fragments in my app. So, I would love to have a more generic or application level solution.
Implement Application.ActivityLifecycleCallbacks, that will provide all activity callback in your application class.
public class AppController extends Application implements
Application.ActivityLifecycleCallbacks
{
#Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(this);
}
#Override
public void onActivityCreated(Activity activity, Bundle bundle) {
}
#Override
public void onActivityStarted(Activity activity) {
}
#Override
public void onActivityResumed(Activity activity) {
}
#Override
public void onActivityPaused(Activity activity) {
}
#Override
public void onActivityStopped(Activity activity) {
}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
#Override
public void onActivityDestroyed(Activity activity) {
}
}
You could try with this flag FLAG_ACTIVITY_LAUNCHER_FROM _HISTORY:
if((getIntent().getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY )!=0){
Log.d(TAG, "Called from history");
//clear flag from history
Intent intent = getIntent().setFlags( getIntent().getFlags() & (~ Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY));
setIntent(intent);
}
Source : Android - detecting application launch from home or history
When "A" Activity is start from recent, this flag is present.
Now this flag will be also present if "A" activity call "B" activity and on "B" user press back.
So you have to check flag and when you detect it you have clear intent by removing this flag, source: Remove a Paint Flag in Android
Try below sample
/**
* TODO : After update to API level 14 (Android 4.0),
* We should implement Application.ActivityLifecycleCallbacks
*/
public class GlobalApplication extends android.app.Application
{
private boolean inForeground = true;
private int resumed = 0;
private int paused = 0;
public void onActivityResumed( Activity activity )
{
++resumed;
if( !inForeground )
{
// Don't check for foreground or background right away
// finishing an activity and starting a new one will trigger to many
// foreground <---> background switches
//
// In half a second call foregroundOrBackground
}
}
public void onActivityPaused( Activity activity )
{
++paused;
if( inForeground )
{
// Don't check for foreground or background right away
// finishing an activity and starting a new one will trigger to many
// foreground <---> background switches
//
// In half a second call foregroundOrBackground
}
}
public void foregroundOrBackground()
{
if( paused >= resumed && inForeground )
{
inForeground = false;
}
else if( resumed > paused && !inForeground )
{
inForeground = true;
}
}
}
Put below code in your all activities.
public class BaseActivity extends android.app.Activity
{
private GlobalApplication globalApplication;
#Override
protected void onCreate()
{
globalApplication = (GlobalApplication) getApplication();
}
#Override
protected void onResume()
{
super.onResume();
globalApplication.onActivityResumed(this);
}
#Override
protected void onPause()
{
super.onPause();
globalApplication.onActivityPaused(this);
}
#Override
public void onDestroy()
{
super.onDestroy();
}
}
I would suggest using LifecycleObserver. If your Application class implements this interface it marks a class as a LifecycleObserver, it does not have any methods, instead, it relies on OnLifecycleEvent annotated methods. The usage is simple:
public class AndroidApplication extends Application implements LifecycleObserver {
#OnLifecycleEvent(Lifecycle.Event.ON_START)
public void onAppStart() {
//enter code here
}
#OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public void onAppStop() {
//enter code here
}
...etc
}
With Lifecycle.Event you can access all lifecycle states through Enum. It is part of androidx.
I start AsyncTask and put Context in constructor of the Task.
On onPostExecute I want to know is my activity active now and was not recreated. How it's better to do?
For this I can:
create randomNumber in onCreate Activity and then put it in Application class;
onPause/onDestroy set randomNumber to 0 and in onResume restore randomNumber;
in onPreExecute() get randomNumber and in onPostExecute compare randomNumber with Application randomNumber.
May be I can use Context for making decision ... ?
There are many approaches to check if the activity is still there.
I usually create a SkeletonActivity with the following structure:
public class SkeletonActivity extends FragmentActivity {
private boolean paused = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
paused = false;
}
#Override
public void onPause() {
super.onPause();
paused = true;
}
#Override
public void onResume() {
super.onResume();
paused = false;
}
#Override
public void onStart() {
super.onStart();
paused = false;
}
public boolean isPaused() {
return paused;
}
}
Now let all your activities extend this SkeletonActivity. Finally change this Base Class to change the paused flag as you wish (For example update it in onDestroy()
Another way would be to have a Context instance inside your SkeletonActivity:
public class SkeletonActivity extends FragmentActivity {
private Context mContext;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;
}
#Override
protected void onDestroy() {
mContext = null;
super.onDestroy();
}
public boolean isPaused() {
return mContext==null;
}
}
Please have a look at this example. I do store the Context in the AsyncTask. During onRetainNonConfigurationInstance() I set it to null and during onCreate() I patch the Context to the currently running Activity. You can enhance that by setting "task = null" in onDestroy():
public class MyActivity extends Activity {
/* package */ MyActivity context;
private class MyAsyncTask extends AsyncTask<Void, Void, Void> {
public MyAsyncTask(final MyActivity context) {
super();
this.context = context;
}
#Override
protected Cursor doInBackground(/* ... */) {
// ...
}
#Override
protected void onPostExecute(/* ... */) {
if (context != null) {
// ...
}
}
#Override
protected void onPreExecute (/* ... */) {
// ...
}
}
/* package */ MyAsyncTask task;
#Override
public void onCreate(final Bundle bundle) {
// ...
Bundle bundleExtras = getIntent().getExtras();
if (bundleExtras != null) {
task = (MyAsyncTask) getLastNonConfigurationInstance();
if (task != null) {
task.context = this;
// ...
} else {
task = new MyAsyncTask(this);
task.execute();
}
}
}
#Override
public Object onRetainNonConfigurationInstance() {
if (task != null) {
// ...
task.context = null;
}
return task;
}
}
It will be help full for other guys.
if you press the home button or back button onStop() will called.so declare a variable inside the activity like
boolean isActivityclosed=false;
During the onStop() method set value to true.
#Override
protected void onStop() {
super.onStop();
isActivityclosed=true;
}
now, at postExcecute method check the variable is true or not. if its true then your activity is in background.