使用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非官方示例。

 

結束

 更加深刻體會“堅持就是勝利”這句話的意義。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM