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);
}
- 查看调用 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);
}
}
- 查看所有注册 ChangeNotifier 的数据集
ytw012@rom:~/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);
- 查看上述的监听 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();
}
}