public class MyClass {
private static MyClass instance = null;
private MyActivity myActivity;
private Button button;
public static MyClass getInstance(){
if (instance == null){
instance = new MyClass();
}
return instance;
}
private MyClass(){};
public void initialize(MyActivity activity){
myActivity = activity;
}
public void releaseMemory(){
instance = null;
}
}
Here in this approach whenver my application exits then
I can release memory by calling releaseMemory() so that myActivity
instance will not be leaked.
public class MyClass {
private static final MyClass instance = new MyClass();
private MyActivity myActivity;
private Button button;
private MyClass(){};
public void initialize(MyActivity activity){
myActivity = activity;
}
public void releaseMemory(){
instance = null; //Can't make it null
//Can do for individual variables
myActivity = null;
button = null;
}
}
In this approach as MyClass instance is final I can't make it null
instead I can make individual variables as null.
So is My understanding correct or am I missing anything respective to memory leaks?
First question, why do you need to keep a reference on an Activity object ?
For most usage you just need a Context object. So if you want to avoid leaking your Activity take the habit to do the following by replacing :
private MyActivity myActivity;
public void initialize(MyActivity activity){
myAcitivity = activity;
}
with:
private Context myContext;
public void initialize(Context context){
myContext = context.getApplicationContext();
}
Since Activity is an implementation of Context, your code should keep working. The method getApplicationContext() will always return an Application Context which you can leak all you want.
Now if an Application Context is not enough and you really need an Activity object (if you need to start other Activity without creating a new task for exemple), first ask yourself why you can't do that in your Activity class directly. If you can't, then maybe you have taken bad decision regarding your code architecture.
If you really need to store Activity object in other object (singleton or not), please remember that activities have a lifecycle, and you need your Activity to notify your MyClass that it will be no longer available (if you don't, and you forget to call releaseMemory(), then your Activity will leak when it goes to background) :
public class MyActivity {
MyClass myClass; // instance initialize somewhere in your code
onPause() {
myClass.setActivity(this);
}
onResume() {
myClass.setActivity(null);
}
}
public class MyClass {
#Nullable Activity myActivity; // tha #Nullable annotation helps you remember to do null checks before using this field.
public void setActivity(Activity activity) {
myActivity = activity;
}
}
In case your MyClass is also a Fragment, you can do the job of setting and releasing your Activity in the methods onAttach() and onDetach() (those methods are automaticaly called by Android, so you don't need to try to call them in your Activity).
Finally, I would advise against your first code sample, because even if you call your method releaseMemory() and ensure you have no reference left in your object A, there is nothing you can do to ensure an object B doesn't still have a reference to your class MyClass.
final field can't be modified. that's why final modifier exists.
second version of your singleton is better regarding conccurency. but it will be never gc'ed until you close the app.
Related
I started to learn MVP but I have a few questions related the SharedPreferences, as far as I know if I want to save a value in the sharedPreferences I need to pass this value to the presenter and the presenter calls the model to save the value, the same logic I would apply if I want to get or remove a value from the sharedPreference, but how is the best way to do that if I shouldn't pass the Context?
I sae a few code and the people used to pass the Context in the constructor method direct to the Model, but I still don't think that's a good idea.
Do you guys have any ideas?
Thanks,
Thales
Android specific imports should never exist in the Presenter if you want to keep it unit testable.
What you can do is, make an abstraction layer above SharedPreferences let's call it Cache, it would be an interface with all the needed caching methods, you would then provide a concrete implementation of it using SharedPreferences.
Here is a quick illustration of the idea:
interface Cache {
// Your caching methods
}
class CacheImpl implements Cache {
private SharedPreferences sharedPrefs;
public CacheImpl(Context context) {
// Takes a context to init sharedPrefs.
}
// implements all of Cache's methods
}
Then you would pass a reference for that implementation to the Presenter's constructor (better yet using DI to inject it to your presenters constructor):
Cache cache = new CacheImpl(myContext); // Naturally that would be an activity context
MyPresenter presenter = new MyPresenter(cache);
Then in your presenter you would receive that instance in the constructor:
private Cache cache;
public MyPresenter(Cache cache) {
this.cache = cache;
}
You can then use the cache variable without knowing about it's concrete implementation nor should you provide it a context.
Create a Storage class Object inside View and pass the context inside Storage Class constructor.
Then pass this storage class object in presenter (constructor) from View class.
Then whenever you need to save or get some data from your presenter - Then simply call the method of storage class from the object you have passed.
This way you will not need to send the context to your presenter.
View class
public class ViewClass extends ActionBarActivity {
private MyPresenter presenter;
private MyStorage storage;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
storage = new MyStorage(this);
presenter = new MyPresenter(this,storage);
}
}
MyStorage Class
public class MyStorage {
private Context mContext;
public MyStorage(Context context) {
this.mContext = context;
}
public void saveData(String data){
}
public String getData(){
return "";
}
}
MyPresenter class
public class MyPresenter {
private final ViewClass mView;
private final MyStorage mStorage;
public MyPresenter(ViewClass viewClass, MyStorage storage) {
this.mView = viewClass;
this.mStorage = storage;
}
}
In an Android app, is there anything wrong with the following approach:
public class MyApp extends android.app.Application {
private static MyApp instance;
public MyApp() {
instance = this;
}
public static Context getContext() {
return instance;
}
}
and pass it everywhere (e.g. SQLiteOpenHelper) where context is required (and not leaking of course)?
There are a couple of potential problems with this approach, though in a lot of circumstances (such as your example) it will work well.
In particular you should be careful when dealing with anything that deals with the GUI that requires a Context. For example, if you pass the application Context into the LayoutInflater you will get an Exception. Generally speaking, your approach is excellent: it's good practice to use an Activity's Context within that Activity, and the Application Context when passing a context beyond the scope of an Activity to avoid memory leaks.
Also, as an alternative to your pattern you can use the shortcut of calling getApplicationContext() on a Context object (such as an Activity) to get the Application Context.
In my experience this approach shouldn't be necessary. If you need the context for anything you can usually get it via a call to View.getContext() and using the Context obtained there you can call Context.getApplicationContext() to get the Application context. If you are trying to get the Application context this from an Activity you can always call Activity.getApplication() which should be able to be passed as the Context needed for a call to SQLiteOpenHelper().
Overall there doesn't seem to be a problem with your approach for this situation, but when dealing with Context just make sure you are not leaking memory anywhere as described on the official Google Android Developers blog.
Some people have asked: how can the singleton return a null pointer?
I'm answering that question. (I cannot answer in a comment because I need to post code.)
It may return null in between two events: (1) the class is loaded, and (2) the object of this class is created. Here's an example:
class X {
static X xinstance;
static Y yinstance = Y.yinstance;
X() {xinstance=this;}
}
class Y {
static X xinstance = X.xinstance;
static Y yinstance;
Y() {yinstance=this;}
}
public class A {
public static void main(String[] p) {
X x = new X();
Y y = new Y();
System.out.println("x:"+X.xinstance+" y:"+Y.yinstance);
System.out.println("x:"+Y.xinstance+" y:"+X.yinstance);
}
}
Let's run the code:
$ javac A.java
$ java A
x:X#a63599 y:Y#9036e
x:null y:null
The second line shows that Y.xinstance and X.yinstance are null; they are null because the variables X.xinstance ans Y.yinstance were read when they were null.
Can this be fixed? Yes,
class X {
static Y y = Y.getInstance();
static X theinstance;
static X getInstance() {if(theinstance==null) {theinstance = new X();} return theinstance;}
}
class Y {
static X x = X.getInstance();
static Y theinstance;
static Y getInstance() {if(theinstance==null) {theinstance = new Y();} return theinstance;}
}
public class A {
public static void main(String[] p) {
System.out.println("x:"+X.getInstance()+" y:"+Y.getInstance());
System.out.println("x:"+Y.x+" y:"+X.y);
}
}
and this code shows no anomaly:
$ javac A.java
$ java A
x:X#1c059f6 y:Y#152506e
x:X#1c059f6 y:Y#152506e
BUT this is not an option for the Android Application object: the programmer does not control the time when it is created.
Once again: the difference between the first example and the second one is that the second example creates an instance if the static pointer is null. But a programmer cannot create the Android application object before the system decides to do it.
UPDATE
One more puzzling example where initialized static fields happen to be null.
Main.java:
enum MyEnum {
FIRST,SECOND;
private static String prefix="<", suffix=">";
String myName;
MyEnum() {
myName = makeMyName();
}
String makeMyName() {
return prefix + name() + suffix;
}
String getMyName() {
return myName;
}
}
public class Main {
public static void main(String args[]) {
System.out.println("first: "+MyEnum.FIRST+" second: "+MyEnum.SECOND);
System.out.println("first: "+MyEnum.FIRST.makeMyName()+" second: "+MyEnum.SECOND.makeMyName());
System.out.println("first: "+MyEnum.FIRST.getMyName()+" second: "+MyEnum.SECOND.getMyName());
}
}
And you get:
$ javac Main.java
$ java Main
first: FIRST second: SECOND
first: <FIRST> second: <SECOND>
first: nullFIRSTnull second: nullSECONDnull
Note that you cannot move the static variable declaration one line upper, the code will not compile.
Application Class:
import android.app.Application;
import android.content.Context;
public class MyApplication extends Application {
private static Context mContext;
public void onCreate() {
super.onCreate();
mContext = getApplicationContext();
}
public static Context getAppContext() {
return mContext;
}
}
Declare the Application in the AndroidManifest:
<application android:name=".MyApplication"
...
/>
Usage:
MyApplication.getAppContext()
You are trying to create a wrapper to get Application Context and there is a possibility that it might return "null" pointer.
As per my understanding, I guess its better approach to call- any of the 2
Context.getApplicationContext() or Activity.getApplication().
It is a good approach. I use it myself as well. I would only suggest to override onCreate to set the singleton instead of using a constructor.
And since you mentioned SQLiteOpenHelper: In onCreate () you can open the database as well.
Personally I think the documentation got it wrong in saying that There is normally no need to subclass Application. I think the opposite is true: You should always subclass Application.
I would use Application Context to get a System Service in the constructor. This eases testing & benefits from composition
public class MyActivity extends Activity {
private final NotificationManager notificationManager;
public MyActivity() {
this(MyApp.getContext().getSystemService(NOTIFICATION_SERVICE));
}
public MyActivity(NotificationManager notificationManager) {
this.notificationManager = notificationManager;
}
// onCreate etc
}
Test class would then use the overloaded constructor.
Android would use the default constructor.
I like it, but I would suggest a singleton instead:
package com.mobidrone;
import android.app.Application;
import android.content.Context;
public class ApplicationContext extends Application
{
private static ApplicationContext instance = null;
private ApplicationContext()
{
instance = this;
}
public static Context getInstance()
{
if (null == instance)
{
instance = new ApplicationContext();
}
return instance;
}
}
I'm using the same approach, I suggest to write the singleton a little better:
public static MyApp getInstance() {
if (instance == null) {
synchronized (MyApp.class) {
if (instance == null) {
instance = new MyApp ();
}
}
}
return instance;
}
but I'm not using everywhere, I use getContext() and getApplicationContext() where I can do it!
I know the original question was posted 13 years ago, and this is the Kotlin version of getting context everywhere.
class MyApplication : Application() {
companion object {
#JvmStatic
private var instance: MyApplication? = null
#JvmStatic
public final fun getContext(): Context? {
return instance
}
}
override fun onCreate() {
instance = this
super.onCreate()
}
}
I often have to deal with this kind an error when programming in Java on Android.
For example I have a class where I set a flag.
public class ViewActivity extends Activity {
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
...
}
In another class I want to reset the FLAG_KEEP_SCREEN_ON
class DrawOnTop extends View {
...
if (condition) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
but this doesn't work, since I get "The method getWindow is undefined for the type DrawOnTop".
So I try to define a clearFlags method in ViewActivity class
void clearFlags() {
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
and to call it from the DrawOnTop class:
if (condition) {
ViewActivity.clearFlags();
}
This doesn't work as well: I get "Cannot make a static reference to the non-static method clearFlags() from the type ViewActivity".
Well, let's make it static then.
static void clearFlags() {
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
and then I get "Cannot make a static reference to the non-static method getWindow from the type Activity"
How could I execute such a statement?
If your DrawOnTop class is nested within the ViewActivity you can create a local Context variable and use it to call the getWindow(). If that's not the case then create a receiver in your activity class then from DawOnTop send an intent with your trigger to do whatever the job is. Do not instantiate your activity class, bad idea!
You can send getWindow() as parameter into clearFlags method.
Call clearFlags(Window window) from your activity: WindowHelper.getInstance().clearFlags(getWindow());
Helper class:
public class WindowHelper {
public static final WindowHelper instance = new WindowHelper();
public static WindowHelper getInstance() {
return instance;
}
public void clearFlags(Window window) {
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
}
I tried to implement the solutions suggested by Aksaçlı and this turned out to be very simple:
In the ViewActivity class DrawonTop is called this way:
mDrawOnTop = new DrawOnTop(this);
The constructor of the second class contains this:
public DrawOnTop(Context context) {
super(context);
Therefore ViewActivity.clearFlags(); has simply to be rewritten as ((ViewActivity)getContext()).clearFlags();
Perhaps you should refer to an initialised object in your static method. So instead of:
void clearFlags() {
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
you should create a static instance variable of your window:
private static staticWindowInstance;
void clearFlags() {
getStaticWindowInstance().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
For more information, you should check out the Singleton design pattern.
So I am using getdefaultsharedpreferences in a method called onLoadFinish (it's from a pdf library from android).
Here's the code:
public void onLoadFinish(DocumentState.OPEN state) {
//some irrelevant code here
SharedPreferences pref= PreferenceManager.getDefaultSharedPreferences(getActivity());
String text = pref.getString("example_list","");
int foo = Integer.parseInt(text);
goToPage(foo);
//some irrelevant code there
}
So the main task of the code is to get a value from my example_list preference (a string), turn it into an integer and put this integer into my goTopage();, which makes the app jump to a certain page in my pdf document.
The problem is this part:
PreferenceManager.getDefaultSharedPreferences(getActivity())
getActivity isn't working. I have tried getApplicationContext() aswell. What should be in the brackets of getDefaultSharedPreferences()?
getDefaultSharedPreferences expects an instance of Context class. getActivity method is declared in the Fragment class, so, unless your onLoadFinish method is declared in any Fragment successor, you can't use it. Per your comments, if I understood you correctly, onLoadFinish is declared inside Activity. If so, you can just use this keyword to pass the context, because Activity is a successor of Context. If this method is declared in another class, you should pass context to it, via constructor injection, for example.
EDIT Example of providing context via constructor injection.
Let's say you have the following interface:
public interface MyInterface {
void myAction();
}
And you have a class, which implements it and requires an instance of Context to do the work:
public class MyClass implements MyInterface {
private WeakReference<Context> mContext;
public MyClass(Context context) {
this.mContext = new WeakReference<Context>(context);
}
#Override
public void myAction() {
Context ctx = mContext.get();
if (ctx != null){
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
//do stuff
}
}
}
As you can see, Context instance is injected via constructor and we don't keep a strong reference to the context (actually it depends on specific needs). This class can be used inside Activity in the following way:
MyClass myClass = new MyClass(this);
Or inside fragment:
MyClass myClass = new MyClass(getActivity());
PreferenceManager should be used with a PreferenceActivity.
Just use context.getSharedPreferences("pref_name", Context.MODE_PRIVATE);
getDefaultSharedPreferences required your application context a parameter,
Try this,
public class MyActivity extends ActionBarActivity
{
......
.......
PreferenceManager.getDefaultSharedPreferences(MyActivity.this);
......
......
}
I've just started with Android Development. Now, I want to make an enumeration. Each enum-object within that enumeration, I want to assign a name to. I want to get that name from my project resources (res/values/strings.xml).
But from within the enumeration type, I can't call getResources().getString(R.string.string_name).
Is that because the enumeration doesn't extend an activity or something?
How can I fetch the strings from the resources anyway?
Thanks!
P.S. The enumeration is not nested within a class. If it is possible, I want to keep it as a seperate file.
getResources().getString() is simply a shorthand for getActivity().getResources().getString().
So getResources() still rely on having a context (activity, fragment, context, etc).
To overcome this, i personally extended the Application class and made a public static Context available, so you can always access resources and such in your entire application, no matter if you're in an activity or a POJO.
Example:
public class MyApp extends Application {
public static Context context;
#Override
public void onCreate() {
super.onCreate();
context = getApplicationContext();
}
}
With this, you can always call:
MyApp.context.getResources().getString(xxxx);
Enumerators are implicitly static. As in when they are created, your activity and therefor its Context is nowhere to be found. If there's no Context, there are no Resources either.
Perhaps what you want to use is a static class with custom constructor instead of an enumerator. Example:
private static class MyConstants {
private Resources mResources;
public String constant1;
public String constant2;
public String constant3;
public MyConstants(Context ctx) {
mResources = ctx.getResources();
constant1 = mResources.getString(R.id.string1);
constant2 = mResources.getString(R.id.string2);
constant3 = mResources.getString(R.id.string3);
}
}
MyConstants mConstants;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_book);
mConstants = new MyConstants(this);
Log.e(TAG, mConstants.constant1);
...
}