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

Gallery3d 学习笔记(8)

2013年04月10日 ⁄ 综合 ⁄ 共 7833字 ⁄ 字号 评论关闭

前面7节我们讲了很多东西,我们再来回顾一下:

  在程序还没有开始的时候,系统的一个程序com.android.providers.media就把内置存储器和外置存储器默默的扫描了一遍,建立了数据库和缓存缩略图,

我们还弄清楚了Gallery3D是通过RenderView通过OpenGL 刷的界面,而且界面分为很多层,而且将层分了5个层列表:刷新列表 不透明列表 半透明列表 触摸列表 系统列表。

按键都是转到Root层处理的,而触摸则是透过RenderView的持续刷新的函数里面分发给各个层的,由各个层自己处理,根层的触摸和按键最后都转到了叫做GridInputProcess的类里面处理了。

然后我们发现触摸长按没有层处理,而根层触摸处理都转到GridInputProcess处理,发现这里有OnLongPress的处理,这里的处理是透过Hud层来设置的,完成设置模式的切换。

触摸和按键的消息基本弄明白了,后面又对界面的刷新简单看了一下,发现在GridLayer不透明层的刷新中,发现了对缩略图界面的绘制

然后在主Activity里面发现了对SD卡的检测,最后是初始化数据源,设置对应的数据源,发现所有的数据都是来自数据源,并且其中先后开启了两个线程MediaSets 和MediaFeed,MediaSets只是等待数据源初始化结束。而MediaFeed这个线程我们还没有仔细的分析,我们现在看下。

在run里面的代码

            while (!Thread.interrupted() && !mIsShutdown) {

基本上是个死循环,死循环在做什么呢

在检测是否又新的图片或者视频出现,如果有就会更新,怎么更新呢

		// Start the thumbnailer.
		CacheService.startCache(this, true);

至于他们为什么在onStop函数里面写,我理解是在Activity停止后,用户可能去拍摄新图片,为了对新的图片有Cache,在后台开启服务进行Cache

我们来看下CacheService这个类

public final class CacheService extends IntentService {}

说明什么?说明CacheService是一个IntentService,IntentService是什么,就是一个开启了一个线程的Service,(不清楚的自己看下源码)每次处理线程中的消息时都会调用一个函数叫做:OnHandleIntent(),我们看下里面的内容:

    @Override
    protected void onHandleIntent(final Intent intent) {
        if (DEBUG)
            Log.i(TAG, "Starting CacheService");
        if (Environment.getExternalStorageState() == Environment.MEDIA_BAD_REMOVAL) {
            sAlbumCache.deleteAll();
            putLocaleForAlbumCache(Locale.getDefault());
        }
        Locale locale = getLocaleForAlbumCache();
        if (locale != null && locale.equals(Locale.getDefault())) {

        } else {
            // The locale has changed, we need to regenerate the strings.
            markDirty();
        }
        if (intent.getBooleanExtra("checkthumbnails", false)) {
            startNewThumbnailThread(this);
        } else {
            final Thread existingThread = THUMBNAIL_THREAD.getAndSet(null);
            if (existingThread != null) {
                existingThread.interrupt();
            }
        }
    }

.

里面有个Intent的参数获取,checkthumbnails,获取后开启的线程,先找下谁发出了带有这个参数的Intent,

    public static final void startCache(final Context context, final boolean checkthumbnails) {
        final Locale locale = getLocaleForAlbumCache();
        final Locale defaultLocale = Locale.getDefault();
        if (locale == null || !locale.equals(defaultLocale)) {
            sAlbumCache.deleteAll();
            putLocaleForAlbumCache(defaultLocale);
        }
        final Intent intent = new Intent(ACTION_CACHE, null, context, CacheService.class);
        intent.putExtra("checkthumbnails", checkthumbnails);
        context.startService(intent);
    }

原来就是刚才我们看的的startCache()这个函数,我们再来看startNewThumbnailThread

    public static final void startNewThumbnailThread(final Context context) {
        restartThread(THUMBNAIL_THREAD, "ThumbnailRefresh", new Runnable() {
            public void run() {
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                try {
                    // It is an optimization to prevent the thumbnailer from
                    // running while the application loads
                    Thread.sleep(THUMBNAILER_WAIT_IN_MS);
                } catch (InterruptedException e) {
                    return;
                }
                CacheService.buildThumbnails(context);
            }
        });
    }

这里面又开启了一个线程,主要工作就是buildThumbnails,建立缩略图的Cache

    private static final void buildThumbnails(final Context context) {
        if (DEBUG)
            Log.i(TAG, "Preparing DiskCache for all thumbnails.");
        ImageList list = getImageList(context);
        final int size = (list.ids == null) ? 0 : list.ids.length;
        final long[] ids = list.ids;
        final long[] timestamp = list.timestamp;
        final long[] thumbnailIds = list.thumbids;
        final DiskCache thumbnailCache = LocalDataSource.sThumbnailCache;
        for (int i = 0; i < size; ++i) {
            if (Thread.interrupted()) {
                return;
            }
            final long id = ids[i];
            final long timeModifiedInSec = timestamp[i];
            final long thumbnailId = thumbnailIds[i];
            if (!isInThumbnailerSkipList(thumbnailId)) {
                if (!thumbnailCache.isDataAvailable(thumbnailId, timeModifiedInSec * 1000)) {
                    byte[] retVal = buildThumbnailForId(context, thumbnailCache, thumbnailId, id, false, DEFAULT_THUMBNAIL_WIDTH,
                            DEFAULT_THUMBNAIL_HEIGHT, timeModifiedInSec * 1000);
                    if (retVal == null || retVal.length == 0) {
                        // There was an error in building the thumbnail.
                        // We record this thumbnail id
                        addToThumbnailerSkipList(thumbnailId);
                    }
                }
            }
        }
        thumbnailCache.flush();
        if (DEBUG)
            Log.i(TAG, "DiskCache ready for all thumbnails.");
    }

主要的工作就是buildThumbnailForId做的,里面又调用了一个函数writeBitmapToCache,我们看下内容

    public static final byte[] writeBitmapToCache(final DiskCache thumbnailCache, final long thumbId, final long origId,
            final Bitmap bitmap, final int thumbnailWidth, final int thumbnailHeight, final long timestamp) {
        final int width = bitmap.getWidth();
        final int height = bitmap.getHeight();
        // Detect faces to find the focal point, otherwise fall back to the
        // image center.
        int focusX = width / 2;
        int focusY = height / 2;
        // We have commented out face detection since it slows down the
        // generation of the thumbnail and screennail.

        // final FaceDetector faceDetector = new FaceDetector(width, height, 1);
        // final FaceDetector.Face[] faces = new FaceDetector.Face[1];
        // final int numFaces = faceDetector.findFaces(bitmap, faces);
        // if (numFaces > 0 && faces[0].confidence() >=
        // FaceDetector.Face.CONFIDENCE_THRESHOLD) {
        // final PointF midPoint = new PointF();
        // faces[0].getMidPoint(midPoint);
        // focusX = (int) midPoint.x;
        // focusY = (int) midPoint.y;
        // }

        // Crop to thumbnail aspect ratio biased towards the focus point.
        int cropX;
        int cropY;
        int cropWidth;
        int cropHeight;
        float scaleFactor;
        if (thumbnailWidth * height < thumbnailHeight * width) {
            // Vertically constrained.
            cropWidth = thumbnailWidth * height / thumbnailHeight;
            cropX = Math.max(0, Math.min(focusX - cropWidth / 2, width - cropWidth));
            cropY = 0;
            cropHeight = height;
            scaleFactor = (float) thumbnailHeight / height;
        } else {
            // Horizontally constrained.
            cropHeight = thumbnailHeight * width / thumbnailWidth;
            cropY = Math.max(0, Math.min(focusY - cropHeight / 2, height - cropHeight));
            cropX = 0;
            cropWidth = width;
            scaleFactor = (float) thumbnailWidth / width;
        }
        final Bitmap finalBitmap = Bitmap.createBitmap(thumbnailWidth, thumbnailHeight, Bitmap.Config.RGB_565);
        final Canvas canvas = new Canvas(finalBitmap);
        final Paint paint = new Paint();
        paint.setDither(true);
        paint.setFilterBitmap(true);
        canvas.drawColor(0);
        canvas.drawBitmap(bitmap, new Rect(cropX, cropY, cropX + cropWidth, cropY + cropHeight), new Rect(0, 0, thumbnailWidth,
                thumbnailHeight), paint);
        bitmap.recycle();

        // Store (long thumbnailId, short focusX, short focusY, JPEG data).
        final ByteArrayOutputStream cacheOutput = new ByteArrayOutputStream(16384);
        final DataOutputStream dataOutput = new DataOutputStream(cacheOutput);
        byte[] retVal = null;
        try {
            dataOutput.writeLong(origId);
            dataOutput.writeShort((int) ((focusX - cropX) * scaleFactor));
            dataOutput.writeShort((int) ((focusY - cropY) * scaleFactor));
            dataOutput.flush();
            finalBitmap.compress(Bitmap.CompressFormat.JPEG, 80, cacheOutput);
            retVal = cacheOutput.toByteArray();
            synchronized (thumbnailCache) {
                thumbnailCache.put(thumbId, retVal, timestamp);
            }
            cacheOutput.close();
            finalBitmap.recycle();
        } catch (Exception e) {
            ;
        }
        return retVal;
    }

将图片缩略图写到了Cache中。好,马上我们就要弄清楚数据源的数据从什么地方来了。

我们看DataSource类中有个关键的函数loadMedaSets

    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);
            }
        }
    }

数据源的数据有两部分,其中一部分是开机时mediaprovider提供的T卡和内置存储器的数据,另外在开启时,会启动一个MediaFeed的线程检测是否有改变,新发现的数据将由CacheService将其处理变成Cache,最后,通过CacheService.loadMediaSets来加载数据。

到现在我们只清楚用户消息的处理和数据的流向的过程,下次我们开始将各个界面的刷新和切换,再会。

抱歉!评论已关闭.