Ik wil een video opslaan in de sqlite-database. PS Ik wil niet het pad opslaan, maar de eigenlijke video-inhoud.
Tenzij de video's erg kort zijn en weinig ruimte innemen (zeg tot 200k elk, misschien 1/10e van een seconde, maar dit hangt af van het formaat waarin het is opgeslagen), dan zou je waarschijnlijk problemen en uitzonderingen/crashes tegenkomen.
- Het gebruik van een telefoon van ongeveer 2 seconden zwart nam 2,2 MB in beslag, 2 seconden voor het daadwerkelijk opnemen van een video nam 7 MB in beslag.
Hoewel SQLite de mogelijkheid heeft om relatief grote BLOB's op te slaan volgens:-
Maximale lengte van een string of BLOB
Het maximum aantal bytes in een string of BLOB in SQLite wordt gedefinieerd door de preprocessor-macro SQLITE_MAX_LENGTH. De standaardwaarde van deze macro is 1 miljard (1 duizend miljoen of 1.000.000.000). U kunt deze waarde tijdens het compileren verhogen of verlagen met een opdrachtregeloptie zoals deze:
-DSQLITE_MAX_LENGTH=123456789 De huidige implementatie ondersteunt alleen een string of BLOB-lengte tot 231-1 of 2147483647. En sommige ingebouwde functies, zoals hex() kunnen ruim voor dat punt mislukken. Bij onveiligheidsgevoelige toepassingen kunt u het beste niet proberen de maximale tekenreeks- en bloblengte te vergroten. In feite zou je er goed aan kunnen doen om de maximale string- en blob-lengte te verlagen tot iets meer in het bereik van een paar miljoen als dat mogelijk is.
Tijdens een deel van SQLite's INSERT- en SELECT-verwerking wordt de volledige inhoud van elke rij in de database gecodeerd als een enkele BLOB. De parameter SQLITE_MAX_LENGTH bepaalt dus ook het maximum aantal bytes in een rij.
De maximale tekenreeks- of BLOB-lengte kan tijdens runtime worden verlaagd met behulp van de interface thesqlite3_limit(db,SQLITE_LIMIT_LENGTH,size). Limieten in SQLite
CursorWindow . van de Android SDK heeft een limiet van 2Mb en dat is voor alle kolommen van de rij(en) als buffers. Zelfs als je Video's met succes kunt opslaan, is het mogelijk dat je die Video's niet kunt ophalen.
De aanbevolen manier is wat je niet wilt, namelijk het pad naar de video opslaan.
Als ik de video opsla in mijn interne/externe opslag en in plaats daarvan het pad opsla, hoe kan ik dan vanaf een ander apparaat toegang krijgen tot dezelfde video.
U zou hetzelfde probleem hebben met de database omdat het doorgaans wordt opgeslagen in de applicatiegegevens die beschermd zijn. Tenzij de database een reeds bestaande database is (d.w.z. gevuld met gegevens), in welk geval de database via de APK met de App wordt gedistribueerd.
Als het laatste een reeds bestaande database is die via de APK wordt gedistribueerd, dan kunnen de video's ook als onderdeel van de APK worden gedistribueerd en dus even beschermd en zichtbaar als de database.
Als het je bedoeling is om video's te distribueren tussen apparaten die geen deel uitmaken van de APK, dan is SQlite waarschijnlijk niet de juiste oplossing omdat het een embedded database is en er geen ingebouwde client/server-functionaliteit is.
Behalve wat als mijn apparaat wordt geformatteerd, dan verlies ik alle gegevens.
In een dergelijk scenario zou de database net zo kwetsbaar zijn als alle andere gegevens , want dat is alles wat de database is, een bestand, net als een video, een Word-document enz. die allemaal een geschikte toepassing nodig hebben om de inhoud te bekijken/wijzigen. Als de database echter een reeds bestaande database is, zou het eenvoudigweg opnieuw installeren van de app de database en andere bestanden van de APK herstellen.
Werkvoorbeeld
Hierbij wordt de voorgestelde/aanbevolen methode gebruikt, ervan uitgaande dat de video's met de APK moeten worden gedistribueerd.
- Noteer video's met dank aan Voorbeeldvideo's
Na het maken van een nieuw project werden 4 video's gedownload en gekopieerd naar de res/raw-map (na het maken van de raw-map) volgens:-
De Database Helper (subklasse van SQLiteOpenHelper) is gemaakt voor een tabel met 2 kolommen en met- _id kolom (notitie met de naam _id voor gebruik met SimpleCursorAdapter ).- video_path voor het opslaan van het pad/de naam van de video (niet het volledige pad maar voldoende om het pad te kunnen bepalen uit de opgeslagen gegevens)- Opmerking UNIEK is gecodeerd om te voorkomen dat er dubbele worden toegevoegd.
Met een basismethode om rijen toe te voegen en te verwijderen en om alle rijen te extraheren (via een cursor voor gebruik met de 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);
}
}
Een vrij eenvoudige MainActivity.java (zie opmerkingen)
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());
}
}
}
Resultaten
Toen het voor het eerst begon (er wordt niets afgespeeld):-
Na lang op de 1Mb-video te hebben geklikt (het DB-item verwijderen):-
Na het klikken op een video in de lijst:-