美文网首页JavaAndroid
对象池-享元模式

对象池-享元模式

作者: WilsonMing | 来源:发表于2022-09-09 22:29 被阅读0次

    享元模式(FlyweightPattern)--实现对象的复用

    image.png

    代码

    • 享元接口或者抽象类
    open abstract class Flyweight {
        abstract fun operation(extrinsicState: String)
    }
    
    • 享元实体类
    /**
     * 享元实体类
     *
     * @constructor
     * TODO
     *
     * @param intrinsicState 内部状态intrinsicState作为成员变量,同一个享元对象其内部状态是一致的
     */
    class ConcreteFlyweight(intrinsicState:String) : Flyweight() {
        /**
         * 外部状态extrinsicState在使用时由外部设置,不保存在享元对象中,
         *
         * @param extrinsicState
         */
        override fun operation(extrinsicState: String) {
            println("外部设置数据$extrinsicState")
        }
    }
    
    • 享元工厂类
    /**
     * 享元工厂类
     *
     */
    class FlyweightFactory {
        companion object{
            //定义一个HashMap用于存储享元对象,实现享元池
            val flyweights = HashMap<String, Flyweight>()
            fun getFlyweight(key: String): Flyweight? {
                //如果对象存在,则直接从享元池获取
                return if (flyweights.containsKey(key)){
                    flyweights[key]
                }else{
                    //如果对象不存在,先创建一个新的对象添加到享元池中,然后返回
                    val flyweight = ConcreteFlyweight(key)
                    flyweights[key] = flyweight
                    flyweights[key]
                }
            }
        }
    
    }
    
    • 测试代码
    fun main() {
        val flyweight1=FlyweightFactory.getFlyweight("key")
        val flyweight2=FlyweightFactory.getFlyweight("key")
        println("flyweight1==flyweight2:${flyweight1==flyweight2}")
        flyweight1?.operation("外部设置1")
        flyweight2?.operation("外部设置2")
    }
    

    适用场景

    (1) 一个系统有大量相同或者相似的对象,造成内存的大量耗费。
    (2) 对象的大部分状态都可以外部化,可以将这些外部状态传入对象中。
    (3) 在使用享元模式时需要维护一个存储享元对象的享元池,而这需要耗费一定的系统资源, 因此,应当在需要多次重复使用享元对象时才值得使用享元模式。

    优缺点

    • 优点:
      (1) 可以极大减少内存中对象的数量,使得相同或相似对象在内存中只保存一份,从而可以节 约系统资源,提高系统性能。
      (2) 享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同 的环境中被共享。
    • 缺点
      (1) 享元模式使得系统变得复杂,需要分离出内部状态和外部状态,这使得程序的逻辑复杂 化。
      (2) 为了使对象可以共享,享元模式需要将享元对象的部分状态外部化,而读取外部状态将使 得运行时间变长。

    Android系统提供对象池实现

    Pools

    public interface Pool<T> {
            @Nullable
            T acquire();
    
            boolean release(@NonNull T instance);
        }
    
    public static class SimplePool<T> implements Pool<T> {
            private final Object[] mPool;
    
            private int mPoolSize;
    
            public SimplePool(int maxPoolSize) {
                if (maxPoolSize <= 0) {
                    throw new IllegalArgumentException("The max pool size must be > 0");
                }
                mPool = new Object[maxPoolSize];
            }
    
            @Override
            @SuppressWarnings("unchecked")
            public T acquire() {
                if (mPoolSize > 0) {
                    final int lastPooledIndex = mPoolSize - 1;
                    T instance = (T) mPool[lastPooledIndex];
                    mPool[lastPooledIndex] = null;
                    mPoolSize--;
                    return instance;
                }
                return null;
            }
    
            @Override
            public boolean release(@NonNull T instance) {
                if (isInPool(instance)) {
                    throw new IllegalStateException("Already in the pool!");
                }
                if (mPoolSize < mPool.length) {
                    mPool[mPoolSize] = instance;
                    mPoolSize++;
                    return true;
                }
                return false;
            }
    
            private boolean isInPool(@NonNull T instance) {
                for (int i = 0; i < mPoolSize; i++) {
                    if (mPool[i] == instance) {
                        return true;
                    }
                }
                return false;
            }
        }
    
    public static class SynchronizedPool<T> extends SimplePool<T> {
            private final Object mLock = new Object();
    
            public SynchronizedPool(int maxPoolSize) {
                super(maxPoolSize);
            }
    
            @Override
            public T acquire() {
                synchronized (mLock) {
                    return super.acquire();
                }
            }
    
            @Override
            public boolean release(@NonNull T instance) {
                synchronized (mLock) {
                    return super.release(instance);
                }
            }
        }
    

    Android源码使用享元模式

    • RecyclerView#RecyclerViewPool
        public static class RecycledViewPool {
            private static final int DEFAULT_MAX_SCRAP = 5;
    
            static class ScrapData {
                final ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
                int mMaxScrap = DEFAULT_MAX_SCRAP;
                long mCreateRunningAverageNs = 0;
                long mBindRunningAverageNs = 0;
            }
            SparseArray<ScrapData> mScrap = new SparseArray<>();
            private int mAttachCount = 0;
    
            public void clear() {
                for (int i = 0; i < mScrap.size(); i++) {
                    ScrapData data = mScrap.valueAt(i);
                    data.mScrapHeap.clear();
                }
            }
    
           
            public void setMaxRecycledViews(int viewType, int max) {
                ScrapData scrapData = getScrapDataForType(viewType);
                scrapData.mMaxScrap = max;
                final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
                while (scrapHeap.size() > max) {
                    scrapHeap.remove(scrapHeap.size() - 1);
                }
            }
    
          
            public int getRecycledViewCount(int viewType) {
                return getScrapDataForType(viewType).mScrapHeap.size();
            }
    
         
            @Nullable
            public ViewHolder getRecycledView(int viewType) {
                final ScrapData scrapData = mScrap.get(viewType);
                if (scrapData != null && !scrapData.mScrapHeap.isEmpty()) {
                    final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
                    for (int i = scrapHeap.size() - 1; i >= 0; i--) {
                        if (!scrapHeap.get(i).isAttachedToTransitionOverlay()) {
                            return scrapHeap.remove(i);
                        }
                    }
                }
                return null;
            }
    
           
            int size() {
                int count = 0;
                for (int i = 0; i < mScrap.size(); i++) {
                    ArrayList<ViewHolder> viewHolders = mScrap.valueAt(i).mScrapHeap;
                    if (viewHolders != null) {
                        count += viewHolders.size();
                    }
                }
                return count;
            }
    
         
            public void putRecycledView(ViewHolder scrap) {
                final int viewType = scrap.getItemViewType();
                final ArrayList<ViewHolder> scrapHeap = getScrapDataForType(viewType).mScrapHeap;
                if (mScrap.get(viewType).mMaxScrap <= scrapHeap.size()) {
                    return;
                }
                if (DEBUG && scrapHeap.contains(scrap)) {
                    throw new IllegalArgumentException("this scrap item already exists");
                }
                scrap.resetInternal();
                scrapHeap.add(scrap);
            }
    
            long runningAverage(long oldAverage, long newValue) {
                if (oldAverage == 0) {
                    return newValue;
                }
                return (oldAverage / 4 * 3) + (newValue / 4);
            }
    
            void factorInCreateTime(int viewType, long createTimeNs) {
                ScrapData scrapData = getScrapDataForType(viewType);
                scrapData.mCreateRunningAverageNs = runningAverage(
                        scrapData.mCreateRunningAverageNs, createTimeNs);
            }
    
            void factorInBindTime(int viewType, long bindTimeNs) {
                ScrapData scrapData = getScrapDataForType(viewType);
                scrapData.mBindRunningAverageNs = runningAverage(
                        scrapData.mBindRunningAverageNs, bindTimeNs);
            }
    
            boolean willCreateInTime(int viewType, long approxCurrentNs, long deadlineNs) {
                long expectedDurationNs = getScrapDataForType(viewType).mCreateRunningAverageNs;
                return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
            }
    
            boolean willBindInTime(int viewType, long approxCurrentNs, long deadlineNs) {
                long expectedDurationNs = getScrapDataForType(viewType).mBindRunningAverageNs;
                return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
            }
    
            void attach() {
                mAttachCount++;
            }
    
            void detach() {
                mAttachCount--;
            }
    
            void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
                    boolean compatibleWithPrevious) {
                if (oldAdapter != null) {
                    detach();
                }
                if (!compatibleWithPrevious && mAttachCount == 0) {
                    clear();
                }
                if (newAdapter != null) {
                    attach();
                }
            }
    
            private ScrapData getScrapDataForType(int viewType) {
                ScrapData scrapData = mScrap.get(viewType);
                if (scrapData == null) {
                    scrapData = new ScrapData();
                    mScrap.put(viewType, scrapData);
                }
                return scrapData;
            }
        }
    
    
    • 事件分发ViewGourp#TouchTarget.obtain(View,pointerIdBits)
    private static final class TouchTarget {
            private static final int MAX_RECYCLED = 32;
            private static final Object sRecycleLock = new Object[0];
            private static TouchTarget sRecycleBin;
            private static int sRecycledCount;
    
            public static final int ALL_POINTER_IDS = -1; // all ones
    
            // The touched child view.
            @UnsupportedAppUsage
            public View child;
    
            // The combined bit mask of pointer ids for all pointers captured by the target.
            public int pointerIdBits;
    
            // The next target in the target list.
            public TouchTarget next;
    
            @UnsupportedAppUsage
            private TouchTarget() {
            }
    
            public static TouchTarget obtain(@NonNull View child, int pointerIdBits) {
                if (child == null) {
                    throw new IllegalArgumentException("child must be non-null");
                }
    
                final TouchTarget target;
                synchronized (sRecycleLock) {
                    if (sRecycleBin == null) {
                        target = new TouchTarget();
                    } else {
                        target = sRecycleBin;
                        sRecycleBin = target.next;
                         sRecycledCount--;
                        target.next = null;
                    }
                }
                target.child = child;
                target.pointerIdBits = pointerIdBits;
                return target;
            }
    
            public void recycle() {
                if (child == null) {
                    throw new IllegalStateException("already recycled once");
                }
    
                synchronized (sRecycleLock) {
                    if (sRecycledCount < MAX_RECYCLED) {
                        next = sRecycleBin;
                        sRecycleBin = this;
                        sRecycledCount += 1;
                    } else {
                        next = null;
                    }
                    child = null;
                }
            }
        }
    
    • Message
    public final class Message {
        public int what;
       
        public int arg1;
        
        public int arg2;
    
        public Object obj;
    
        public Messenger replyTo;
    
        public static final int UID_NONE = -1;
        
        public int sendingUid = UID_NONE;
        
        public int workSourceUid = UID_NONE;
        
        @UnsupportedAppUsage
        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
        public long when;
    
        /*package*/ Bundle data;
    
        @UnsupportedAppUsage
        /*package*/ Handler target;
    
        @UnsupportedAppUsage
        /*package*/ Runnable callback;
    
        // sometimes we store linked lists of these things
        @UnsupportedAppUsage
        /*package*/ Message next;
    
        /** @hide */
        public static final Object sPoolSync = new Object();
        private static Message sPool;
        private static int sPoolSize = 0;
    
        private static final int MAX_POOL_SIZE = 50;
        private static boolean gCheckRecycle = true;
    
        public static Message obtain() {
            synchronized (sPoolSync) {
                if (sPool != null) {
                    Message m = sPool;
                    sPool = m.next;
                    m.next = null;
                    m.flags = 0; // clear in-use flag
                    sPoolSize--;
                    return m;
                }
            }
            return new Message();
        }
    
        
        public static Message obtain(Message orig) {
            Message m = obtain();
            m.what = orig.what;
            m.arg1 = orig.arg1;
            m.arg2 = orig.arg2;
            m.obj = orig.obj;
            m.replyTo = orig.replyTo;
            m.sendingUid = orig.sendingUid;
            m.workSourceUid = orig.workSourceUid;
            if (orig.data != null) {
                m.data = new Bundle(orig.data);
            }
            m.target = orig.target;
            m.callback = orig.callback;
            return m;
        }
    
        
        public static Message obtain(Handler h) {
            Message m = obtain();
            m.target = h;
            return m;
        }
    
       
        public static Message obtain(Handler h, Runnable callback) {
            Message m = obtain();
            m.target = h;
            m.callback = callback;
            return m;
        }
        
        public static Message obtain(Handler h, int what) {
            Message m = obtain();
            m.target = h;
            m.what = what;
            return m;
        }
        
        public static Message obtain(Handler h, int what, Object obj) {
            Message m = obtain();
            m.target = h;
            m.what = what;
            m.obj = obj;
            return m;
        }
       
        public static Message obtain(Handler h, int what, int arg1, int arg2) {
            Message m = obtain();
            m.target = h;
            m.what = what;
            m.arg1 = arg1;
            m.arg2 = arg2;
            return m;
        }
    
        public static Message obtain(Handler h, int what,
                int arg1, int arg2, Object obj) {
            Message m = obtain();
            m.target = h;
            m.what = what;
            m.arg1 = arg1;
            m.arg2 = arg2;
            m.obj = obj;
    
            return m;
        }
    
        public void recycle() {
            if (isInUse()) {
                if (gCheckRecycle) {
                    throw new IllegalStateException("This message cannot be recycled because it "
                            + "is still in use.");
                }
                return;
            }
            recycleUnchecked();
        }
      
        @UnsupportedAppUsage
        void recycleUnchecked() {
            // Mark the message as in use while it remains in the recycled object pool.
            // Clear out all other details.
            flags = FLAG_IN_USE;
            what = 0;
            arg1 = 0;
            arg2 = 0;
            obj = null;
            replyTo = null;
            sendingUid = UID_NONE;
            workSourceUid = UID_NONE;
            when = 0;
            target = null;
            callback = null;
            data = null;
    
            synchronized (sPoolSync) {
                if (sPoolSize < MAX_POOL_SIZE) {
                    next = sPool;
                    sPool = this;
                    sPoolSize++;
                }
            }
        }
        /*package*/ boolean isInUse() {
            return ((flags & FLAG_IN_USE) == FLAG_IN_USE);
        }
        
        public Message() {
        }
    }
    

    相关文章

      网友评论

        本文标题:对象池-享元模式

        本文链接:https://www.haomeiwen.com/subject/knhanrtx.html