Android Open Source - droidkit-engines List Engine






From Project

Back to project page droidkit-engines.

License

The source code is released under:

MIT License

If you think the Android project droidkit-engines listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package com.droidkit.engine.list;
//from w  ww.  j av  a 2  s .c  o m
import android.os.Message;
import android.os.SystemClock;

import com.droidkit.actors.*;
import com.droidkit.engine.Engines;
import com.droidkit.engine._internal.RunnableActor;
import com.droidkit.engine._internal.util.Utils;
import com.droidkit.engine.common.ValueCallback;
import com.droidkit.engine.event.Events;
import com.droidkit.engine.event.NotificationCenter;
import com.droidkit.engine._internal.util.SortedArrayList;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

public class ListEngine<V> {

    static {
        Engines.init();
    }

    private static final AtomicInteger NEXT_ID = new AtomicInteger(1);


    ////////////////////////////////////////////////////////////////////////////////////////////////


    /**
     * Map for storing values
     */
    protected final ConcurrentHashMap<Long, V> inMemoryMap;


    /**
     * List for storing values, always synchronized with inMemorySortedList2
     */
    protected final SortedArrayList<V> inMemorySortedList1;


    /**
     * List for storing values, always synchronized with inMemorySortedList1
     */
    protected final SortedArrayList<V> inMemorySortedList2;


    /**
     * Database interface
     */
    protected final StorageAdapter storageAdapter;

    /**
     * Id used in sending of NotificationCenter events
     */
    protected final int listEngineId;

    /**
     * Loop for all in-memory lists operation
     */
    protected final ActorRef listActor;

    /**
     * Loop for all database operations
     */
    protected final ActorRef dbActor;

    /**
     * Ui Notifications actor
     */
    protected final ActorRef uiActor;

    /**
     * Adapter of data for Engine
     */
    protected final DataAdapter<V> dataAdapter;

    /**
     * Creating ListEngine instance
     *
     * @param storageAdapter storage adapter
     * @param dataAdapter    data adapter
     */
    public ListEngine(final StorageAdapter storageAdapter,
                      final DataAdapter<V> dataAdapter) {

        this.listEngineId = NEXT_ID.getAndIncrement();

        Comparator<V> comparator = new Comparator<V>() {
            @Override
            public int compare(V lhs, V rhs) {
                long lKey = dataAdapter.getSortKey(lhs);
                long rKey = dataAdapter.getSortKey(rhs);

                if (lKey > rKey) {
                    return 1;
                } else if (lKey < rKey) {
                    return -1;
                } else {
                    return 0;
                }
            }
        };

        this.inMemorySortedList1 = new SortedArrayList<V>(comparator);
        this.inMemorySortedList2 = new SortedArrayList<V>(comparator);
        this.inMemorySortedList = inMemorySortedList1;

        this.inMemoryMap = new ConcurrentHashMap<Long, V>();
        this.storageAdapter = storageAdapter;
        this.dataAdapter = dataAdapter;

        dbActor = ActorSystem.system().actorOf(db());
        listActor = ActorSystem.system().actorOf(memoryList());
        uiActor = ActorSystem.system().actorOf(RunnableActor.class, "engines_notify");
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////

    protected volatile int lastSliceSize = 1;

    protected volatile int currentDbOffset = 0;

    protected volatile boolean isDbSliceLoadingInProgress = false;

    protected volatile SortedArrayList<V> inMemorySortedList;

    ////////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * @return Id unique for current ListEngine instance, must be used in NotificationListeners
     */
    public int getListEngineId() {
        return listEngineId;
    }

    public synchronized void addItem(final V value) {
        addOrUpdateItem(value, false, true);
    }

    public synchronized void updateItem(final V value) {
        addOrUpdateItem(value, true, false);
    }

    public synchronized void addOrUpdateItem(final V value) {
        addOrUpdateItem(value, true, true);
    }

    protected synchronized void addOrUpdateItem(final V value, boolean update, final boolean add) {

        final boolean isAddOnly = !update && add;
        final boolean isUpdateOnly = update && !add;
        final boolean isAddOrUpdate = update && add;

        final long id = dataAdapter.getId(value);

        modifyInMemoryList(new InMemoryListModification<V>() {
            @Override
            public void modify(SortedArrayList<V> list) {
                if (isAddOrUpdate || isUpdateOnly) {
                    Iterator<V> it = list.iterator();
                    while (it.hasNext()) {
                        V value = it.next();
                        if (dataAdapter.getId(value) == id) {
                            it.remove();
                            break;
                        }
                    }
                }
                list.add(value);
            }
        }, 1);

        inMemoryMap.put(id, value);

        dbActor.send(new Runnable() {
            @Override
            public void run() {
                final long dbStart = SystemClock.uptimeMillis();
                if (isUpdateOnly || isAddOrUpdate) {
                    storageAdapter.insertOrReplaceSingle(value);
                } else if (isAddOnly) {
                    storageAdapter.insertSingle(value);
                }
            }
        });
    }

    public synchronized void addItems(final ArrayList<V> values) {
        addOrUpdateItems(values, false, true);
    }

    public synchronized void updateItems(final ArrayList<V> values) {
        addOrUpdateItems(values, true, false);
    }

    public synchronized void addOrUpdateItems(final ArrayList<V> values) {
        addOrUpdateItems(values, true, true);
    }

    protected synchronized void addOrUpdateItems(final ArrayList<V> values, boolean update, final boolean add) {

        final boolean isAddOnly = !update && add;
        final boolean isUpdateOnly = update && !add;
        final boolean isAddOrUpdate = update && add;

        final ArrayList<V> toRemove = new ArrayList<V>();

        for (V val : values) {
            final long id = dataAdapter.getId(val);
            final V originalValue = inMemoryMap.get(id);

            if ((originalValue != null && isUpdateOnly) || isAddOnly || isAddOrUpdate) {
                if (originalValue != null && isAddOrUpdate || isUpdateOnly) {
                    toRemove.add(originalValue);
                }
                inMemoryMap.put(id, val);
            }
        }

        modifyInMemoryList(new InMemoryListModification<V>() {
            @Override
            public void modify(SortedArrayList<V> list) {
                list.removeAll(toRemove);
                list.addAll(values);
            }
        }, values.size());

        dbActor.send(new Runnable() {
            @Override
            public void run() {
                final long dbStart = SystemClock.uptimeMillis();
                if (isUpdateOnly || isAddOrUpdate) {
                    storageAdapter.insertOrReplaceBatch(values);
                } else if (isAddOnly) {
                    storageAdapter.insertBatch(values);
                }
                // showDebugToast("addOrUpdateItems DB: " + (SystemClock.uptimeMillis() - dbStart) + "ms");

            }
        });
    }

    public synchronized void removeItem(final long key) {
        final V val = inMemoryMap.remove(key);
        if (val != null) {
            modifyInMemoryList(new InMemoryListModification<V>() {
                @Override
                public void modify(SortedArrayList<V> list) {
                    Iterator<V> it = list.iterator();
                    while (it.hasNext()) {
                        V value = it.next();
                        if (dataAdapter.getId(value) == key) {
                            it.remove();
                            break;
                        }
                    }
                }
            }, -1);
        }

        dbActor.send(new Runnable() {
            @Override
            public void run() {
                storageAdapter.deleteSingle(key);
            }
        });
    }

    public synchronized int getCountInMemoryList() {
        return getActiveList().size();
    }

    public synchronized void loadNextListSlice(final int limit) {
        if (!isDbSliceLoadingInProgress && lastSliceSize > 0) {
            isDbSliceLoadingInProgress = true;


            dbActor.send(new Runnable() {
                @Override
                public void run() {
                    try {
                        // Logger.d(TAG, "Loading new slice: offset:" + currentDbOffset + ", limit:" + limit);
                        final long start = SystemClock.uptimeMillis();

                        final ArrayList<V> list = storageAdapter.loadListSlice(limit, currentDbOffset);

                        if (list != null) {
                            lastSliceSize = list.size();
                            currentDbOffset += lastSliceSize;

                            // Logger.d(TAG, "Loaded " + lastSliceSize + " items in " + (SystemClock.uptimeMillis() - start) + "ms");
                            // showDebugToast("loadNextListSlice(" + limit + ") in " + (SystemClock.uptimeMillis() - start) + "ms");

                            modifyInMemoryList(new InMemoryListModification<V>() {
                                @Override
                                public void modify(SortedArrayList<V> targetList) {
                                    targetList.addAll(list);
                                }
                            }, list.size());

                            for (V val : list) {
                                inMemoryMap.put(dataAdapter.getId(val), val);
                            }
                        }

                    } catch (Exception e) {
                        // Logger.e(TAG, "Error loading DB slice", e);
                        e.printStackTrace();
                    }
                    isDbSliceLoadingInProgress = false;
                }
            });
        }
    }

    public synchronized void loadAll() {
        if (!isDbSliceLoadingInProgress && lastSliceSize > 0) {
            isDbSliceLoadingInProgress = true;

            dbActor.send(new Runnable() {
                @Override
                public void run() {
                    try {
                        // Logger.d(TAG, "Loading whole list");
                        final long start = SystemClock.uptimeMillis();

                        final ArrayList<V> list = storageAdapter.loadAll();

                        if (list != null) {
                            lastSliceSize = list.size();
                            currentDbOffset += lastSliceSize;

                            // Logger.d(TAG, "Loaded " + lastSliceSize + " items in " + (SystemClock.uptimeMillis() - start) + "ms");
                            // showDebugToast("loadAll() in " + (SystemClock.uptimeMillis() - start) + "ms");

                            modifyInMemoryList(new InMemoryListModification<V>() {
                                @Override
                                public void modify(SortedArrayList<V> targetList) {
                                    targetList.clear();
                                    targetList.addAll(list);
                                }
                            }, list.size());

                            for (V val : list) {
                                inMemoryMap.put(dataAdapter.getId(val), val);
                            }
                        }

                    } catch (Exception e) {
                        e.printStackTrace();
                        // Logger.e(TAG, "Error loading DB slice", e);
                    }
                    isDbSliceLoadingInProgress = false;
                }
            });
        }
    }

    /**
     * Try to find value in memory by key
     *
     * @param key
     * @return Value
     */
    public synchronized V getValueFromMemory(long key) {
        V value = inMemoryMap.get(key);
        return value;
    }

    public synchronized void getValueFromDb(final long key, final ValueCallback<V> valueCallback) {
        dbActor.send(new Runnable() {
            @Override
            public void run() {
                V v = (V) storageAdapter.getById(key);
                inMemoryMap.put(key, v);
                valueCallback.value(v);
            }
        });
    }

    public synchronized V getValueFromMemoryList(int index) {
        if (index >= 0 && index < getActiveList().size()) {
            return getActiveList().get(index);
        } else {
            return null;
        }
    }

    public synchronized void clear() {

        clearInMemory();

        dbActor.send(new Runnable() {
            @Override
            public void run() {
                final long dbStart = SystemClock.uptimeMillis();
                storageAdapter.deleteAll();
                currentDbOffset = 0;
                // showDebugToast("DB: " + (SystemClock.uptimeMillis() - dbStart) + "ms");
            }
        }, 0);
    }

    public synchronized void clearMemoryInternal() {
        final int changeSize = inMemorySortedList1.size();
        inMemorySortedList1.clear();
        inMemorySortedList2.clear();
        inMemoryMap.clear();
        lastSliceSize = 1;
        currentDbOffset = 0;
        NotificationCenter.getInstance().fireEvent(Events.LIST_ENGINE_UI_LIST_UPDATE, listEngineId, new Integer[]{changeSize});
    }

    private synchronized void clearInMemory() {
        if (Utils.isUIThread()) {
            clearMemoryInternal();
        } else {
            uiActor.send(new Runnable() {
                @Override
                public void run() {
                    clearMemoryInternal();
                }
            });
        }
    }

    private void switchActiveList() {
        synchronized (inMemoryListSync) {
            if (inMemorySortedList == inMemorySortedList1) {
                inMemorySortedList = inMemorySortedList2;
            } else {
                inMemorySortedList = inMemorySortedList1;
            }
        }
    }

    private SortedArrayList<V> getInactiveList() {
        synchronized (inMemoryListSync) {
            if (inMemorySortedList == inMemorySortedList1) {
                return inMemorySortedList2;
            } else {
                return inMemorySortedList1;
            }
        }
    }

    private SortedArrayList<V> getActiveList() {
        synchronized (inMemoryListSync) {
            return inMemorySortedList;
        }
    }

    private synchronized void modifyInMemoryList(InMemoryListModification<V> modification, int changeSize) {
        listActor.send(new ChangeList<V>(modification, changeSize));
    }

    private final Object inMemoryListSync = new Object();

    private interface InMemoryListModification<V> {
        void modify(final SortedArrayList<V> list);
    }

    public ActorSelection db() {
        return new ActorSelection(Props.create(RunnableActor.class, new ActorCreator<RunnableActor>() {
            @Override
            public RunnableActor create() {
                return new RunnableActor();
            }
        }).changeDispatcher("db"), "list_db_" + listEngineId);
    }

    public ActorSelection memoryList() {
        return new ActorSelection(Props.create(MemoryListActor.class, new ActorCreator<MemoryListActor>() {
            @Override
            public MemoryListActor create() {
                return new MemoryListActor(ListEngine.this);
            }
        }), "list_" + listEngineId);
    }

    void doChangeList(final ChangeList<V> changeList) {
        synchronized (inMemoryListSync) {
            final InMemoryListModification<V> modification = changeList.getModification();
            final SortedArrayList<V> beginInactiveList = getInactiveList();

            modification.modify(beginInactiveList);

            uiActor.send(new Runnable() {
                @Override
                public void run() {
                    synchronized (inMemoryListSync) {
                        switchActiveList();
                        // Logger.d(TAG, "inMemorySortedList1.size():" + inMemorySortedList1.size() + ", inMemorySortedList2.size():" + inMemorySortedList2.size());
                        NotificationCenter.getInstance().fireEvent(Events.LIST_ENGINE_UI_LIST_UPDATE, listEngineId, new Integer[]{changeList.getSize()});
                        inMemoryListSync.notifyAll();
                    }
                }
            }, 5);

            try {
                inMemoryListSync.wait(5000);
            } catch (InterruptedException e) {
                // Logger.d(e);
            }

            final SortedArrayList<V> endInactiveList = getInactiveList();
            if (beginInactiveList != endInactiveList) {
                modification.modify(endInactiveList);
            } else {
                //SOMETHING WENT WRONG!
                // Logger.e(TAG, "Error in memories list sync");
                // Logger.e(TAG, "inMemorySortedList1.size():" + inMemorySortedList1.size() + ", inMemorySortedList2.size():" + inMemorySortedList2.size());
                uiActor.send(new Runnable() {
                    @Override
                    public void run() {
                        synchronized (inMemoryListSync) {
                            modification.modify(getActiveList());
                            NotificationCenter.getInstance().fireEvent(Events.LIST_ENGINE_UI_LIST_UPDATE, listEngineId, new Integer[]{changeList.getSize()});
                            inMemoryListSync.notifyAll();
                        }
                    }
                }, 5);
                try {
                    inMemoryListSync.wait(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    // Logger.d(e);
                }
            }
        }
    }

    public static class MemoryListActor extends Actor {

        private ListEngine engine;

        public MemoryListActor(ListEngine engine) {
            this.engine = engine;
        }

        @Override
        public void onReceive(Object message) {
            if (message instanceof ChangeList) {
                engine.doChangeList((ChangeList) message);
            }
        }
    }

    private static class ChangeList<V> {
        private final InMemoryListModification<V> modification;
        private final int size;

        private ChangeList(InMemoryListModification<V> modification, int size) {
            this.modification = modification;
            this.size = size;
        }

        public InMemoryListModification<V> getModification() {
            return modification;
        }

        public int getSize() {
            return size;
        }
    }
}




Java Source Code List

com.droidkit.engine.Engines.java
com.droidkit.engine.common.KeyCallback.java
com.droidkit.engine.common.ValueCallback.java
com.droidkit.engine.common.ValuesCallback.java
com.droidkit.engine.event.Events.java
com.droidkit.engine.event.NotificationCenter.java
com.droidkit.engine.event.NotificationListener.java
com.droidkit.engine.event.StateInitValue.java
com.droidkit.engine.event.State.java
com.droidkit.engine._internal.RunnableActor.java
com.droidkit.engine._internal.sqlite.AbstractDao.java
com.droidkit.engine._internal.sqlite.BinarySerializator.java
com.droidkit.engine._internal.sqlite.DbHelper.java
com.droidkit.engine._internal.sqlite.DbProvider.java
com.droidkit.engine._internal.sqlite.FastCursor.java
com.droidkit.engine._internal.sqlite.SqlStatements.java
com.droidkit.engine._internal.util.SortedArrayList.java
com.droidkit.engine._internal.util.Utils.java
com.droidkit.engine._internal.util.WeakEqualReference.java
com.droidkit.engine.keyvalue.DataAdapter.java
com.droidkit.engine.keyvalue.KeyValueEngine.java
com.droidkit.engine.keyvalue.StorageAdapter.java
com.droidkit.engine.keyvalue.sqlite.SQLiteStorageAdapter.java
com.droidkit.engine.keyvalue.sqlite.internal.KeyValueEngineDao.java
com.droidkit.engine.keyvalue.sqlite.internal.KeyValueEngineTableStatements.java
com.droidkit.engine.list.DataAdapter.java
com.droidkit.engine.list.ListEngine.java
com.droidkit.engine.list.StorageAdapter.java
com.droidkit.engine.list.sqlite.SQLiteMultipleStorageAdapter.java
com.droidkit.engine.list.sqlite.SQLiteStorageAdapter.java
com.droidkit.engine.list.sqlite.internal.ListEngineDao.java
com.droidkit.engine.list.sqlite.internal.ListEngineTableStatements.java
com.droidkit.sample.ActivityHelper.java
com.droidkit.sample.BaseActivity.java
com.droidkit.sample.TestMainActivity.java
com.droidkit.sample.listenginetest.TestListEngineActivity.java
com.droidkit.sample.listenginetest.TestProto.java
com.droidkit.sample.view.BlockingListView.java