When you have 2 activities ( A and B ) and A is the MainActivity, now you start your App and A starts B.
B is a activity with a Dialog interacting with the user, creating a Bluetooth Connection and 2 Threads, 1 Receiving and one Sending.
Now, what is the best way to send the Information from A to the threads of B and the other way round?
First I used a static WeakReference, but I heard this causes a lot of problems, so I want to ask for a more common solution.
Please keep in mind, when starting an Activity from another Activity, you can only pass Serializable Objs and simple data. So it is not possible to use a Handler that way.
Here is the static WeakReference I used:
public class T1 extends Thread{
private static WeakReference<T1> weak_T1;
public void T1 (){
weak_T1 = new WeakReference<T1> (This);
}
public static WeakReference getWeakReverence() {
return weak_T1;
}
}
Here is a way to look for a running Thread in the stack:
for (Thread thread : Thread.getAllStackTraces().keySet()) {
if (thread.getName().equalsIgnoreCase("T1")){
T1A =thread;
}else if (thread.getName().equalsIgnoreCase("T2")){
T2A =thread;
}
}
Also possible solution:
public class example extends Thread {
private static example instance;
private example() {
}
public static example getIsntance(){
if(instance == null){
instance = new example();
}
return instance;
}
}
A WeakReference probably isn't what you want, here. That is, presuming that your Thread object either does not terminate, or somehow maintains some information useful to Activity A after Activity B has stopped. If you use a WeakReference, it might become "null" as soon as Activity B ends, and the thread terminates. Just use regular old strong references. It'll ensure T1, and the information contains, continues to exist until you're done with it.
public class ActivityB extends Activity
{
private T1 t1;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
t1 = startMyThread();
}
#Override
public void onBackPressed()
{
ActivityA.tempT1 = t1;
//This technique presumes that Activity A is guaranteed to resume after a
//back button press, based on the arrangement of your backstack, etc. If
//Activity A is started via some other means (e.g., an explicit startActivity(),
//finish(), etc.), then this reference will have to be set prior to
//that call, as well, in order to establish the appropriate "happens before" relationship.
//If you fail to ensure that Activity A resumes after this point, you will
//risk a memory leak.
super.onBackPressed();
}
}
public class ActivityA extends Activity
{
public static T1 tempT1 = null;
private T1 t1;
#Override
public void onResume()
{
super.onResume();
if(tempT1 == null)
{
//Apparently, Activity B hasn't executed yet. Provide the user with a button to start it.
}
else
{
t1 = tempT1;
tempT1 = null; //To avoid a memory leak
//We just retrieved the reference that Activity B left for us.
//Now, change UI states so that the user can see information about t1.
}
}
}
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 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, ...);
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 have got a class that extends IntentService, let's say class A, and then I have got 2 more classes as class B and class C, these classes extends class A. The reason is because I want to initialize everything in class A so that code would be reused in class B and C. So class B and C's onHandleIntent method looks like:
#Override
protected void onHandleIntent(Intent intent) {
super.onHandleIntent(intent);
//Specific init stuff
}
What I want to do is simply do some concurrency checks at class A's either constructor or onHandleIntent. For example when method x is being used(doesn't matter which class uses it) I would like to be able to flag this method being used. Sometimes one method schedules another thread by using Handler.postDelayed, so I want to make sure that flag is kept as in use until the thead work is finished.
So far it sounds like it can be done easily by shared singleton, however each of these classes have their own AlarmManager which extends BroadcastReceiver, so using a shared singleton doesn't really work as lifecycle existence of a singleton is not guaranteed.
The only solution that I can think of is using a database or local file system, which sounds quite silly. So is there another way to flag concurrent methods among different IntentService that are being triggered by AlarmManager?
To give a clear image I'd like to give an example of class A, class B and class C
public class A extends IntentService {
public A(String name) {
super(name);
//initialise objects here
}
#Override
protected void onHandleIntent(Intent intent) {
//initialise objects that uses intent here
}
protected synchronized void methodA() {
//wait until flagB is free
flagA = in_use; //Flag something as being in use here
//change some objects here
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
//do some stuff with objects
flagB = not_in_use; //Flag something as being free here
}
}, (1000 * 45));// 45secs
}
protected synchronized void methodB() {
if (flagA == not_in_use) {
flagB = in_use;
//do some stuff with objects
flagB = not_in_use;
}
else {
//do something else
}
}
public class B extends IntentService {
public A(String name) {
super("B");//Super initializes everything
}
#Override
protected void onHandleIntent(Intent intent) {//This will run every 30 minutes
super.onHandleIntent(intent);
methodA();
}
}
public class C extends IntentService {
public A(String name) {
super("C");//Super initializes everything
}
#Override
protected void onHandleIntent(Intent intent) {//This will run every 45 minutes
super.onHandleIntent(intent);
methodB();
}
}
So basically class B and C doesn't contain any private classes. What i want to do is just like the example above, flag things.
The problem is as I mentioned if it is just a static field, then static field might not stay in the memory as Android OS can kill anything inactive for saving memory.
What I want to do is simply do some concurrency checks at class A's either constructor
Don't block in a constructor. Creating an object should be a free operation. In case of IntentService you would probably get an ANR because the object is constructed from within the main thread.
or onHandleIntent
Yes. That would be a place where you could do that because that's where something actually happens. And it is executed within a background thread so blocking here is ok.
To synchronize between multiple instances of a class (including subclasses) you will need to use something static because that's the only common thing that multiple instances "share".
Synchronizing outside of the lifetime of your app is also (probably) not required. If your app process is killed and restarted from AlarmManager you would have a new singleton to work with. Anything that happened before is dead and so nothing can happen in parallel.
For example synchronize using the .class object of A
public class AService extends IntentService {
public AService() {
super("AService");
}
#Override
protected void onHandleIntent(Intent intent) {
synchronized (AService.class) {
// magic happens here.
}
}
}
class BService extends AService {
void foo() {
synchronized(AService.class) {
// we can't be in here if another intentservice is within above block.
}
}
}
That does work as long as those services are running within the same app process (i.e. every simple app that does not define extra processes). static is globally accessible everywhere in the app.
External locking using e.g. the filesystem would be required if you have multiple processes since they can't access each other's objects at all.
besides synchronized, look into java.util.concurrent for more powerful / better ways of synchronizing. E.g. `Semaphore and ReentrantLock (roughly equivalent to synchronized)
I want to make sure that flag is kept as in use until the thead work is finished.
If you app is killed, you'll have to restart the work if it did not finish. So keeping a "work-progress-flag" beyond the lifetime of your app makes no sense. And static (e.g. your singleton) works fine while it is running.
I am making an Android mobile app. I have a WeakReference to my Activity in the AsyncTask to ensure that it can be garbage collected.
When onPostExecute() gets called, I do
Acitivty activity = mWeakRef.get();
Then I use the activity object to display dialogs to the user etc etc.
My question is, as I am trying to determine which dialog to show and what to do, could my activity object become null? Could it ever become null if the GC runs in between my line of execution? Am I safe to keep using that object from the first get() or do I have to redo get() and check if the value is null right before I use it.
thanks!
It's safe!
As soon as you assign the result of get() to a variable, you have a strong reference again which blocks gargbage collection for this object as long as the new reference exists.
Of course, after this assignment you need to check if activity is null.
I think it's NOT safe. I get a NPE at activity.msgWebView.setVisibility(View.GONE); inside Handler.
```java
private static class HttpStatusHandler extends Handler {
private WeakReference<MessageWebViewActivity> activityWeakReference;
public HttpStatusHandler(WeakReference<MessageWebViewActivity> activityWeakReference) {
this.activityWeakReference = activityWeakReference;
}
#Override
public void handleMessage(Message msg) {
MessageWebViewActivity activity = activityWeakReference.get();
if (activity != null) {
if (msg.what == MSG_URL_OK) {
activity.loadUrl(activity.url);
} else if (msg.what == MSG_URL_ERROR) {
activity.msgWebView.setVisibility(View.GONE);
activity.clPageError.setVisibility(View.VISIBLE);
activity.progressbarLayout.setVisibility(View.GONE);
}
}
}
}
```