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

简介:

一、注意事项

本章节使用的是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非官方示例。

本文转自博客园农民伯伯的博客,原文链接:使用Vitamio打造自己的Android万能播放器(11)—— 本地扫描后台服务,如需转载请自行联系原博主。


目录
相关文章
|
2月前
|
Java 程序员 开发工具
Android|修复阿里云播放器下载不回调的问题
虽然 GC 带来了很多便利,但在实际编码时,我们也需要注意对象的生命周期管理,该存活的存活,该释放的释放,避免因为 GC 导致的问题。
39 2
|
2月前
|
安全 Java 网络安全
Android远程连接和登录FTPS服务代码(commons.net库)
Android远程连接和登录FTPS服务代码(commons.net库)
29 1
|
3月前
|
JavaScript 前端开发 Android开发
让Vite+Vue3项目在Android端离线打开(不需要起服务)
让Vite+Vue3项目在Android端离线打开(不需要起服务)
116 10
|
3月前
|
安全 API 开发工具
Android平台RTMP推送|轻量级RTSP服务如何实现麦克风|扬声器声音采集切换
Android平台扬声器播放声音的采集,在无纸化同屏等场景下,意义很大,早期低版本的Android设备,是没法直接采集扬声器audio的(从Android 10开始支持),所以,如果需要采集扬声器audio,需要先做系统版本判断,添加相应的权限。
|
3月前
|
编解码 开发工具 Android开发
Android平台实现屏幕录制(屏幕投影)|音频播放采集|麦克风采集并推送RTMP或轻量级RTSP服务
Android平台屏幕采集、音频播放声音采集、麦克风采集编码打包推送到RTMP和轻量级RTSP服务的相关技术实现,做成高稳定低延迟的同屏系统,还需要有配套好的RTMP、RTSP直播播放器
|
19天前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台策略
在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。
|
24天前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义视图:打造个性化UI组件
【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。
|
6天前
|
搜索推荐 前端开发 API
探索安卓开发中的自定义视图:打造个性化用户界面
在安卓应用开发的广阔天地中,自定义视图是一块神奇的画布,让开发者能够突破标准控件的限制,绘制出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战技巧,逐步揭示如何在安卓平台上创建和运用自定义视图来提升用户体验。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开新的视野,让你的应用在众多同质化产品中脱颖而出。
33 19