[- Video篇 -]:記一次SQLite的使用

johnychen發表於2021-09-09
0.前言

最近在重構我的視訊播放器,專案有點點複雜,不可能全面的記錄
接下來,挑一些要點來記錄一下,上下文鋪設比較繁瑣,有興趣的可以本系列原始碼:github

一些播放資訊的記錄感覺還是放在資料庫裡好一些,不然感覺很生硬   
以前的SQLite介紹文章有點無病呻吟的感覺,這次來實際用一下,相信感觸會更深 

1.解決視訊播放量的記錄問題
2.解決視訊進入時恢復到上次播放進度
3.解決查詢最近播放的n條記錄的問題
4.解決查詢播放最多的n條記錄的問題
複製程式碼

[- Video篇 -]:記一次SQLite的使用

[- Video篇 -]:記一次SQLite的使用


一、SQLite使用步驟

1.表分析
表欄位
id              標識            主鍵,自增
path            視訊名稱        varchar(120) 唯一 非空
current_pos     當前播放進度    TINYINT 預設為0    
last_play_time  上次播放時間    CHAR(24) 2019-3-1 16:20:00 
play_count      播放次數        INT 預設為0

|--- 建表語句 -------------------------------
CREATE TABLE video_player (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
path VARCHAR(120) UNIQUE NOT NULL,
current_pos TINYINT NOT NULL DEFAULT 0,
last_play_time CHAR(24) NOT NULL,
play_count INT NOT NULL DEFAULT 0
); 
複製程式碼

2.建立VideoDatabaseHelper繼承自SQLiteOpenHelper
/**
 * 作者:張風捷特烈<br/>
 * 時間:2019/4/4/004:13:19<br/>
 * 郵箱:1981462002@qq.com<br/>
 * 說明:資料庫輔助類
 */
public class VideoDatabaseHelper extends SQLiteOpenHelper {

    private static String DATABASE_NAME = "i_video.db";//資料庫名
    private static int DATABASE_VERSION = 1;//資料庫版本

    public VideoDatabaseHelper(@Nullable Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        createSwordTable(db);
    }

    private void createSwordTable(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE video_player (\n" +
                "id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n" +
                "path VARCHAR(120) UNIQUE NOT NULL,\n" +
                "current_pos TINYINT NOT NULL DEFAULT 0,\n" +
                "last_play_time CHAR(24) NOT NULL,\n" +
                "play_count INT NOT NULL DEFAULT 0\n" +
                "); ");

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}
複製程式碼

3.實體類簡單封裝
/**
 * 作者:張風捷特烈<br></br>
 * 時間:2019/4/4/004:13:29<br></br>
 * 郵箱:1981462002@qq.com<br></br>
 * 說明:視屏播放實體類
 */
class VideoBean {
    var id: Int = 0
    var path: String = ""
    var current_pos: Int = 0
    var last_play_time: String = ""
    var play_count: Int = 1
}
複製程式碼

4.建立Dao來運算元據庫

用個單例來獲取VideoDao方便操作


/**
 * 作者:張風捷特烈<br/>
 * 時間:2019/4/4/004:13:26<br/>
 * 郵箱:1981462002@qq.com<br/>
 * 說明:資料庫操作層
 */
public class VideoDao {
    private static VideoDao sVideoDao;
    private SQLiteOpenHelper mHelper;

    public void setHelper(SQLiteOpenHelper helper) {
        mHelper = helper;
    }

    private VideoDao() {
    }

    public static VideoDao newInstance() {
        if (sVideoDao == null) {
            synchronized (VideoDao.class) {
                if (sVideoDao == null) {
                    sVideoDao = new VideoDao();
                }
            }
        }
        return sVideoDao;
    }

    /**
     * 插入
     *
     * @param video 
     */
    public void insert(VideoBean video) {
        if (contains(video.getPath())) {
            addPlayCount(video.getPath());
        } else {
            mHelper.getWritableDatabase().execSQL(
                    "INSERT INTO video_player(path,current_pos,last_play_time,play_count) VALUES(?,?,?,?)",
                    new String[]{
                            video.getPath(),
                            video.getCurrent_pos() + "",
                            video.getLast_play_time(),
                            video.getPlay_count() + ""});
        }
    }

    /**
     * 將某視訊播放量+1,並更新時間
     *
     * @param path 視訊路徑
     */
    private void addPlayCount(String path) {
        int count = getPlayCount(path);
        count++;
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA);
        String now = format.format(System.currentTimeMillis());
        mHelper.getWritableDatabase().execSQL(
                "UPDATE video_player SET play_count=? , last_play_time=?",
                new String[]{count + "", now});

    }

    /**
     * 根據路徑獲取播放量
     *
     * @param path 視訊路徑
     * @return 播放量
     */
    private int getPlayCount(String path) {
        int result = 0;
        Cursor cursor = mHelper.getReadableDatabase().
                rawQuery("SELECT play_count FROM video_player WHERE path=?", new String[]{path});
        if (cursor.moveToNext()) {
            result = cursor.getInt(cursor.getColumnIndex("play_count"));
        }
        cursor.close();
        return result;
    }

    /**
     * 檢測是否包含某視訊
     *
     * @param path 視訊路徑
     * @return 否包含某視訊
     */
    public boolean contains(String path) {
        Cursor cursor = mHelper.getReadableDatabase().
                rawQuery("SELECT path FROM video_player WHERE path=?", new String[]{path});
        boolean has = cursor.moveToNext();
        cursor.close();
        return has;
    }
}
複製程式碼

二、使用

1.關於插入

視屏播放器功能由VideoView實現,我上面封了一層VideoPlayerManager用來管理
在每次設定播放資源時插入資料,上面的插入方法在已經有值時,播放次數會 + 1

[- Video篇 -]:記一次SQLite的使用

|--- 在每次設定播放資源時插入 -------------------------------
VideoBean videoBean = new VideoBean();
videoBean.setPath(info.getDataUrl());
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss",Locale.CHINA);
videoBean.setLast_play_time(format.format(System.currentTimeMillis()));
VideoDao.newInstance().insert(videoBean);
複製程式碼

注意點選前後的播放量數字

[- Video篇 -]:記一次SQLite的使用


2.播放進度的記錄

核心在於暫停時儲存進度,在恰當的時機進行 seekTo 和介面資料回顯及渲染
使用MVP來解耦很方便,Presenter中獲取資料庫進度,順便seekTo,
再將進度資料設定給Model,呼叫View的render() 方法進行渲染

[- Video篇 -]:記一次SQLite的使用

---->[VideoView#pause]------------------------------
@Override
public void pause() {
    saveProgress();//儲存進度
    if (canPlay() && mMediaPlayer.isPlaying()) {
        mMediaPlayer.pause();
    }
}

private void saveProgress() {
    int per = (int) (getCurrentPosition() * 1.f / getDuration() * 100);
    VideoDao.newInstance().saveProgress(mUri.getPath(), per);
}


---->[VideoDao#saveProgress]------------------------------
/**
 * 儲存播放進度
 */
public void saveProgress(String path, int per) {
    if (contains(path)) {
        mHelper.getWritableDatabase().execSQL(
                "UPDATE video_player SET current_pos=? WHERE path =?",
                new String[]{per + "", path});
    }
}

---->[VideoDao#getProgress]------------------------------
/**
 * 根據路徑獲取播放進度
 *
 * @param path 視訊路徑
 * @return 播放進度
 */
public int getProgress(String path) {
    int result = 0;
    Cursor cursor = mHelper.getReadableDatabase().
            rawQuery("SELECT current_pos FROM video_player WHERE path=?", new String[]{path});
    if (cursor.moveToNext()) {
        result = cursor.getInt(cursor.getColumnIndex("current_pos"));
    }
    cursor.close();
    return result;
}
複製程式碼

3.最近播放的n條記錄

獲取近期的指定條數播放資料.png

/**
 * 獲取最近播放的記錄
 *
 * @param count 條數
 * @return 最近播放的count條記錄
 */
public String[] getRecent(int count) {
    String[] strings = new String[count];
    Cursor cursor = mHelper.getReadableDatabase().
            rawQuery("SELECT path FROM video_player ORDER BY last_play_time DESC LIMIT ?",
                    new String[]{count + ""});
    int i = 0;
    while (cursor.moveToNext()) {
        String path = cursor.getString(cursor.getColumnIndex("path"));
        strings[i] = path;
        i++;
    }
    cursor.close();
    return strings;
}

|--- 使用 ---------------------------------
VideoDao.newInstance().getRecent(5);

複製程式碼

4.獲取播放最多的n條

[- Video篇 -]:記一次SQLite的使用

/**
 * 獲取播放最多的n條記錄
 *
 * @param count 條數
 * @return 獲取播放最多的n條記錄
 */
public String[] getMost(int count) {
    String[] strings = new String[count];
    Cursor cursor = mHelper.getReadableDatabase().
            rawQuery("SELECT path FROM video_player ORDER BY play_count DESC LIMIT ?",
                    new String[]{count + ""});
    int i = 0;
    while (cursor.moveToNext()) {
        String path = cursor.getString(cursor.getColumnIndex("path"));
        strings[i] = path;
        i++;
    }
    cursor.close();
    return strings;
}
複製程式碼

小插曲:很詭異的一件事,我檢視將這兩個方法封裝成一個

|--- 一開始我是這樣的 ---------------------
/**
 * 獲取最近播放的記錄
 *
 * @param count 條數
 * @return 最近播放的count條記錄
 */
public String[] getLimit(String by, int count) {
    String[] strings = new String[count];
    Cursor cursor = mHelper.getReadableDatabase().
            rawQuery("SELECT path FROM video_player ORDER BY ? DESC LIMIT ?",
                    new String[]{by,count + ""});
    int i = 0;
    while (cursor.moveToNext()) {
        String path = cursor.getString(cursor.getColumnIndex("path"));
        strings[i] = path;
        i++;
    }
    cursor.close();
    return strings;
}

|--- 然後怎麼都搞不出想要的結果,鬱悶... 這裡說一下,問號只能用來傳值,其他的可以拼接字串

/**
 * 獲取最近播放的記錄
 *
 * @param count 條數
 * @return 最近播放的count條記錄
 */
public String[] getLimit(String by, int count) {
    String[] strings = new String[count];
    Cursor cursor = mHelper.getReadableDatabase().
            rawQuery("SELECT path FROM video_player ORDER BY " + by + " DESC LIMIT ?",
                    new String[]{count + ""});
    int i = 0;
    while (cursor.moveToNext()) {
        String path = cursor.getString(cursor.getColumnIndex("path"));
        strings[i] = path;
        i++;
    }
    cursor.close();
    return strings;
}

/**
 * 獲取播放最多的n條記錄
 *
 * @param count 條數
 * @return 獲取播放最多的n條記錄
 */
public String[] getMost(int count) {
    return getLimit("play_count", 3);
}

/**
 * 獲取最近播放的記錄
 *
 * @param count 條數
 * @return 最近播放的count條記錄
 */
public String[] getRecent(int count) {
    return getLimit("last_play_time", count);
}
複製程式碼

想增加其他的記錄,可以自己擴充套件。Over 本篇記錄 就這樣。

相關文章