现在的位置: 首页 > 综合 > 正文

Gallery3d 学习笔记(7)

2013年05月11日 ⁄ 综合 ⁄ 共 14533字 ⁄ 字号 评论关闭

上次找到了缩略图的绘制的地方,但没有找到缩略图数据是如何来的,也不知道其他界面是怎么刷新的,更不知道界面是如何切换的。

让我们开始,先在Gallery.java 里面的OnCreate中找到一个函数

		sendInitialMessage();

顺着这个函数找下去

	private void sendInitialMessage()
	{
		mNumRetries = 0;
		Message checkStorage = new Message();
		checkStorage.what = CHECK_STORAGE;
		handler.sendMessage(checkStorage);
	}

消息发出去了,要求检查存储器,看下怎么处理的

	private final Handler handler = new Handler()
	{
		@Override
		public void handleMessage(Message msg)
		{
			switch (msg.what)
			{
			case CHECK_STORAGE:
				checkStorage();
				break;
			case HANDLE_INTENT:
				initializeDataSource();
				break;
			}
		}
	};

应该走到,checkStorage里面了

	private void checkStorage()
	{
		mNumRetries++;
		mImageManagerHasStorageAfterDelay = ImageManager.hasStorage();
		if (!mImageManagerHasStorageAfterDelay
				&& mNumRetries < NUM_STORAGE_CHECKS)
		{
			if (mNumRetries == 1)
			{
				mApp.showToast(getResources().getString(R.string.no_sd_card),
						Toast.LENGTH_LONG);
			}
			handler.sendEmptyMessageDelayed(CHECK_STORAGE, 200);
		} else
		{
			handler.sendEmptyMessage(HANDLE_INTENT);
		}
	}

因为

	private static final int NUM_STORAGE_CHECKS = 25;

所以这些意思就是如果没有T卡第一次就会报错,并检查25次,失败就结束,不报错,如果有就发送HANDLE_INTENT消息出来,这个消息看下是InitialDataSource()这个函数处理,看下这个函数,这个函数很长

	private void initializeDataSource()
	{
		final boolean hasStorage = mImageManagerHasStorageAfterDelay;
		// Creating the DataSource objects.
		final PicasaDataSource picasaDataSource = new PicasaDataSource(
				Gallery.this);
		final LocalDataSource localDataSource = new LocalDataSource(
				Gallery.this, LocalDataSource.URI_ALL_MEDIA, false);
		final ConcatenatedDataSource combinedDataSource = new ConcatenatedDataSource(
				localDataSource, picasaDataSource);

		// Depending upon the intent, we assign the right dataSource.
		if (!isPickIntent() && !isViewIntent() && !isReviewIntent())
		{
			localDataSource.setMimeFilter(true, true);
			if (hasStorage)
			{
				mGridLayer.setDataSource(combinedDataSource);
			} else
			{
				mGridLayer.setDataSource(picasaDataSource);
			}
		} else if (isPickIntent())
		{
			final Intent intent = getIntent();
			if (intent != null)
			{
				String type = intent.resolveType(Gallery.this);
				if (type == null)
				{
					// By default, we include images
					type = "image/*";
				}
				boolean includeImages = isImageType(type);
				boolean includeVideos = isVideoType(type);
				localDataSource.setMimeFilter(includeImages, includeVideos);
				if (includeImages)
				{
					if (hasStorage)
					{
						mGridLayer.setDataSource(combinedDataSource);
					} else
					{
						mGridLayer.setDataSource(picasaDataSource);
					}
				} else
				{
					mGridLayer.setDataSource(localDataSource);
				}
				mGridLayer.setPickIntent(true);
				if (hasStorage)
				{
					mApp.showToast(getResources().getString(
							R.string.pick_prompt), Toast.LENGTH_LONG);
				}
			}
		} else
		{ // view intent for images and review intent for images and videos
			final Intent intent = getIntent();
			Uri uri = intent.getData();
			boolean slideshow = intent.getBooleanExtra("slideshow", false);
			final LocalDataSource singleDataSource = new LocalDataSource(
					Gallery.this, uri.toString(), true);
			// Display both image and video.
			singleDataSource.setMimeFilter(true, true);

			if (hasStorage)
			{
				ConcatenatedDataSource singleCombinedDataSource = new ConcatenatedDataSource(
						singleDataSource, picasaDataSource);
				mGridLayer.setDataSource(singleCombinedDataSource);
			} else
			{
				mGridLayer.setDataSource(picasaDataSource);
			}
			mGridLayer.setViewIntent(true, Utils.getBucketNameFromUri(
					getContentResolver(), uri));

			if (isReviewIntent())
			{
				mGridLayer.setEnterSelectionMode(true);
			}

			if (singleDataSource.isSingleImage())
			{
				mGridLayer.setSingleImage(false);
			} else if (slideshow)
			{
				mGridLayer.setSingleImage(true);
				mGridLayer.startSlideshow();
			}
		}
		// We record the set of enabled accounts for picasa.
		mPicasaHandler.sendEmptyMessage(GET_PICASA_ACCOUNT_STATUS);
	}
}

又遇到数据源了

PicasaDataSource   据说google 收购的一家公司做的毕加索数据源

LocalDataSource     本地T 卡数据

combinedDataSource 反正是组合的数据源    可以是 PicasaDataSource + LocalDataSource

singleDataSource    单张图片浏览数据源

里面的mGridLayer根据不同的情况,设置不同的数据源,都是通过一个接口SetDataSource.

我们看这个函数的内容

    public void setDataSource(DataSource dataSource) {
        MediaFeed feed = mMediaFeed;
        mMediaFeed = new MediaFeed(mContext, dataSource, this);
        if (feed != null) {
            // Restore the slot state in the original feed before shutting it down.
            mMediaFeed.copySlotStateFrom(feed);

            feed.shutdown();
            mDisplayList.clear();
            mBackground.clear();
        }

        mMediaFeed.start();
    }

先做了些处理,防止先前已经设置过数据源出现问题,

最后都会调用mMediaFeed.start();

public final class MediaFeed implements Runnable {}

这个类继承于Runnable,并没有继承于Thread ,说明start是一个用户自己写的接口

看下都干了什么事情?

   public void start() {
        final MediaFeed feed = this;
        onResume();
        mLoading = true;
        mDataSourceThread = new Thread(this);
        mDataSourceThread.setName("MediaFeed");
        mIsShutdown = false;
        mAlbumSourceThread = new Thread(new Runnable() {
            public void run() {
                if (mContext == null)
                    return;
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                DataSource dataSource = mDataSource;
                // We must wait while the SD card is mounted or the MediaScanner
                // is running.
                if (dataSource != null) {
                    loadMediaSets();
                }
                mWaitingForMediaScanner = false;
                while (ImageManager.isMediaScannerScanning(mContext.getContentResolver())) {
                    // MediaScanner is still running, wait
                    if (Thread.interrupted())
                        return;
                    mWaitingForMediaScanner = true;
                    try {
                        if (mContext == null)
                            return;
                        showToast(mContext.getResources().getString(Res.string.initializing), Toast.LENGTH_LONG);
                        if (dataSource != null) {
                            loadMediaSets();
                        }
                        Thread.sleep(10000);
                    } catch (InterruptedException e) {
                        return;
                    }
                }
                if (mWaitingForMediaScanner) {
                    showToast(mContext.getResources().getString(Res.string.loading_new), Toast.LENGTH_LONG);
                    mWaitingForMediaScanner = false;
                    loadMediaSets();
                }
                mLoading = false;
            }
        });
        mAlbumSourceThread.setName("MediaSets");
        mAlbumSourceThread.start();
    }

首先调用了onResume方法,设置观察者看是否数据源有变化

  final HashMap<String, ContentObserver> observers = mContentObservers;

然后启动线程等待数据源是否扫描好。

设置了两个线程一个是MediaSets,一个是MediaFeed,只启动了MediaSets这个线程,负责等待数据源是否扫描好。

另外一个线程做什么了,现在什么都没有做,只有当别人调用了一个接口才会启动

    public MediaSet addMediaSet(final long setId, DataSource dataSource) {
        MediaSet mediaSet = new MediaSet(dataSource);
        mediaSet.mId = setId;
        mMediaSets.add(mediaSet);
        if (mDataSourceThread != null && !mDataSourceThread.isAlive()) {
            mDataSourceThread.start();
        }
        mMediaFeedNeedsToRun = true;
        return mediaSet;
    }

我们后面再看谁在调用这个接口。

在很多数据源的刷新时都会调用这个接口,就会启动这个线程,我们可以看LocalDataSource的刷新的函数

   public void refresh(final MediaFeed feed, final String[] databaseUris) {
        // We check to see what has changed.
        long[] ids = CacheService.computeDirtySets(mContext);
        int numDirtySets = ids.length;
        for (int i = 0; i < numDirtySets; ++i) {
            long setId = ids[i];
            if (feed.getMediaSet(setId) != null) {
                MediaSet newSet = feed.replaceMediaSet(setId, this);
                newSet.generateTitle(true);
            } else {
                MediaSet mediaSet = feed.addMediaSet(setId, this);
                if (setId == CAMERA_BUCKET_ID) {
                    mediaSet.mName = CAMERA_STRING;
                } else if (setId == DOWNLOAD_BUCKET_ID) {
                    mediaSet.mName = DOWNLOAD_STRING;
                }
                mediaSet.generateTitle(true);
            }
        }
    }

先看已经运行的线程中的run 里面做了什么

    private void loadMediaSets() {
        if (mDataSource == null)
            return;
        final ArrayList<MediaSet> sets = mMediaSets;
        synchronized (sets) {
            final int numSets = sets.size();
            for (int i = 0; i < numSets; ++i) {
                final MediaSet set = sets.get(i);
                set.mFlagForDelete = true;
            }
            mDataSource.refresh(MediaFeed.this, mDataSource.getDatabaseUris());
            mDataSource.loadMediaSets(MediaFeed.this);
            final ArrayList<MediaSet> setsToRemove = new ArrayList<MediaSet>();
            for (int i = 0; i < numSets; ++i) {
                final MediaSet set = sets.get(i);
                if (set.mFlagForDelete) {
                    setsToRemove.add(set);
                }
            }
            int numSetsToRemove = setsToRemove.size();
            for (int i = 0; i < numSetsToRemove; ++i) {
                sets.remove(setsToRemove.get(i));
            }
            setsToRemove.clear();
        }
        mMediaFeedNeedsToRun = true;
        updateListener(false);
    }

里面会运行mDataSource.loadMediaSets()这个函数,但mDataSource实例对应的是一个接口,我们找它初始化的位置

    public MediaFeed(Context context, DataSource dataSource, Listener listener) {
        mContext = context;
        mListener = listener;
        mDataSource = dataSource;
        mSingleWrapper.setNumExpectedItems(1);
        mLoading = true;
    }

MediaFeed实例化的时候第二个参数是什么类呢?就是我们传进去的数据源类型就会调用到什么类型的refresh和  LoadMediaSets函数

我们就以LocalDataSource为例来研究吧,而它的refresh函数刚才我们就看了,会触发启动线程MediaFeed,这个线程会做什么呢?

很多很多事情,先看下LoadMediaSets:

  public void loadMediaSets(final MediaFeed feed) {
        MediaSet set = null; // Dummy set.
        boolean loadOtherSets = true;
        if (mSingleUri) {
            String name = Utils.getBucketNameFromUri(mContext.getContentResolver(), Uri.parse(mUri));
            long id = Utils.getBucketIdFromUri(mContext.getContentResolver(), Uri.parse(mUri));
            set = feed.addMediaSet(id, this);
            set.mName = name;
            set.mId = id;
            set.setNumExpectedItems(2);
            set.generateTitle(true);
            set.mPicasaAlbumId = Shared.INVALID;
            if (this.getThumbnailCache() != sThumbnailCache) {
                loadOtherSets = false;
            }
        } else if (mBucketId == null) {
            // All the buckets.
            if (mFlattenAllItems) {
                set = feed.addMediaSet(0, this); // Create dummy set.
                set.mName = Utils.getBucketNameFromUri(mContext.getContentResolver(), Uri.parse(mUri));
                set.mId = getBucketId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI.toString() + "/" + set.mName);
                set.setNumExpectedItems(1);
                set.generateTitle(true);
                set.mPicasaAlbumId = Shared.INVALID;
            } else {
                CacheService.loadMediaSets(mContext, feed, this, mIncludeImages, mIncludeVideos, true);
            }
        } else {
            CacheService.loadMediaSet(mContext, feed, this, Long.parseLong(mBucketId));
            ArrayList<MediaSet> sets = feed.getMediaSets();
            if (sets.size() > 0)
                set = sets.get(0);
        }
        // We also load the other MediaSets
        if (!mAllItems && set != null && loadOtherSets) {
            final long setId = set.mId;
            if (!CacheService.isPresentInCache(setId)) {
                CacheService.markDirty();
            }
            CacheService.loadMediaSets(mContext, feed, this, mIncludeImages, mIncludeVideos, false);

            // not re-ordering media sets in the case of displaying a single image
            if (!mSingleUri) {
                feed.moveSetToFront(set);
            }
        }
    }

那么我们现在知道了数据全部来源于数据源。

最后我们看刚才启动的线程做了什么,因为是用this做的构造函数的参数,而且这个类也是继承Runnable,所以线程起来后一定会跑run成员函数

    public void run() {
        DataSource dataSource = mDataSource;
        int sleepMs = 10;
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        if (dataSource != null) {
            while (!Thread.interrupted() && !mIsShutdown) {
                String[] databaseUris = null;
                boolean performRefresh = false;
                synchronized (mRequestedRefresh) {
                    if (mRequestedRefresh.size() > 0) {
                        // We prune this first.
                        int numRequests = mRequestedRefresh.size();
                        for (int i = 0; i < numRequests; ++i) {
                            databaseUris = ArrayUtils.addAll(databaseUris, mRequestedRefresh.get(i));
                        }
                        mRequestedRefresh.clear();
                        performRefresh = true;
                        // We need to eliminate duplicate uris in this array
                        final HashMap<String, String> uris = new HashMap<String, String>();
                        if (databaseUris != null) {
                            int numUris = databaseUris.length;
                            for (int i = 0; i < numUris; ++i) {
                                final String uri = databaseUris[i];
                                if (uri != null)
                                    uris.put(uri, uri);
                            }
                        }
                        databaseUris = new String[0];
                        databaseUris = (String[]) uris.keySet().toArray(databaseUris);
                    }
                }
                boolean settingFeedAboutToChange = false;
                if (performRefresh) {
                    if (dataSource != null) {
                        if (mListener != null) {
                            settingFeedAboutToChange = true;
                            mListener.onFeedAboutToChange(this);
                        }
                        dataSource.refresh(this, databaseUris);
                        mMediaFeedNeedsToRun = true;
                    }
                }
                if (mListenerNeedsUpdate && !mMediaFeedNeedsToRun) {
                    mListenerNeedsUpdate = false;
                    if (mListener != null)
                        synchronized (mMediaSets) {
                            mListener.onFeedChanged(this, mListenerNeedsLayout);
                        }
                    try {
                        Thread.sleep(sleepMs);
                    } catch (InterruptedException e) {
                        return;
                    }
                } else {
                    try {
                        Thread.sleep(sleepMs);
                    } catch (InterruptedException e) {
                        return;
                    }
                }
                sleepMs = 300;
                if (!mMediaFeedNeedsToRun)
                    continue;
                App app = App.get(mContext);
                if (app == null || app.isPaused())
                    continue;
                if (settingFeedAboutToChange) {
                    updateListener(true);
                }
                mMediaFeedNeedsToRun = false;
                ArrayList<MediaSet> mediaSets = mMediaSets;
                synchronized (mediaSets) {
                    int expandedSetIndex = mExpandedMediaSetIndex;
                    if (expandedSetIndex >= mMediaSets.size()) {
                        expandedSetIndex = Shared.INVALID;
                    }
                    if (expandedSetIndex == Shared.INVALID) {
                        // We purge the sets outside this visibleRange.
                        int numSets = mediaSets.size();
                        IndexRange visibleRange = mVisibleRange;
                        IndexRange bufferedRange = mBufferedRange;
                        boolean scanMediaSets = true;
                        for (int i = 0; i < numSets; ++i) {
                            if (i >= visibleRange.begin && i <= visibleRange.end && scanMediaSets) {
                                MediaSet set = mediaSets.get(i);
                                int numItemsLoaded = set.mNumItemsLoaded;
                                if (numItemsLoaded < set.getNumExpectedItems() && numItemsLoaded < 8) {
                                    synchronized (set) {
                                        dataSource.loadItemsForSet(this, set, numItemsLoaded, 8);
                                        set.checkForDeletedItems();
                                    }
                                    if (set.getNumExpectedItems() == 0) {
                                        mediaSets.remove(set);
                                        break;
                                    }
                                    if (mListener != null) {
                                        mListenerNeedsUpdate = false;
                                        mListener.onFeedChanged(this, mListenerNeedsLayout);
                                        mListenerNeedsLayout = false;
                                    }
                                    sleepMs = 100;
                                    scanMediaSets = false;
                                }
                                if (!set.setContainsValidItems()) {
                                    mediaSets.remove(set);
                                    if (mListener != null) {
                                        mListenerNeedsUpdate = false;
                                        mListener.onFeedChanged(this, mListenerNeedsLayout);
                                        mListenerNeedsLayout = false;
                                    }
                                    break;
                                }
                            }
                        }
                        numSets = mediaSets.size();
                        for (int i = 0; i < numSets; ++i) {
                            MediaSet set = mediaSets.get(i);
                            if (i >= bufferedRange.begin && i <= bufferedRange.end) {
                                if (scanMediaSets) {
                                    int numItemsLoaded = set.mNumItemsLoaded;
                                    if (numItemsLoaded < set.getNumExpectedItems() && numItemsLoaded < 8) {
                                        synchronized (set) {
                                            dataSource.loadItemsForSet(this, set, numItemsLoaded, 8);
                                            set.checkForDeletedItems();
                                        }
                                        if (set.getNumExpectedItems() == 0) {
                                            mediaSets.remove(set);
                                            break;
                                        }
                                        if (mListener != null) {
                                            mListenerNeedsUpdate = false;
                                            mListener.onFeedChanged(this, mListenerNeedsLayout);
                                            mListenerNeedsLayout = false;
                                        }
                                        sleepMs = 100;
                                        scanMediaSets = false;
                                    }
                                }
                            } else if (!mListenerNeedsUpdate && (i < bufferedRange.begin || i > bufferedRange.end)) {
                                // Purge this set to its initial status.
                                MediaClustering clustering = mClusterSets.get(set);
                                if (clustering != null) {
                                    clustering.clear();
                                    mClusterSets.remove(set);
                                }
                                if (set.getNumItems() != 0)
                                    set.clear();
                            }
                        }
                    }
                    if (expandedSetIndex != Shared.INVALID) {
                        int numSets = mMediaSets.size();
                        for (int i = 0; i < numSets; ++i) {
                            // Purge other sets.
                            if (i != expandedSetIndex) {
                                MediaSet set = mediaSets.get(i);
                                MediaClustering clustering = mClusterSets.get(set);
                                if (clustering != null) {
                                    clustering.clear();
                                    mClusterSets.remove(set);
                                }
                                if (set.mNumItemsLoaded != 0)
                                    set.clear();
                            }
                        }
                        // Make sure all the items are loaded for the album.
                        int numItemsLoaded = mediaSets.get(expandedSetIndex).mNumItemsLoaded;
                        int requestedItems = mVisibleRange.end;
                        // requestedItems count changes in clustering mode.
                        if (mInClusteringMode && mClusterSets != null) {
                            requestedItems = 0;
                            MediaClustering clustering = mClusterSets.get(mediaSets.get(expandedSetIndex));
                            if (clustering != null) {
                                ArrayList<Cluster> clusters = clustering.getClustersForDisplay();
                                int numClusters = clusters.size();
                                for (int i = 0; i < numClusters; i++) {
                                    requestedItems += clusters.get(i).getNumExpectedItems();
                                }
                            }
                        }
                        MediaSet set = mediaSets.get(expandedSetIndex);
                        if (numItemsLoaded < set.getNumExpectedItems()) {
                            // We perform calculations for a window that gets
                            // anchored to a multiple of NUM_ITEMS_LOOKAHEAD.
                            // The start of the window is 0, x, 2x, 3x ... etc
                            // where x = NUM_ITEMS_LOOKAHEAD.
                            synchronized (set) {
                                dataSource.loadItemsForSet(this, set, numItemsLoaded, (requestedItems / NUM_ITEMS_LOOKAHEAD)
                                        * NUM_ITEMS_LOOKAHEAD + NUM_ITEMS_LOOKAHEAD);
                                set.checkForDeletedItems();
                            }
                            if (set.getNumExpectedItems() == 0) {
                                mediaSets.remove(set);
                                mListenerNeedsUpdate = false;
                                mListener.onFeedChanged(this, mListenerNeedsLayout);
                                mListenerNeedsLayout = false;
                            }
                            if (numItemsLoaded != set.mNumItemsLoaded && mListener != null) {
                                mListenerNeedsUpdate = false;
                                mListener.onFeedChanged(this, mListenerNeedsLayout);
                                mListenerNeedsLayout = false;
                            }
                        }
                    }
                    MediaFilter filter = mMediaFilter;
                    if (filter != null && mMediaFilteredSet == null) {
                        if (expandedSetIndex != Shared.INVALID) {
                            MediaSet set = mediaSets.get(expandedSetIndex);
                            ArrayList<MediaItem> items = set.getItems();
                            int numItems = set.getNumItems();
                            MediaSet filteredSet = new MediaSet();
                            filteredSet.setNumExpectedItems(numItems);
                            mMediaFilteredSet = filteredSet;
                            for (int i = 0; i < numItems; ++i) {
                                MediaItem item = items.get(i);
                                if (filter.pass(item)) {
                                    filteredSet.addItem(item);
                                }
                            }
                            filteredSet.updateNumExpectedItems();
                            filteredSet.generateTitle(true);
                        }
                        updateListener(true);
                    }
                }
            }
        }
    }

那么更多的问题来了,数据从数据源来的,那数据源的数据又是从哪里来的?

其他界面怎么切换的?其他的界面时如何刷新的?

参考文档

http://blog.csdn.net/philofly/article/details/8162259

抱歉!评论已关闭.