Why ViewStub Improves App Performance | Android Interview Question | ViewStub Internals
Did you ever get a use case where you have a rarely used view? So, you’ve decided to keep the visibility of view GONE and set visibility to VISIBLE on specific cases. Here, inflating the view i.e. loading the layout resource every time for a rarely view, is redundant. Can we improve the performance? But, improving performance is boring. So let’s have an interesting story.
Okay! Let’s imagine you have 2 magical boxes. One box can hide itself and when you call it, it brings out a toy.🧸 Let’s call it “Include”.
The other box is special. Because it can turn invisible and when it does, it brings out a surprise toy from inside it! It doesn’t show anything until we tell it to. Let’s call it “ViewStub”.🪄
When we decide it’s time to show something, like a new toy,🚂 we make the ViewStub visible. When it becomes visible, it magically brings out a special toy from inside it. This special toy is a drawing🌇 or a picture that we’ve made before and stored inside the ViewStub.
Now, instead of calling it a “toy,” let’s call it “layout resource.” It’s like a picture we want to show on the screen. And the ViewStub is like a special hiding place for these pictures.
When we want to show a view or bunch of views from the ViewStub, we just say, “Hey ViewStub, it’s time to show us what you’ve got!” And it brings out the picture for us to see.
But here’s the cool part: We can also decide what type of view we want to show later. We can tell the ViewStub, “Hey, next time you show us something, make sure it’s this specific picture.” So, when it decides to show us something, it picks that specific picture🚂 we asked for.
What is ViewStub?
Android Documentation says: “ViewStub is an invisible, zero-sized View that can be used to lazily inflate layout resources at runtime.”
Let’s see how we can use ViewStub:
First, in XML, we declare the UI:
<ViewStub android:id="@+id/banner_stub"
android:inflatedId="@+id/banner"
android:layout="@layout/layout_banner"
android:layout_width="120dip"
android:layout_height="40dip" />
- id: ViewStub defined can be found using the id “banner_stub”.
- inflatedId: This attribute is used to set the id of the inflated View. Once the view is inflated using the layout “layout_banner” can be found using “banner”. We can set it programmatically by using the setInflatedId() method.
- layout: This attribute is used to supply an identifier for the layout resource to inflate. We can set it programmatically by using setLayoutResource(int) method.
//declare
var stub: ViewStub? = null
//initialize in onCreate()
stub = findViewById(R.id.banner_stub)
val inflatedView: View = stub?.inflate()
How ViewStub work internally?
- ViewStub itself is a zero-sized view. It’s dumb & lightweight. Let’s deep-dive into the source code of ViewStub Class.
/***
** A Simplified Version for explanation
***/
public ViewStub(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context);
mInflatedId = a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID);
mLayoutResource = a.getResourceId(R.styleable.ViewStub_layout, 0);
mID = a.getResourceId(R.styleable.ViewStub_id, NO_ID);
a.recycle();
setVisibility(GONE);
setWillNotDraw(true);
}
- In its constructor, we can see, that its visibility is set to GONE and
setWillNotDraw
flag
is also enabled.
2. Once we inflate the ViewStub or set its visibility to VISIBLE or INVISIBLE, the layout resource is inflated. Before inflation ViewStub exists in the view hierarchy, helping to maintain a flat view tree.
3. Once ViewStub is inflated, it replaces itself in its parent with an inflated view or views. Let’s see how this happens under the hood.
/***
** A Simplified Version of inflate() method which returns the view
***/
//access parent view
final ViewParent viewParent = getParent();
if (viewParent != null && viewParent instanceof ViewGroup)
final ViewGroup parent = (ViewGroup) viewParent;
//this inflates the view with layout resource that was previously
//set by setLayoutResource() and returns inflated view
final View view = inflateViewNoAdd(parent);
//here's the most interesting part where viewstub replaces itself with it's parent
replaceSelfWithView(view, parent);
mInflatedViewRef = new WeakReference<>(view);
mInflateListener.onInflate(this, view);
return view;
}
}
Now let’s have a look into the setVisibility
method of ViewStub. This method also inflates the view in case visibility is passed as VISIBLE or INVISIBLE
/***
** A Simplified Version of setVisibility(int) method
***/
if (mInflatedViewRef != null) {
//in case view is already inflated and present
//in view hierarchy just update visibility
View view = mInflatedViewRef.get();
if (view != null) {
view.setVisibility(visibility);
}
} else {
super.setVisibility(visibility);
if (visibility == VISIBLE || visibility == INVISIBLE) {
//here's the actual magic happends where
//we're calling inflate() internally
inflate();
}
}
}
Why to use ViewStub?
- When there is a complex view that you want to render occasionally, viewstub is the way to go. ViewStub will reduce memory usage and speed up UI rendering by loading the views at runtime only when they are needed.
- When you want to inflate the view lazily at runtime or based on some UI logic.
- ViewStub will help you keep the View Hierarchy flat when the actual layout is not in use.
If you liked the article💌, please let me know by pressing on the clap👏 button below. Feel free to follow me on LinkedIn. ❣️
P.S. You can clap 50 times on Medium! Just long press that little icon.
What next do you want me to write ✍️ on?