I need to show an Activity each time a user is inactive for X amount of time. I am trying to achieve that with a custom CountDownTimer, which starts onUserInteraction in my BaseActivity:
#Override
public void onUserInteraction() {
super.onUserInteraction();
inactivityTimer.cancel();
inactivityTimer.start();
}
In my custom CountDownTimer, I start the desired Activity onFinish:
#Override
public void onFinish() {
BaseActivity baseActivity = new BaseActivity();
Log.i("TIMER ENDED: ", "NOW STARTING LOCKACTIVITY");
baseActivity.showLock();
}
And this is my showLock() method in BaseActivity
public void showLock() {
Intent intent = new Intent(getApplicationContext(), LockActivity.class);
startActivity(intent);
}
What I'm getting is a NPE every time the timer ends. (java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object reference) even though I tried using getApplication().getBaseContext(), this, this.getBaseContext(), this.getApplicationContext(), getApplicationContext()and
getBaseContext() instead of getApplicationContext()
However, if I set the Context in the method call like this:
#Override
public void onFinish() {
BaseActivity baseActivity = new BaseActivity();
Context context = MyApplication.getInstance().getApplicationContext();
Log.i("TIMER ENDED: ", "NOW STARTING LOCKACTIVITY");
baseActivity.showLock(context);
}
And this in showLock():
public void showLock(Context context) {
Intent intent = new Intent(context, LockActivity.class);
startActivity(intent);
}
This time I get another NPE (java.lang.NullPointerException: Attempt to invoke virtual method 'android.app.ActivityThread$ApplicationThread android.app.ActivityThread.getApplicationThread()' on a null object reference).
So my question is, how do I get a proper Context every time my showLock() is called from the current Activity, which will be calling that method?
Important note: each and every Activity in my project inherits BaseActivity, which on its own inherits AppCompatActivity.
EDIT
I gave Marcin's suggestion a try and after dealing with a couple of errors I ended up using his approach. If someone else is curious and wants to know how to open an activity after X amount of inactivity this worked for me:
Since all my Activities inherit one main BaseActivity I put there a custom Handler, which holds a WeakReference to said BaseActivity. I also overrode handleMessage, where I call my desired method:
private static class InactivityHandler extends Handler {
private WeakReference<BaseActivity> baseActivityWeakReference;
private InactivityHandler(BaseActivity baseActivity) {
baseActivityWeakReference = new WeakReference<>(baseActivity);
}
#Override
public void handleMessage(Message msg) {
BaseActivity baseActivity = baseActivityWeakReference.get();
if (baseActivity != null) {
baseActivity.showLock();
}
}
}
and in onUserInteraction send a Message to the queue after some time:
#Override
public void onUserInteraction() {
super.onUserInteraction();
inactivityHandler.removeMessages(MESSAGE_WHAT, MESSAGE_TOKEN);
inactivityHandler.sendMessageDelayed(inactivityHandler.obtainMessage(MESSAGE_WHAT, MESSAGE_TOKEN), DELAY_TIME);
}
And for the curious, here is my showLock method:
public void showLock() {
Intent intent = new Intent(this, LockActivity.class);
startActivity(intent);
}
From you description I assume that after the user is inactive for some time your app needs to present a lock screen where the user needs to reenter their credentials.
Unless the whole scenario has any counting involved (for example you display an actual count down), a CountDownTimer may not be the best to perform this task.
Instead you could use a Handler. In Android, the Main Thread has it's associated message queue. Handlers are able to post messages to this queue to receive them later, at the given time.
Your example implementation could look like that:
private static class LockScreenHandler extends Handler {
private WeakReference<BaseActivity> activityRef;
public LockScreenHandler(BaseActivity activity) {
activityRef = new WeakReference<>(activity);
}
#Override public handleMessage(Message msg) {
BaseActivity activity = activityRef.get();
if (activity != null) {
activity.showLock();
} // Otherwise the activity got destroyed in the meantime
}
}
You may send either Runnables or Messages with the Handler. In our case a Message is perfectly fine. Therefore in your Base Activity you may have some Message-related fields:
private static final int MESSAGE_WHAT = 1;
private static final Object MESSAGE_TOKEN = new Object();
And then you use your handler in onUserInteraction:
#Override public void onUserInteraction() {
super.onUserInteraction();
handler.removeMessages(MESSAGE_WHAT, MESSAGE_TOKEN);
handler.postDelayed(handler.obtainMessage(MESSAGE_WHAT, MESSAGE_TOKEN), INACTIVITY_DELAY);
}
If you decide to follow your CountDownTimer solution you may follow the same technique, by creating a static inner class and giving your activity in the constructor.
Whichever way you go, it is important to note, that your BaseActivity can be destroyed by the system and improper usage of Handler (and CountDownTimer which internally relies on Handler) can prevent the reference to this activity from being destroyed and therefore lead to a memory leak. Therefore:
If you use a Handler or a CountDownTimer as an inner class, make sure it is static. Non-static inner classes hold a reference to their enclosing classes. Messages hold references to their target Handlers, so as long as the message is in the queue it's handler cannot get destroyed.
Use a WeakReference to hold your activity for the same reason as outlined above. WeakReferences are cleared if nothing else holds a reference to the given object.
An Activity is a Context itself. So just use this within an Activity.
public void showLock() {
Intent intent = new Intent(this, LockActivity.class);
startActivity(intent);
}
If this is not an option because you are overriding a function you should use MainActivity.this (when the MainActivity is the name of your activity)
MainActivity.this.startActivity(MainActivity.this, ...);
Related
I am having trouble saving the state/singleton of my application.
When the application starts a loading screen (activity) is shown and a singleton is initialized with values from a webservice call (note that network access cannot run on the main thread).
After the singleton is created I open my main activity. Note that values from the singleton are required to build the layout.
Now assume the app goes in the background and is killed there (e.g. because of low memory). My singleton instance is deleted as the app is killed. When I switch back to my app it tries to recreate the main activity. As I mentioned earlier the values from the singleton are required to build the layout, so this leads to a NullPointerException (when I try to access members of the singleton, as it is not there anymore).
Can I somehow tell android to start the first loading activity after the app was killed? It would be great if I could refresh the singleton before the layout is recreated, but this seems to be a problem as network calls can not be on the main thread and therefore not block until the refresh is finished.
I assume that I could save the singleton in all activities onStop and recreate it in the onCreate methods, but this seems a bit too unpredictable and would probably lead to a inconsistent state...
Another way could be to just always finish my activity onStop, but this would lead to losing on which tab the user last and so on, even if the app is not killed, so this is not a good option.
Any ideas on how to solve this?
Why not just use a SharedPreferences instead of a singleton?
Anytime you want to save some global state, commit it to preferences. Anytime you want to read the global state, read it back from preferences.
Then you don't have to concern yourself with application lifecycle at all, as your data will always be preserved regardless of what the phone is doing.
For something like that I used a pseudo singelton object as a Application class. This object will be created on the beginning and will be in the memory. But note that the system will terminate the application if the memory is needed by other applications. However this object is persitent even if all activities are temporally terminated.
To use that you need to declare that in your android manifest like here:
<application android:label="#string/app_name"
android:icon="#drawable/icon"
android:description="#string/desc"
android:name=".MySingeltonClass"
...
Here is a code example:
public abstract class MySingeltonClass extends Application {
// ...
public void informClientOnline() {
clientOnline=true;
Log.v(LOG_TAG, "Client is online!");
}
public void informClientShutdown() {
clientOnline=false;
Log.v(LOG_TAG, "Client is going offline. Waiting for restart...");
Timer t=new Timer("shutdowntimer", false);
t.schedule(new TimerTask() {
#Override
public void run() {
if(!clientOnline) {
Log.v(LOG_TAG, "Client has not restartet! Shutting down framework.");
shutdown();
System.exit(0);
}
}
}, 5000);
}
}
this two functions are called like this:
((MySingeltonClass)getApplicationContext()).informClientOnline();
You could save your Singleton when onSaveInstanceState() in the Activity gets called. All you need to do is to make it implement Parcelable (it's Androids own form of serialization), then you can put it in the outState Bundle in onSaveInstanceState() which will allow you to retrieve it laver in onCreate() or onRestoreInstanceState() in the Activity, whichever you like.
I've included an example for you:
public class TestActivity extends Activity {
private MySingleton singleton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState.containsKey("singleton")) {
singleton = savedInstanceState.getParcelable("singleton");
} else {
singleton = MySingleton.getInstance(5);
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable("singleton", singleton);
}
public static class MySingleton implements Parcelable {
private static MySingleton instance;
private int myData;
private MySingleton(int data) {
myData = data;
}
public static MySingleton getInstance(int initdata) {
if(instance == null) {
instance = new MySingleton(initdata);
}
return instance;
}
public static final Parcelable.Creator<MySingleton> CREATOR = new Creator<TestActivity.MySingleton>() {
#Override
public MySingleton[] newArray(int size) {
return new MySingleton[size];
}
#Override
public MySingleton createFromParcel(Parcel source) {
return new MySingleton(source.readInt());
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeInt(myData);
}
}
}
I would like to know if it's possible to clear all activities from an old one. I would like to use enventBus to do this.
Example of a stack of activities:
startActivity(A) then startActivity(B) then startActivity(C) then startActivity(D)...
Activity B is registered onEvent(ClearStackFromHere()) with eventBus.
And from Activity D I want to post the event post(new ClearStackFromHere) with eventBus too.
So, is it possible to clear the stack of activities from B ?
What should I write inside my ClearStackFromHere().
Thanks,
I have implemented a similar solution in one of my projects.
What I needed was a way to keep only the most recent 3 activities in the back stack, and clear the others before them. This only applies to a certain Navigation flow within my application where it becomes possible that an infinite amount of Activities can be added to the back stack.
e.g. A opens B - which opens C, C can then open another instance of A or B... etc.
I should note that this solution uses EventBus 2.4.0 and there may be a better way to implement it with 3.0+.
First off, I defined a helper called ActivityTracker. It keeps track of what Activities are currently active, as well as an identifier for each activity. It also has methods that can be called to finish all activities in the back stack except for the most recent n amount.
public class ActivityTracker {
private static ArrayList<String> activityStack = new ArrayList<>();
//Notify the Tracker of a new Activity to track
public static void activityActive(String uuid){
addToBackStack(uuid);
}
//Notify the tracker of an Activity that should no longer be tracked
public static void finishing(String uuid){
removeFromBackStack(uuid);
}
//Call this to clear entire back stack
public static void killAllBackStackActivities(){
killPreviousActivities(0);
}
//Call this to clear back stack while keeping most recent X amount
private static void killPreviousActivities(int keepAmount){
if(activityStack.size() <= keepAmount) {
return;
}
//Copy to not manipulate while looping.
String[] tempList = activityStack.toArray(new String[activityStack.size()]);
int counter = activityStack.size();
for(String id : tempList){
if(counter == keepAmount){
return;
}
counter--;
//Send notification to kill specific activity
EventBus.getDefault().post(new ActivityShouldDieEvent(id));
}
}
private static void addToBackStack(String uuid){
if(!activityStack.contains(uuid)){
activityStack.add(uuid);
killPreviousActivities(3); //Always kill all activities except most recent 3.
}
}
private static void removeFromBackStack(String uuid){
if(activityStack.contains(uuid))
activityStack.remove(uuid);
}
}
Then, I defined a subclass of AppCompatActivity called BackStackTrackActivity. All relevant Activities in the app extend this class. The subclass looks like this:
public class BackStackTrackActivity extends AppCompatActivity {
//Random ID for activity to be identified by
protected String uuid = UUID.randomUUID().toString();
//Receive notification that activity should finish
public void onEvent(ActivityShouldDieEvent ev){
if(ev.getUuid().equals(this.uuid)){
finish();
}
}
#Override
protected void onDestroy() {
super.onDestroy();
//Unregister from EventBus
EventBus.getDefault().unregister(this);
//Tell tracker to stop tracking
ActivityTracker.finishing(uuid);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Register for events
EventBus.getDefault().register(this);
//Tell tracker to track activity
ActivityTracker.activityActive(uuid);
}
}
With some work, I think you will be able to adapt this solution into something that meets your needs.
I hope that helps.
I am sorry for my bad english skills. I'm new to programming/stackoverflow and try to create a little android quiz app. This app has to connect to a php server and login/getquestion...
The simplest example is the login. The user has to type in his data and then i have to connect.
To provide that the Gui doesnt freeze i have to use asynchronous tasks.
Here the activity's code:
public void login(final String username, final String password) {
final Activity a = this;
FutureTask t = new FutureTask(new Callable() {
public Object call() {
Connection.GetInstance(a).login(username,password);
afterLoginTry(username,password);
return null;
}
});
t.run();
}
This calls a method in another class, which calls another FutureTask which calls an AsyncTask. At the end there is always an public synchronized method such as afterlogintry(). This works but it's a bit slow and i think dirty code.
My main problem is that i don't know how to give results back through different layers of classes and especially to the activity without using hotfixes all the time.
Is there any good explanation or tutorial, which describes how to design such a construct?
Thx for help
The way you can pass AsyncTask results back to other classes, is by declaring callbacks for the task, that will then report the result to a listener. Here is how it works.
First, you must declare an interface in your AsyncTask which contains a method that will send out the result of the task. So in my example task below, my result is a String. The String gets passed to onPostExecute() when the task finishes its work. I then call my callback method on a registered listener, and pass that return value on to whoever is listening for it. You register a listener by passing in an instance of your callbacks from whichever class is creating the task.
public class MyTask extends AsyncTask<String, Void, String> {
MyTaskCallback listener;
public MyTask(MyTaskCallback listener) {
this.listener = listener;
}
protected String doInBackground(String... params) {
String input = params[0];
//do work
input += "did some work on this String";
return input;
}
//When the thread finishes its work, this gets
//called on the main UI thread
protected void onPostExecute(String result) {
listener.onResultReceived(result);
}
public interface MyTaskCallback {
void onResultReceived(String result);
}
}
So next we need to register a listener for these callbacks, so when the result comes in from the task, it will get reported directly to our class. So let's say we have a simple Activity. The way we register the callbacks is to use the implements keyword on our class declaration, and then to actually implement the callback method in the class itself. We then create our task, and we pass in this which is our Activity that implements the callbacks. A simple example Activity that does this looks like this:
public class TaskActivity extends AppCompatActivity implements MyTask.MyTaskCallback {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout);
//we pass in "this" because our Activity itself
//implements the callbacks below.
MyTask myTask = new MyTask(this);
myTask.execute();
}
//Here we implement our callback method, so the task
//can send its results straight through here
public void onResultReceived(String theResult) {
Log.d("TASK RESULT", "Here is our result String: "+theResult);
}
}
Now, our task has our Activity connected to it, through the callbacks we passed into it. So now when our task gets a result, we can send it directly to our listener, which is our Activity, and the result will come right through to our implemented onResultReceived method.
Callbacks are a great way to pass information around between classes while also keeping everything very separated. Hope this helps!
this is my current code
public class ButtonActivity extends Activity {
int count = 0;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
button.setText("Got Pressed:" + ++count);
}
});
}
}
My question why after each call of onDestroy() and the subsequent call of onCreate(), the count gets reset. I looked up on onDestroy() and saw "that is there to let your app have a final chance to clean things up before the activity does get destroyed but it does not mean that the activity will be called" My initial idea was that count is an instance variable and that onCreate() creates a new instance of my class ButtonActivity. Can anyone confirm or refute my intial thoughts ?
If you don't want it reset - make it static: currently the counter is per object
Making count static will solve your problem. (because static member don't belong to a particular instance)
But to be more precise : onCreate don't create a new instance of your activity. In fact : Android-OS create a new instance and give you a chance to initialize it by calling onCreate(...).
And, more generally : you can be sure that onCreate() will never be called twice on the same instance: if you see that onCreate() is called it's because Android-OS has just created a new instance of your Activity (it can be done for many reason, like screen rotation, ...).
More about activity lifecycle here (probably one of the most important concept to understand when you do Android development !)
Storing static data in your activity class, as was suggested here, is a bad practice
and should be avoided.
Why?
Because Android may destroy background activities TOGETHER with their static data
whenever it feels resources are running low. Thus you may declare static fields inside you activity, but you will not get the static behavior you aim for,
What can you do instead:
Option 1:
Create a custom Application class and place your static data there. You will
not even have to declare it as static since Application obj is a singleton and is
guaranteed to stay in memory for as long the the app is alive.
public class MyApplication extends Application {
private DataClass mydata;
// probably with a getter & setter
}
Remember to declare your activity in the manifest:
<application
android:name="mypackage.MyApplication" <--------
.....
Option 2: Serialize your data to shared preferences. This is what they are for.
I'm working with a fairly common situation right now - download some data over the web, then update a view to display it. Clearly, I want to do the web download in the background, and then update the view on the main UI thread. Now looking at my code, I'm a little worried about my Activity and its UI elements being killed off before I update them. Here's the essence of what I have in mind:
Thread update = new Thread() {
public void run() {
final Data newData = requestData();
if (newData != null) {
post(new Runnable() {
public void run() {
Toast.makeText(MyClass.this, "I'll do things here that depend on my context and views being valid", Toast.LENGTH_SHORT).show();
}
});
}
}
};
update.start();
It seems possible that while I'm downloading data, the activity may be destroyed. What happens then? Will my thread continue to execute? Will I end up trying to access dead objects?
Usually I do this by AsycTask, but the work seemed simple enough this time to just inline the threads-launching-threads stuff. Will I make things any better by using an AsyncTask instead?
If your Context is an Activity, you can check if it is finishing or has finished with the isFinishing() method:
if ( context instanceof Activity ) {
Activity activity = (Activity)context;
if ( activity.isFinishing() ) {
return;
}
}
Toast.makeText(context, "I'll do things here that depend on my context and views being valid", Toast.LENGTH_SHORT).show();
What you really want to use is an AsyncTaskLoader. These are my new favorite classes in the Android API. I use them all the time and they were made to solve problems just like this. You won't have to worry about when to stop your download or anything like that. All the threading logic is taken care of for you, including telling the thread to stop if the activity has been closed. Just say what it is you want to do in the loadInBackground() method. Note that if you are developing for an API lower than 3.0, you can still access all the loaders via the Android Support Package.
If you use anonymous classes, they will have an internal reference to the outer class, so it's not like it becomes inaccessible all of a sudden because other references have been cleared. AsyncTask actually doesn't change anything, it uses similar mechanics for notifying about results.
You can use loaders, they are designed to be in sync with the activity lifecycle. They are available only since Android 3.0, but you can use support package to work with them on any device with 1.6 or later.
There is even a simpler solution, you can just use a boolean field which indicates whether activity has gone away. You should set this field in onPause() (or whenever you think you won't need the notifications anymore) and check for it when you show toast. You won't even have to use synchronization, since this field is confined to the main thread, so it's absolutely safe. By the way, if you change this field somewhere else than in onDestroy(), don't forget to add a statement which resets your field back in the counterpart method.
public class MyActivity extends Activity {
private boolean activityDestroyed = false;
#Override
protected void onDestroy() {
activityDestroyed = true;
}
private void updateData() {
new Thread() {
#Override
public void run() {
final Data newData = requestData();
if (newData == null) return;
runOnUiThread(new Runnable() {
public void run() {
if (activityDestroyed) return;
Toast.makeText(MyActivity.this, "Blah",
Toast.LENGTH_SHORT).show();
}
});
}
}.start();
}
}
I usually use Weak Reference to avoid leaking context in views
Weak Reference for Context
private var mContext: WeakReference<Context?>? = null
Assign Context
mContext = WeakReference(appContext)
Get Context
mContext .get()
Validate Context
if (mContext?.get() is Activity &&
(mContext?.get() as Activity).isFinishing){
return
}
Kurtis is right. However, if you REALLY want to keep it simple, you can try this:
class MyActivity extends Activity {
static MyActivity context;
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
MyActivity.context = this;
}
#Override
public void onDestroy() {
super.onDestroy();
MyActivity.context = null;
}
}
And then you just use MyActivity.context in your class (and check for null there). If you want the toast to not even show up when your app is in the background, use onPause/onResume instead.
Again, this is the quick and lazy approach. AsyncTask or AsyncTaskLoader is how you should be doing things.