My Database Adapter Class
public class DatabaseAdapter {
// Declare a DatabaseHelper object reference
DatabaseHelper helper;
// Declare a SQLiteDatabase object reference.
SQLiteDatabase db;
// SQLiteDatabase class has methods to create, delete, execute SQL commands and perform other common database
// management tasks.
// Define an ArrayList of Term object.
ArrayList<Term> termsList = new ArrayList<Term>();
// What is Term?
// You'll create a Term class to contain and model the information and make it more easy to implement.
// Define the constructor for DatabaseAdapter
public DatabaseAdapter(Context context){
// Instantiate helper
helper = new DatabaseHelper(context);
// Call getWritableDatabase() method on helper. This is going to give you an object of SQLiteDatabase. Store that in db.
db = helper.getWritableDatabase();
// Now, this SQLiteDatabase object, db, is going to represent the database you have and you are going to use that object
// to perform the different queries that you want to do, for example, insert, update or delete from database.
}
// Define a method to close the database
public void close() {
helper.close();
}
public int deleteData(long id) {
// Define the whereArgs String array
String whereArgs[] = {""+id};
// Call delete() method on db
return db.delete(DatabaseHelper.TABLE_NAME, DatabaseHelper.KEY_ID + "=?", whereArgs);
// delete() returns the number of rows deleted as an integer.
}
public int updateTermFullForm(long id, String meaning) {
// To update the database, you need to create an object of the class called ContentValues that acts like a map, inside which you can
// put your key-value pairs.
// Here, what is expected is the name of the key that you give here is the name of the column in your table
// and the value you want to put inside the column goes in the second parameter.
ContentValues contentValues = new ContentValues();
contentValues.put(DatabaseHelper.KEY_MEANING, meaning);
// You need to create whereArgs[] array. whereArgs[] is just an array that contains the values that are substituted inside the
// question mark (?) of whereClause at run-time, when you are executing the query.
// whereArgs[] is going to contain the values for against you want to compare.
String whereArgs[] = {""+id};
// Call update() method on db
return db.update(DatabaseHelper.TABLE_NAME, contentValues, DatabaseHelper.KEY_ID + "=?", whereArgs);
// update() returns the number of rows affected as an integer.
// The plain sql statement for this can be:
// UPDATE ct SET full_form="New Value" WHERE _id=2
}
public long insertTerm(String words, String meaning) {
// Define a new ContentValues object
ContentValues contentValues = new ContentValues();
// Add term and fullForm into that
contentValues.put(DatabaseHelper.KEY_WORDS, words);
contentValues.put(DatabaseHelper.KEY_MEANING, meaning);
// Call insert() method on db object and return
return db.insert(DatabaseHelper.TABLE_NAME, null, contentValues);
}
// Next, define a method that returns an ArrayList of specific Term objects where the term starts with the String in parameter.
public ArrayList<Term> getSomeTerms(String termStartsWith){
// Call query() method on db and store the returned cursor.
Cursor cursor = db.query(DatabaseHelper.TABLE_NAME, new String[]{DatabaseHelper.KEY_ID, DatabaseHelper.KEY_WORDS,
DatabaseHelper.KEY_MEANING}, DatabaseHelper.KEY_WORDS + " like '"
+ termStartsWith + "%'",null,null,null,null);
// Here, % is a wildcard character which indicates 0 or any number of characters. So, there can be any number of characters
// after βAβ, or "B" or "S" etc.
// The plain sql statement for this can be:
// SELECT * FROM ct WHERE terms LIKE 'A%';
// Use a while loop to traverse the database and populate the ArrayList of Term objects
while (cursor.moveToNext()){
// Get the database column index or position by passing the column name
int index1 = cursor.getColumnIndex(DatabaseHelper.KEY_ID);
// Now, get the value of id for that cell
long id = cursor.getInt(index1);
// Do the same thing to get values from other two columns
int index2 = cursor.getColumnIndex(DatabaseHelper.KEY_WORDS);
String words = cursor.getString(index2);
int index3 = cursor.getColumnIndex(DatabaseHelper.KEY_MEANING);
String meaning = cursor.getString(index3);
// Create a Term object from database values
Term term = new Term(id, words, meaning);
// Add the Term object to termsList
termsList.add(term);
}
// return termList
return termsList;
}
// For managing all the operations related to the database, a helper class has been provided by Android
// and it is called SQLiteOpenHelper.
// It takes care of opening the database if it exists, creating it if it does not exists, and upgrading it as necessary.
// So, inside DatabaseAdapter you'll create a static inner class that extends SQLiteOpenHelper.
private static class DatabaseHelper extends SQLiteOpenHelper{
// Define some private static final String variables to store information related to the database
private static final String DATABASE_NAME ="Test.db";
// Database name must be unique within an app, not across all the apps.
private static final String TABLE_NAME = "words";
// When you do change the structure of the database change the version number from 1 to 2
private static final int DATABASE_VERSION = 7;
static final String KEY_ID = "id";
static final String KEY_WORDS = "words";
static final String KEY_MEANING = "meaning";
private Context context;
// Define the constructor
public DatabaseHelper(Context context){
super(context, DATABASE_NAME, null, DATABASE_VERSION);
// Store the context received from constructor into this class's context variable
this.context = context;
}
// Since, you're not creating or upgrading the database since you're using a pre-created database file
// copied to the right location, you don't need to write any code inside onCreate() or onUpgrade().
#Override
public void onCreate(SQLiteDatabase db) {
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
}
My PreCreate Database Class
public class PreCreateDB {
static String destPath;
static String destPathwithFilename;
// Lets define copyDB() method
public static void copyDB(Context context){
// Defile two String variables containing path upto "database" folder and "CTDB" file respectively
destPath = "/data/data/" + context.getPackageName() + "/databases";
destPathwithFilename = destPath+"/Test.db";
// Create two File objects from those Strings
File fPath = new File(destPath);
File fPathWithName = new File(destPathwithFilename);
// Now, the question is, why we created two separate File objects?
// It's because in some devices databases folder will be automatically created by Android system.
// In some other devices it won't be there by default.
// So, we need to check if it's not present in the device.
if(!fPath.exists()){
// If true, you'll create the databases folder
fPath.mkdirs();
// And then copy the CTDB Database file from assets folder to databases folder.
// You'll define a method named rawCopy that takes an InputStream and an OutputStream.
// This method will copy the file.
try {
rawCopy(context.getAssets().open("Test.db"), new FileOutputStream(destPath+"/Test.db"));
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void rawCopy(InputStream inputStream, OutputStream outputStream) throws IOException {
// To copy 1k bytes at a time, create a byte array of size 1024
byte[] buffer = new byte[1024];
// Declare an integer variable to store the total number of bytes read from the buffer.
int length;
// If you call read() method on inputStream object and pass buffer as parameter, it will read 1024 bytes at a time.
// It returns -1 if there is no more data because the end of the stream has been reached.
// Using this information you use a while loop to read from the inputStream and write to the outputStream.
// This copies the database file CTDB from assets folder to data/data/[package-name]/databases folder.
while((length = inputStream.read(buffer)) > 0){
outputStream.write(buffer, 0, length);
}
// Close the input and output streams once you're done
inputStream.close();
outputStream.close();
}
public static void resetDB(Context context) {
// Call rawCopy() inside try block
try {
rawCopy(context.getAssets().open("Test.db"), new FileOutputStream(destPathwithFilename));
} catch (IOException e) {
e.printStackTrace();
}
}
}
MY Show Term Class
public class ShowTerm extends AppCompatActivity {
// Declare a DatabaseAdapter object reference
static DatabaseAdapter databaseAdapter;
// Declare a RecyclerView object reference
static RecyclerView rvTerms;
// Declare an Adapter object reference
TermsAdapter termsAdapter;
// Declare a LayoutManager object reference
RecyclerView.LayoutManager layoutManager;
// Define an ArrayList of type Term
static ArrayList<Term> termsList = new ArrayList<>();
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.show_term);
// Get the received String from Intent
String termStartsWith = getIntent().getStringExtra("termStartsWith");
// Instantiate DatabaseAdapter class and pass this for the Context
databaseAdapter = new DatabaseAdapter(this);
// Call getSomeTerms() on databaseAdapter object and store the returned ArrayList in
// termsList
termsList = databaseAdapter.getSomeTerms(termStartsWith);
// Obtain a handle for the RecyclerView
rvTerms = findViewById(R.id.rvTerms);
// You may use this setting to improve performance if you know that changes
// in content do not change the layout size of the RecyclerView
rvTerms.setHasFixedSize(true);
// Instantiate the linear layout manager
layoutManager = new LinearLayoutManager(this);
// Set the layout with RecyclerView
rvTerms.setLayoutManager(layoutManager);
// Create an instance of TermsAdapter. Pass context, termsList and the
// RecyclerView to the constructor
termsAdapter = new TermsAdapter(this, termsList, rvTerms);
// Finally, attach the adapter with the RecyclerView
rvTerms.setAdapter(termsAdapter);
}
}
My Bengali + Hindi Mix Sqlite Database
MainActivity Ui
This Sqlite query giving blank results for Hindi words but English words are showning Properly
My MainActivity Class
public class MainActivity extends AppCompatActivity {
// Store the text to be shared in a String
String shareBody = "Download CTD App now and know about all the important Computer Terms and their Full Forms: \n" +
"https://play.google.com/store/apps/details?id=com.sandipbhattacharya.computertermsdictionary";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Create a class containing static methods to copy the database file
// from assets folder into: data/data/package-name/databases folder, from where the app can access it.
// Lets name it PreCreateDB.
// From MainActivity, call the copyDB method of PreCreateDB and pass "this" for Context
PreCreateDB.copyDB(this);
}
public void show(View view) {
// We have set a text with every button. This text simply contains an alphabet.
// Get the clicked Button's text and store in a String variable
String termStartsWith = ((Button) view).getText().toString().trim();
// Create an Intent to go to another Activity where you can show all the Terms that start with a letter termStartsWith contains
Intent intent = new Intent(this, ShowTerm.class);
// Set termStartsWith with the Intent object as Extra
intent.putExtra("termStartsWith", termStartsWith);
// Start the Activity with the Intent
startActivity(intent);
// Create the ShowTerm class.
}
public void reset(View view) {
// You'll use Android AlertDialog to ask the user about his/her choice to continue or discontinue the reset operation.
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("Resetting will delete all your personal data. Proceed?");
builder.setCancelable(true);
builder.setPositiveButton("Yes",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// Here, you call a method and pass MainActivity.this as context,
// to replace the database file from assets folder to databases folder.
PreCreateDB.resetDB(MainActivity.this);
}
});
builder.setNegativeButton("No",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
AlertDialog alertDialog = builder.create();
alertDialog.show();
}
public void addNew(View view) {
// You'll use an Intent to go to AddNew Activity
Intent intent = new Intent(this, AddNew.class);
startActivity(intent);
}
public void rate(View view) {
// Create an Intent that opens a URL in Google Play
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("https://play.google.com/store/apps/details?id=" + getPackageName()));
// As you configure this intent, pass "com.android.vending" into Intent.setPackage() so that users see your app's details
// in the Google Play Store app instead of a chooser. Make sure your emulator has pre-installed Play Store app.
intent.setPackage("com.android.vending");
// Start the Activity
try {
startActivity(intent);
}catch (ActivityNotFoundException e) {
Toast.makeText(this, "Couldn't launch Play Store", Toast.LENGTH_LONG).show();
}
}
public void shareApp(View view) {
// Create a send Intent
Intent sendIntent = new Intent(Intent.ACTION_SEND);
// Set the Sharing Type
sendIntent.setType("text/plain");
// Pass your sharing content using the putExtra() method of the Intent
sendIntent.putExtra(Intent.EXTRA_SUBJECT, "Share CTD App");
sendIntent.putExtra(Intent.EXTRA_TEXT, shareBody);
// Next, instruct Android system to let the user choose their sharing medium
startActivity(Intent.createChooser(sendIntent, "Share using"));
// This will pass the sendIntent along with a title to be displayed at the top of the chooser.
// When the user chooses an application from the list, your share content will be passed to that application,
// where he/she will be able to edit the content before sending it if they wish to do so.
}
}
Please Help me because im beginner in android development and it is very important for my dictionary app π
Solved the problem, in Sqlite database your table creation rules are also important when using external database.
Related
In my app, I have a database of objects that are generated by the user and then saved to the internal storage using shared prefs when the user leaves the app. Now when the user re-opens the app, that data is retrieved and presented to the user for further editing. I noticed that when I roll out an update to my app and the user installs it, all the data is lost. I tried retrieving it by saving the app's current version code using shared prefs and then comparing it to the current one in order to know when it's an app update and then i call the Read & Write data methods to retrieve the old data but with no luck. Any ideas on how i should approach this issue?
SerializeGLB.java:
public class SerializeGLBData {
/**
* Writes the Global User Box's cardList to the user's internal storage using the Gson
* library so that the user doesn't lose his/her data.
* #param cardList The list to write to the internal storage
* #param context Getting the app's current context
*/
public static void Write(ArrayList<Card> cardList, Context context) {
SharedPreferences appPrefs = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = appPrefs.edit();
Gson gson = new Gson();
String cardsGLBJson = gson.toJson(cardList);
editor.putString("cardsGLB",cardsGLBJson);
editor.apply();
editor.commit();
Log.d("WriteData","Data written successfully!");
}
/**
* Reads the cards list that gets saved when the app closes
* #param context Get the app's current context
* #return Returns an ArrayList of Card Objects containing the card info
*/
public static ArrayList<Card> ReadCards(Context context) {
SharedPreferences appPrefs = PreferenceManager.getDefaultSharedPreferences(context);
Gson gson = new Gson();
String cardsGLBJson = appPrefs.getString("cardsGLB","");
Type type = new TypeToken<ArrayList<Card>>(){}.getType();
return gson.fromJson(cardsGLBJson,type);
}
}
private void checkForFirstRun() {
final String PREF_VERSION_CODE_KEY = "version_code";
final int DOESNT_EXIST = -1;
// Get current version code
int currentVersionCode = BuildConfig.VERSION_CODE;
// Get saved version code
SharedPreferences prefs = getSharedPreferences(PREFS_NAME,MODE_PRIVATE);
int savedVersionCode = prefs.getInt(PREF_VERSION_CODE_KEY, DOESNT_EXIST);
// Check for first run or upgrade
if(currentVersionCode == savedVersionCode) {
// This is just a normal run
Log.d("RUN_TYPE:" , "Normal Run");
} else if(savedVersionCode == DOESNT_EXIST) { // This is a new install(or the user cleared the shared prefs)
CallWriteDataMethods(this);
Log.d("RUN_TYPE:", "New Install");
// Showing the tutorial page when the app starts for the first time
Intent tutorialIntent = new Intent(this, Tutorial.class);
startActivity(tutorialIntent);
UsernameDialog dialog = new UsernameDialog();
dialog.setCancelable(false);
dialog.show(getFragmentManager(),"USERNAME_DIALOG");
} else if(currentVersionCode > savedVersionCode) { // This is an upgrade
CallWriteDataMethods(this);
Log.d("RUN_TYPE:","Update");
}
// Update the shared prefs with the current version code
prefs.edit().putInt(PREF_VERSION_CODE_KEY,currentVersionCode).apply();
return;
}
public static void CallWriteDataMethods(Context context) {
// Write all the -empty- data from GlobalDataHolder to the internal memory to avoid a first time read error
SerializeGLBData.Write(GlobalDataHolder.cards,context);
// Write all the -empty- data from JPDataHolder to the internal memory to avoid a first time read error
SerializeJPData.Write(JPDataHolder.cards,context);
}
/**
* Calls every available Read method to retrieve all available data from the GLB database
*/
public static void callReadDataMethodsGLB(Context context) {
GlobalDataHolder.cards = SerializeGLBData.ReadCards(context);
Log.i("Read Methods[GLB]", "ReadMethods called!");
}
/**
* Calls every available Read method to retrieve all available data from the JP database
*/
public static void callReadDataMethodsJP(Context context) {
JPDataHolder.cards = SerializeJPData.ReadCards(context);
Log.i("Read Methods[JP]", "ReadMethods called!");
}
How does your SerializeGLBData.Write function works? Because by reading your code, when you are in the case of an upgrade, you are only calling the CallWriteDataMethods directly, and according to your comment in it:
// Write all the -empty- data from GlobalDataHolder to the internal memory to avoid a first time read error
You are writing the memory with empty data. Are your writing functions checking if data exists before putting empty data in it?
So something like
if(!prefs.contains("your_data_key")) {
// your code to add data
}
I want to create the sqlite database file DatabaseName.db with few entities that should be created in path of application (/data/data/MyApplicationName/databases/DatabaseName.db) when I try to execute the snippet code bellow, however the DatabaseName.db file is not there. Why ?
MyDatabaseSample db = Room.databaseBuilder(context,
MyClass.class,
"DatabaseName.db")
.addCallback(new RoomDatabase.Callback() {
#Override
public void onCreate(#NonNull SupportSQLiteDatabase ssdb) {
super.onCreate(db);
Log.d(TAG, "Database created - populate database");
}).build();
The database is created in the path of application only if I create an instance of a entity object and insert it in database right after get the database reference db. As I want to pre-populate database just after database creation, I think just make sense do it inside onCreate method of callback, but onCreate will never be called. So, How can I create the "DatabaseName.db" file with all tables representing entities and populate the database using callback ?
OBS: I am using Room version use 1.1.0-alpha2 and compiling with SDK android API 27.
I think you need to define some Room entities before pre-populate the db that's what i have done and it works just as expected, here is some code of what i have done so far:
public class DatabaseCreator {
private static MyDatabaseSample appDatabase;
private static final Object LOCK = new Object();
public synchronized static MyDatabaseSample getDBInstance(final Context context){
if(appDatabase == null) {
synchronized (LOCK) {
if (appDatabase == null) {
RoomDatabase.Callback appDBCallback = new RoomDatabase.Callback() {
#Override
public void onCreate(#NonNull SupportSQLiteDatabase db) {
super.onCreate(db);
try {
ReadScript.insertFromFile(context, R.raw.populate_db, db);
} catch (IOException e) {
Log.d("DB Population Error", e.toString());
}
}
};
appDatabase = Room.databaseBuilder(context,
MyDatabaseSample.class, "DatabaseName").addCallback(appDBCallback).build();
}
}
}
return appDatabase;
}
}
The code above is a singleton that uses the Callback's onCreate to pre-populate the db using a "raw resource" (To add raw resources to your project just create a folder inside your res folder like this "res/raw") that contains an sql script. To read the script i have used this code:
public class ReadScript {
public static int insertFromFile(Context context, int resourceCode, SupportSQLiteDatabase db) throws IOException {
// Reseting Counter
int result = 0;
// Open the resource
InputStream insertsStream = context.getResources().openRawResource(resourceCode);
BufferedReader insertReader = new BufferedReader(new InputStreamReader(insertsStream));
// Iterate through lines (assuming each insert has its own line and theres no other stuff)
while (insertReader.ready()) {
String insertStmt = insertReader.readLine();
if(insertStmt != null){
if(insertStmt.isEmpty()) continue;
db.execSQL(insertStmt);
result++;
Log.d("Statement #", Integer.toString(result));
}
}
insertReader.close();
// returning number of inserted rows
return result;
}
}
And then you just create the db instance by doing:
MyDatabaseSample db = DatabaseCreator.getDBInstance(getContext());
Note: You can try to create tables inside the raw script but i haven't tried it yet.
Goog luck.
So Azure spit the following code for me to insert into an activity (Android Studio is what I'm using)
Add the following line to the top of the .java file containing your launcher activity:
import com.microsoft.windowsazure.mobileservices.*;
Inside your activity, add a private variable
private MobileServiceClient mClient;
Add the following code the onCreate method of the activity:
mClient = new MobileServiceClient("https://pbbingo.azurewebsites.net", this);
Add a sample item class to your project::
public class ToDoItem{ public String id; public String Text;}
In the same activity where you defined mClient, add the following code:
ToDoItem item = new ToDoItem();
item.Text = "Don't text and drive";
mClient.getTable(ToDoItem.class).insert(item, new TableOperationCallback<item>(){
public void onCompleted(ToDoItem entity, Exception exception, ServiceFilter response)
{
if(exception == null){
//Insert Succeeded
} else {
//Insert Failed
}
}});
My goal is to create a login page. I understand that the above was probably offered up more with a ToList in mind. I just want to get the syntax correct today. The problem I think, is my basic class structure. I have created an OnClick Listener within my on create that gets the ID from a button in my layout. I don't need it checking for anything in the database until the button has been actually clicked to either login or register.
public class LoginClass extends AppCompatActivity{
public void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.MyLoginLayout);
MobileServiceClient mClient = null;
try {
mClient = new MobileServiceClient ("myAzureWebsite", "AzureKey", this);
} catch (MalformedURLException e) {
e.printStackTrace();
}
Button Attempt = (Button) findViewById (R.id.mySubmitButton);
final MobileServiceClient finalMClient = mClient; // finalized so I can use it later.
Attempt.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick (View v) {
final View thisView = v;
final MyToDoItemClass item = new MyToDoItemClass();
In MyToDoItemClass I have two variables (Both String) Just left over from
the example of a ToDoList (they are String ID and String Text)
item.Text = "Filler";
item.ID = "Fill";
finalMClient.getTable(MyToDoItemClass.class).insert(new Table OperationCallback<item>() { //<--- I'm getting an error that the variable, item
is from an unknown class...
public void onCompleted (Item entity, Exception exception, ServiceFilterResponse response){
if(exception == null) {
Intent i = new Intent (LoginClass.this, MainActivity.class);
startActivity(i);
}else{
Toast.makeText(thisView.getContext(), "Failed", Toast.LENGTH_LONG).show();
}}
});
}
});
}}
The problem is with that the TableOperationCallback is saying that the item from MyToDoItemClass class is from an unknown class.
There are many issues in your code, as below.
According to the javadoc for class MobileServiceClient, there is not a method insert(TableOperationCallback<E> callback), so the code finalMClient.getTable(MyToDoItemClass.class).insert(new Table OperationCallback<item>() {...} is invalid.
The generics E in Table OperationCallback<E> means that you need to write a POJO class name instead of E, not an object variable name like item, so the correct code should be new Table OperationCallback<MyToDoItemClass>, please see the Oracle tutorial for Generics to know more details.
The figure below shows all methods insert of class MobileServiceClient. The bold word Deprecated under the method name means that you should not use it for developing on new project, itβs only compatible for old project on the new version of Java SDK.
Please follow the offical tutorial to develop your app. Any concern, please feel free to let me know.
Here are following my classes:
StatsObjectId.java
public class StatsObjectId extends Activity {
DBClass db;
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
db = new DBClass(this);
}
public void addObjId(String objid){
Log.e("objectid","This is the object id going to store: "+objid);
db.addObjectId(objid); //This is the line# 105
if(getObjId()){
Log.e("objectid","Successfully stored!");
}else{
Log.e("objectid","Error in storing object id!");
}
}
public boolean getObjId(){
boolean result;
try{
c = db.getObjectId();
c.moveToFirst();
String str = c.getString(c.getColumnIndex("objectid"));
Log.e("objectid","Object id returned form DB: "+str);
result = true;
}catch(CursorIndexOutOfBoundsException e){
Log.e("objectid","Cursor index out of bound");
result = false;
e.printStackTrace();
}catch(Exception e){
Log.e("objectid","Some Another Exception");
result = false;
e.printStackTrace();
}
return result;
}
ParseComServerAccessor.java
public class ParseComServerAccessor {
//I am skipping some irrelevant code
public void putStats(String authtoken, String userId, Tas statsToAdd) throws Exception {
//Again skip some code
//Here I got some HttpResponse and I need to extract an object id and save it to database
HttpResponse response = httpClient.execute(httpPost);
String responseString = EntityUtils.toString(response.getEntity());
JSONObject json = new JSONObject(responseString);
Log.e("objectid","Now Object Id is: "+json.getString("objectId") );
StatsObjectId ob = new StatsObjectId();
ob.addObjId(json.getString("objectId")); // This is the line#156
//skip some code
}
}
TasSyncAdapter.java
public class TasSyncAdapter extends AbstractThreadedSyncAdapter {
//skipped Constructor code
public void onPerformSync(Account account, Bundle extras, String authority,
ContentProviderClient provider, SyncResult syncResult) {
//skipped some code
ParseComServerAccessor parseComService = new ParseComServerAccessor();
//skipped some code again
parseComService.putStats(authToken, userObjectId, remoteTas); //This is the line# 134
//skip some code
}
}
Now finally when I run my app... this is the following Log Cat
Tag Text
objectid This is the object id going to store: 9AFysqffz7
System.err java.lang.NullPointerException
System.err at com.myapp.ds_app.StatsObjectId.addObjId(StatsObjectId.java:105)
System.err at com.myapp.ds_app.syncadapter.ParseComServerAccessor.putStats(ParseComServerAccessor.java:156)
System.err at com.myapp.ds_app.syncadapter.TasSyncAdapter.onPerformSync(TasSyncAdapter.java:134)
System.err at android.content.AbstractThreadedSyncAdapter$SyncThread.run(AbstractThreadedSyncAdapter.java:254)
DBClass.java
public class DBClass extends SQLiteOpenHelper {
private static final String DATABASE_NAME="myapp.db";
public DBClass(Context cxt){
super(cxt, DATABASE_NAME, null, 1);
}
#Override
public void onCreate(SQLiteDatabase mydatabase) {
mydatabase.execSQL("CREATE TABLE IF NOT EXISTS temp (objectid STRING)");
}
public Cursor getObjectId(){
Cursor cursor = getReadableDatabase().rawQuery("SELECT objectid FROM temp", null);
return cursor;
}
public void addObjectId(String objid){
try{
ContentValues cv = new ContentValues(1);
Log.e("objectid","In DBClass and object id: "+objid);
cv.put("objectid", objid);
Log.e("objectid","Content value contains: "+cv.toString());
getWritableDatabase().insert("temp", "objectid", cv);
}catch(NullPointerException e){
e.printStackTrace();
}
}
}
Now, I am stucked at this point!
So far, I need to save just a single value. I tried to create a file instead of saving a value in database. But again there is some exception of ContextWrapper.
I am currently interested to deal with database.
Please let me know if you guys need any other information.
I would really appreciate if any one please explain this thing. I'm android newbie and would love to learn about this problem. Thanks in advance!
StatsObjectId ob = new StatsObjectId();
You are instanciating an Activity class. You are not allowed to do that. (There should really be something in Android to tell you when you do that) Basically, the context is not initialized, because android needs to do that in order to have a functional Activity.
Plus, Android (when it creates the Activity) calls the onCreate method with a proper context. You don't (and you can't, either), therefore your db is null.
In AbstractThreadedSyncAdapter, you have a getContext method to get a proper context. Use this to initialize your database and to insert data in it, rather than passing it to the Activity object.
I need some help with debugging my application. First of all: In emulator and on some other devices my app is running fine. On my device I got a force close (without a force close message).
The "crash" happens if the Activity of the app is changed.
Here is some code of the MainActivity class. It just reads html content from a web page over webview. And no, it is NOT possible to do this over HttpRequest because I was not able to simulate the post request.
public class MainActivity extends Activity {
public final static String EXTRA_HTML = "com.example.com.test.HTML";
private WebView mWebView;
private ProgressDialog mDialog;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mWebView = (WebView) findViewById(R.id.webView1);
CookieSyncManager.createInstance(this);
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.removeAllCookie();
mWebView.setBackgroundColor(0);
mWebView.setWebChromeClient(new WebChromeClient() {
public boolean onConsoleMessage(ConsoleMessage cmsg) {
if (cmsg.message().startsWith("MAGIC")) {
mDialog.cancel();
/*HashMap<String, String> message = new HashMap<String, String>();*/
String msg = cmsg.message().substring(5);
Intent intent = new Intent(MainActivity.this,
ReadDataActivity.class);
/*message.put("message", msg);*/
/*intent.putExtra(EXTRA_HTML, message);*/
intent.putExtra(EXTRA_HTML, msg);
startActivity(intent);
}
return false;
}
});
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.getSettings().setPluginState(PluginState.OFF);
mWebView.getSettings().setLoadsImagesAutomatically(false);
mWebView.getSettings().setBlockNetworkImage(true);
mWebView.getSettings().setAppCacheEnabled(true);
mWebView.getSettings().setSavePassword(true);
mWebView.getSettings()
.setCacheMode(WebSettings.LOAD_NORMAL);
mWebView.setWebViewClient(new WebViewClient() {
public void onPageFinished(WebView view, String address) {
if (address.indexOf("mySession") != -1) {
view.loadUrl("javascript:console.log('MAGIC'+document.getElementsByTagName('html')[0].innerHTML);");
}
});
mWebView.loadUrl("http://www.myurl.de");
}
So, in the onConsoleMessage() method I just pass the html code to another Activity class which read, parse and display the content.
The problem is now that at this point when the ReadDataActivity class should be loaded the application just close and go back to the home screen without any message or user dialog.
Is it possible that the html code which is passed as a string to the ReadDataActivity is to big? I also try to add the html code as a string in a HashMap but the problem is the same.
Some ideas what I can do to debug the problem? Maybe I should try to create a Parcelable object?
In the emulator everything is working fine.
As per my experience (sometime ago), you are able to parcel up to 1MB of data in a Bundle for IPC. This limit can be reduced if a lot of transactions are happening at a given time. Further information here.
In order to overcome this issue, I would suggest you to save your content on a temp file and pass the path/URI of your temp file to your second activity. Then in your second activity, read the contents out from file, perform your desired operations and finally delete that file.
If you want, you may also incorporate Shared_Preferences for this task - if you think handling files is cumbersome.
I did some research on the maximum amount of data you can transfer using an Intent. And it seems that the limit is nowhere near 1MB or 90KB, it's more like 500KB (tested on API 10, 16, 19 and 23).
I wrote a blog post about this topic, you can find it here: http://web.archive.org/web/20200217153215/http://neotechsoftware.com/blog/android-intent-size-limit
The size limit of Intent is still pretty low in Jelly Bean, which is somewhat lower than 1MB (around 90K), so you should always be cautious about your data length, even if your application targets only latest Android versions.
I have seen that by writing and reading from a file consists of less performance .
Then I have seen this solution : . So I am using this solution :
public class ExtendedDataHolder {
private static ExtendedDataHolder ourInstance = new ExtendedDataHolder();
private final Map<String, Object> extras = new HashMap<>();
private ExtendedDataHolder() {
}
public static ExtendedDataHolder getInstance() {
return ourInstance;
}
public void putExtra(String name, Object object) {
extras.put(name, object);
}
public Object getExtra(String name) {
return extras.get(name);
}
public boolean hasExtra(String name) {
return extras.containsKey(name);
}
public void clear() {
extras.clear();
}
}
Then in MainActivity I have called it like the following :
ExtendedDataHolder extras = ExtendedDataHolder.getInstance();
extras.putExtra("extra", new byte[1024 * 1024]);
extras.putExtra("other", "hello world");
startActivity(new Intent(MainActivity.this, DetailActivity.class));
and in DetailActivity
ExtendedDataHolder extras = ExtendedDataHolder.getInstance();
if (extras.hasExtra("other")) {
String other = (String) extras.getExtra("other");
}
The fixed size of 1MB is not only limited to intents. As Intents, Content Providers, Messenger, all system services like Telephone, Vibrator etc. utilize IPC infrastructure provider by Binder. Moreover the activity lifecycle callbacks also use this infrastructure.
1MB is the overall limit on all the binder transactions executed in the system at a particular moment.
In case there are lot of transactions happening when the intent is sent,it may fail even though extra data is not large.
http://codetheory.in/an-overview-of-android-binder-framework/
A little late to the game, but I just ran up against the same issue. Writing the data to file didn't really make sense performance-wise in my case, but I came across this in my search for answers:
http://developer.android.com/guide/faq/framework.html#3
Using a singleton is better for me as there's no need for disk IO. Better performance if the data doesn't need to be persisted.
Here's an example implementation:
public class DataResult {
private static DataResult instance;
private List<SomeObject> data = null;
protected DataResult() {
}
public static DataResult getInstance() {
if (instance == null) {
instance = new DataResult();
}
return instance;
}
public List<SomeObject> getData() { return data; }
public void setData(List<SomeObject> data) { this.data = data; }
}
Then you can set using this in one activity:
DataResult.getInstance().setData(data);
And get it in the other activity like this:
List<SomeObject> data = DataResult.getInstance().getData();
The Binder transaction buffer has a limited fixed size - 1Mb.
But the problem is that buffer shared by all transactions in progress for the process.
So try to keep your intent's data as small as possible every time.
The use of static String variable is good. If there is a need for the user to go back & forth between different pieces of HTML, you can also use LruCache like this:
static LruCache<String, String> mMemoryCache;
final int kiloByte = 1024;
.
.
final int maxMemoryKB = (int) (Runtime.getRuntime().maxMemory() / kiloByte);
// then choose how much you want to allocate for cache
final int cacheSizeKB = maxMemoryKB / 8;
.
.
mMemoryCache = new LruCache<String, String>(cacheSizeKB) {
//#Override
protected int sizeOf(String key, String value) {
try {
byte[] bytesUtf8 = value.getBytes("UTF-8");
return bytesUtf8.length / kiloByte;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return -1;
}
};
.
.
String cacheKey = generateUniqueString(key);
if (mMemoryCache.get(key) == null) {
mMemoryCache.put(cacheKey, yourContent);
}
Intent intent = new Intent(getApplicationContext(), ReadDataActivity.class);
intent.putExtra(EXTRA_HTML, cacheKey);
startActivity(intent);
Then on the ReadDataActivity side
Intent intent = getIntent();
String cacheKey = intent.getStringExtra(EXTRA_HTML);
String contentString = MainActivity.mMemoryCache.get(cacheKey);
doSomethingWith(contentString);
This idea came from here.
An alternative solution for passing large data between activities is to use a static field. In your case add this line to ReadDataActivity class
public static String msg;
Then you can use the static field msg within MainActivity class as follows
ReadDataActivity.msg = cmsg.message().substring(5);
And finally start your activity without extra put
Intent intent = new Intent(MainActivity.this, ReadDataActivity.class);
startActivity(intent);