Would it be there noticeable speed improvement if I would create local references to needed view elements (e.g. EditText or Button) in my activity (in onCreate()) and use them for accessing needed elements or it does not much matter if I always use findViewById() when I need to access some particular element?
It's an old question, but let me post my answer for anyone who happen to be curious just as me: at least in api level 17 (4.2.2), DFS (Depth First Search) is used to look for a view, and no caching mechanism exists (meaning search will be repeated every time you call findViewById()).
It will be a speed improvement if you have a complex layout and you are accessing those Views too often. It is a good practice to define private variables and to bind them to the references returned by findViewById once in onCreate() then accessing them throughout your code.
If you are accessing those Views just once for, let's say, adding OnClickListeners to them, I don't think it is needed to create local references, as you call them.
If you are using a ListActivity this is what the View Holder pattern advocates. I would say it depends on how often the view is getting refreshed.
Here's a video and pdf from Google I/O which talks about implementing a ViewHolder pattern in the getView method of a ListAdapter
The world of ListView
Related
I have two adapters:
private StreamingNowAdapter mStreamingNowAdapter;
private UserStreamingNowAdapter mUserStreamingNowAdapter;
When I load them with Arrays
mStreamingNowAdapter = new StreamingNowAdapter(mEventId, mEventName,
mEventPreview, mNumberViewers, mContext);
mUserStreamingNowAdapter = new UserStreamingNowAdapter(mUserId,
mUsername, mProfilePic, mStreamingUserNumberViewers, mContext);
and call notifyDataSetChanged(); how come when I call this method on one adapter, both adapters become notified and displayed?
I only called the method on the first adapter
mStreamingNowAdapter.notifyDataSetChanged();
and it loads my data into my second recycler mUsersStreamingNowAdapter without me calling notifyDataSetChanged on the second adapter.
Is this a bug or is it supposed to be like this?
Edit1: Two separate recyclerviews, both adapters attached to their own recyclers.
RecyclerView mRecyclerViewStreaming =
view.findViewById(R.id.fraghome_recycler_streaming_now);
RecyclerView mRecyclerViewFollowing =
view.findViewById(R.id.fraghome_recycler_followed_users);
mRecyclerViewStreaming.setLayoutManager(new
LinearLayoutManager(getContext()));
mRecyclerViewFollowing.setLayoutManager(new
LinearLayoutManager(mContext, LinearLayoutManager.HORIZONTAL, false));
mRecyclerViewStreaming.setAdapter(mStreamingNowAdapter);
mRecyclerViewFollowing.setAdapter(mUserStreamingNowAdapter);
I've checked the layout multiple times to make sure both the recyclerview's id's are not identical. Still can't figure out why this is happening.
A correctly managed RecyclerView and its required artifact (an Adapter, its data, etc.) should not interfere with another correctly managed RecyclerView or any other component for what is worth.
Unfortunately, the shared code you provided is not sufficient to determine what could be happening in your project.
To the best of my knowledge, RecyclerView adapters don't talk to each other in any way so a notifyDataSetChanged() on an adapter, should have no impact whatsoever on another adapter, regardless of where said adapters are declared (assuming they are different instances, of course).
All this being said, I'll offer you my personal comments about the code you posted. Please don't take it personally, for I'm reviewing the code, not the author, from a pure technical point of view based upon my experience with Android.
Fragment
It immediately caught my attention to see that a Fragment, a component known for being completely wild when it comes to lifecycle management and state, has so many responsibilities.
It starts by having no less than eight array lists, and managing two recyclerviews, and its respective adapters, and even the LayoutManagers used by the lists.
Not happy with having to manage all this, this Fragment also needs to handle a networking layer, error handling (not yet implemented), and data storage (in memory for now).
Because you may have put this together really fast simply to illustrate a case, I will let the fragment in peace for now.
Data
You're putting a lot of stress on your fragment and adapters by splitting the data in a very inefficient way. 4 array lists, one per field? What?
for(HomeFragmentPojo.StreamingNow streamingNow : streamingNowArrayList){
mEventId.add(streamingNow.getEvent_id());
mEventName.add(streamingNow.getEvent_name());
mEventPreview.add(streamingNow.getEvent_preview());
mNumberViewers.add(streamingNow.getNumber_viewers());
}
Why are you doing this?
If you have a StreamingNow object that has all 4 fields.
Why don't you have a ListAdapter<StreamingNow, ViewHolder>() that simply takes a list of StreamingNow objects?
You can simply do adapter.submitList(...) to pass the list once you get it and the adapter (when properly created) will do the right thing.
If you ever update the list, you simply submit it again, and the adapter will calculate the difference and only update the rows that need to be updated.
The BIND method, is also simple, because it would look like: (pseudo code)
viewHolder.eventName = getItem(position).name
viewHolder.viewerCount = getItem(position).viewerCount
//For the image...
Glide.with(mContext)
.load(getItem(position).preview)
.apply(RequestOptions.centerCropTransform())
.into(viewHolder.preview);
...
You get the idea.
Based upon the properties I see, your StreamingNow "pojo" kinda looks like that
class StreamingNow {
String id;
String name;
String preview; //maybe an URL? You use Glide so in the bind method, use it.
String viewerCount;
}
When I look at your other adapter, I see that it has the same data...
Id, Name, Pic, Count of Viewers.
There is no need to use a different adapter, you can reuse the same adapter assuming they have to behave the same way; if the Users adapter has to do something different on click, for example, you could pass the min information needed for the adapter to decide what to do when an item is clicked. Or better yet, you could simply pass a generic Listener so the adapter simply calls "this item "X" was clicked". Because these adapters aren't here to make decisions and call framework things, they have a lot of work in their plates, and this is not and should not be their responsibility.
Whoever manages, creates, and maintains these adapters (so far, your Fragment) should be the one in charge of doing this. Or even better yet, a ViewModel or Presenter should receive this and make an informed decision.
In any case, the click event inside the adapter is not the biggest problem here, and it will work anyway.
What I am trying to leave written here, is that your problem is a lack of separation of concerns between components.
The smaller and more concrete your classes are, the easier it is to replace them, test them, debug them, enhance them, etc.
You're throwing all your code in a big place. If you separate it, it's a lot easier to keep it under control.
So, what's the deal?
I suggest you take a step back, enhance your RecyclerView to use a ListAdapter
I wouldn't perform the network request in onCreate, instead do it in onViewCreated or similar, because during onCreate in a Fragment, there's no guarantee that the view will be created. (Remember when I told you Fragments were a pain?).
I wouldn't have that network code in the Fragment to be honest; it's quite simple to add a ViewModel and have the data there. Then you can have the ViewModel request the data during onCreate, but you'll only get the LiveData from it when your lifecycle allows it... which is the whole point of LiveData and observing it.
If this sounds like a lot of work, it may as well be, especially if you are new to Android. But it will pay off.
Good luck!
Whenever we need a reference to the widget, we uses findByViewById.
When we are referring the widget lots of time in the code of the same Activity class, we can follow either of the approach:
Call findViewById every time in Activity lifecycle.
Get it first time, store the reference as a private instance variable of the Activity class.
Which approach is beter? What would be pros and cons of each approach in terms of performance and memory. Please help.
EDIT: If we move to new activity from A to B, we do not finish A as we want to open A on pressing back. In this scenario how to approach above problem? Please help.
Both approaches have their risks. In general, you should call findViewById() the less times you can, by the other hand, storing a reference on the Activity class may lead to memory leaks. It depends so much on what you want to do, how much times are you calling it and basing on it choose one of the approaches. For that, you'll need to analyze your code and if you're not clear about which is better, just try both and choose the "less bad", but generally the first approach is worse than the second one because you know you'll always have to find across ALL elements you've defined an id.
Most developers use method 2, mostly because its more effective. If your layout is complicated then findViewById must traverse its tree to find given widget which takes time. In list views you mostly use ViewHolder pattern which allows you to store references to list item widgets. Since lists are redrawn very ofthen this greatly speeds up its rendering.
Storing widgets in private references is quite safe, those references gets invalidated on configuration changes, but your activity is also destroyed then.
The second possibility is clearly the better.
findViewById iterates through the whole view hierachy, which of course costs much more time than a reference.
Dianne Hackborn (Android engineer) gave some details about the topic here: https://groups.google.com/forum/#!topic/android-developers/_22Z90dshoM
Accessing a member variable is always faster than any function call. The used space for that variable is insignificant.
By the way: The code looks much cleaner!
You should decide this according to your purpose. Holding an object for your views are faster than getting your view with an activity method. But this also means that you are using a memory for your reference and it can cause a memory leak.
I may be wrong since i am new to Android, but i prefer storing a a variable;
it's less coding to write.
for example: if you have to access an imageview that is nested in layouts how would you wish to access it and get its tag.
Access 1:
public Integer getTag(){
FrameLayout frame1 = (FrameLayout) findViewById(R.id.frame_1);
LinearLayout linear3 = (LinearLayout) frame1.findViewById(R.id.linear_3);
ImageView imgView = (ImageView) linear3.findViewById(R.id.myImg);
return Integer.valueOf( imgView.getTag().ToString());
}
Access 2:
private ImageView myImageView;
#Override
public void onCreate( Bundle savedInstanceState){
//set access to variable
}
public Integer getTag(){
//return Integer.valueOf( myImageView.getTag().ToString());
//can be written
Integer mTag = Integer.valueOf(myImageView.getTag().ToString());
return mTag;
}
Say I have an adapter that interfaces with a shared data set. During the layout of a composite view (i.e. listView), getView is called and the adapter is accessed. GetView is called for subsequent elements. The adapter provides direct locked references to elements in the shared data set.
Now the dataset may change from underneath the adapter. In this scenario the adapter would be invalidated when the data set changes. However, it is possible that the data is invalidated while the UI is in a draw or layout phase.
My question is, what happens if another thread invalidates the dataset while the view is iterating through it's children (getView is being called, and the adapter is being accessed)?
It depends on what your backing data looks like and how you've invalidated it, as well as how your getView is implemented. Depending on these things you may get a view that represents old data, new data, or some undefined state which may result in unexpected data or even a NullPointerException or other exception.
A more detailed answer can be provided if you supply more detail in your question about the type of data and adapter. Source code for your adapter, particularly your getView method would also allow a more specific answer.
You should always modify the contents of your adapter from your UI thread to prevent issues from this. Otherwise you'll occasionally get a ConcurrentModificationException, NullPointerException or an IndexOutOfBoundsException.
I highly recommend the I/O 2010 talk about ListViews if you haven't had a chance to watch it yet, there is a ton of good info in it about how they work and how to optimize them.
Google I/O 2010 - The World of ListView
I'm looking for guidance as to how to modularize my code. I have an activity and a listAdapter and they are getting pretty complex. I'm not sure what code should live where and how much knowledge each of these 2 classes should have of each other. How do you decide whether to put code in an activity or its adapter? And what patterns do you use to keep these classes as lean as possible?
Your description is too generic, so I cannot give you an exact answer (would be useful to explain why they are getting bigger and bigger, what is the extra code good for).
But generically speaking, just think about what each class supposed to do. The "Activity" (as I see it), is a main controller, it "knows everybody", and it connects the other components together (the ListView with the list adapter). The list adapter's purpose is simply to map data to views. If they are getting bigger, extract new (utility) classes.
For example assume a big part of the code in ListAdapter formats timestamps (eg. takes timestamp as long value, and based on current time creates a string like "2 hours ago"). Then it makes sense to create a new utility class called TimeFormat (with a constructor which takes a context, you'll need it later to fetch string resources). Then the ListAdapter will create an instance of this class.
Another example would be data saving. In that case you could create a class called "Model" or "Document" (again with a constructor taking a "Context" instance). It would be responsible (for example) to load the data by parsin XML files, and to save the data by generating XML files. In this case this class would be instantiated by the activity.
Also note that the ListAdapter should really do what it supposed to do: create/setup views based on data. It should never depend on other views (in other views it should work with any ListView in any layout file). So if you have "findViewById" call, which access a view outside of the ListView (or the ListView itself), then that code should be moved to the activity.
Also, when in doubt you can try to find an open source application, which is relatively mature, and does something similarn (and see how that is solving the problem).
Per the adapater documentation in android
An Adapter object acts as a bridge between an AdapterView and the underlying data for that view. The Adapter provides access to the data items. The Adapter is also responsible for making a View for each item in the data set.
So if your code has to do with getting the data to display or creating the views, then it goes in the adapter. Everything else goes in the Activity or else where. If you're spending a lot of code retrieving the information you want to display, consider using some sort of AsyncTaskLoader class. Note that loader classes can be accessed from API Levels less than 3.0 using the android compatibility package.
In the source code of Activity.java, I see some methods bellow :
public View findViewById(int id) {
return getWindow().findViewById(id);
}
and the definition of getWindow method:
public Window getWindow() {
return mWindow;
}
But as the following rules:
Avoid Internal Getters/Setters
In native languages like C++ it's
common practice to use getters (e.g. i
= getCount()) instead of accessing the field directly (i = mCount). This is
an excellent habit for C++, because
the compiler can usually inline the
access, and if you need to restrict or
debug field access you can add the
code at any time.
On Android, this is a bad idea.
Virtual method calls are expensive,
much more so than instance field
lookups. It's reasonable to follow
common object-oriented programming
practices and have getters and setters
in the public interface, but within a
class you should always access fields
directly.
Without a JIT, direct field access is
about 3x faster than invoking a
trivial getter. With the JIT (where
direct field access is as cheap as
accessing a local), direct field
access is about 7x faster than
invoking a trivial getter. This is
true in Froyo, but will improve in the
future when the JIT inlines getter
methods.
so I want to know why android developers not access this mWindow object directly? If the JIT of the current android versions cannot inline the access, getWindow().findViewById(id) will costs more time than mWindow.findViewById(id), and findViewById is a rather frequently used method.
First: you can't access it because it's private.
Why is it private?
As you said, accessing members directly is faster. On the other hand, you are invoking a method that isn't very fast as it will lookup for some view in the view hierarchy. So using a method instead of a direct access will incur in a small overhead in terms of percentage of the total time that it would take to perform that task.
Anyway, I believe that the reason for this is encapsulation.
You are invoking something you don't own (that is the Android SDK). So, you shouldn't make any assumptions of whats happening "in the other side". Simply use this method and expect that it will return the view you want (or null if it doesn't exists).
Maybe the next version of android will use a different method to lookup a view, not calling getWindow(). If you use this method, they (Google/Android) can simply mark the method as deprecated and "forward" your invocation to the newest implementation. If you were calling directly getWindow(), maybe you would be looking for something that is no longer placed in there.
You can't access the mWindow property directly - it's private.
And I wouldn't care about the speed of findViewById, since you only need to call it once for every view in your layout in your onCreate() method and store the views in members of your activity. You do call findViewById only once per view, don't you? ;-)
However, if you really care about these things, you could call getWindow() for yourself, store it into a local variable and call findViewById on it directly. I wouldn't recommend this because all your performance increasements here are not worth the time and anyway will be obsolete with future versions of the JIT.
If you do this I would be very interested in the amount of microseconds you saved. :-)
We have a reason to smile now...
The android documentation which says to avoid internal getters and setters will change soon, supposedly progruard was added to Gingerbread platform which does a fine job of inlining accessor's, please refer to "Avoid Internal Getters/Setters" is bad advice and these two SO posts.
https://stackoverflow.com/a/6716573/892055
https://stackoverflow.com/a/4930538/892055