Искам да съхраня видео в sqlite база данни. P.S. Не искам да съхранявам пътя, а действителното видео съдържание.
Освен ако видеоклиповете не са много кратки и заемат малко място (да речем до 200k всеки, може би 1/10 от секундата, но това зависи от формата, в който са записани), вероятно ще срещнете проблеми и изключения/сривове.
- Използването на телефон около 2 секунди черно заеха 2,2Mb, 2 секунди действително записване на видеоклип заеха 7Mb.
Въпреки че SQLite има способността да съхранява относително големи BLOB според :-
Максимална дължина на низ или BLOB
Максималният брой байтове в низ или BLOB в SQLite се дефинира от макроса на препроцесора SQLITE_MAX_LENGTH. Стойността по подразбиране на този макрос е 1 милиард (1 хиляда милион или 1 000 000 000). Можете да увеличите или намалите тази стойност по време на компилиране, като използвате опция от командния ред, като тази:
-DSQLITE_MAX_LENGTH=123456789 Текущата реализация ще поддържа само низ или BLOB дължина до 231-1 или 2147483647. И някои вградени функции като hex() може да се провалят доста преди тази точка. Приложения, чувствителни към несигурност, най-добре е да не се опитвате да увеличавате максималната дължина на низа и петна. Всъщност може да е добре да намалите максималната дължина на низа и блобовете до нещо повече в диапазона от няколко милиона, ако това е възможно.
По време на част от обработката INSERT и SELECT на SQLite, пълното съдържание на всеки ред в базата данни се кодира като един BLOB. Така параметърът SQLITE_MAX_LENGTH също така определя максималния брой байтове в ред.
Максималната дължина на низ или BLOB може да бъде намалена по време на изпълнение с помощта на интерфейса sqlite3_limit(db,SQLITE_LIMIT_LENGTH,size). Ограничения в SQLite
CursorWindow на Android SDK има ограничение от 2Mb и това е за всички колони на ред(ове) if буфери. По този начин, дори ако можете да съхранявате видеоклипове успешно, може да не сте в състояние да извлечете тези видеоклипове.
Препоръчителният начин е това, което не искате, тоест да съхраните пътя към видеоклипа.
Ако съхраня видеото във вътрешното/външно хранилище и вместо това запазя пътя, тогава как ще мога да осъществя достъп до същото от друго устройство.
Ще имате същия проблем с базата данни тъй като обикновено се съхранява в данните на приложенията, които са защитени. Това е, освен ако базата данни не е вече съществуваща база данни (т.е. попълнена с данни), в който случай базата данни се разпространява с приложението чрез APK.
Ако последното е вече съществуваща база данни, разпространявана чрез APK, тогава видеоклиповете също могат да се разпространяват като част от APK и следователно толкова защитени, колкото и достъпни като базата данни.
Ако намерението ви е да разпространявате видеоклипове между устройства, които не са част от APK, тогава SQlite вероятно не е правилното решение, тъй като е вградена база данни и няма вградена клиент/сървър функционалност.
Освен това, ако устройството ми бъде форматирано, ще загубя всички данни.
В такъв сценарий базата от данни би била също толкова уязвима, колкото всички други данни , тъй като това е всичко, което базата данни е, файл, точно като видео, текстов документ и т.н., които се нуждаят от подходящо приложение за преглед/промяна на съдържанието. Въпреки това, ако базата данни е вече съществуваща база данни, тогава просто повторното инсталиране на приложението ще възстанови базата данни и други файлове от APK.
Работен пример
Това използва метода Suggested/Recommended, като се приема, че видеоклиповете трябва да се разпространяват с APK.
- Забележка на видеоклипове с любезното съдействие на Примерни видеоклипове
След създаването на нов проект 4 видеоклипа бяха изтеглени и копирани в папката res/raw (след създаването на raw папката) съгласно :-
Помощникът за база данни (подклас на SQLiteOpenHelper) е създаден за таблица с 2 колони с _id колона (бележка с име _id за използване с SimpleCursorAdapter ).- video_path за съхраняване на пътя/името на видеоклипа (не пълният път, но достатъчен, за да може да се определи пътя от съхранените данни)- Забележка УНИКАЛНО е кодирано, за да спре добавянето на дубликати.
С някакъв основен метод, позволяващ добавянето и изтриването на редове и извличането на всички редове (чрез курсор за използване с SimpleCursorAdapter).
DBHelper.java
public class DBHelper extends SQLiteOpenHelper {
public static final String DBNAME = "myvideos";
public static final int DBVERSION = 1;
public static final String TBL_VIDEO = "video";
public static final String COL_VIDEO_ID = BaseColumns._ID;
public static final String COL_VIDEO_PATH = "video_path";
SQLiteDatabase mDB;
public DBHelper(Context context) {
super(context, DBNAME, null, DBVERSION);
mDB = this.getWritableDatabase();
}
@Override
public void onCreate(SQLiteDatabase db) {
String crt_video_table = "CREATE TABLE IF NOT EXISTS " + TBL_VIDEO + "(" +
COL_VIDEO_ID + " INTEGER PRIMARY KEY," +
COL_VIDEO_PATH + " TEXT UNIQUE" +
")";
db.execSQL(crt_video_table);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
public long addVideo(String path) {
ContentValues cv = new ContentValues();
cv.put(COL_VIDEO_PATH,path);
return mDB.insert(TBL_VIDEO,null,cv);
}
public Cursor getVideos() {
return mDB.query(TBL_VIDEO,null,null,null,null,null,null);
}
public int deleteVideoFromDB(long id) {
String whereclause = COL_VIDEO_ID + "=?";
String[] whereargs = new String[]{String.valueOf(id)};
return mDB.delete(TBL_VIDEO,whereclause,whereargs);
}
}
Доста прост MainActivity.java (виж коментарите)
public class MainActivity extends AppCompatActivity {
TextView mMyTextView;
ListView mVideoList;
VideoView mVideoViewer;
DBHelper mDBHlpr;
Cursor mCsr;
SimpleCursorAdapter mSCA;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMyTextView = this.findViewById(R.id.mytext);
mVideoList = this.findViewById(R.id.videolist);
mVideoViewer = this.findViewById(R.id.videoviewer);
mDBHlpr = new DBHelper(this);
addVideosFromRawResourceToDB();
}
@Override
protected void onDestroy() {
mCsr.close(); //<<<<<<<<<< clear up the Cursor
super.onDestroy();
}
@Override
protected void onResume() {
super.onResume();
manageListView(); //<<<<<<<<<< rebuild and redisplay the List of Videos (in case they have changed)
}
/**
* Setup or Refresh the ListView adding the OnItemClick and OnItemLongClick listeners
*/
private void manageListView() {
mCsr = mDBHlpr.getVideos();
// Not setup so set it up
if (mSCA == null) {
// Instantiate the SimpleCursorAdapter
mSCA = new SimpleCursorAdapter(
this,
android.R.layout.simple_list_item_1, // Use stock layout
mCsr, // The Cursor with the list of videos
new String[]{DBHelper.COL_VIDEO_PATH}, // the column (columns)
new int[]{android.R.id.text1}, // the view id(s) into which the column(s) data will be placed
0
);
mVideoList.setAdapter(mSCA); // Set the adpater for the ListView
/**
* Add The Long Click Listener (will delete the video row from the DB (NOT the video))
*/
mVideoList.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
mDBHlpr.deleteVideoFromDB(id);
manageListView(); // <<<<<<<<<< refresh the ListView as data has changed
return true;
}
});
/**
* Play the respective video when the item is clicked
* Note Cursor should be at the correct position so data can be extracted directly from the Cursor
*/
mVideoList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
setCurrentVideo(mCsr.getString(mCsr.getColumnIndex(DBHelper.COL_VIDEO_PATH)));
}
});
} else {
mSCA.swapCursor(mCsr); //<<<<<<<<<< apply the changed Cursor
}
}
/**
* Set the currrent video and play it
* @param path the path (resource name of the video)
*/
private void setCurrentVideo(String path) {
mVideoViewer.setVideoURI(
Uri.parse(
"android.resource://" + getPackageName() + "/" + String.valueOf(
getResources().getIdentifier(
path,
"raw",
getPackageName())
)
)
);
mVideoViewer.start();
}
/**
* Look at all the resources in the res/raw folder and add the to the DB (not if they are duplicates due to UNQIUE)
*/
private void addVideosFromRawResourceToDB() {
Field[] fields=R.raw.class.getFields();
for(int count=0; count < fields.length; count++){
Log.i("Raw Asset: ", fields[count].getName());
mDBHlpr.addVideo(fields[count].getName());
}
}
}
Резултати
При първото стартиране (нищо не се възпроизвежда) :-
След дълго щракване върху 1Mb видео (изтриване на записа в DB) :-
След като щракнете върху видеоклип в списъка :-