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);
}
}
}
}
```
Related
Well I wrote a program which uses internet access and makes https requests. So i had to use AsyncTask to do the request part. My issue is that in my main activity, I have a function that increments a spinner after every https request made. In my backgroundTask(that is my AsyncTask class), i have included an static integer called :resultTest which numbers every exception generated. I just want to pass a value(either 0/1 or true/false) depending on if the connection was successful to my main thread. This value that will be passed will determine if I should increment the spinner or not.
Here is the the OnPostExecute part of my backgroundTask, just to give you an idea how it works:
#Override
protected void onPostExecute(String result) {
//this method will be running on UI thread
pdLoading.dismiss();
if (resultTest == 0) {
Toast.makeText(ctx,"Successfully Recorded",Toast.LENGTH_LONG).show();
}
else{
String msg = "";
switch(resultTest){
case 1:msg = "Internal App Error";
case 2:msg="Server Problem";
case 3:msg="Server Returned Error";
case 4:msg="Connection Problem";
}
Toast.makeText(ctx,msg,Toast.LENGTH_LONG).show();
}
}
Here is the part in my main thread where I instantiated the BackgroundTask and the event which depends on the value returned:
BackgroundTaskWater backgroundTaskWater = new BackgroundTaskWater(getActivity());
backgroundTaskWater.execute(field, val);
// FIX THIS INCREMENT
if(code == 0) {
meterItemIncrement(meters);
screen.setText("");
}
How can I pass the value of the variable resultTest in the backgroundTask to the variable code in the main thread?
I have tried to use static method and getter function but failed. The value doesn't get passed
I even tried to change the variable code in the main thread to public static and passed the value from the backgroundTask but when the backgroundTask gets destroyed, the value resets. the value does not get passed.
Any idea or suggestion will be greatly appreciated!!
Can't you move this code
if(code == 0) {
meterItemIncrement(meters);
screen.setText("");
}
to onPostExecute method?
In your asyncTask create interface object and assign it in constructor:
private OnResultCallBack onResultCallBack;
public BackgroundTaskWater (Context mContext, OnResultCallBack onResultCallBack) {
this.mContext = mContext;
this.onResultCallBack= onResultCallBack;
}
Now, in onPostExecute() process you message if success or not and pass it as parameter in interface:
if (this.onResultCallBack != null) {
this.onResultCallBack.onSuccess(msg);
}
NOTE: you also have to create an interface with parameter you want as result as:
public interface OnResultCallBack{
void onSuccess(String msg);
}
while creating object of your asyncTask:
BackgroundTaskWater backgroundTaskWater = new BackgroundTaskWater(getActivity(), new OnResultCallback() {
#Override
public void onSuccess(String msg) {
// HERE YOU WILL GET YOU MESSAGE!! (AS IN MAIN THREAD)
// IF YOU WANT STATUS CODE HERE, ADD SECOND PARAMETER IN interface
}
});
backgroundTaskWater.execute(field, val);
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.
}
}
}
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 have an Android activity (Whatever.java) and I want to call into Whatever.java a protected void onDestroy method writen in my MainActivity.
onDestroy method of MainActivity.java is :
/** The final call you receive before your activity is destroyed. */
protected void onDestroy() {
DebugLog.LOGD("ImageTargets::onDestroy");
super.onDestroy();
// Cancel potentially running tasks
if (mInitQCARTask != null
&& mInitQCARTask.getStatus() != InitQCARTask.Status.FINISHED) {
mInitQCARTask.cancel(true);
mInitQCARTask = null;
}
if (mLoadTrackerTask != null
&& mLoadTrackerTask.getStatus() != LoadTrackerTask.Status.FINISHED) {
mLoadTrackerTask.cancel(true);
mLoadTrackerTask = null;
}
// Ensure that all asynchronous operations to initialize QCAR
// and loading the tracker datasets do not overlap:
synchronized (mShutdownLock) {
// Do application deinitialization in native code:
deinitApplicationNative();
// Unload texture:
mTextures.clear();
mTextures = null;
// Destroy the tracking data set:
destroyTrackerData();
// Deinit the tracker:
deinitTracker();
// Deinitialize QCAR SDK:
QCAR.deinit();
}
System.gc();
}
How can I do it please?
Thank you very much
I have a better idea: use
Activity.finish();
instead. Calling finish() will ensure that onDestroy() is called.
It is not a good idea for a programmer to invoke lifecycle methods like onDestroy(). They are meant to be overriden by the programmer, and they may be called by the OS under appropriate conditions.
I'm not saying the application would blow up. Just that you should know the distinction between methods a programmer can call and methods that are meant to be overriden. That's why finish() is a public method while onDestroy() is protected.
My problem was based on the fact that I should deinitialize my resources.
I have just deinitialized my app, calling one of its already implemented methods named QCAR.deinit();
Thank you very much by the way ZygoteInit and Squonk !
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.