Eu quero armazenar um vídeo no banco de dados sqlite. P.S. Eu não quero armazenar o caminho, mas o conteúdo real do vídeo.
A menos que os vídeos sejam muito curtos e ocupem pouco espaço (digamos até 200k cada, talvez 1/10 de segundo, mas depende do formato em que são salvos), você provavelmente encontrará problemas e exceções/travamentos.
- Usar um telefone em torno de 2 segundos de preto consumia 2,2 Mb, enquanto 2 segundos de gravação de vídeo consumia 7 Mb.
Embora o SQLite tenha a capacidade de armazenar BLOBs relativamente grandes conforme:-
Comprimento máximo de uma string ou BLOB
O número máximo de bytes em uma string ou BLOB no SQLite é definido pela macro do pré-processador SQLITE_MAX_LENGTH. O valor padrão dessa macro é 1 bilhão (1 bilhão ou 1.000.000.000). Você pode aumentar ou diminuir esse valor em tempo de compilação usando uma opção de linha de comando como esta:
-DSQLITE_MAX_LENGTH=123456789 A implementação atual suportará apenas uma string ou comprimento BLOB até 231-1 ou 2147483647. E algumas funções internas como hex() podem falhar bem antes desse ponto. Aplicativos sensíveis à insegurança, é melhor não tentar aumentar a string máxima e o comprimento do blob. Na verdade, você pode fazer bem em diminuir o comprimento máximo da string e do blob para algo mais na faixa de alguns milhões, se isso for possível.
Durante parte do processamento INSERT e SELECT do SQLite, o conteúdo completo de cada linha no banco de dados é codificado como um único BLOB. Assim, o parâmetro SQLITE_MAX_LENGTH também determina o número máximo de bytes em uma linha.
O comprimento máximo de string ou BLOB pode ser reduzido em tempo de execução usando a interface sqlite3_limit(db,SQLITE_LIMIT_LENGTH,size).Limites no SQLite
A CursorWindow do SDK do Android tem uma limitação de 2Mb e isso é para todas as colunas da(s) linha(s) se buffers. Assim, mesmo que você consiga armazenar vídeos com sucesso, talvez não consiga recuperar esses vídeos.
A maneira recomendada é o que você não deseja, ou seja, armazenar o caminho para o vídeo.
Se eu armazenar o vídeo no meu armazenamento interno/externo e armazenar o caminho, como poderei acessar o mesmo de outro dispositivo.
Você teria o mesmo problema com o banco de dados como normalmente é armazenado nos dados de aplicativos que são protegidos. Ou seja, a menos que o banco de dados seja um banco de dados pré-existente (ou seja, preenchido com dados), caso em que o banco de dados é distribuído com o aplicativo por meio do APK.
Se este último, um banco de dados pré-existente distribuído através do APK, os vídeos também podem ser distribuídos como parte do APK e, portanto, tão protegidos e exponíveis quanto o banco de dados.
Se sua intenção é distribuir vídeos entre dispositivos que não fazem parte do APK, o SQlite provavelmente não é a solução correta, pois é um banco de dados incorporado e não possui funcionalidade cliente/servidor incorporada.
Além disso, se meu dispositivo for formatado, perderei todos os dados.
Nesse cenário, o banco de dados seria tão vulnerável quanto qualquer outro dado , pois isso é tudo o que o banco de dados é, um arquivo, assim como um vídeo, um documento do Word etc., todos precisam de um aplicativo adequado para visualizar/alterar o conteúdo. No entanto, se o banco de dados for um banco de dados pré-existente, basta reinstalar o aplicativo para restaurar o banco de dados e outros arquivos do APK.
Exemplo de trabalho
Isso usa o método sugerido/recomendado, supondo que os vídeos sejam distribuídos com o APK.
- Observar vídeos cortesia de vídeos de amostra
Após a criação do novo projeto, 4 vídeos foram baixados e copiados para a pasta res/raw (depois de criar a pasta raw) conforme :-
O Database Helper (subclasse de SQLiteOpenHelper) foi criado para uma tabela de 2 colunas com- _id coluna (nota chamada _id para uso com SimpleCursorAdapter ).- video_path para armazenar o caminho/nome do vídeo (não o caminho completo, mas suficiente para poder determinar o caminho dos dados armazenados)- Nota UNIQUE foi codificado para impedir que duplicatas sejam adicionadas.
Com algum método básico para permitir que linhas sejam adicionadas e excluídas e que todas as linhas sejam extraídas (por meio de um Cursor para uso com o 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);
}
}
Um MainActivity.java bastante simples (Ver comentários)
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());
}
}
}