blob: e413d7ff9c3ab7ec3ef07994c473c03b6b4689a6 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.util;
import android.content.Context;
import android.os.Handler;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.AnyThread;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import com.android.launcher3.util.ViewPool.Reusable;
/**
* Utility class to maintain a pool of reusable views.
* During initialization, views are inflated on the background thread.
*/
public class ViewPool<T extends View & Reusable> {
private final Object[] mPool;
private final LayoutInflater mInflater;
private final ViewGroup mParent;
private final int mLayoutId;
private int mCurrentSize = 0;
public ViewPool(Context context, @Nullable ViewGroup parent,
int layoutId, int maxSize, int initialSize) {
mLayoutId = layoutId;
mParent = parent;
mInflater = LayoutInflater.from(context);
mPool = new Object[maxSize];
if (initialSize > 0) {
initPool(initialSize);
}
}
@UiThread
private void initPool(int initialSize) {
Preconditions.assertUIThread();
Handler handler = new Handler();
// LayoutInflater is not thread safe as it maintains a global variable 'mConstructorArgs'.
// Create a different copy to use on the background thread.
LayoutInflater inflater = mInflater.cloneInContext(mInflater.getContext());
// Inflate views on a non looper thread. This allows us to catch errors like calling
// "new Handler()" in constructor easily.
new Thread(() -> {
for (int i = 0; i < initialSize; i++) {
T view = inflateNewView(inflater);
handler.post(() -> addToPool(view));
}
}, "ViewPool-init").start();
}
@UiThread
public void recycle(T view) {
Preconditions.assertUIThread();
view.onRecycle();
addToPool(view);
}
@UiThread
private void addToPool(T view) {
Preconditions.assertUIThread();
if (mCurrentSize >= mPool.length) {
// pool is full
return;
}
mPool[mCurrentSize] = view;
mCurrentSize++;
}
@UiThread
public T getView() {
Preconditions.assertUIThread();
if (mCurrentSize > 0) {
mCurrentSize--;
return (T) mPool[mCurrentSize];
}
return inflateNewView(mInflater);
}
@AnyThread
private T inflateNewView(LayoutInflater inflater) {
return (T) inflater.inflate(mLayoutId, mParent, false);
}
/**
* Interface to indicate that a view is reusable
*/
public interface Reusable {
/**
* Called when a view is recycled / added back to the pool
*/
void onRecycle();
}
}