一、注意事項
注意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;
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();
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)) {
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;
public IBinder onBind(Intent intent) {
return mBinder;
}
代碼說明:
1、預設Service是在主線程中運作的,掃描比較耗時,這裡啟用了一個新的線程。
2、使用了觀察者模式來通知資料變化,沒有用廣播。
3、暫時去掉了截圖,截圖報錯,還在修改。
三、OPlayer
3.1 下載下傳
請移步#Taocode(SVN):(沒有賬戶的請注冊一個即可)
3.2 更新
a). 資料庫層使用了ormlite架構。
b). 将掃描視訊檔案修改為背景服務,單獨起了一個線程。
c). 整合Vitamio 3.0,修改反射為覆寫io.voo.vitamio.R。
四、Vitamio
已建立Vitamio開發者聯盟官方QQ群!群号為:246969281
注意:目前僅接受已經開發基于Vitamio産品的開發者申請加入,申請理由請填寫産品的名稱和連結!
五、八卦
想在這裡澄清一下OPlayer與VPlayer、Vitamio的關系:
1、OPlayer這個項目是跟公司沒有關系,純屬于個人業餘愛好作品。
2、OPlayer使用的是Vitamio,但不會比大家優先擷取Vitamio的新版本。
3、OPlayer非官方示例。