NextFaze/power-adapters

Let ItemAdapter supply item IDs

DSteve595 opened this issue · 4 comments

For some applications, having stable item IDs is necessary. Currently if a PowerAdapter with stable IDs is composed with a ViewFactory or varargs layoutResources, it'll lose its stable item IDs as ItemAdapter doesn't support it. I think a way to supply item IDs when using the append() methods would be helpful. Maybe for layoutResources, have them default to the layout res (which is an int)?

This is part of a wider problem, which is ensuring composed adapters with stable item IDs form a composite adapter whose ID ranges don't accidentally overlap. If they overlapped it'd result in strange behavior in RecyclerView, which would be difficult to debug. I also don't want users to have to worry about stable IDs outside of overriding getItemId(). Adding your suggestion would compromise that and force the user to be aware of the full item ID range within the composite PowerAdapter.

Consider this related problem: what if your model IDs are strings? Many systems use UUIDs.

My thoughts right now are to change PowerAdapter.getItemId() to return an Object instead. Since an Object is unique in the system, any headers/footers would automatically have a stable ID. The challenge is how to map the objects into a range of longs in a way that isn't prone to memory leaks, and has decent performance.

Added #50 to track this potential solution.

Object item IDs would be a wonderful solution, as then they'd have basically infinite levels of scope. But yeah, no idea how they could be mapped to longs effectively.

Another idea: What if, as part of the hasStableIds() contract, PowerAdapters could specify a range of IDs needed? For example:

@Override
protected long getItemIdUpperBound() {
    // Maybe my list will always be reasonably small, so:
    return 100000;
    // Or maybe it's a list of up to 5 items:
    return 4;
    // Or maybe I have no idea, and my IDs are hard to predict:
    return Long.MAX_VALUE;
}

That adapter's long getItemId() must then only return values between 0 and getItemIdUpperBound(). Maybe throw an IllegalStateException if it returns something outside.

Then, we could verify on every adapter composition that the sum of every adapter's count doesn't exceed LONG.MAX_VALUE. So an adapter with getMaximumItemIds() = Long.MAX_VALUE is fine, but if it's composed with another adapter and stable IDs are needed, throw an IllegalStateException or something. The effective getItemId() would be the composed PowerAdapter's getItemId() plus getItemIdUpperBound() + 1 for every PowerAdapter that comes before it.

ViewFactorys or varargs layoutResources would have getItemIdUpperBound() = viewCount - 1 and getItemId() = indexOfView.

It's not perfect, but it would enable a lot of cases where IDs are known and bounded (like incrementing database IDs or ViewFactorys). And it wouldn't be necessary to specify when stable IDs aren't needed.

This unfortunately wouldn't cover many common cases, such as string IDs, or even integer IDs generated on a remote database, since communicating the bounds typically wouldn't be possible.

Thankfully, I think object IDs are possible using a WeakHashMap and/or WeakReferences.