使用Vitamio打造自己的Android万能播放器(11)—— 本地扫描后台服务

前言

Vitamio和OPlayer系列文章得到广大同行的关注和支持,非常感谢!离系列文章的上篇有几个月了,各种忙暂停了本系列文章,非常抱歉!离OPlayer正式上线还有距离,还有很多要做的事情,继续前行!

 

声明

  欢迎转载,但请保留文章原始出处:) 

    博客园:http://www.cnblogs.com

    农民伯伯: http://over140.cnblogs.com  

 

正文

一、注意事项

本章节使用的是Vitamio 3.0(2012-11-07)请大家及时更新。

注意3.0整合的适合建议使用Library方式整合Vitamio,否则需要反射或者使用OPlayer的方法,覆盖R(参见代码)。 

 

二、主要代码:

/** 媒体扫描 */
public class MediaScannerService extends Service implements Runnable {

    private static final String SERVICE_NAME = "com.nmbb.oplayer.service.MediaScannerService";
    /** 扫描文件夹 */
    public static final String EXTRA_DIRECTORY = "scan_directory";
    /** 扫描文件 */
    public static final String EXTRA_FILE_PATH = "scan_file";
    public static final String EXTRA_MIME_TYPE = "mimetype";

    public static final int SCAN_STATUS_NORMAL = -1;
    /** 开始扫描 */
    public static final int SCAN_STATUS_START = 0;
    /** 正在扫描 扫描到一个视频文件 */
    public static final int SCAN_STATUS_RUNNING = 1;
    /** 扫描完成 */
    public static final int SCAN_STATUS_END = 2;
    /**  */
    private ArrayList<IMediaScannerObserver> observers = new ArrayList<IMediaScannerObserver>();
    private ConcurrentHashMap<String, String> mScanMap = new ConcurrentHashMap<String, String>();

    /** 当前状态 */
    private volatile int mServiceStatus = SCAN_STATUS_NORMAL;
    private DbHelper<POMedia> mDbHelper;
    private Map<String, Object> mDbWhere = new HashMap<String, Object>(2);

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

        mDbHelper = new DbHelper<POMedia>();
    }

    /** 是否正在运行 */
    public static boolean isRunning() {
        ActivityManager manager = (ActivityManager) OPlayerApplication.getContext().getSystemService(Context.ACTIVITY_SERVICE);
        for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
            if (SERVICE_NAME.equals(service.service.getClassName()))
                return true;
        }
        return false;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent != null)
            parseIntent(intent);

        return super.onStartCommand(intent, flags, startId);
    }

    /** 解析Intent */
    private void parseIntent(final Intent intent) {
        final Bundle arguments = intent.getExtras();
        if (arguments != null) {
            if (arguments.containsKey(EXTRA_DIRECTORY)) {
                String directory = arguments.getString(EXTRA_DIRECTORY);
                Logger.i("onStartCommand:" + directory);
                //扫描文件夹
                if (!mScanMap.containsKey(directory))
                    mScanMap.put(directory, "");
            } else if (arguments.containsKey(EXTRA_FILE_PATH)) {
                //单文件
                String filePath = arguments.getString(EXTRA_FILE_PATH);
                Logger.i("onStartCommand:" + filePath);
                if (!StringUtils.isEmpty(filePath)) {
                    if (!mScanMap.containsKey(filePath))
                        mScanMap.put(filePath, arguments.getString(EXTRA_MIME_TYPE));
                    //                    scanFile(filePath, arguments.getString(EXTRA_MIME_TYPE));
                }
            }
        }

        if (mServiceStatus == SCAN_STATUS_NORMAL || mServiceStatus == SCAN_STATUS_END) {
            new Thread(this).start();
            //scan();
        }
    }

    @Override
    public void run() {
        scan();
    }

    /** 扫描 */
    private void scan() {
        //开始扫描
        notifyObservers(SCAN_STATUS_START, null);

        while (mScanMap.keySet().size() > 0) {

            String path = "";
            for (String key : mScanMap.keySet()) {
                path = key;
                break;
            }
            if (mScanMap.containsKey(path)) {
                String mimeType = mScanMap.get(path);
                if ("".equals(mimeType)) {
                    scanDirectory(path);
                } else {
                    scanFile(path, mimeType);
                }

                //扫描完成一个
                mScanMap.remove(path);
            }

            //任务之间歇息一秒
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Logger.e(e);
            }
        }

        //全部扫描完成
        notifyObservers(SCAN_STATUS_END, null);

        //第一次扫描
        OPreference pref = new OPreference(this);
        if (pref.getBoolean(OPlayerApplication.PREF_KEY_FIRST, true))
            pref.putBooleanAndCommit(OPlayerApplication.PREF_KEY_FIRST, false);

        //停止服务
        stopSelf();
    }

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            for (IMediaScannerObserver s : observers) {
                if (s != null) {
                    s.update(msg.what, (POMedia) msg.obj);
                }
            }
        }
    };

    /** 扫描文件 */
    private void scanFile(String path, String mimeType) {
        save(new POMedia(path, mimeType));
    }

    /** 扫描文件夹 */
    private void scanDirectory(String path) {
        eachAllMedias(new File(path));
    }

    /** 递归查找视频 */
    private void eachAllMedias(File f) {
        if (f != null && f.exists() && f.isDirectory()) {
            File[] files = f.listFiles();
            if (files != null) {
                for (File file : f.listFiles()) {
                    //                    Logger.i(f.getAbsolutePath());
                    if (file.isDirectory()) {
                        //忽略.开头的文件夹
                        if (!file.getAbsolutePath().startsWith("."))
                            eachAllMedias(file);
                    } else if (file.exists() && file.canRead() && FileUtils.isVideo(file)) {
                        save(new POMedia(file));
                    }
                }
            }
        }
    }

    /**
     * 保存入库
     * 
     * @throws FileNotFoundException
     */
    private void save(POMedia media) {
        mDbWhere.put("path", media.path);
        mDbWhere.put("last_modify_time", media.last_modify_time);
        //检测
        if (!mDbHelper.exists(media, mDbWhere)) {
            try {
                if (media.title != null && media.title.length() > 0)
                    media.title_key = PinyinUtils.chineneToSpell(media.title.charAt(0) + "");
            } catch (Exception ex) {
                Logger.e(ex);
            }
            media.last_access_time = System.currentTimeMillis();

            //提取缩略图
            //            extractThumbnail(media);
            media.mime_type = FileUtils.getMimeType(media.path);

            //入库
            mDbHelper.create(media);

            //扫描到一个
            notifyObservers(SCAN_STATUS_RUNNING, media);
        }
    }

    /** 提取生成缩略图 */
    private void extractThumbnail(POMedia media) {
        final Context ctx = OPlayerApplication.getContext();
        //        ThumbnailUtils.
        Bitmap bitmap = ThumbnailUtils.createVideoThumbnail(ctx, media.path, ThumbnailUtils.MINI_KIND);
        try {
            if (bitmap == null) {
                //缩略图创建失败
                bitmap = Bitmap.createBitmap(ThumbnailUtils.TARGET_SIZE_MINI_THUMBNAIL_WIDTH, ThumbnailUtils.TARGET_SIZE_MINI_THUMBNAIL_HEIGHT, Bitmap.Config.RGB_565);
            }

            media.width = bitmap.getWidth();
            media.height = bitmap.getHeight();

            //缩略图
            bitmap = ThumbnailUtils.extractThumbnail(bitmap, ConvertUtils.dipToPX(ctx, ThumbnailUtils.TARGET_SIZE_MICRO_THUMBNAIL_WIDTH), ConvertUtils.dipToPX(ctx, ThumbnailUtils.TARGET_SIZE_MICRO_THUMBNAIL_HEIGHT), ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
            if (bitmap != null) {
                //将缩略图存到视频当前路径
                File thum = new File(OPlayerApplication.OPLAYER_VIDEO_THUMB, UUID.randomUUID().toString());
                media.thumb_path = thum.getAbsolutePath();
                //thum.createNewFile();
                FileOutputStream iStream = new FileOutputStream(thum);
                bitmap.compress(Bitmap.CompressFormat.JPEG, 85, iStream);
                iStream.close();
            }

            //入库

        } catch (Exception ex) {
            Logger.e(ex);
        } finally {
            if (bitmap != null)
                bitmap.recycle();

        }
    }

    // ~~~ 状态改变 

    /** 通知状态改变 */
    private void notifyObservers(int flag, POMedia media) {
        mHandler.sendMessage(mHandler.obtainMessage(flag, media));
    }

    /** 增加观察者 */
    public void addObserver(IMediaScannerObserver s) {
        synchronized (this) {
            if (!observers.contains(s)) {
                observers.add(s);
            }
        }
    }

    /** 删除观察者 */
    public synchronized void deleteObserver(IMediaScannerObserver s) {
        observers.remove(s);
    }

    /** 删除所有观察者 */
    public synchronized void deleteObservers() {
        observers.clear();
    }

    public interface IMediaScannerObserver {
        /**
         * 
         * @param flag 0 开始扫描 1 正在扫描 2 扫描完成
         * @param file 扫描到的视频文件
         */
        public void update(int flag, POMedia media);
    }

    // ~~~ Binder 

    private final MediaScannerServiceBinder mBinder = new MediaScannerServiceBinder();

    public class MediaScannerServiceBinder extends Binder {
        public MediaScannerService getService() {
            return MediaScannerService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

}

代码说明:

1、默认Service是在主线程中运行的,扫描比较耗时,这里启用了一个新的线程。

2、使用了观察者模式来通知数据变化,没有用广播。

3、暂时去掉了截图,截图报错,还在修改。 

 

三、OPlayer

3.1 下载
请移步#Taocode(SVN):(没有账户的请注册一个即可)
项目地址:http://code.taobao.org/p/oplayer
SVN地址:http://code.taobao.org/svn/oplayer/ 

3.2 更新
a). 数据库层使用了ormlite框架。
b). 将扫描视频文件修改为后台服务,单独起了一个线程。

c). 整合Vitamio 3.0,修改反射为覆盖io.voo.vitamio.R。 

 

四、Vitamio

已建立Vitamio开发者联盟官方QQ群!群号为:246969281
注意:目前仅接受已经开发基于Vitamio产品的开发者申请加入,申请理由请填写产品的名称和链接!
官方微博:http://weibo.com/vitamio 
 

五、八卦

想在这里澄清一下OPlayer与VPlayer、Vitamio的关系:

1、OPlayer这个项目是跟公司没有关系,纯属于个人业余爱好作品。

2、OPlayer使用的是Vitamio,但不会比大家优先获取Vitamio的新版本。

3、OPlayer非官方示例。

 

结束

 更加深刻体会“坚持就是胜利”这句话的意义。

转载:http://www.cnblogs.com/over140/archive/2012/11/07/2759241.html

时间: 2024-11-08 19:43:49

使用Vitamio打造自己的Android万能播放器(11)—— 本地扫描后台服务的相关文章

[重要]使用Vitamio打造自己的Android万能播放器(10)—— 本地播放 (缩略图、视频信息、视频扫描服务)

前言 Vitamio是我们团队的诚意之作,除了要将VPlayer打造成Android最好的播放器,也要将Vitamio打造成Android最好的播放器组件.新版发布内测SDK虽然还有一些问题,但已经具备高可扩展性.便捷性和许多内置实用的功能,后续文章将继续深挖和介绍Vitamio的使用细节和方法.本章将提供显示缩略图.视频大小等视频信息,并介绍使用Vitamio内置的ContentProvider以及文件扫描服务.   注意 本章ContentProvider在2012-8-31发布的SDK中已

使用Vitamio打造自己的Android万能播放器(2)—— 手势控制亮度、音量、缩放

前言 本章继续完善播放相关播放器的核心功能,为后续扩展打好基础.   声明 欢迎转载,但请保留文章原始出处:)  博客园:http://www.cnblogs.com 农民伯伯: http://over140.cnblogs.com   系列 1.使用Vitamio打造自己的Android万能播放器(1)--准备     正文 一.实现目标  1.1 亮度控制 模仿VPlayer界面: 1.2 声音控制  模仿VPlayer界面:     1.3 画面缩放  根据下面API提供画面的拉伸.剪切.

使用Vitamio打造自己的Android万能播放器(4)——本地播放(快捷搜索、数据存储)

前言 关键字:Vitamio.VPlayer.Android播放器.Android影音.Android开源播放器 本章节把Android万能播放器本地播放的主要功能(缓存播放列表和A-Z快速查询功能)完成,和播放组件关系不大,但用到一些实用的技术,欢迎交流!   声明 欢迎转载,但请保留文章原始出处:)  博客园:http://www.cnblogs.com 农民伯伯: http://over140.cnblogs.com    系列 1.使用Vitamio打造自己的Android万能播放器(1

使用Vitamio打造自己的Android万能播放器(8)——细节优化

前言 成功的产品往往在细节之处也做到极致,产品和项目从使用的角度来看最大的区别我认为也就是细节的处理上.开播视频的目标是产品,前面7篇文章高歌猛进,添加了很多的功能,也该停下来休整一下,优化一下,也欢迎交流反馈做得不周到的地方.由于开源,大家也可以按照自己的喜好来添加修改不同的功能和细节.   声明 欢迎转载,但请保留文章原始出处:)  博客园:http://www.cnblogs.com 农民伯伯: http://over140.cnblogs.com    系列 1.使用Vitamio打造自

使用Vitamio打造自己的Android万能播放器(3)——本地播放(主界面、播放列表)

前言 打造一款完整可用的Android播放器有许多功能和细节需要完成,也涉及到各种丰富的知识和内容,本章将结合Fragment.ViewPager来搭建播放器的主界面,并实现本地播放基本功能.系列文章提供截图.代码说明.源码下载,欢迎交流!   声明 欢迎转载,但请保留文章原始出处:)    博客园:http://www.cnblogs.com   农民伯伯: http://over140.cnblogs.com    系列 1.使用Vitamio打造自己的Android万能播放器(1)--准备

使用Vitamio打造自己的Android万能播放器(1)——准备

前言 虽然Android已经内置了VideoView组件和MediaPlayer类来支持开发视频播放器,但支持格式.性能等各方面都十分有限,这里与大家一起利用开源项目Vitamio来打造属于自己的Android万能播放器!   注意 请下载新发布的SDK(2012-7-9): 使用Vitamio打造自己的Android万能播放器--新版Vitamio内测SDK发布    声明 欢迎转载,但请保留文章原始出处:)  博客园:http://www.cnblogs.com 农民伯伯: http://o

使用Vitamio打造自己的Android万能播放器(7)——在线播放(下载视频)

前言 本章将实现非常实用的功能--下载在线视频.涉及到多线程.线程更新UI等技术,还需思考产品的设计,如何将新加的功能更好的融入到现有的产品中,并不是简单的加一个界面就行了,欢迎大家交流产品设计和技术细节实现!   声明 欢迎转载,但请保留文章原始出处:)  博客园:http://www.cnblogs.com 农民伯伯: http://over140.cnblogs.com    系列 1.使用Vitamio打造自己的Android万能播放器(1)--准备 2.使用Vitamio打造自己的An

使用Vitamio打造自己的Android万能播放器(5)——在线播放(播放优酷视频)

前言 为了保证每周一篇的进度,又由于Vitamio新版本没有发布, 决定推迟本地播放的一些功能(截图.视频时间.尺寸等),跳过直接写在线播放部分的章节.从Vitamio的介绍可以看得出,其支持http.m3u8等多种网络协议,本章将编写播放优酷视频的例子.   声明 欢迎转载,但请保留文章原始出处:)  博客园:http://www.cnblogs.com 农民伯伯: http://over140.cnblogs.com    系列 1.使用Vitamio打造自己的Android万能播放器(1)

Android 使用Vitamio打造自己的万能播放器(6)——在线播放(播放列表)_Android

前言 新版本的VPlayer由设计转入开发阶段,预计开发周期为一个月,这也意味着新版本的Vitamio将随之发布,开发者们可以和本系列文章一样,先开发其他功能.本章内容为"在线视频播放列表",集合了主流各大视频网站的手机版,欢迎给"开播视频"反馈改进建议! 系列 1.Android 使用Vitamio打造自己的万能播放器(1)--准备 2.Android 使用Vitamio打造自己的Android万能播放器(2)-- 手势控制亮度.音量.缩放 3.Android 使