Android Gallery2流程分析

Android 系统的多媒体数据provider

packages/providers/MediaProvider/
含有类MediaProvider.java、MediaScannerService.java、MediaScannerReceiver.java,
编译后生成MediaProvider.apk。会在开机时扫描本机和sdcard上的媒体文件(图片、视频、音频),并在/data/data/com.android.providers.media/databases 目录下生成internal.db(/system/meida)和external-?.db(/sdcard)两个数据库文件。此后所有的多媒体信息都从这两个数据库中获取。

rameworks/base/core/java/android/provider/MediaStore.java
提供的多媒体数据库,所有多媒体数据信息都可以从这里提取。数据库的操作通过利用ContentResolver调用相关的接口实现。

frameworks/base/media/java/android/media/
提供了android上 多媒体应用层的操作接口。主要说明:
• MediaPlayer.java:提供了视频、音频、数据流的播放控制等操作的接口。
• MediaScanner*.java:提供了媒体扫描接口的支持,媒体扫描后加入数据库中,涉及MediaScannerConnection.java和MediaScannerConnectionClient.java。




gallery2的功能交互:



android 媒体文件类型:

 

 

以下转自:

http://blog.csdn.net/shareviews/article/details/7953912

Android4.0图库Gallery2代码分析(二) 数据管理和数据加载

一 图库数据管理

Gallery2的数据管理 DataManager(职责:管理数据源)- MediaSource(职责:管理数据集) - MediaSet(职责:管理数据项)。DataManager中初始化所有的数据源(LocalSource, PicasaSource, MtpSource, ComboSource, ClusterSource, FilterSource, UriSource, SnailSource),将数据源放到一个Hash表中,提供存取操作。MediaSource负责管理数据集,以LoacalSource为例,从他的createMediaObject函数可以看出,根据路径他可以创建出LocalMediaSet, LocalMedia, LocalImage, LocalVideo等。MediaSet负责管理数据项MediaItem. 图库数据管理简要图如图-1


图-1:图库数据管理简化图

二 数据项(MediaItem)的加载过程。

下面介绍一下,Albumpage加载缩略图列表的过程为例子。AlbumSetPage加载相册缩略图和PhotoPage加载大图的过程,请读者自行分析。

在相册页面(AlbumPage)和缩略图数据加载相关的有两个成员变量AlbumDataLoader和AlbumSlotRender。AlbumDataLoader负责维护要加载数据提供一个管理,将要加载的数据项MediaItem放在链表中进行存取操作,动态的增删改查操作。在AlbumPage类的Onresume函数中调用了AlbumDataLoader的resume,AlbumDataLoader的resume创建了一个线程,随时处理数据的变化(MediaItem的增删改查)。AlbumDataLoader的作用过程见图2。

图-2:AlbumDataLoader的作用过程

       AlbumDataLoader负责数据模型的维护,AlbumSlotRender负责数据的缩略图的加载工作,为了提高性能,数据加载使用了【线程池】。AlbumSlotRender从AlbumDataLoader获取要加载的数据MediaItem, 根据每一个MediaItem的状态确定是是否Bitmap缩略图的是需要加载、回收、还是等待等。对于需要加载的缩略图,提交到线程池中。AlbumSlotRender的作用过程见图3。数据加载过程中,【同步问题】其实也是需要重点分析的,由于时间有限,这里就缩略了,见谅!


图-3:AlbumSlotRender的作用过程





以下转自: http://www.2cto.com/kf/201312/267018.html

 

gallery2 实例分析:

GalleryAppImpl 在androidManifest.xml中注册Application标签,应用创建时就会被初始化,维护应用内部全局数据。
主要看几个函数:initializeAsyncTask(),GalleryUtils.initialize(this),


private void initializeAsyncTask() {
    // AsyncTask class needs to be loaded in UI thread.
    // So we load it here to comply the rule.
    try {
        Class.forName(AsyncTask.class.getName());
    } catch (ClassNotFoundException e) {
    }
}
Class.forName(AsyncTask.class.getName())会返回AsyncTask这个类,并要求JVM查找并加载AsyncTask 类,也就是说JVM会执行AsyncTask类的静态代码段。


public static void initialize(Context context) {
    DisplayMetrics metrics = new DisplayMetrics();
    WindowManager wm = (WindowManager)
            context.getSystemService(Context.WINDOW_SERVICE);
    wm.getDefaultDisplay().getMetrics(metrics);
    sPixelDensity = metrics.density;
    Resources r = context.getResources();
    TiledScreenNail.setPlaceholderColor(r.getColor(
            R.color.bitmap_screennail_placeholder));
    initializeThumbnailSizes(metrics, r);
}
GalleryUtil是Gallery的工具类,获得了屏幕参数,WindowManager,Resource等。
onCreate中由于mStitchingProgressManager赋值为null,所以暂时未调用getDataManager()。这个函数下面会介绍到,主要用来管理Gallery的数据路径层次。


Gallery 从launcher进入Gallery,图库应用会进入应用主入口Gallery.java。onCreate()中加载布局main.xml。


@Override
protected void onCreate(Bundle savedInstanceState) {
    …...
    setContentView(R.layout.main);
 
    if (savedInstanceState != null) {
       getStateManager().restoreFromState(savedInstanceState);
    } else {
        initializeByIntent();
    }
}
savedInstanceState是在Activity在onPause时保存状态用的,这里暂时为null。


    private void initializeByIntent() {
        Intent intent = getIntent();
        String action = intent.getAction();
 
        if (Intent.ACTION_GET_CONTENT.equalsIgnoreCase(action)){
            startGetContent(intent);
        } else if(Intent.ACTION_PICK.equalsIgnoreCase(action)) {
            // We do NOT really support the PICKintent. Handle it as
            // the GET_CONTENT. However, we needto translate the type
            // in the intent here.
            Log.w(TAG, "action PICK is notsupported");
            String type =Utils.ensureNotNull(intent.getType());
            if(type.startsWith("vnd.android.cursor.dir/")) {
                if(type.endsWith("/image")) intent.setType("image/*");
                if(type.endsWith("/video")) intent.setType("video/*");
            }
            startGetContent(intent);
        } else if(Intent.ACTION_VIEW.equalsIgnoreCase(action)
                ||ACTION_REVIEW.equalsIgnoreCase(action)){
            startViewAction(intent);
        } else {
            startDefaultPage();
        }
}
从这个函数看,如果从相册应用图标进入,会走startDefaultPage流程,如果外部打开图片,会走startGetContent流程。先看默认的startDefaultPage流程吧。


    public void startDefaultPage() {
        PicasaSource.showSignInReminder(this);
        Bundle data = new Bundle();
        data.putString(AlbumSetPage.KEY_MEDIA_PATH,
               getDataManager().getTopSetPath(DataManager.INCLUDE_ALL));
        getStateManager().startState(AlbumSetPage.class,data);
        mVersionCheckDialog =PicasaSource.getVersionCheckDialog(this);
        if (mVersionCheckDialog != null) {
           mVersionCheckDialog.setOnCancelListener(this);
        }
}
data里面存了相册顶层路径:"/combo/{/mtp,/local/all,/picasa/all}"

 

 StateManager和AbstractGalleryActivity

 

StateManager
StateManager中有个Stack mStack,类似于ActivityManager中的ActivityStack。用于控制相册界面的窗口堆栈管理,成员为StateEntry类。再看startState这个函数:


    public void startState(Class klass,
            Bundle data) {
        Log.v(TAG, "startState " + klass);
        ActivityState state = null;
        try {
            // 用窗口类创建一个ActivityState实例
            state = klass.newInstance();
        } catch (Exception e) {
            throw new AssertionError(e);
        }
        // 堆栈非空
        if (!mStack.isEmpty()) {
             // 获取栈顶ActivityState
            ActivityState top = getTopState();
           top.transitionOnNextPause(top.getClass(), klass,
                   StateTransitionAnimation.Transition.Incoming);
            // 调用栈顶ActivityState的onPause
            if (mIsResumed) top.onPause();
        }
        // 初始化当前的ActivityState,这个和startActivity非常相似
        state.initialize(mActivity, data);
        // 初始化后入栈
        mStack.push(new StateEntry(data, state));


        Log.d(TAG, "startState:startState->onCreate");
        // 调用新ActivityState实例的onCreate,这个和startActivity的流程又好相似
        state.onCreate(data, null);
        Log.d(TAG, "startState:startState->resume");
        // 调用新ActivityState实例的onResume
        if (mIsResumed) state.resume();
}
可以说这个函数和启动应用activity的流程非常相似,只是简化了流程而已。另外,startStateForResult也和startActivityForResult类似。
AbstractGalleryActivity 在介绍AlbumSetPage、AlbumPage、PhotoPage等页面前,必须先介绍AbstractGalleryActivity,因为以上三个页面的父类ActivityState中有成员变量AbstractGalleryActivity mActivity。相册的Activity实际上只有一个,可以说就是这个mActivity,通过StateManager和DataManager来控制显示不同的页面。
继承自Activity,其中有几个重要成员:GLRootView mGLRootView,StateManager mStateManager,DataManager,GalleryActionBar mActionBar,OrientationManager mOrientationManager。
其中GalleryActionBar就是相册各个界面顶部的ActionBar控件。按照Activity的生命周期,在onCreate中创建OrientationManager对象赋予mOrientationManager,调用toggleStatusBarByOrientation在屏幕横竖屏时对window的全屏标记做增加和清除。
    // Shows status bar in portrait view, hide in landscape view
    private void toggleStatusBarByOrientation() {
        if (mDisableToggleStatusBar) return;


        Window win = getWindow();
        if (getResources().getConfiguration().orientation== Configuration.ORIENTATION_PORTRAIT) {
            win.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
        } else {
           win.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
        }
    }
在setContentView()中加载GLRootView。在onStart()中注册AlertDialog的onClick处理。在onResume(),onPause()中都会分别对StateManager,DataManager做对应的onResume, onPause操作。例如:
    @Override
    protected void onResume() {
        super.onResume();
        mGLRootView.lockRenderThread();
        try {
            getStateManager().resume();
            getDataManager().resume();
        } finally {
            mGLRootView.unlockRenderThread();
        }
        mGLRootView.onResume();
        mOrientationManager.resume();
    }
其中 mGLRootView会调用 lockRenderThread(),在执行完xxx.resume()后会再调用unlockRenderThread()。下面是GLRootView的lockXXX()和unlockXXX()函数。
    @Override
    public void lockRenderThread() {
        mRenderLock.lock();
    }


    @Override
    public void unlockRenderThread() {
        mRenderLock.unlock();
    }
其中 mRenderLock是ReentrantLock对象。那么ReentrantLock(可重入锁)的作用是什么?
因为Gallery的每个页面AlbumSet、AlbumPage、PhotoPage、PhotoView都是同一个Activity,界面的控制是通过StateManager以及DataManager,切换进入每个状态页面,都会获取不同的GLRootView,所以必须保证GLRootView是锁定情况下,才能对StateManager以及DataManager做处

 

以下转自:

http://blog.csdn.net/buleideli/article/details/8491744

一、

1.1.1 进入gallery
进行如下关键操作。
        先进入gallery’中处理。Gallery中initializeByIntent中最终会调用启动albumset,代码如下。
Bundledata = new Bundle();
       data.putString(AlbumSetPage.KEY_MEDIA_PATH,
              getDataManager().getTopSetPath(DataManager.INCLUDE_ALL));
       getStateManager().startState(AlbumSetPage.class,data);
        初始化view,创建albumsetview并注册其侦听者,该侦听者负责侦听用户点击触摸操作。
        创建GLView类的对象,在该对象重载实现onlayout()以及render函数。其中onlayout()在每次进入这个主界面时调用,render()在每次该界面视图改变时调用,例如触摸等操作使得界面视图改变调用render来重绘。
        创建界面上的其他元素。
        创建过渡动画。
 
1.1.2 退出gallery
        调用onbackpressed退出。
        最终执行退出操作是由StateManager的finishState。该函数会先出栈一个activitystate,后再判断是否栈为空,为空才真正终止应用退出。
 
1.1.3 打开相册
在点击进入相册前进行如下操作。
        若进入的相册下一级已经没有相册了,那就启动albumpage。
        若进入的相册下一级仍有相册了,那就启动albumsetpage。
进入albumpage后,主要步骤跟进入gallery时差不多。
1.1.4 打开相片或视频
1)打开图片见3.1.5。
2)打开视频见下面分析。主要操作见下面。
        ControllerOverlay用于播放控件的实现,诸如时间条,屏幕中间显示的播放状态按钮等等。
        加载各种view,控件。
        Movieplayer中的Bookmarker是个书签功能,看代码就是负责保存本次播放的位置,等挂起后重新进入gallery可以从上一次播放位置继续播放。
1.1.5 播放幻灯片
主要操作如下。
        初始化数据以及view。
        指定播放幻灯片按什么顺序播放,或是随机,或是顺序。
1.1.6 删除图片或视频
主要操作如下。
        删除操作在三个地方有,gallery主界面、相册内界面、图片浏览界面,他们都是由界面上方的actionbar按钮触发。
        Albumsetpage、albumpage均是创建ActionModeHandler来实现加载这个actionbar。Photopage是通过创建MenuExecutor来加载这个actionbar。另外这个actionbar同时包括actiobar的其他操作,诸如裁剪、编辑等等。
        最终的删除操作是通过获取mediaobject对象,对其进行操作实现的。Mediaobject同时是mediaset、mediaitem的父类,多态性就能保证对相册或是相片进行删除操作。删除操作最终通过conresolver.delete实现。
1.1.7 开机与media相关的数据库流程
流程见下。
        创建mediaprovider。Mediaprovider中oncreate主要做如下处理。
        挂载内部存储器,创建内部存储器的数据库。
        创建侦听者来侦听卸载存储器的事件,包括卸载内部存储器以及外部存储器。
        挂载外部存储器,创建外部存储器的数据库。
        创建线程,用于处理更新缩略图的请求。缩略图请求分为两种,一种是图片缩略图,处理原则是FIFO,先请求先处理;另一种是音乐专辑缩略图,处理原则是LIFO,后请求先处理。


        与2.3一样,先通过mediascanerreceiver接收到Intent.ACTION_MEDIA_MOUNTED这个加载intent,调用scan对全盘媒体文件进行扫描。
        接着调用到mediascannerservice。ServiceHandler中对指定路径进行扫描。调用到mediascannerservice.scan()。扫描前发送Intent.ACTION_MEDIA_SCANNER_STARTED广播,接着创建mediascanner进行扫描,扫描后发送Intent.ACTION_MEDIA_SCANNER_FINISHED。接收这两个广播的地方找不到。应该是用于提示外部的应用扫描的开始以及结束。
        在扫描前会插入数据库一条记录,之后就删除该条记录。为什么要在之前插入一条记录,之后就删除这条记录,并且只是获取了uri,并没对这个uri进行操作,看不懂。代码如下。
 
UriscanUri = getContentResolver().insert(MediaStore.getMediaScannerUri(),values);
//...
getContentResolver().delete(scanUri,null, null);
        调用mediascanner.scanDirectories扫描所有目录。
          mediascanner.scanDirectories中,有如下关键操作。
        Initalize():初始化各个媒体uri。总共有如下uri。
mAudioUri
mVideoUri
mImagesUri
mThumbsUri
mFilesUri
        Prescan():扫描mFilesUri所示的文件。写入filecache中。同时计算mImagesUri的文件数。
        processDirectory():遍历扫描所有路径。具体进入c++代码,看不懂。
        Postscan():删除掉文件系统中不存在但数据库中存在的文件项,此删除为删除数据库中记录项。
1.1.8    Filemanager中删除媒体文件
删除单个文件主要流程见下。
在Filemanager中,主要操作流程如下。
        删除该文件。
        FileMgrMenuUtil.notifyFileSystemChanged中发送Intent.ACTION_MEDIA_SCANNER_SCAN_FILE通知删除文件成功。Mediareceiver接收到就交给mediascannerservice进行scanfile()处理。
        Mediascannerservice.scanfile(),最终调用到Mediascanner.scansinglefile()处理。主要进行如下处理。
        初始化各种扫描需要的东西,如路径等。
        Prescan():更新媒体文件的filecache。Filecache是一个索引表,存放媒体文件的关键数据,以提高访问这些文件的速度。
        Doscanfile():在这里主要是置标志位表示已经在文件系统找到该文件。
        均是调用mediaobject.delete()完成删除操作,其实最终调用的是mediaobject派生出的mediaset、mediaitem。而跟进代码最终是在mediaprovider.delete处理。
        Mediaprovider.delete()主要是删除数据库中该条记录。
 
在Gallery中,主要需要注意流程如下。
        均是调用mediaobject.delete()完成删除操作,其实最终调用的是mediaobject派生出的mediaset、mediaitem。而跟进代码最终是在mediaprovider.delete处理。
        Mediaprovider.delete()主要是删除数据库中该条记录。
        但是不能找到地方处理删除文件操作。
 
 
删除目录。
在filemanager中,流程稍有不同,主要见以下。
        会调用到RecursiveDeleteTask线程进行递归删除目录下文件的处理。
        FileMgrMenuUtil.notifyFileSystemChanged中发送Intent.ACTION_MEDIA_MOUNTED通知删除文件夹成功。Mediascannerservice进行scan()处理。其主要操作跟“开机与media相关的数据库流程”内的Mediascannerservice.scan()大同小异,区别在与扫描范围的不同。
 
在gallery中,操作见上面的删除单个文件操作。
1.1.9    Filemanager中移动媒体文件
移动单个文件。
在filemanager中,主要注意以下。
        交由MoveTask线程处理,线程中通过file.renameto()来实现将文件移动到另一个地方,这个方法只是更改了原来文件路径。
        FileMgrMenuUtil.notifyFileSystemChanged中发送Intent.ACTION_MEDIA_SCANNER_SCAN_FILE通知移动文件成功。接着Mediascannerservice同样进行单个文件扫描处理。
 
移动一整个目录。
在filemanager中,主要注意以下。
        主要通过以下数据结构来存放待处理的文件列表。
private ArrayList<FileInfo>mCheckedFileList = newArrayList<FileInfo>();
        同样还是MoveTask线程处理这些文件。
        FileMgrMenuUtil.notifyFileSystemChanged中发送Intent.ACTION_MEDIA_MOUNTED通知移动文件夹成功。Mediascannerservice进行scan()处理。
1.1.10 Filemanager中重命名媒体文件
重命名单个文件与文件夹操作基本一致。
Filemanager中,主要操作流程如下。
        同样使用file.renameto()进行重命名文件操作。
        同样使用FileMgrMenuUtil.notifyFileSystemChanged发送intent消息通知重命名操作,使得Mediascannerservice再去扫描。这里发送的intent消息,重命名单个文件与重命名文件夹是不一样的,与之前区别一样。
1.1.11 Filemanager中复制媒体文件
复制单个文件。
在filemanager中,主要操作流程如下。
        检查目的地址空间足够与否。
        同样使用FileMgrMenuUtil.notifyFileSystemChanged发送intent消息通知重命名操作,使得Mediascannerservice再去扫描。这里发送的intent消息,重命名单个文件与重命名文件夹是不一样的,与之前区别一样。
 
复制目录。与单个文件操作基本一致。主要见以下。
        递归实现copy。
1.1.12 增加媒体文件
在camera拍摄一张照片后,主要进行如下操作。
        生成这个jpeg格式的图片。
        ContentResolver中插入一条关于这照片的记录到对应数据库中。

 

二、

3.1.1 Gallery

gallery的主程序类。

其类图如下。

相关类说明
1)GalleryActionBar
用于在相册主界面出现的排序下拉框,如下。

3.1.2 AbstractGalleryActivity

实现基本的activity操作的类,作为某些类的基类。
其类图如下。

相关类说明
1)GalleryActivity 
接口类,主要是一些获取AbstractGalleryActivity成员变量的方法。
public interface GalleryActivity extends GalleryContext {
public StateManager getStateManager();
public GLRoot getGLRoot();
public PositionRepository getPositionRepository();
public GalleryApp getGalleryApplication();
public GalleryActionBar getGalleryActionBar();
}
public interface GalleryContext {
public ImageCacheService getImageCacheService();
public DataManager getDataManager();

public Context getAndroidContext();

public Looper getMainLooper();
public Resources getResources();
public ContentResolver getContentResolver();
public ThreadPool getThreadPool();
}
2)StateManager
其负责管理activity的各种状态的启动挂起等。
通过管理一个栈,来维护所有activity的显示状态。
private Stack<StateEntry> mStack = new Stack<StateEntry>();
同时保存一个GalleryActivity的mcontext,用来方便对相应activity进行操作。
一般通过如下来启动一个activity。
mActivity.getStateManager().startState(ManageCachePage.class, data);
其相当于
context.startActivity()
或是通过如下启动一个需要处理结果的activity。
mActivity.getStateManager().startStateForResult(
PhotoPage.class, REQUEST_PHOTO, data);
其相当于
context.startActivityForResult()
以下是类图。

一个activity对应着多个显示状态。见如下定义。
private Stack<StateEntry> mStack = new Stack<StateEntry>();
private static class StateEntry {
public Bundle data;
public ActivityState activityState;

public StateEntry(Bundle data, ActivityState state) {
this.data = data;
this.activityState = state;
}
}
而显示状态通过StateEntry中的ActivityState变量来标识。
abstract public class ActivityState {
public static final int FLAG_HIDE_ACTION_BAR = 1;
public static final int FLAG_HIDE_STATUS_BAR = 2;
public static final int FLAG_SCREEN_ON = 3;
…}
ActivityState用于控制activity的显示模式,是否需要点亮显示、是否需要显示状态栏、是否需要显示动作按钮。

3)GLRootView
实现绘图的类。其类图如下。

小贴士
1)GLSurfaceView介绍
GLSurfaceView是一个视图,继承至SurfaceView,它内嵌的surface专门负责OpenGL渲染。
GLSurfaceView提供了下列特性:
1> 管理一个surface,这个surface就是一块特殊的内存,能直接排版到android的视图view上。
2> 管理一个EGL display,它能让opengl把内容渲染到上述的surface上。
3> 用户自定义渲染器(render)。
4> 让渲染器在独立的线程里运作,和UI线程分离。
5> 支持按需渲染(on-demand)和连续渲染(continuous)。
6> 一些可选工具,如调试。

使用GLSurfaceView
通常会继承GLSurfaceView,并重载一些和用户输入事件有关的方法。如果你不需要重载事件方法,GLSurfaceView也可以直接使用, 你可以使用set方法来为该类提供自定义的行为。例如,GLSurfaceView的渲染被委托给渲染器在独立的渲染线程里进行,这一点和普通视图不一 样,setRenderer(Renderer)设置渲染器。

初始化GLSurfaceView
初始化过程其实仅需要你使用setRenderer(Renderer)设置一个渲染器(render)。当然,你也可以修改GLSurfaceView一些默认配置。
* setDebugFlags(int)
* setEGLConfigChooser(boolean)
* setEGLConfigChooser(EGLConfigChooser)
* setEGLConfigChooser(int, int, int, int, int, int)
* setGLWrapper(GLWrapper) 

定制android.view.Surface
GLSurfaceView默认会创建像素格式为PixelFormat.RGB_565的surface。如果需要透明效果,调用 getHolder().setFormat(PixelFormat.TRANSLUCENT)。透明(TRANSLUCENT)的surface的像 素格式都是32位,每个色彩单元都是8位深度,像素格式是设备相关的,这意味着它可能是ARGB、RGBA或其它。

选择EGL配置
Android设备往往支持多种EGL配置,可以使用不同数目的通道(channel),也可以指定每个通道具有不同数目的位(bits)深度。因此, 在渲染器工作之前就应该指定EGL的配置。GLSurfaceView默认EGL配置的像素格式为RGB_656,16位的深度缓存(depth buffer),默认不开启遮罩缓存(stencil buffer)。
如果你要选择不同的EGL配置,请使用setEGLConfigChooser方法中的一种。

调试行为
你可以调用调试方法setDebugFlags(int)或setGLWrapper(GLSurfaceView.GLWrapper)来自定义 GLSurfaceView一些行为。在setRenderer方法之前或之后都可以调用调试方法,不过最好是在之前调用,这样它们能立即生效。

设置渲染器
总之,你必须调用setRenderer(GLSurfaceView.Renderer)来注册一个GLSurfaceView.Renderer渲染器。渲染器负责真正的GL渲染工作。

渲染模式
渲染器设定之后,你可以使用setRenderMode(int)指定渲染模式是按需(on demand)还是连续(continuous)。默认是连续渲染。

Activity生命周期
Activity窗口暂停(pause)或恢复(resume)时,GLSurfaceView都会收到通知,此时它的onPause方法和 onResume方法应该被调用。这样做是为了让GLSurfaceView暂停或恢复它的渲染线程,以便它及时释放或重建OpenGL的资源。

事件处理
为了处理事件,一般都是继承GLSurfaceView类并重载它的事件方法。但是由于GLSurfaceView是多线程操作,所以需要一些特殊的处 理。由于渲染器在独立的渲染线程里,你应该使用Java的跨线程机制跟渲染器通讯。queueEvent(Runnable)方法就是一种相对简单的操 作,例如下面的例子。
class MyGLSurfaceView extends GLSurfaceView {

private MyRenderer mMyRenderer;

public void start() {
mMyRenderer = ...;
setRenderer(mMyRenderer);
}

public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
queueEvent(new Runnable() {
// 这个方法会在渲染线程里被调用
public void run() {
mMyRenderer.handleDpadCenter();
}});
return true;
}
return super.onKeyDown(keyCode, event);
}
}

(注:如果在UI线程里调用渲染器的方法,很容易收到“call to OpenGL ES API with no current context”的警告,典型的误区就是在键盘或鼠标事件方法里直接调用opengl es的API,因为UI事件和渲染绘制在不同的线程里。更甚者,这种情况下调用glDeleteBuffers这种释放资源的方法,可能引起程序的崩溃, 因为UI线程想释放它,渲染线程却要使用它。)

2)GLSurfaceView的非交互使用

使用GLSurfaceView开发3D应用时,如果是非交互式的应用,可以直接使用GLSurfaceView。如果需要交互式的行为,则需要继承
GLSurfaceView并重写一些方法。交互式应用示例见下篇。

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.opengl.GLSurfaceView.Renderer;
import android.os.Bundle;
import android.provider.OpenableColumns;
/**
* 本示例演示OpenGL ES开发3D应用
* 该Activity直接使用了GLSurfaceView
* 这是因为GLSurfaceView可以直接使用,除非需要接受用户输入,和用户交互,才需要重写一些GLSurfaceView的方法
* 如果开发一个非交互式的OpenGL应用,可以直接使用GLSurfaceView。参照本示例
* @author Administrator
*
*/
public class NonInteractiveDemo extends Activity {

private GLSurfaceView mGLView;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

mGLView = new GLSurfaceView(this);
//这里需要指定一个自定义的渲染器
mGLView.setRenderer(new DemoRenderer());
setContentView(mGLView);

}


public void onPause(){
super.onPause();
mGLView.onPause(); //当Activity暂停时,告诉GLSurfaceView也停止渲染,并释放资源。
}

public void onResume(){
super.onResume();
mGLView.onResume(); //当Activity恢复时,告诉GLSurfaceView加载资源,继续渲染。
}




}
class DemoRenderer implements Renderer{
@Override
public void onDrawFrame(GL10 gl) {
//每帧都需要调用该方法进行绘制。绘制时通常先调用glClear来清空framebuffer。
//然后调用OpenGL ES其他接口进行绘制
gl.glClear(GL10.GL_COLOR_BUFFER_BIT|GL10.GL_DEPTH_BUFFER_BIT);

}
@Override
public void onSurfaceChanged(GL10 gl, int w, int h) {
//当surface的尺寸发生改变时,该方法被调用,。往往在这里设置ViewPort。或者Camara等。 
gl.glViewport(0, 0, w, h);
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// 该方法在渲染开始前调用,OpenGL ES的绘制上下文被重建时也会调用。
//当Activity暂停时,绘制上下文会丢失,当Activity恢复时,绘制上下文会重建。

//do nothing special
}

}

3)GLSurfaceView交互式

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.app.Activity;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.MotionEvent;
/**
* 本示例演示OpenGL ES开发3D应用
* 该Activity使用了自定义的GLSurfaceView的子类
* 这样,我们可以开发出和用户交互的应用,比如游戏等。
* 需要注意的是:由于渲染对象是运行在一个独立的渲染线程中,所以
* 需要采用跨线程的机制来进行事件的处理。但是Android提供了一个简便的方法
* 我们只需要在事件处理中使用queueEvent(Runnable)就可以了.

* 对于大多数3D应用,如游戏、模拟等都是持续性渲染,但对于反应式应用来说,只有等用户进行了某个操作后再开始渲染。
* GLSurfaceView支持这两种模式。通过调用方法setRenderMode()方法设置。
* 调用requestRender()继续渲染。


* @author Administrator
*
*/
public class InteractiveDemo extends Activity {

private GLSurfaceView mGLView;

public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
mGLView = new DemoGLSurfaceView(this); //这里使用的是自定义的GLSurfaceView的子类
setContentView(mGLView);
}


public void onPause(){
super.onPause();
mGLView.onPause();
}

public void onResume(){
super.onResume();
mGLView.onResume();
}
}
class DemoGLSurfaceView extends GLSurfaceView{
DemoRenderer2 mRenderer;

public DemoGLSurfaceView(Context context) {
super(context);
//为了可以激活log和错误检查,帮助调试3D应用,需要调用setDebugFlags()。
this.setDebugFlags(DEBUG_CHECK_GL_ERROR|DEBUG_LOG_GL_CALLS);
mRenderer = new DemoRenderer2();
this.setRenderer(mRenderer);
}

public boolean onTouchEvent(final MotionEvent event){
//由于DemoRenderer2对象运行在另一个线程中,这里采用跨线程的机制进行处理。使用queueEvent方法
//当然也可以使用其他像Synchronized来进行UI线程和渲染线程进行通信。
this.queueEvent(new Runnable() {

@Override
public void run() {

//TODO:
mRenderer.setColor(event.getX()/getWidth(), event.getY()/getHeight(), 1.0f);
}
});

return true;
}

}
/**
* 这个应用在每一帧中清空屏幕,当tap屏幕时,改变屏幕的颜色。
* @author Administrator
*
*/
class DemoRenderer2 implements GLSurfaceView.Renderer{

private float mRed;
private float mGreen;
private float mBlue;
@Override
public void onDrawFrame(GL10 gl) {
// TODO Auto-generated method stub
gl.glClearColor(mRed, mGreen, mBlue, 1.0f);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT|GL10.GL_DEPTH_BUFFER_BIT);
}
@Override
public void onSurfaceChanged(GL10 gl, int w, int h) {
// TODO Auto-generated method stub
gl.glViewport(0, 0, w, h);
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// TODO Auto-generated method stub

}

public void setColor(float r, float g, float b){
this.mRed = r;
this.mGreen = g;
this.mBlue = b;
}

}

GLRootView的构造函数中对其进行初始化操作,设置渲染器,设置EGL,设置背景图,使用RGB565作为像素格式。


三、

3.1.3 AlbumSetPage
负责处理刚进入相册显示的所有相册界面。

其类图如下。


相关类说明
1)SelectionManager.SelectionListener
负责对选择状态时的侦听。

2)SelectionManager
负责对选择状态的处理。
其类图如下。

mSourceMediaSet主要用于对相册数据进行管理,其具体作用待分析。MediaSet类请参见Mediaset。
mClickedSet主要用于对选中的媒体文件的路径进行管理。
mListener就是侦听者。
mDataManager主要用于管理相册数据。
mPressedPath存放当前点中的媒体数据的路径。

选择分为以下几种状态。
SelectionManager.ENTER_SELECTION_MODE
SelectionManager.LEAVE_SELECTION_MODE
SelectionManager.SELECT_ALL_MODE
进入退出全选,对应每种状态都要相应处理。

3)GalleryActionBar.ClusterRunner
主要实现doCluster函数。该函数用于实现按模式排序时的activity跳转。
通过GalleryActionBar实现排序,原理就是打包bundle值,重新开启一个activity,新的activity接受到这个bundle值。
4)EyePosition.EyePositionListener
人眼定位的侦听者,主要实现onEyePositionChanged,用于对人眼位置改变的侦听。未研究。
5)EyePosition
处理人眼定位的类。未研究。
6)MediaSet.SyncListener
处理异步加载mediaset的结果侦听者。实现onSyncDone()函数。
该函数处理异步加载的结果。

7)StaticBackground
管理背景图图片。

8)AlbumSetView
负责绘制所有相册的view。
如下是类图。

8.1)SLotView
将GLView再封装。未研究。

8.2)GLView
关于GLView类介绍的注释如下。通过其可以知道GLView大致是用来将内容渲染到GLCanvas画布上的,同时处理一些触摸事件。未研究。

// GLView is a UI component. It can render to a GLCanvas and accept touch
// events. A GLView may have zero or more child GLView and they form a tree
// structure. The rendering and event handling will pass through the tree
// structure.
//
// A GLView tree should be attached to a GLRoot before event dispatching and
// rendering happens. GLView asks GLRoot to re-render or re-layout the
// GLView hierarchy using requestRender() and requestLayoutContentPane().
//
// The render() method is called in a separate thread. Before calling
// dispatchTouchEvent() and layout(), GLRoot acquires a lock to avoid the
// rendering thread running at the same time. If there are other entry points
// from main thread (like a Handler) in your GLView, you need to call
// lockRendering() if the rendering thread should not run at the same time.
8.3)AlbumSetSlidingWindow
负责滑动显示图片相关。
9)AlbumSetDataAdapter
数据适配器。
10)ActionModeHandler
对操作栏上的操作进行管理。
目前有如下操作。
private static final int SUPPORT_MULTIPLE_MASK = MediaObject.SUPPORT_DELETE
| MediaObject.SUPPORT_ROTATE | MediaObject.SUPPORT_SHARE
| MediaObject.SUPPORT_CACHE | MediaObject.SUPPORT_IMPORT;
3.1.4 AlbumPage
其基本结构与albumsetpage差不多,里面的view变成albumview。



四、

3.1.5 PhotoPage

其类图如下。


一般创建流程如下。顺序无先后
1)绘制视图,添加需要用到的控件或各种view。
2)为需要侦听者的控件或view加侦听者。
3)绑定相应model,例如这里绑定photopage.model。
4)创建渲染对象,GLView。各个视图用到的view都是加载在其上的,通过GLView.addComponent()实现。
其他主要page,诸如albumsetpage、albumpage。
相关类说明
1)PhotoView.PhotoTapListener
主要是处理onSingleTapUp事件。用于处理点击媒体文件时的动作。
2)PhotoView
主要处理photo的view。
其类图如下。

2.1)ScreenNailEntry
主要保存上下两个缩略图的显示的数据,显示这两个缩略图。
手势识别相关
ScaleGestureDetector、GestureDetector、DownUpDetector、PositionController都是跟手势识别相关的。例如左滑动、右滑动切换图片,手势放大图片等,以及过渡动画。
2.2)TileImageView
负责每个图片的处理流程。
存在一个状态机,标示每种decode相关的状态。如下图:

Tile作为一个绘制的单位,可以理解为,其封装了解码后的图片数据。TileQueue是存放tile的队列,这些tile就是等待最终调用canvas绘制的数据。GLCanvas就是封装了canvas的类。
Tileimageview.render就是绘制图片的开始。里面会调用到queueForDecode()将绘制的tile丢入tilequeue中等待被处理。一旦轮到该tile被绘制,则调用decodetile将此tile解码。
ImageCacheRequest.run调用DecodeUtils. requestDecode完成最终的解码操作。
存在三个队列,如下:
Decodequeue:存放等待decode的tile。
Uploadqueue:存放等待upload的tile。Upload就是将解码的数据送到CAVANS绘制出来,因此都是从decode中出来的。
Recycledqueue:存放要回收的tile。
mActiveTiles用于存放已激活的tile的map。
2.3)EdgeView
缩略图中每张图的蓝色外框。
2.4)Model
用于沟通UI层与数据层的桥梁。
其定义如下。

    public static interface Model extendsTileImageView.Model {
        public void next();
        public void previous();
        public void jumpTo(int index);
        public int getImageRotation();
 
        // Return null if the specified imageis unavailable.
       public ImageDatagetNextImage();
        public ImageData getPreviousImage();
    }

2.5)ProgressSpinner
进度圆圈。
2.6)手势识别处理相关
private final ScaleGestureDetector mScaleDetector;//两点触摸放大缩小等处理
private final GestureDetector mGestureDetector;
private final DownUpDetector mDownUpDetector;//点击处理相关
private PhotoTapListener mPhotoTapListener;
private final PositionController mPositionController;
3)FilmStripView.Listener
显示图片下方的缩略图滚动条的处理侦听者。主要涉及点击跳转的侦听。如下
public interface Listener {
// Returns false if it cannot jump to the specified index at this time.
boolean onSlotSelected(int slotIndex);
}


4)FilmStripView
处理在这个 控件之上的操作,诸如触摸移动长按等动作。



五、

3.1.6 SlideshowPage
处理幻灯片动画显示。

其类图如下。


1)SlideshowView
负责处理幻灯片播放的view。
1.1)SlideshowAnimation
其继承自Animation,负责具体幻灯片切换的显示。这里用到两个对象,mPrevAnimation负责上一张图片动画显示,mCurrentAnimation负责当前图片动画显示,显示动画效果就是扩大,淡入淡出。
Animation类的基本用法如下:
1)使用前需要设置每张图片显示周期,使用setDuration()。
2)开始播放,使用start()。
3)calculate()返回单张图片是否在一个周期显示完毕。
1.2)FloatAnimation
与SlideshowAnimation配合,未研究。
1.3)BitmapTexture
存放显示的图片。这里也用到两个对象,mPrevTexture负责存放上一张图片数据,mCurrentTexture负责存放当前图片数据。
2)Slide
负责存放具体相片数据。
3)ShuffleSource
负责生成随机的图片序列。用于随机幻灯片显示图片。
4)SequentialSource
负责生成顺序的图片序列。用于顺序幻灯片显示图片。
3.1.7 PhotoDataAdapter
Photodataadapter属于photopage的数据层,负责在图片数据更新时请求刷新UI。
1)渲染显示各个界面元素的原理
Photopage界面上,显示的元素种类分为三种。完整画面显示的图片,位于图片下方的缩略图图片,可能出现的GIF图片。
Photopage界面上一般存在多个元素,一个完整图片以及很多张缩略图图片,要同时刷新这么多元素,只能多线程,而涉及的线程那么多,最好就是用线程池来管理。简单说,在需要渲染界面元素时,就将该渲染任务丢入线程池中,线程池中若有空闲线程就分配来处理之,没有则该任务挂起,等待一定时间。同时线程池容许存放的任务数有上限。如下图表示线程池处理渲染的原理图。

这里的发起更新图片请求通过updateImageRequests()来实现。

2)数据层与UI层的桥梁
该类保存了一个PhotoView,看其函数调用,很多都是notify开头的,可见该对象主要在这里扮演桥梁的角色。一旦数据更新了,就会经由它通知UI层进行更新,或是一旦UI层变化了了,就经由它通知数据层更新。
各种代码片段如下。
通知UI层更新。

通知数据层更新。


3)ReloadTask
继承自thread。该线程是是与SourceListener共同配合,处理当前显示图片检测到路径不存在时的情形。诸如,在文件管理器或是图库删除此图片时,就会进行该处理,通知界面各元素更新。

4)各种图片元素表示相关类
ImageEntry 类用于表示当前显示的图片。其结构如下:
private static class ImageEntry {
public int requestedBits = 0;
public int rotation;
public BitmapRegionDecoder fullImage;
public Bitmap screenNail;
public Future<Bitmap> screenNailTask;
public Future<BitmapRegionDecoder> fullImageTask;
public boolean failToLoad = false;
// the below members are added for Gif animation
public GifDecoder gifDecoder;
public Future<GifDecoder> gifDecoderTask;
public Bitmap currentGifFrame;
}

FullImageListener用于侦听全屏显示的图片的更新。
ScreenNailListener用于侦听缩略图显示的图片的更新。
ScreenNailJob用于缩略图更新时的具体处理,与ScreenNailListener配合使用。
GifDecoderListener用于侦听GIF图片的更新。
GifAnimation类用于表示当前显示的GIF图片,其结构如下:
private static class GifAnimation {
public ImageEntry entry;
public GifDecoder gifDecoder;
public int animatedIndex;
public int currentFrame;
public int totalFrameCount;
}
GifAnimationRunnable是负责GIF解码显示的runnale类。

5)图片缓存处理相关
要想在图片切换时速度加快,就应该建立一套缓存机制。Gallery 4.0主要通过在保存当前显示图片临近图片元素来实现。如前面所说,图片元素包括,完整画面显示的图片,位于图片下方的缩略图图片,可能出现的GIF图片。对于缩略图图片,IMAGE_CACHE_SIZE限定保存个数,这里是5,其余两种元素各保留一个。
保存图片元素使用ImageFetch,其类定义如下。
private static class ImageFetch {
int indexOffset;
int imageBit;
public ImageFetch(int offset, int bit) {
indexOffset = offset;
imageBit = bit;
}
}
6)图片裁切相关数据处理
相关类是TileImageViewAdapter。
7)DataManager
Mediaset以及mediaitem都用一个64bit的id来标识。高32bit用来标识其与父集的关系,低32bit用来标识本身,其作为私有id。对于mediaset,该私有id是唯一的。对于mediaitem,该私有id在其父集中是唯一的,全局来说并非唯一的。
父集的概念,应该就是文件夹的概念。Mediaitem不能作为其他元素的父集,而mediaset可以作为mediaitem的父集。
Datamanager用于管理mediaset以及mediaitem组成的树状数据结构的类。

8)DataListener
幻灯片播放时图片数据改变等时的侦听者。






评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值