Singleton as a data source for RecyclerView.Adapter - java

Can I have adapter that uses singleton instance as a data source? I am not going to have any static references to Views/Context, only plain data stored in singleton. Will there be any leaks or any downsides of such solution?
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
/* SINGLETON */
private DataManager manager;
public MyAdapter(DataManager manager) {
this.manager = manager;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// create vh
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
// bind vh
}
#Override
public int getItemCount() {
return manager.itemsCount();
}
}
DataManager is a singleton object that holds state/data in my Service.
Thanks in advance.

Your adapter referencing a singleton doesn't leak anything. Your data manager can leak memory if it's not implemented correctly.
Using singletons to store static data is just fine. You can just dispose the data when you're done using it.

There are two cases that you should use or not.
If you'r getting data from Server and pass to Adapter then you should uses Singleton because it is a good idea for holding data instead of calling WebService every-time.
If your data is static then you need not to hold the data in Singleton because it will slow down your app.

As it's a singleton, this means the class holds a reference to an instance of the class, and therefore the data will always be held in memory. This could cause your app to become less responsive if it is holding lots of resources.
In addition, if your app has a large footprint, Android's OS will be more likely to destroy your app in the event of low memory.
If possible (and it usually is), avoid singletons.

Related

Android MVP - Share Preference

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;
}
}

Android - "No enclosing instance of type 'some.abstract.class.name' class is in scope" error when extended

I have an abstract adapter class from an external library:
public abstract class DragItemAdapter<T, VH extends DragItemAdapter.ViewHolder> extends RecyclerView.Adapter<VH> {
//Their other codes
public class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(final View itemView, int handleResId) {
super(itemView);
//The rest of their codes
}
}
}
And I have my Adapter extended that adapter
public class ChecklistAdapter extends DragItemAdapter<Pair<Integer, SomeClass>, ViewHolderForChecklist> {
#Override
public ViewHolderForChecklist onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
grab = R.id.grab;
return new ViewHolderForChecklist(view,grab);
}
}
If my ViewHolderForChecklist is an inner class of the ChecklistAdapter it works fine. But if I move the ViewHolderForChecklist to a brand new class
public class ViewHolderForChecklist extends DragItemAdapter<Pair<Long, SomeClass>, ViewHolderForChecklist>.ViewHolder { // The error is at this line
public ViewHolderForChecklist(final View itemView, int grab) {
super(itemView, grab);
}
#Override
public void onItemClicked(View view) {
}
#Override
public boolean onItemLongClicked(View view) {
return true;
}
}
There is an error in real time
No enclosing instance of type 'library.package.name.DragItemAdapter' class is in scope
and the error when compile
error: an enclosing instance that contains DragItemAdapter.ViewHolder is required
Using "move" from Refractor has the same problem. I'm still new to this kind of... 'nested-class" so I don't know what is wrong with this or what kind of info should I include more.
Thank you!
ViewHolder is an inner class of DragItemAdapter (because it wasn't declared static). That means that every object of class ViewHolder must be associated with an object of class DragItemAdapter (actually, it would have to be a subclass of DragItemAdapter). You can think of ViewHolder having a hidden instance variable like
DragItemAdapter __outerObject;
The ViewHolder can directly access instance variables and methods belonging to the __outerObject.
That means that when you say new ViewHolder(...), you have to have some DragItemAdapter for the ViewHolder to be associated with.
The same applies to any subclass of ViewHolder, including ViewHolderChecklist, since the subclass inherits the hidden __outerObject variable.
In the first example, where ViewHolderChecklist is inside a ChecklistAdapter, the onCreateViewHolder method will always be called on a ChecklistAdapter instance. When that method says new ViewHolderChecklist, the new object's __outerObject will be set to the ChecklistAdapter instance. Also, if an outside class has a ChecklistAdapter adapter;, it can use that to create a new ViewHolderChecklist by saying adapter.new ViewHolderChecklist(...).
When you move ViewHolderChecklist outside the class, though, there's no way for a new instance to be created, since there's no way to use new in a way that would tell it what its __outerObject is supposed to be. The adapter.new ViewHolderChecklist(...) syntax won't work, because that syntax is only allowed for nested classes, and ViewHolderChecklist isn't a nested class. So ViewHolderChecklist has to be a nested class inside a subclass of DragItemAdapter.
Correction: It's actually possible to declare ViewHolderChecklist like this. However, you have to give it an explicit constructor and it has to have a Qualified Superclass Constructor Invocation (see this; see also https://stackoverflow.com/questions/40501642/what-rule-prohibits-this-nested-class-reference/40501815.

inheritance of viewholder class

I'm an android developer and I've got this issue regarding some OOP concepts in android. Whenever we create an adapter (lets take a recycler adapter ..) I usually do something like this:
public class MyAdapter extends RecyclerView.Adapter<"MyAdapter.MyViewHolder">
{
public MyAdapter(){}
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType){}
public void onBindViewHolder(MyViewHolder holder, int position){}
public class MyViewHolder extends RecyclerView.ViewHolder{}
My question is in case we have our MyViewHolder as an internal class in our adapter (like written above....), why does it have to be declared as public/protected ?? Anytime I try to make the class private,I get an error at the top of the adapter class: 'packagename.MyAdapter.MyViewHolder has a private access in packagename.MyAdapter'
Ever since i've been looking at tutorials (text or video) which explain how adapters work, they have never tried to explain why the MyViewHolder class needs to be declared as public. I really wanna understand this stuff.

how to unregister BaseAdapter that depends on other source

I have a MyAdapter extends BaseAdapter that extract information from JniSource. MyAdapter implements JniSource.Observer and get a callback when underlying JNI data is changed.
My Question: I can JniSource.registerObserver(MyAdapter) in constructor but where to call JniSource.unregisterObserver(MyAdapter).
MyAdapter.finalize() cannot be the place because JniSource is still having reference (inside LinkedList) to MyAdapter.
If I don't call JniSource.unregisterObserver() this would cause a leak.
Extra Note: all JniSource methods are static.
You need to use Dispose Pattern in this case.
(From Wikipedia)
The dispose pattern is a design pattern which is used to handle resource cleanup and prevent resource leaks in runtime environments that use automatic garbage collection.
Create an interface Disposable
public interface Disposable {
public void dispose();
}
Implement it in MyAdapter class and call the JniSource.unregisterObserver(MyAdapter) method in the implemented dispose method.
Call dispose method when you are done using MyAdapter object. This has to be done manually as garbage collector does not guaranty if an object would be destroyed.
Edit:
In case of android, life-cycle methods are best positions for your code. In this case, onDestroy method seems to be the best candidate. In other platforms, programmer has to manage life-cycle somehow (mostly manually). This should be thought about at that time.
The idea is simple, when MyAdapter have observer, then MyAdapter register itself to the JniSource. using this way i do not have to mantain anything absolute. (life cycle is relative).
Added benifit: This also prevent from unwanted changed callback from JniSource since there are no observer for MyAdapter
public abstract class MyAdapter extends BaseAdapter implements JniSource.Observer {
private int observer_count = 0;
#Override
public void registerDataSetObserver(DataSetObserver observer) {
super.registerDataSetObserver(observer);
if(observer_count == 0) {
JniSource.registerObserver(this);
}
observer_count++;
}
#Override
public void unregisterDataSetObserver(DataSetObserver observer) {
super.unregisterDataSetObserver(observer);
//FIXME: what if unregisterDataSetObserver got called for a non-registered observer?
observer_count--;
if(observer_count == 0) {
JniSource.unregisterObserver(this);
}
}
public void onJniSourceDataChanged() {
notifyDataSetChanged();
}
};

Avoiding class scoped factories with RoboGuice

I'm trying to use a Model-View-Presenter pattern in my Android project. I am using the excellent RoboGuice project with AssistedInject to manage my dependencies. I am struggling with the style of creating instances of my presenters.
AssistedInject appears to require me to first inject a factory to create a presenter and then use that factory to create an instance of a presenter. Injection (appears) to only work at the class scope level; I cannot inject local variables. My methods do not need both a factory and a presenter; I only care about the factory long enough to generate a single presenter. I'd like to avoid keeping the mostly useless factory around.
More Detail
In my implementation of the pattern, I choose for each presenter and view to hold a reference to the other. A presenter must usually be passed one or more "Service" objects that are used to interact with the model. An Android Activity is an implementation of a (MVP) View. An Activity must be the composition root of any Android application. Therefore, each activity must instantiate a presenter, and that presenter needs a service as well as a reference to the view.
In general, presenters look like
public class GreatStuffPresenter {
private final SomeService service;
private final GreatStuffView view;
#Inject
public GreatStuffPresenter(SomeService service, #Assisted GreatStuffView view) {
this.service = service;
this.view = view;
bind();
}
public void bind() {
Record r = service.getSomeRecord();
view.setField(r.field);
}
}
and Activites look like
public class GreatStuffActivity extends RoboActivity {
#Inject private final GreatStuffPresenterFactory presenterFactory;
private GreatStuffPresenter presenter;
#Override
public void onCreate(...) {
super.onCreate(savedInstanceState);
setContentView(R.layout.create_update_record);
presenter = presenterFactory.create(this);
}
}
Now What?
I am dissatisfied that I must scope the presenterFactory at the instance level; I only need it during onCreate(). Am I missing some additional magic that RoboGuice could perform for me? If not, is there a better practice or pattern I should be using to avoid this unnecessary scoping?
In the end, I decided to get the RoboGuice injector, ask it for an instance of my factory, and create the presenter I'm looking for. I am reasonably happy with this approach; I'm not polluting my class with variables I won't use, and the line seems reasonably simple.
public class GreatStuffActivity extends RoboActivity {
private GreatStuffPresenter presenter;
#Override
public void onCreate(...) {
super.onCreate(savedInstanceState);
setContentView(R.layout.create_update_record);
presenter = RoboGuice.getInjector(this).getInstance(GreatStuffPresenterFactory.class).create(this);
}
}

Categories