best way to manage a history from both activity and service? - persistence

Short version: what is the best practice way to access maintain a history for certain messages from both an activity and from a service?
Long version:
I have an activity and a service, which both may be running or not. I want to keep a message log (history) in an object an persist it in a file and be able to e.g. delete entries.
When I have such history in the service and one in the activity I run into sync problems. So, any advice, what the best solution would be?
ideally I could use the methods from the history class in both the service and activity. Probably not possible.
I could write and read the file in each action. Probably not very efficient in the long run.
do I really need to setup a service for the history and handle all actions with it via intents?
It is a bit similiar to "proper way to access DB from both Activity and a started Service?", but with just an own class instead of a SQLite DB.
Any advice?
Conclusion: Use a ContentProvider with a SQLite-DB. Short version of the code:
package com.example.history;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
public class HistoryContentProvider extends ContentProvider {
static final String PROVIDER_NAME = "com.example.HistoryContentProvider";
static final String URL = "content://" + PROVIDER_NAME + "/history";
static final Uri CONTENT_URI = Uri.parse(URL);
static final String id = "id";
static final String normalized_number = "normalized_number";
static final String display_name = "display_name";
static final int uriCode = 1;
static final UriMatcher uriMatcher;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(PROVIDER_NAME, "history", uriCode);
}
#Override
public boolean onCreate() {
Context context = getContext();
DatabaseHelper dbHelper = new DatabaseHelper(context);
db = dbHelper.getWritableDatabase();
if (db != null) {
return true;
}
return false;
}
#Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables(TABLE_NAME);
Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, sortOrder);
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}
#Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)) {
case uriCode:
return "vnd.android.cursor.dir/history";
default:
throw new IllegalArgumentException("Unsupported URI: " + uri);
} }
#Override
public Uri insert(Uri uri, ContentValues values) {
long rowID = db.insert(TABLE_NAME, "", values);
if (rowID > 0) {
Uri _uri = ContentUris.withAppendedId(CONTENT_URI, rowID);
getContext().getContentResolver().notifyChange(_uri, null);
return _uri;
}
throw new SQLException("Failed to add a record into " + uri);
}
#Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int count = 0;
switch (uriMatcher.match(uri)) {
case uriCode:
count = db.delete(TABLE_NAME, selection, selectionArgs);
getContext().getContentResolver().notifyChange(uri, null);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
return count;
}
#Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
int count = 0;
switch (uriMatcher.match(uri)) {
case uriCode:
count = db.update(TABLE_NAME, values, selection, selectionArgs);
getContext().getContentResolver().notifyChange(uri, null);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
return count;
}
private SQLiteDatabase db;
static final String DATABASE_NAME = "historyDb";
static final String TABLE_NAME = "history";
static final int DATABASE_VERSION = 3;
static final String CREATE_DB_TABLE = " CREATE TABLE " + TABLE_NAME
+ " (id INTEGER PRIMARY KEY AUTOINCREMENT, "
+ normalized_number + " TEXT NOT NULL, "
+ display_name + " TEXT NOT NULL, ";
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_DB_TABLE);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
onCreate(db);
}
}
}

I have an activity and a service, which both may be running or not. I
want to keep a message log (history) in an object an persist it in a
file and be able to e.g. delete entries.
What you are describing there sounds exactly like a ContentProvider! Link to documentation.
You can use a ContentResolver instance to access data in the ContentProvider from anywhere, be it Activity or Service. The ContentProvider and ContentResolver already handle most of the work for you and basically you just need to implement how you want to save the data in the ContentProvider. The rest is already taken care of! The ContentProvider may have been designed to be used with a SQLiteDatabase - and I would recommend that you use a database - but there is nothing preventing you from saving the data in another way.

If you are not looking for DB style persistence, then maybe a Queue with File backed persistence is what you are looking for:
This maybe of use
https://github.com/square/tape/blob/master/tape/src/main/java/com/squareup/tape/QueueFile.java
Tip: Create a QueueFile singleton in your App class, and access it from your Activities or services.

Related

Replacement for "GROUP BY" in ContentResolver query in Android Q ( Android 10, API 29 changes)

I'm upgrading some legacy to target Android Q, and of course this code stop working:
String[] PROJECTION_BUCKET = {MediaStore.Images.ImageColumns.BUCKET_ID,
MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME,
MediaStore.Images.ImageColumns.DATE_TAKEN,
MediaStore.Images.ImageColumns.DATA,
"COUNT(" + MediaStore.Images.ImageColumns._ID + ") AS COUNT",
MediaStore.Files.FileColumns.MEDIA_TYPE,
MediaStore.MediaColumns._ID};
String BUCKET_GROUP_BY = " 1) and " + BUCKET_WHERE.toString() + " GROUP BY 1,(2";
cur = context.getContentResolver().query(images, PROJECTION_BUCKET,
BUCKET_GROUP_BY, null, BUCKET_ORDER_BY);
android.database.sqlite.SQLiteException: near "GROUP": syntax error (code 1 SQLITE_ERROR[1])
Here it supposed to obtain list of images with album name, date, count of pictures - one image for each album, so we can create album picker screen without querying all pictures and loop through it to create albums.
Is it possible to group query results with contentResolver since SQL queries stoped work?
(I know that ImageColumns.DATA and "COUNT() AS COUNT" are deprecated too, but this is a question about GROUP BY)
(There is a way to query albums and separately query photo, to obtain photo uri for album cover, but i want to avoid overheads)
Unfortunately Group By is no longer supported in Android 10 and above, neither any aggregated functions such as COUNT. This is by design and there is no workaround.
The solution is what you are actually trying to avoid, which is to query, iterate, and get metrics.
To get you started you can use the next snipped, which will resolve the buckets (albums), and the amount of records in each one.
I haven't added code to resolve the thumbnails, but is easy. You must perform a query for each bucket Id from all the Album instances, and use the image from the first record.
public final class AlbumQuery
{
#NonNull
public static HashMap<String, AlbumQuery.Album> get(#NonNull final Context context)
{
final HashMap<String, AlbumQuery.Album> output = new HashMap<>();
final Uri contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
final String[] projection = {MediaStore.Images.Media.BUCKET_DISPLAY_NAME, MediaStore.Images.Media.BUCKET_ID};
try (final Cursor cursor = context.getContentResolver().query(contentUri, projection, null, null, null))
{
if ((cursor != null) && (cursor.moveToFirst() == true))
{
final int columnBucketName = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.BUCKET_DISPLAY_NAME);
final int columnBucketId = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.BUCKET_ID);
do
{
final String bucketId = cursor.getString(columnBucketId);
final String bucketName = cursor.getString(columnBucketName);
if (output.containsKey(bucketId) == false)
{
final int count = AlbumQuery.getCount(context, contentUri, bucketId);
final AlbumQuery.Album album = new AlbumQuery.Album(bucketId, bucketName, count);
output.put(bucketId, album);
}
} while (cursor.moveToNext());
}
}
return output;
}
private static int getCount(#NonNull final Context context, #NonNull final Uri contentUri, #NonNull final String bucketId)
{
try (final Cursor cursor = context.getContentResolver().query(contentUri,
null, MediaStore.Images.Media.BUCKET_ID + "=?", new String[]{bucketId}, null))
{
return ((cursor == null) || (cursor.moveToFirst() == false)) ? 0 : cursor.getCount();
}
}
public static final class Album
{
#NonNull
public final String buckedId;
#NonNull
public final String bucketName;
public final int count;
Album(#NonNull final String bucketId, #NonNull final String bucketName, final int count)
{
this.buckedId = bucketId;
this.bucketName = bucketName;
this.count = count;
}
}
}
This is a more efficient(not perfect) way to do that.
I am doing it for videos, but doing so is the same for images to. just change MediaStore.Video.Media.X to MediaStore.Images.Media.X
public class QUtils {
/*created by Nasib June 6, 2020*/
#RequiresApi(api = Build.VERSION_CODES.Q)
public static ArrayList<FolderHolder> loadListOfFolders(Context context) {
ArrayList<FolderHolder> allFolders = new ArrayList<>();//list that we need
HashMap<Long, String> folders = new HashMap<>(); //hashmap to track(no duplicates) folders by using their ids
String[] projection = {MediaStore.Video.Media._ID,
MediaStore.Video.Media.BUCKET_ID,
MediaStore.Video.Media.BUCKET_DISPLAY_NAME,
MediaStore.Video.Media.DATE_ADDED};
ContentResolver CR = context.getContentResolver();
Uri root = MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL);
Cursor c = CR.query(root, projection, null, null, MediaStore.Video.Media.DATE_ADDED + " desc");
if (c != null && c.moveToFirst()) {
int folderIdIndex = c.getColumnIndexOrThrow(MediaStore.Video.Media.BUCKET_ID);
int folderNameIndex = c.getColumnIndexOrThrow(MediaStore.Video.Media.BUCKET_DISPLAY_NAME);
int thumbIdIndex = c.getColumnIndexOrThrow(MediaStore.Video.Media._ID);
int dateAddedIndex = c.getColumnIndexOrThrow(MediaStore.Video.Media.DATE_ADDED);
do {
Long folderId = c.getLong(folderIdIndex);
if (folders.containsKey(folderId) == false) { //proceed only if the folder data has not been inserted already :)
long thumbId = c.getLong(thumbIdIndex);
String folderName = c.getString(folderNameIndex);
String dateAdded = c.getString(dateAddedIndex);
Uri thumbPath = ContentUris.withAppendedId(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, thumbId);
folders.put(folderId, folderName);
allFolders.add(new FolderHolder(String.valueOf(thumbPath), folderName, dateAdded));
}
} while (c.moveToNext());
c.close(); //close cursor
folders.clear(); //clear the hashmap becuase it's no more useful
}
return allFolders;
}
}
FolderHolder model class
public class FolderHolder {
private String folderName;
public long dateAdded;
private String thumbnailPath;
public long folderId;
public void setPath(String thumbnailPath) {
this.thumbnailPath = thumbnailPath;
}
public String getthumbnailPath() {
return thumbnailPath;
}
public FolderHolder(long folderId, String thumbnailPath, String folderName, long dateAdded) {
this.folderId = folderId;
this.folderName = folderName;
this.thumbnailPath = thumbnailPath;
this.dateAdded = dateAdded;
}
public String getFolderName() {
return folderName;
}
}
GROUP_BY supporting in case of using Bundle:
val bundle = Bundle().apply {
putString(
ContentResolver.QUERY_ARG_SQL_SORT_ORDER,
"${MediaStore.MediaColumns.DATE_MODIFIED} DESC"
)
putString(
ContentResolver.QUERY_ARG_SQL_GROUP_BY,
MediaStore.Images.ImageColumns.BUCKET_ID
)
}
contentResolver.query(
uri,
arrayOf(
MediaStore.Images.ImageColumns.BUCKET_ID,
MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME,
MediaStore.Images.ImageColumns.DATE_TAKEN,
MediaStore.Images.ImageColumns.DATA
),
bundle,
null
)

Database update without data loss. FATAL EXCEPTION: ModernAsyncTask #1

I need to implement an update of the database lying in the assets. User data, namely, in the "favorite" record or not, should be saved.
I already asked a question and they helped me -https://stackoverflow.com/a/53827525/10261947
Everything worked in a test application. But when I transferred the code (exactly the same) to the real application, an error occurs - E/AndroidRuntime: FATAL EXCEPTION: ModernAsyncTask #1
Process: rodionova.lyubov.brodsky, PID: 4196
java.lang.RuntimeException: An error occurred while executing doInBackground()
at android.support.v4.content.ModernAsyncTask$3.done(ModernAsyncTask.java:161)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:383)
at java.util.concurrent.FutureTask.setException(FutureTask.java:252)
at java.util.concurrent.FutureTask.run(FutureTask.java:271)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:784)
Caused by: java.lang.IllegalArgumentException: the bind value at index 4 is null
at android.database.sqlite.SQLiteProgram.bindString(SQLiteProgram.java:169)
at android.database.sqlite.SQLiteProgram.bindAllArgsAsStrings(SQLiteProgram.java:205)
at android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:47)
at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1397)
at android.database.sqlite.SQLiteDatabase.queryWithFactory(SQLiteDatabase.java:1239)
at android.database.sqlite.SQLiteDatabase.query(SQLiteDatabase.java:1110)
at android.database.sqlite.SQLiteDatabase.query(SQLiteDatabase.java:1278)
at rodionova.lyubov.brodsky.db.PoemsDbHelper.insertCorePoem(PoemsDbHelper.java:121)
at rodionova.lyubov.brodsky.db.PoemsDbHelper.getNewPoems(PoemsDbHelper.java:90)
at rodionova.lyubov.brodsky.db.PoemsDbHelper.onUpgrade(PoemsDbHelper.java:41)
at com.readystatesoftware.sqliteasset.SQLiteAssetHelper.getWritableDatabase(SQLiteAssetHelper.java:197)
at com.readystatesoftware.sqliteasset.SQLiteAssetHelper.getReadableDatabase(SQLiteAssetHelper.java:254)
at rodionova.lyubov.brodsky.db.PoemsProvider.query(PoemsProvider.java:45)
at android.content.ContentProvider.query(ContentProvider.java:1057)
If you do not perform the update, the application is working properly, so I will post only the code DbHelper
public class PoemsDbHelper extends SQLiteAssetHelper {
public static final String DB_NAME = "brodsky.db";
public static final int DBVERSION = 3;
public static final String TBLNAME = "poems_table";
public static final String COL_ID = "id";
public static final String COL_TITLE = "title";
public static final String COl_POEM = "poem";
public static final String COL_SUBJECT = "subject";
public static final String COL_YEARS = "years";
public static final String COL_FAVOURITE = "favorite";
Context mContext;
public PoemsDbHelper(Context context) {
super(context, DB_NAME, null, DBVERSION);
mContext = context;
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if(newVersion > oldVersion)
getNewPoems(mContext, db);
}
private void getNewPoems(Context context, SQLiteDatabase db) {
InputStream is;
OutputStream os;
final String tempNewDbName = "temp_brodsky.db";
int buffersize = 4096;
byte[] buffer = new byte[buffersize];
String newDBPath = mContext.getDatabasePath(tempNewDbName).getPath();
File newDBFile = new File(newDBPath);
if (newDBFile.exists()) {
newDBFile.delete();
}
File newDBFileDirectory = newDBFile.getParentFile();
if (!newDBFileDirectory.exists()) {
newDBFileDirectory.mkdirs();
}
try {
is = context.getAssets().open("databases/" + DB_NAME);
os = new FileOutputStream(newDBFile);
int bytes_read;
while ((bytes_read = is.read(buffer,0,buffersize)) > 0) {
os.write(buffer);
}
os.flush();
os.close();
is.close();
}catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("Ouch updated database not copied - processing stopped - see stack-trace above.");
}
long id = maxid(db) + 1;
SQLiteDatabase newdb = SQLiteDatabase.openDatabase(newDBFile.getPath(),null,SQLiteDatabase.OPEN_READONLY);
Cursor csr = newdb.query(TBLNAME,null,null,null,null,null,null);
long insert_result;
db.beginTransaction();
while (csr.moveToNext()) {
insert_result = insertCorePoem(
db,
id,
csr.getString(csr.getColumnIndex(COL_TITLE)),
csr.getString(csr.getColumnIndex(COl_POEM)),
csr.getString(csr.getColumnIndex(COL_SUBJECT)),
csr.getString(csr.getColumnIndex(COL_YEARS)),
csr.getString(csr.getColumnIndex(COL_FAVOURITE))
);
if (insert_result > 0) {
id++;
}
}
db.setTransactionSuccessful();
db.endTransaction();
csr.close();
newDBFile.delete();
}
public long insertCorePoem(SQLiteDatabase db, long id, String title, String poem, String subject, String years, String favourite) {
String whereclause = COL_TITLE + "=? AND " + COl_POEM + "=? AND " + COL_SUBJECT + "=? AND " + COL_YEARS + "=?";
String[] whereargs = new String[]{
title,
poem,
subject,
years
};
Cursor csr = db.query(TBLNAME,null,whereclause,whereargs,null,null,null);
boolean rowexists = (csr.getCount() > 0);
csr.close();
if (rowexists) {
Log.d("INSERTCOREPOEM","Skipping insert of row");
return -2;
}
ContentValues cv = new ContentValues();
cv.put(COL_ID,id);
cv.put(COL_TITLE,title);
cv.put(COl_POEM,poem);
cv.put(COL_SUBJECT,subject);
cv.put(COL_YEARS,years);
cv.put(COL_FAVOURITE,favourite);
Log.d("INSERTCOREPOEM","Inserting new column with id " + String.valueOf(id));
return db.insert(TBLNAME, null, cv);
}
private long maxid(SQLiteDatabase db) {
long rv = 0;
String extractcolumn = "maxid";
String[] col = new String[]{"max(" + COL_ID + ") AS " + extractcolumn};
Cursor csr = db.query(TBLNAME,col,null,null,null,null,null);
if (csr.moveToFirst()) {
rv = csr.getLong(csr.getColumnIndex(extractcolumn));
}
csr.close();
return rv;
}
}
I do not understand what is wrong. Identical code works great friend application. I would be grateful for the help.
Your issue is that you likely have a value of null in the years column of a row or rows in the updated database that data is being copied from.
Although you could change the code to handle (skip insertion or use provide a year value) the end result may not be desired. So the most likely fix would be to amend the database to have valid/useful year values.

Using pre-populated database in Room by copy database file from assert

I want to use pre-populated database in Android Room. I found a way to make it through using the callback, and filled up the database files.
But something is wrong, I'm sure that the database is copied normally, but it remains empty in the device monitor and android emulator. Can you please help me
public abstract class AppDatabase extends RoomDatabase {
private static AppDatabase INSTANCE;
private static final String DB_NAME = "base.db";
static Context ctx;
public abstract Dao dao();
public static AppDatabase getDatabase(Context context) {
if (INSTANCE == null) {
ctx = context;
synchronized (AppDatabase.class) {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(context,
AppDatabase.class, DB_NAME)
.allowMainThreadQueries()
.addCallback(rdc)
.build();
}
}
}
return INSTANCE;
}
private static RoomDatabase.Callback rdc = new RoomDatabase.Callback() {
public void onCreate(SupportSQLiteDatabase db) {
new PopulateDbAsync(INSTANCE, ctx).execute();
Log.d("db create ", "table created when db created first time in onCreate");
}
public void onOpen(#NonNull SupportSQLiteDatabase db) {
ContentValues contentValues = new ContentValues();
}
};
private static class PopulateDbAsync extends AsyncTask<Void, Void, Void> {
private Dao dao;
AssetManager assetManager = ctx.getAssets();
PopulateDbAsync(AppDatabase db, Context context) {
Dao = db.Dao();
ctx = context;
}
#Override
protected Void doInBackground(final Void... params) {
String DB_PATH = "/data/data/mypackage/databases/";
String DB_NAME = "base.db";
try {
Log.d("AppDatabase","Trying copy database file");
OutputStream myOutput = new FileOutputStream(DB_PATH + DB_NAME);
byte[] buffer = new byte[1024];
int length;
InputStream myInput = ctx.getAssets().open("base.db");
while ((length = myInput.read(buffer)) > 0) {
myOutput.write(buffer, 0, length);
}
myInput.close();
myOutput.flush();
myOutput.close();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
}
I solved after spending 6 hours on researching and R & D .
Context is that : - I want to put already existing finaldb.db(which is present inside assests folder) into room database .
Step 1 :
copy this framework files from here link
Step 2 :
You need to migrate , chill i have code :)
#Database(entities = {Status.class}, version = 1,exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
public abstract DataDao StatusDao();
private static AppDatabase INSTANCE;
public static AppDatabase getDatabase(Context context) {
if (INSTANCE == null) {
INSTANCE = createDatabase(context);
}
return (INSTANCE);
}
private static final Migration MIGRATION_2_3 = new Migration(1, 2) {
#Override
public void migrate(#NonNull SupportSQLiteDatabase database) {
Log.d("kkkk","bc");
String SQL_CREATE_TABLE = "CREATE TABLE IF NOT EXISTS 'Status' " +
"( 'id' INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT," +
" 'category' TEXT NOT NULL," +
" 'sub_category' TEXT NOT NULL," +
" 'content' TEXT NOT NULL," +
" 'favourite' INTEGER DEFAULT(0))";
database.execSQL(SQL_CREATE_TABLE);
}
};
private static AppDatabase createDatabase(Context context) {
RoomDatabase.Builder<AppDatabase> builder =
Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class,
context.getString(R.string.dbase_name));
return (builder.openHelperFactory(new AssetSQLiteOpenHelperFactory())
.allowMainThreadQueries()
.addMigrations(MIGRATION_2_3)
.build());
}
}
In MIGRATION_2_3 you have to create table exactly same as current database(which is present in assests folder)
want to learn about migration
Step 3 :
Now table is created successfully in room database !
In case of crash see your logcat , in which its written in understandable form .
You cannot, properly, do the copy of the database in the onCreate method.
When the onCreate method is called the database has been created (the created database is passed to the method). You have to do the copy before the onCreate method and before the database is opened.
You could override's the RoomDatabase init method and do the copy from that method or do the copy before invoking the databaseBuilder.
I'm solved it.
Database class:
#Database(entities = {Entity1.class, Entity2.class, Entity3.class}, version = 1, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
private static AppDatabase INSTANCE;
public abstract Entity1Dao 1Dao();
public abstract Entity2Dao 2Dao();
public abstract Entity3Dao 3Dao();
public static AppDatabase getDatabase(Context context) {
if (INSTANCE == null) {
INSTANCE = createDatabase(context);
}
return (INSTANCE);
}
private static AppDatabase createDatabase(Context context) {
RoomDatabase.Builder<AppDatabase> builder =
Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class,
context.getString(R.string.dbase_name));
return (builder.openHelperFactory(new AssetSQLiteOpenHelperFactory())
.allowMainThreadQueries()
.build());
}
}
Also you should get SQL Helpers, link
My issue is a little bit different than the OP.
I was copying the database file from internal storage which I downloaded from the internet, not from assets. And java.lang.RuntimeException: Unable to copy database file is raised because I didn't grant READ_EXTERNAL_STORAGE before this, or in general granting WRITE_EXTERNAL_STORAGE as READ_EXTERNAL_STORAGE is included in WRITE_EXTERNAL_STORAGE and already need the write permission for downloading the file.

SQLite app won't run at all. Force close message appears

I am creating an app using SQLite.
It crashes before loading.
I fixed all viable errors and since I do not even get a log info, I am having troubles figuring out the error.
Please help.
SQLHelper
public class MySQLiteHelper extends SQLiteOpenHelper {
// Database Version
private static final int DATABASE_VERSION = 1;
// Database Name
private static final String DATABASE_NAME = "MediaDB";
// Media table name
private static final String TABLE_MEDIA = "media";
// Media Table Columns names
private static final String KEY_ID = "id";
private static final String KEY_TYPE = "type";
private static final String KEY_TITLE = "title";
private static final String KEY_AUTHOR = "author";
private static final String[] COLUMNS = {KEY_ID,KEY_TYPE,KEY_TITLE,KEY_AUTHOR};
public MySQLiteHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
// SQL statement to create media table
String CREATE_MEDIA_TABLE = "CREATE TABLE media ( " +
"id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"tyoe TYPE, "+
"title TEXT, "+
"author TEXT )";
// create media table
db.execSQL(CREATE_MEDIA_TABLE);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// Drop older media table if existed
db.execSQL("DROP TABLE IF EXISTS media");
// create fresh media table
this.onCreate(db);
}
//ADD MEDIA
public void addMedia(Media media){
//for logging
Log.d("addMedia", media.toString());
// get reference to writable DB
SQLiteDatabase db = this.getWritableDatabase();
// create ContentValues to add key "column"/value
ContentValues values = new ContentValues();
values.put(KEY_TYPE, media.getType()); // get title
values.put(KEY_TITLE, media.getTitle()); // get title
values.put(KEY_AUTHOR, media.getAuthor()); // get author
// insert
db.insert(TABLE_MEDIA, // table
null, //nullColumnHack
values); // key/value -> keys = column names/ values = column values
// close
db.close();
}
//GET MEDIA
public Media getMedia(int id){
// get reference to readable DB
SQLiteDatabase db = this.getReadableDatabase();
// build query
Cursor cursor =
db.query(TABLE_MEDIA, // table
COLUMNS, // column names
" id = ?", // selections
new String[] { String.valueOf(id) }, // d. selections args
null, // group by
null, // having
null, // order by
null); // limit
// if we got results get the first one
if (cursor != null)
cursor.moveToFirst();
// build media object
Media media = new Media();
media.setId(Integer.parseInt(cursor.getString(0)));
media.setType(cursor.getString(1));
media.setTitle(cursor.getString(2));
media.setAuthor(cursor.getString(3));
//log
Log.d("getMedia("+id+")", media.toString());
// return media
return media;
}
//GET ALL MEDIA
public List<Media> getAllMedia() {
List<Media> medias = new LinkedList<Media>();
// build the query
String query = "SELECT * FROM " + TABLE_MEDIA;
// get reference to writable DB
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery(query, null);
// go over each row, build media and add it to list
Media media = null;
if (cursor.moveToFirst()) {
do {
media = new Media();
media.setId(Integer.parseInt(cursor.getString(0)));
media.setType(cursor.getString(1));
media.setTitle(cursor.getString(2));
media.setAuthor(cursor.getString(3));
// Add media to media
medias.add(media);
} while (cursor.moveToNext());
}
Log.d("getAllMedia()", medias.toString());
// return media
return medias;
}
//UPDATE
public int updateMedia(Media media) {
// get reference to writable DB
SQLiteDatabase db = this.getWritableDatabase();
// create ContentValues to add key "column"/value
ContentValues values = new ContentValues();
values.put("type", media.getType()); // get title
values.put("title", media.getTitle()); // get title
values.put("author", media.getAuthor()); // get author
// updating row
int i = db.update(TABLE_MEDIA, //table
values, // column/value
KEY_ID+" = ?", // selections
new String[] { String.valueOf(media.getId()) }); //selection args
// close
db.close();
return i;
}
//DELETE
public void deleteMedia(Media media) {
// get reference to writable DB
SQLiteDatabase db = this.getWritableDatabase();
// delete
db.delete(TABLE_MEDIA, //table name
KEY_ID+" = ?", // selections
new String[] { String.valueOf(media.getId()) }); //selections args
// close
db.close();
//log
Log.d("deleteMedia", media.toString());
}
}
Media object class
public class Media {
private int id;
private String type;
private String title;
private String author;
public Media(){}
public Media(String type, String title, String author) {
super();
this.type = type;
this.title = title;
this.author = author;
}
//getters & setters
// getting ID
public int getId(){
return this.id;
}
// setting id
public void setId(int id){
this.id = id;
}
// getting type
public String getType(){
return this.type;
}
// setting title
public void setType(String type){
this.type = type;
}
// getting title
public String getTitle(){
return this.title;
}
// setting title
public void setTitle(String title){
this.title = title;
}
// getting author
public String getAuthor(){
return this.author;
}
// setting author
public void setAuthor(String author){
this.author = author;
}
#Override
public String toString() {
return "Media [id=" + id + ", type=" + type + ",title=" + title + ", author=" + author
+ "]";
}
}
Main Activity
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MySQLiteHelper db = new MySQLiteHelper(this);
/**
* CRUD Operations
* */
// add Media
db.addMedia(new Media("Book", "Android Application Development Cookbook", "Wei Meng Lee"));
db.addMedia(new Media("Book", "Android Programming: The Big Nerd Ranch Guide", "Bill Phillips and Brian Hardy"));
db.addMedia(new Media("Book", "Learn Android App Development", "Wallace Jackson"));
// get all media
List<Media> list = db.getAllMedia();
// delete one media
db.deleteMedia(list.get(0));
// get all media
db.getAllMedia();
}
}
There's an error in you table creation:
String CREATE_MEDIA_TABLE = "CREATE TABLE media ( " +
"id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"tyoe TYPE, "+
"title TEXT, "+
"author TEXT )";
TYPE is not a valid SQLlite Data Type.
Please refer to this page: https://www.sqlite.org/datatype3.html
I'd write your table creation as
String CREATE_MEDIA_TABLE = "CREATE TABLE media (" +
"id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"tyoe TEXT, "+
"title TEXT, "+
"author TEXT)";

network broadcast from bluestacks - Beacon-v1

The latest update of Bluestacks is sending a network broadcast every 2 seconds from port 10505.
Beacon-v1|pcName|WindowsPC
OpenSensor-v2|54321
to IP 255.255.255.255
this wasnt happening with the previous version. is this some autosync announcement waiting to talk with another device? i dont want bluestacks talking to other networked devices unless i tell it to do so. i havent checked off or agreed to anything yet that says it requires a network broadcast like this.
even if it's only 53 bytes it's still network pollution to me. how can i turn this off until i actually want it ? thanks
package com.example.test5;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class DBAdapter {
private static final String TAG = "DBAdapter";
private static final String DATABASE_NAME = "TestDB"; //Database Name 'TestDB'
private static final int DATABASE_VERSION =1; //Change Version to upgrade db
private final Context context;
private DatabaseHelper DBHelper;
private SQLiteDatabase db;
public DBAdapter(Context ctx)
{
this.context = ctx;
DBHelper = new DatabaseHelper(context);
}
private static class DatabaseHelper extends SQLiteOpenHelper
{
DatabaseHelper(Context context)
{
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db)
{
db.execSQL("create table USER(Id INTEGER NOT NULL PRIMARY KEY,USER_ID TEXT NOT NULL,USER_NAME TEXT NOT NULL,PASSWORD TEXT NOT NULL, ACTIVE_YN TEXT NOT NULL default 'Y')");
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion,
int newVersion)
{
db.execSQL("DROP TABLE IF EXISTS USER");
onCreate(db);
}
}
//---opens the database---
public DBAdapter open() throws SQLException
{
db = DBHelper.getWritableDatabase();
return this;
}
//---closes the database---
public void close()
{
DBHelper.close();
}
//---insert a User into the database---
public void insertUser(String USER_ID,String USER_NAME,String PASSWORD)
{
ContentValues initialValues = new ContentValues();
initialValues.put("USER_ID", USER_ID);
initialValues.put("USER_NAME", USER_NAME);
initialValues.put("PASSWORD", PASSWORD);
db.insert("USER", null, initialValues);
}
//---updates
public boolean updateUserStatusById(String USER_NAME,String USER_ID )
{
ContentValues args = new ContentValues();
args.put("USER_NAME", USER_NAME);
return db.update("USER", args,
"USER_ID='" + USER_ID+"'", null) > 0;
}
//executing query and it returns fields which are specified in the query ..........
public Cursor getQueryResult(String MY_QUERY) throws SQLException
{
return db.rawQuery(MY_QUERY, null);
}
}
Nandakishore P
I find this to be an annoyance as well and would like to turn it off. So far, I have found that the offending executable is:
C:\program files (x86)\bluestacks\hd-frontend.exe
And in C:\ProgramData\BlueStacks\Logs\BlueStacksUsers.log there are entries for:
2015-12-10 08:46:56.378000 HD-Frontend( 2632: 4588): 1 INFO OpenSensor: Beacon port 10505, interval 2000, host name MYHOSTNAME
Having a block entry every two seconds on my firewall is unacceptable!