Jeg vil gemme en video i sqlite database. P.S. Jeg ønsker ikke at gemme stien, men det faktiske videoindhold.
Medmindre videoerne er meget korte og fylder lidt (f.eks. op til 200k hver, måske 1/10 af et sekund, men det vil afhænge af det format, de er gemt i), vil du sandsynligvis støde på problemer og undtagelser/nedbrud.
- At bruge en telefon omkring 2 sekunders sort optog 2,2 Mb, 2 sekunders faktisk optagelse af en video tog 7 Mb.
Selvom SQLite har evnen til at gemme relativt store BLOB'er som pr. :-
Maksimal længde af en streng eller BLOB
Det maksimale antal bytes i en streng eller BLOB i SQLite er defineret af præprocessormakroen SQLITE_MAX_LENGTH. Standardværdien af denne makro er 1 milliard (1 tusind millioner eller 1.000.000.000). Du kan hæve eller sænke denne værdi på kompileringstidspunktet ved at bruge en kommandolinjemulighed som denne:
-DSQLITE_MAX_LENGTH=123456789 Den aktuelle implementering understøtter kun en streng eller BLOB-længde op til 231-1 eller 2147483647. Og nogle indbyggede funktioner som hex() kan mislykkes længe før det tidspunkt. Usikkerhedsfølsomme applikationer er det bedst ikke at forsøge at øge den maksimale streng- og klatlængde. Faktisk kan du gøre klogt i at sænke den maksimale streng- og klatlængde til noget mere i størrelsesordenen nogle få millioner, hvis det er muligt.
Under en del af SQLites INSERT- og SELECT-behandling bliver det komplette indhold af hver række i databasen kodet som en enkelt BLOB. Så parameteren SQLITE_MAX_LENGTH bestemmer også det maksimale antal bytes i en række.
Den maksimale streng- eller BLOB-længde kan sænkes under kørsel ved hjælp af grænsefladen thesqlite3_limit(db,SQLITE_LIMIT_LENGTH,size). Grænser i SQLite
Android SDK's CursorWindow har en begrænsning på 2Mb, og det er for alle kolonnerne i rækken/rækkerne hvis buffere. Som sådan, selvom du kan gemme videoer med succes, er du muligvis ikke i stand til at hente disse videoer.
Den anbefalede måde er, hvad du ikke ønsker, det er at gemme stien til videoen.
Hvis jeg gemmer videoen i mit interne/eksterne lager og gemmer stien i stedet for, hvordan vil jeg så kunne få adgang til den samme fra en anden enhed.
Du ville have det samme problem med databasen da det typisk er gemt i applikationsdataene, som er beskyttet. Det er, medmindre databasen er en allerede eksisterende database (dvs. udfyldt med data), i hvilket tilfælde databasen distribueres med appen via APK'en.
Hvis sidstnævnte er en allerede eksisterende database distribueret via APK'en, så kan videoerne også distribueres som en del af APK'en og dermed lige så beskyttet som og så synlig som databasen.
Hvis din hensigt er at distribuere videoer mellem enheder, der ikke er en del af APK'en, er SQlite sandsynligvis ikke den rigtige løsning, da det er en indlejret database og ikke har nogen klient/server-funktionalitet indbygget.
Udover hvad hvis min enhed bliver formateret, så mister jeg alle data.
I et sådant scenario ville databasen være lige så sårbar som alle andre data , da det er alt, databasen er, en fil, ligesom en video, et word-dokument osv., som alle har brug for et passende program for at se/ændre indholdet. Men hvis databasen er en allerede eksisterende database, vil blot geninstallation af appen gendanne databasen og andre filer fra APK'en.
Arbejdseksempel
Dette bruger den foreslåede/anbefalede metode, forudsat at videoerne skal distribueres med APK'en.
- Bemærk videoer med tilladelse fra Eksempelvideoer
Efter oprettelse af nyt projekt blev 4 videoer downloadet og kopieret til res/raw-mappen (efter oprettelse af raw-mappen) som pr. :-
Databasehjælperen (underklasse af SQLiteOpenHelper) blev oprettet til en tabel med 2 kolonner og med- _id kolonne (note med navnet _id til brug med SimpleCursorAdapter ).- video_path til lagring af stien/navnet på videoen (ikke den fulde sti, men tilstrækkelig til at kunne bestemme stien ud fra de lagrede data)- Bemærk UNIQUE er blevet kodet for at stoppe dubletter, der tilføjes.
Med en eller anden grundlæggende metode til at tillade rækker at blive tilføjet og slettet og for alle rækker at blive udtrukket (via en markør til brug med 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);
}
}
En ret ligetil MainActivity.java (se kommentarer)
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());
}
}
}
Resultater
Da første gang startede (intet spiller) :-
Efter langt klik på 1 Mb-videoen (sletning af DB-posten):-
Efter at have klikket på En video på listen :-