I'm using a custom Database class in my code to manage my database and handle transactions. Whenever I instantiate it, I pass the application context to it's constructor. Reading up on the articles at the Android developer site makes me wonder if I'm doing something that could cause a huge memory leak in my application. Simplified, my code looks like this, first off an activity:
public class MyActivity extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.somelayout);
Database db = new Database(getApplicationContext());
}
}
And my database class (in a seperate file) looks like this:
public class Database
{
Context context;
public Database(Context context)
{
this.context = context;
}
public DatabaseHelper extends SQLiteOpenHelper
{
// Pass the context to the constructor etc etc.
}
}
The code might have bugs, I wrote it quickly in notepad. Anyway, this got me worried that the db object keeps the context when the user navigates away from the activity, thus uneccesarily spending a huge amount of resources. If this is indeed the case, how can I avoid this? Is there a way to destroy and object when it is no longer needed?
The object referenced by db is eligible for garbage collection as soon as onCreate finishes. So there is no problem here.
If you made db or Database.context into a static field, that's when you should start to worry.
If the Database object holds resources and is not closed properly you might get into trouble.
If at all possible stay at pure SQL level, and use JDBC pooling to get standard way of handling these things.
Related
I'm using an ASyncTask in my app to get some data (a short URL) via a REST API from a web service (Bitly).
When the ASyncTask completes I want to pass the result back to my MainActivity.
Getting the data back to the MainActivity is acheievd by using the onPostExecute method of the AsyncTask.
I've read and read and read about how to do this and there seem to be two general approaches.
Originally I was using a 'WeakReference' approach whereby at the start of the AsyncTask class you create a weak reference to your MainActivity as follows:
private class getShortURL extends AsyncTask<String, Void, String> {
private WeakReference<MainActivity> mainActivityWeakReference;
myASyncTask(MainActivity activity) {
mainActivityWeakReference = new WeakReference<>(activity);
}
{etc etc}
With this approach your AsyncTask class sits outside of your MainActivity class and so a lot of things need to be referenced via the weak reference.
This worked fine (except I suspected - possibly incorrectly - that this weak reference may have been the cause of occassional NPEs), but I then found another way of doing things.
This second approach involved moving the ASyncTask class inside of the MainActivity class.
This way I was able to access everything that was accessible in the MainActivity class directly, inlcuding UI elements and methods defined in the MainActivity. It also means that I can access resources such as strings etc and can generate toasts to advise the user what is happening.
In this case the whole of the WeakReference code above can be removed and the AsyncTask class can be made private.
I am also then able to do things like this directly in onPostExecute or to keep this in a method within the MainActivity that I can call directly from onPostExecute:
shorten_progress_bar.setIndeterminate(false);
shorten_progress_bar.setVisibility(View.INVISIBLE);
if (!shortURL.equals("")) {
// Set the link URL to the new short URL
short_link_url.setText(shortURL);
} else {
CommonFuncs.showMessage(getApplicationContext(), getString(R.string.unable_to_shorten_link));
short_link_url.setHint(R.string.unable_to_shorten_link);
}
(note that CommonFuncs.showMessage() is my own wrapper around the toast function to make it easier to call).
BUT, Android Studio then gives a warning that "the AsyncTask class should be static or leaks might occur".
If I make the method static I then get a warning that the method from the MainActivity that I want to call from onPostExecute cannot be called as it is non-static.
If I make that method from MainActivity a static method, then it cannot access string resources and any other methods that are non static - and down the rabbit hole I go!
The same is true, as you would expect, if I just move the code from the method in the MainActivity into the onPostExecute method.
So...
Is having an AsyncTask as a non-static method really a bad thing? (My
app seems to work fine with this warning in AS, but I obviously don't
want to be creating a memory leak in my app.
Is the WeakReference appraoch actually a more correct and safer approach?
If I use the WeakReference approach, how can I create things like toasts which need to be run on the UI thread and access string
resources etc from the MainActivity?
I read somewhere about creating an interface but got a bit lost and couldn't find that again. Also would this not have the same kind of reliance on the MainActivity that a WeakReference does and is that a bad thing?
I'm really looking for best practice guidance on how to get some data back to the MainActivity and the UI thread from an AsyncTask that is safe and doesn't risk memory leaks.
Is having an AsyncTask as a non-static method really a bad thing? (My app seems to work fine with this warning in AS, but I obviously don't want to be creating a memory leak in my app.
Yes, your Views and your Context will leak.
Enough rotations and your app will crash.
Is the WeakReference approach actually a more correct and safer approach?
It's lipstick on a dead pig, WeakReference in this scenario is more-so a hack than a solution, definitely not the correct solution.
What you're looking for is a form of event bus from something that outlives the Activity.
You can use either retained fragments* or Android Architecture Component ViewModel for that.
And you'll probably need to introduce Observer pattern (but not necessarily LiveData).
If I use the WeakReference approach, how can I create things like toasts which need to be run on the UI thread and access string resources etc from the MainActivity?
Don't run that sort of thing in doInBackground().
I'm really looking for best practice guidance on how to get some data back to the MainActivity and the UI thread from an AsyncTask that is safe and doesn't risk memory leaks.
The simplest way to do that would be to use this library (or write something that does the same thing yourself, up to you), put the EventEmitter into a ViewModel, then subscribe/unsubscribe to this EventEmitter inside your Activity.
public class MyViewModel: ViewModel() {
private final EventEmitter<String> testFullUrlReachableEmitter = new EventEmitter<>();
public final EventSource<String> getTestFullUrlReachable() {
return testFullUrlReachableEmitter;
}
public void checkReachable() {
new testFullURLreachable().execute()
}
private class testFullURLreachable extends AsyncTask<Void, Void, String> {
...
#Override
public void onPostExecute(String result) {
testFullUrlReachableEmitter.emit(result);
}
}
}
And in your Activity/Fragment
private MyViewModel viewModel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
// ...
}
private EventSource.NotificationToken subscription;
#Override
protected void onStart() {
super.onStart();
subscription = viewModel.getTestFullUrlReachable().startListening((result) -> {
// do `onPostExecute` things here
});
}
#Override
protected void onStop() {
super.onStop();
if(subscription != null) {
subscription.stopListening();
subscription = null;
}
}
Anyone who has ever worked with Android Studio knows that it has a very helpful code linting capacity that helps programmers avoid common anti-patterns and mistakes.
Such a system is sometimes annoying however, and in this particular case I think it's just being that.
I have a AsyncTask like the follwing
class MyAsyncTask extends AsyncTask<Void, Void, MyDataType> {
private Context context;
MyAsyncTask(Context _context) {
context = _context;
}
#Override
protected void onPreExecute() {
// Show a progress dialog or something
// to indicate that we're doing some work
// here.
}
#Override
protected MyDataType doInBackground(Void... args) {
return generateData(); // returns `MyDataType` of course
}
#Override
protected void onPostExecute(MyDataType data) {
// Deliver the data and then
context = null;
}
}
And of course, Android Studio is kindly telling me that the context field leaks a Context object.
My questions is,
is this really leaking the Context object? or,
is context = null; guaranteed to run and thus there is no leak?
This is different than Warning: This AsyncTask class should be static or leaks might occur because this is NOT A non-static inner class of a Context like in that case.
note: I am not a Java programmer, and I don't understand garbage collection very well. I have mostly worked with c and there I know how memory is managed. Garbage collected languages make me uncomfortable because I don't know how this "garbage collection" occurs.
Yes it is leaking of Context, because your Context object can't be garbage collected before your AsyncTask wasn't terminated.
doInBackground() method can proceed some large operation and before terminating your Context will be still alive.
Good practice to wrap your Context object into WeakReference so if this object can be accessed only by WeakReference's it will be collected.
I have an application class and it holds a reference of MyAdapter class:
public class MyApplication extends Application {
......
private static MyAdapter sMyAdapter;
public static MyAdapter getMyAdapter() {
if (sMyAdapter == null) {
sMyAdapter = new MyAdapter(this);
MyApplication.setMyAdapter(sMyAdapter);
}
return sMyAdapter;
}
public static void setMyAdapter(MyAdapter myAdapter) {
sMyAdapter = myAdapter;
}
......
}
MyAdapter class is a customized android adapter class, and the application Context is passed to the Adapter. The application holds a reference of it because it may be used anytime till the application is still running.
The problem is that now I need an Activity Context in the Adapter to start another Activity when some button is clicked because if I use application Context I need to add a Intent flag FLAG_ACTIVITY_NEW_TASK which I don't want to because that way the new Activity being started will be running in a new task. I tried a lot with changing launch mode and taskAffinity but either new issues came up or the Activity will be running in a new task.
So I am thinking to hold an Activity reference which shows the button in the Adapter class, and to avoid memory leak, I came up with the following:
public class MyActivity extends Activity {
......
#override
public void onResume() {
......
MyApplication.getMyAdapter().setActivity(this);
......
}
......
#override
public void onDestroy() {
......
MyApplication.getMyAdapter().setActivity(null);
......
}
}
Then in the Adapter class I will use the Activity reference to start another Activity. I tested and this worked fine but the question is would this avoid memory leak and is this a proper way to hold the Activity reference when onResume and release it when onDestroy? Is there any other decent way to achieve my purpose? Thanks.
would this avoid memory leak
Not really. You MyApplication object will still keep a reference to your adapter with all it's 'contents' (further references).
Yes, you've got rid of keeping the destroyed Activity around, and you might feel it's okay to keep the adapter because you're 'going to need it again anyways', still this whole construct is a horrible code smell and will with certainty introduce new memory leaks and other problems as you develop this further.
Logically, this adapter is part of an Activity, and as the Activity 'dies', so should the adapter.
I'm sure there's a reason why you felt you needed that adapter on your application, so I'd post another question asking 'how can I achieve soandso without my application knowing of my adapter'.
I had an Android application(MyApp, say) that used ApplicationContext extensively. The ApplicationContext was made available via a class that extended Application.
class MyApp extends Application {
static Context mContext = null;
public void onCreate() {
mContext = getApplicationContext();
}
public static Context getContext() {
return mContext;
}
}
I would like to convert this application to a library and use it in another application ( AnotherApp, say). Now I see that AnotherApp already has a similar class that extends Application and gives its context everywhere within itself. When I move MyApp as a library into AnotherApp, my code will not be able to get ApplicationContext anymore - as only one class can be declared in Manifest (android:name=".AnotherApp")
What is the best way to make application context available within library? I do not mind making extensive code changes, but would like to know options I have - other than passing context to every api in my library.
A library should never use the getApplicationCOntext it is meant for the main program.
you can pass the context using a function or save it into a public static variable in your class in the beginning.
Note about code design
This completely depends on how you want to use the library, will the functions be used in the main app ? Will the activity be directly called ? and a bunch of other code design things like that.
If you want two activities sometimes the best way it to make them into separate applications and make one call the other using an Intent. Like how google maps and other inbuilt services are used.
If you want to use your library's function in the main application, you should not be creating an activity at all. Rather you should make an abstract class that the user can inherit from and use your class through.
I have done similar stuffs in my Application.
Its true you will not able to get context in your library
You will have context of only AnotherApp
If you want to use Context in your library in that case you need to have some method which can pass your AnotherApp's context to your library.
For example
class MyApp extends Application {
static Context mContext = null;
public void onCreate() {
mContext = getApplicationContext();
objecofYourLibClass = new MyApp();
objecofYourLibClass.yourMethod(mContext);
}
}
Now you will able to use context in your Library.
I am trying to generate a notification from a class, Utilities.java, outside of the subclass of Context. I've thought about providing a SingletonContext class and have looked at posts ike this. I'd like to be able to return != null Context object since the notification can be generated at any given time because it is generated from a messageReceived() callback.
What are there downsides to doing something like this:
public static Context c;
public class MainActivity extends Activity{
#Override
public void onStart()
super.onStart()
c = this.getApplicationContext();
}
//other method somewhere outside this class
public Context getContext(){
return MainActivity.c
}
I don't think it would be any different than putting this on the onCreate(), however, it guarantees that the context is up to date when the activity starts.
The Context keeps a reference to this activity in memory, which you might not want. Perhaps use
this.getApplicationContext();
instead. This will still let you do file IO and most other things a context requires. Without a specific reference to this activity.
Maybe you should overwrite the onResume Method.
If you open a new activity, and switch back, the onStart method will not getting invoked.
Android Lifecycle: doc
BTW: I read about problems with ApplicationContext using a dialog or toast, so if you use the context to create on of these you should use your Activity as context.