I want to enforce the factory pattern for certain Fragment, since according to this question, it's the way to go for passing data to Fragments.
However, to enforce it, me must prevent instantiating the fragment. The way to do this is making the constructor private, according to this question.
The problem is that I get this error: could not find Fragment constructor
According to this question, All Fragment classes you create must have a public, no-arg constructor.
So apparently I cannot make the constructor private. So how can I enforce the Factory pattern?
You can't enforce it by compiler because a fragment have to be public to restore fragment manager's state.
You can add a custom lint/detekt rule to your project which will fail build if Fragment's constructor called every where except the fragment's factory.
Related
I wrote a very simple program with one Activity that contains a fragment.
I made the Fragment constructor to be private and i'm using static newInstance() method to return the fragment.
The problem start when i'm rotating the phone. I'm getting an exception that says:
Unable to start activity ComponentInfo{com.example.todeleteimmediatley/com.example.MainActivity}: androidx.fragment.app.Fragment$InstantiationException: Unable to instantiate fragment com.example.DatesFragment: could not find Fragment constructor
I debuged the program and saw that the exception is in the first line in onCreate method (when calling to super.onCreate()).
Someone can explain me why the Fragment must have a constructor and why the exception occurs in the super.onCreate() phase?
When you rotate your device, your Activity will be destroyed and recreated. Part of this is destroying and recreating any Fragments that your Activity is hosting.
During the recreation step, the Android framework needs to instantiate a new instance of your Fragment. By default, it does so by invoking your Fragment's no-argument constructor. This means that this constructor must (a) exist and (b) be public.
This recreation of Fragments is triggered by the super.onCreate() of your Activity.
The general recommendation is to create a newInstance() factory method (as you have done), but to leave the default constructor alone (i.e., do not make it private). Yes, this means that it is still possible for someone to call the constructor directly, which you don't want, but it is required if you don't want to get involved with FragmentFactory.
Further questions from comments
Does it mean that in onCreate I should check if the container already contain a Fragment before adding and commiting? Because as I understand from you,the onCreate().super will restore the old Fragment.
My recommendation here is to only commit the Fragment transaction once, the first time your Activity starts up. Generally, this is achieved by checking that the savedInstanceState Bundle is null before committing the transaction:
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.replace(R.id.foo, FooFragment.newInstance(...))
.commit();
}
Because your Fragments are part of the instance state of your Activity, any recreation (any time savedInstanceState is not null) will be handled for you automatically.
Why it is better In newInstance method to add the information that the Fragment needs to the Bundle and not to store them as a members(attributes) of the Fragment?
Everything comes back to the fact that the Android framework needs to create a new instance of your Fragment. If you simply have member fields on your Fragment that you set, the Android framework will not be aware of these and will have no way to save or restore them.
However, the arguments Bundle is something that Android does know about. Arguments are considered part of the Fragment's instance state (and therefore part of the containing Activity's instance state), and will be automatically saved and restored. That is why you can only put certain types of data into the arguments Bundle; Android only knows how to "write" and "read" certain types of data during this recreation process.
In Java
If a class does not define any constructor, then at compile-time, the compiler will generate a constructor that has no arguments (we usually called it as default constructor, no-argument constructor or zero-argument constructor).
If a class defines any constructor that has arguments, then at compile-time, the compiler will not generate a default constructor.
Someone can explain me why the Fragment must have a constructor?
You don't need to define a construtor if your fragment does not receive any parameter.
Why the exception occurs in the super.onCreate() phase?
In Android, there are several scenarios when the system needs to re-create an activity, such as.
When configuration changed, such as users rotate screen orientation or change language.
When the system is on low memory
When re-creating an activity, the system will create a new instance of the activity, then call activity lifecycle, the first one will be onCreate() callback. In your activity (for example MainActivity), you need to call super.onCreate() of its parent. This statement will restore all fragments (such as DatesFragment in your case) that managed by the activity.
Because this is a new instance of MainActivity, so it needs to create a new instance of all managed fragments as well (including DatesFragment). To do that they will invoke the default argument constructor.
But in DatesFragment you made the default constructor is private, which means it only accessed inside that class, there is no way the system can invoke the constructor of DatesFragment class, so they throw InstantiationException.
Solution:
If your fragment does not receive any parameter, don't define any constructor
If you need to pass parameters to your fragment, then use default constructor along with setArguments(Bundle)
I am hoping to use the MVP pattern in Android that I roughly came up with the following packages and classes:
Here are 3 packages:
com.myview
mainActivity //public
com.ipresenter
IPresenter //public
com.PresenterImpl
Presenter //protected
Presenter implements IPresenter.
What I intend to do is to make Presenter protected and can be instantiated only in mainActivity. Other words, I would like to limit its visibility and creation of this class can only be made possible through mainActivity class.
Since its constructor is implicitly declared as protected, I cannot instantiate Presenter within class mainActivity.
How could I do this without having to make Presenter public? How can I limit the visibility of a (or some) class within a package and allow its creation only through another class that lies within another package?
I thought about the classLoader Iterator method, but is this an overkill or it is not necessary because there are other ways / patterns?
Many books seem to suggest to limit the visibility of classes as much as we can, so I am taking this opportunity to learn and utilize good practice.
Addendum:
In addition to the aforementioned, my ultimate goal is to be able to declare the presenter within the mainActivity using the interface type. So, if in the future I had more than one presenters, they would all still be of the interface type IPresenter.
Why are the lifeCycle methods in android have access specifiers as protected ?
what i understand about Access-specifiers is as below::
But why should we need to make all the life-cycle methods as
protected
I notice this when i override the lifecycle methods
I know over-riding the methods of Activity class as methods in
Activity class are defined protected
But why are they defined as protected
They are protected for encapsulation within the framework package android.app and subclasses.
They are to be called by android.app.ActivityManager (same package) only. Depending on the method implementation, things could get messed up, if one can call those methods arbitrarily, from anywhere.
So, this is by design and that design helps to avoid certain conceptual errors.
If you really must have a public method, just implement one and use it from outside and within the corresponding lifecycle method.
However, though not recommended in this case, one could override protected methods with public methods.
i am defining here why public and protected and how it work:
It's useful to have public onClick methods because you can "force" certain buttons to be clicked programmatically. A common example of this is causing the same to code to execute when the user presses the enter key, or pressed the Submit button.
I don't think Android calls Activity.onCreate directly. Note that Activity inherits from Context (which does have a public constructor). It is my understanding that the constructor triggers some events to occur, and the onCreate/Pause/Resume/Destroy methods are called internally to the class at the appropriate time.
For example, when you create an activity, the view XML file has to be parsed and inflated. This happens automatically, so there's something happening behind the scenes that you don't directly control.
Not public because those lifecycle methods are essentially used internally by the SDK and are not meant to be called by any other classes (you are not supposed to call anywhere activity.onResume() from any class, this is done automatically).
Not private to allow some custom code to be ran by subclasses.
Is it safe for an Android AsyncTask that's an inner class of an Activity to read the Activity's private member fields while in AsyncTask.doInBackground()? Thanks in advance.
Generally, no. If the activity is undergoing a configuration change and is being destroyed and recreated, your background thread will be talking to the wrong instance, which may cause problems for you.
Ideally, the doInBackground() of an AsyncTask should be able to run independently of its launching component (activity, service, etc.). I suggest that you create a constructor on your AsyncTask and pass in whatever is needed. Or, have the AsyncTask be managed by a dynamic fragment that uses setRetainInstance(), in which case (AFAIK) it should be safe for the task to access private data members of the fragment, since the fragment is not going anywhere.
when an inner classes access private members (fields or functions) of the enclosing class, the compiler will generate accessor functions to those members. this will be breaking of encapsulation, some argue if it is a Good Thing or a Bad Thing.
So far, to accomplish certain functional goals, I have been getting away with handing out my app's main activity object as a parameter to the constructors of other classes, which then store it as a private variable.
I do this, not because I need access to the entire activity, but rather because I need access to:
Members (either data or
methods) of the activity
Data members which aren't initialized yet
at the time those constructors were
called.
It works, but I have the constant feeling that I am doing something fundamentally wrong in terms of proper OOD.
Especially in regard to point #1:
The members that are so "private" to
Activity become, in essence, a pool
of global variables mess.
In addition, those other classes
that were created for the purpose of
modularity, are now dependent on
knowledge of the activity class,
which makes them not really
re-usable outside this app...
For these reasons, I try to avoid passing an activity as a parameter to constructors as much as possible, but in the Android development environment I find it more difficult to do, for reasons I don't fully understand yet.
My questions:
Are there recommended "rules of
thumb" that can help avoid this
trap of taking "a shortcut" by
passing an activity as a parameter?
Are there cases in which passing an
activity as a parameter is
conceptually justified?
Generally speaking, you should avoid keeping references to the activities. If you really need, store a WeakReference to your activity. This is to avoid memory leaks.
As you said, by passing a reference to an activity, you introduce a dependency between the other object and your activity class. Give some sample code so that we could give an example of how to refactor it.
I have found it best to keep values that multiple classes will require in a separate Util class. That way, you do not have to pass the main Activity around to other classes.
An alternative to this is to pass the required values that the main Activity has as parameters to the other classes as needed.
To your 2nd question, I cannot think of any reason that you would have to pass your main activity and then call methods on it.