天天看點

08 Gallery 源碼-監聽資料庫變化

0. 原文拜讀

https://blog.csdn.net/lin20044140410/article/details/77454079

1. DataManager.registerChangeNotifier 資料庫監聽器

Gallery不會去掃描sdcard,手機記憶體等儲存設備;隻是去查詢資料庫,及監聽資料庫的變化。

在DataManager中對具體的uri注冊監聽,在資料變化時會調用onChange(),然後調用資料源的notifyContentChanged();進一步調用會去喚醒處于等待狀态的加載線程。

package com.android.gallery3d.data;

public class DataManager implements StitchingChangeListener {

    public void registerChangeNotifier(Uri uri, ChangeNotifier notifier) {
        NotifyBroker broker = null;
        synchronized (mNotifierMap) {
            broker = mNotifierMap.get(uri);
            if (broker == null) {
                broker = new NotifyBroker(mDefaultMainHandler);
                // 注冊資料庫 url 監聽
                mApplication.getContentResolver()
                        .registerContentObserver(uri, true, broker);
                mNotifierMap.put(uri, broker);
            }
        }
        broker.registerNotifier(notifier);
    }
           
  1. 檢視調用 registerChangeNotifier
package com.android.gallery3d.data;

// This handles change notification for media sets.
public class ChangeNotifier {

    public ChangeNotifier(MediaSet set, Uri uri, GalleryApp application) {
        mMediaSet = set;
        application.getDataManager().registerChangeNotifier(uri, this);
    }

    public ChangeNotifier(MediaSet set, Uri[] uris, GalleryApp application) {
        mMediaSet = set;
        for (int i = 0; i < uris.length; i++) {
            application.getDataManager().registerChangeNotifier(uris[i], this);
        }
    }
           
  1. 檢視所有注冊 ChangeNotifier 的資料集
[email protected]:~/Android_Build_CS/android$ grep -irn "ChangeNotifier" packages/apps/Gallery/
packages/apps/Gallery/src/com/android/gallery3d/data/LocalAlbum.java:57:    private final ChangeNotifier mNotifier;
packages/apps/Gallery/src/com/android/gallery3d/data/LocalAlbum.java:90:        mNotifier = new ChangeNotifier(this, mBaseUri, application);
packages/apps/Gallery/src/com/android/gallery3d/data/LocalAlbumSet.java:53:    private final ChangeNotifier mNotifier;
packages/apps/Gallery/src/com/android/gallery3d/data/LocalAlbumSet.java:66:        mNotifier = new ChangeNotifier(this, mWatchUris, application);
packages/apps/Gallery/src/com/android/gallery3d/data/SecureAlbum.java:50:    private final ChangeNotifier mNotifier;
packages/apps/Gallery/src/com/android/gallery3d/data/SecureAlbum.java:60:        mNotifier = new ChangeNotifier(this, mWatchUris, application);
           
  1. 檢視上述的監聽 uri
// SecureAlbum.java
    private static final Uri[] mWatchUris =
        {Images.Media.EXTERNAL_CONTENT_URI, Video.Media.EXTERNAL_CONTENT_URI};
        
    // LocalAlbumSet.java 添加對Files的監聽(編輯相冊的場景)
    // 外部擴充媒體路徑
    public static final String EXTERNAL_MEDIA = "external";
    
    private static final Uri[] mWatchUris =
            {Images.Media.EXTERNAL_CONTENT_URI, Video.Media.EXTERNAL_CONTENT_URI,
                    MediaStore.Files.getContentUri(EXTERNAL_MEDIA)};
    
    // LocalAlbum.java
    if (isImage) {
        mBaseUri = Images.Media.EXTERNAL_CONTENT_URI;
    } else {
        mBaseUri = Video.Media.EXTERNAL_CONTENT_URI;
    }
           

2. DataManager.onChange 資料庫變化回調事件

2.1 DataManager.NotifyBroker.onChange

package com.android.gallery3d.data;

public class DataManager implements StitchingChangeListener {

    private static class NotifyBroker extends ContentObserver {
        private WeakHashMap<ChangeNotifier, Object> mNotifiers =
                new WeakHashMap<ChangeNotifier, Object>();

        public NotifyBroker(Handler handler) {
            super(handler);
        }

        public synchronized void registerNotifier(ChangeNotifier notifier) {
            mNotifiers.put(notifier, null);
        }

        @Override
        public synchronized void onChange(boolean selfChange) {
            for (ChangeNotifier notifier : mNotifiers.keySet()) {
                notifier.onChange(selfChange);
            }
        }
    }
           

2.2 ChangeNotifier.onChange

package com.android.gallery3d.data;

// This handles change notification for media sets.
public class ChangeNotifier {

    private MediaSet mMediaSet;

    protected void onChange(boolean selfChange) {
        if (mContentDirty.compareAndSet(false, true)) {
            mMediaSet.notifyContentChanged();
        }
    }
           

2.3 MediaSet.notifyContentChanged

packages/apps/Gallery/src/com/android/Gallery/data/MediaSet.java:211: public void notifyContentChanged() {

package com.android.gallery3d.data;

public abstract class MediaSet extends MediaObject {

    // This should be called by subclasses when the content is changed.
    public void notifyContentChanged() {
        synchronized (mLock) {
            for (ContentListener listener : mListeners.keySet()) {
                // 資料庫資料變化事件
                listener.onContentDirty();
            }
        }
    }

    // 注冊監聽資料庫資料變化接口
    // NOTE: The MediaSet only keeps a weak reference to the listener. The
    // listener is automatically removed when there is no other reference to
    // the listener.
    public void addContentListener(ContentListener listener) {
        synchronized (mLock) {
            mListeners.put(listener, null);
        }
    }

    public void removeContentListener(ContentListener listener) {
        synchronized (mLock) {
            mListeners.remove(listener);
        }
    }
           

檢視調用 onContentDirty 的重寫情況

packages/apps/Gallery/src/com/android/photos/shims/MediaSetLoader.java:55:        public void onContentDirty() {
packages/apps/Gallery/src/com/android/photos/shims/MediaItemsLoader.java:58:        public void onContentDirty() {
packages/apps/Gallery/src/com/android/gallery3d/data/FilterDeleteSet.java:256:    public void onContentDirty() {
packages/apps/Gallery/src/com/android/gallery3d/data/ClusterAlbumSet.java:91:    public void onContentDirty() {
packages/apps/Gallery/src/com/android/gallery3d/data/ClusterAlbum.java:205:    public void onContentDirty() {
packages/apps/Gallery/src/com/android/gallery3d/data/LocalMergeAlbum.java:190:    public void onContentDirty() {
packages/apps/Gallery/src/com/android/gallery3d/data/FilterEmptyPromptSet.java:59:    public void onContentDirty() {
packages/apps/Gallery/src/com/android/gallery3d/data/FilterTypeSet.java:81:    public void onContentDirty() {
packages/apps/Gallery/src/com/android/gallery3d/data/ComboAlbum.java:95:    public void onContentDirty() {
packages/apps/Gallery/src/com/android/gallery3d/data/ComboAlbumSet.java:88:    public void onContentDirty() {
packages/apps/Gallery/src/com/android/gallery3d/gadget/WidgetService.java:175:        public void onContentDirty() {
packages/apps/Gallery/src/com/android/gallery3d/gadget/MediaSetSource.java:94:    public void onContentDirty() {
packages/apps/Gallery/src/com/android/gallery3d/gadget/MediaSetSource.java:202:        public void onContentDirty() {
packages/apps/Gallery/src/com/android/gallery3d/app/PhotoDataAdapter.java:1182:        public void onContentDirty() {
packages/apps/Gallery/src/com/android/gallery3d/app/AlbumDataLoader.java:214:        public void onContentDirty() {
packages/apps/Gallery/src/com/android/gallery3d/app/AlbumSetDataLoader.java:251:        public void onContentDirty() {
packages/apps/Gallery/src/com/android/gallery3d/app/TimeLineDataLoader.java:268:        public void onContentDirty() {
packages/apps/Gallery/src/com/android/gallery3d/app/SlideshowDataAdapter.java:150:        public void onContentDirty() {
           
  • 例如 AlbumDataLoader.MySourceListener.onContentDirty 事件
package com.android.gallery3d.app;

public class AlbumDataLoader {

    private ReloadTask mReloadTask;

    private class MySourceListener implements ContentListener {
        @Override
        public void onContentDirty() {
            // 重新進行資料加載
            if (mReloadTask != null) mReloadTask.notifyDirty();
        }
    }
    
    /*
     * The thread model of ReloadTask
     *      *
     * [Reload Task]       [Main Thread]
     *       |                   |
     * getUpdateInfo() -->       |           (synchronous call)
     *     (wait) <----    getUpdateInfo()
     *       |                   |
     *   Load Data               |
     *       |                   |
     * updateContent() -->       |           (synchronous call)
     *     (wait)          updateContent()
     *       |                   |
     *       |                   |
     */
    private class ReloadTask extends Thread {

        private volatile boolean mActive = true;
        private volatile boolean mDirty = true;
        private boolean mIsLoading = false;

        private void updateLoading(boolean loading) {
            if (mIsLoading == loading) return;
            mIsLoading = loading;
            mMainHandler.sendEmptyMessage(loading ? MSG_LOAD_START : MSG_LOAD_FINISH);
        }

        @Override
        public void run() {
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

            boolean updateComplete = false;
            while (mActive) {
                synchronized (this) {
                    if (mActive && !mDirty && updateComplete) {
                        updateLoading(false);
                        if (mFailedVersion != MediaObject.INVALID_DATA_VERSION) {
                            Log.d(TAG, "reload pause");
                        }
                        Utils.waitWithoutInterrupt(this);
                        if (mActive && (mFailedVersion != MediaObject.INVALID_DATA_VERSION)) {
                            Log.d(TAG, "reload resume");
                        }
                        continue;
                    }
                    mDirty = false;
                }
                updateLoading(true);
                long version = mSource.reload();
                UpdateInfo info = executeAndWait(new GetUpdateInfo(version));
                updateComplete = info == null;
                if (updateComplete) continue;
                if (info.version != version) {
                    info.size = mSource.getMediaItemCount();
                    info.version = version;
                }
                if (info.reloadCount > 0) {
                    info.items = mSource.getMediaItem(info.reloadStart, info.reloadCount);
                }
                executeAndWait(new UpdateContent(info));
            }
            updateLoading(false);
        }

        public synchronized void notifyDirty() {
            mDirty = true;
            notifyAll();
        }

        public synchronized void terminate() {
            mActive = false;
            notifyAll();
        }
    }
           

繼續閱讀