I need some text data (street names of a 2 million town) to get into my android application. I think the best way to do this is to store it into a sqlite database read-only when it starts the first time. As I read, doing this with a pre-defined database is pretty tedious and not clean but you have to copy it from external storage or something and have the data twice then. So I thought about using a CSV file from raw resources and delete it after import, but this is not possible too because this data will be built into the sdk file and can't be deleted any more.
My target is it to make some kind of initial data transfer from local and delete this data source. Any ideas how to achieve that properly? I'd like to go without downloading the data from a server because this would mean that the only reason my application needs an internet connection is because of downloading the initial data. Otherwise it wouldn't need an internet connection.
What about putting the .csv file in you asset folder and read it.
String next[] = {};
List<String[]> list = new ArrayList<String[]>();
try {
CSVReader reader = new CSVReader(new InputStreamReader(getAssets().open("test.csv")));
for(;;) {
next = reader.readNext();
if(next != null) {
list.add(next);
} else {
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
I'm thinking I'd compress the data in the initial installation resource and expand it on install. This answer sees to reference a 7zip open source java api.
Data compression on Android (other than java.util.zip ?)
Meanwhile I know that the data I will use in my app is too complex to populate the data by hand. Moreover the app needs internet access at any rate, so I will use a Json string to get the data.
Related
I am making an app that blocks inappropriate websites for parental control, I have the blocked websites in a text file which 50MB in size. I want to add them all to room database so that I can check if a url is blocked or not.
But reading and looping through each line in the text file taking forever, is there any better way I can read the file and add each line to room database?
FileInputStream inputStream = null;
Scanner sc = null;
try {
inputStream = new FileInputStream(path);
sc = new Scanner(inputStream, "UTF-8");
while (sc.hasNextLine()) {
String line = sc.nextLine();
// insert to room database
}
// note that Scanner suppresses exceptions
if (sc.ioException() != null) {
throw sc.ioException();
}
} finally {
if (inputStream != null) {
inputStream.close();
}
if (sc != null) {
sc.close();
}
}
Make it as json and place it your project..
And when starting the application show a syncing state and load data to db ..
Do this only once.
You have a few options. As previously stated you could load the websites into json. This website should help automate that a bit. https://pdfmall.com/txt-to-json. This approach would still lead to an O(n) time complexity for searching.
While it will take a bit of memory, the method I'd recommend would be to build a custom object that holds all of those lines as Strings. Every time you start the app you would need to rebuild this object, but that's better than scanning constantly. You could implement an Async thread to do this in the background.
If you take this approach I would recommend storing them in a Olog(n) data structure like a binary tree. You don't want to just throw them into an Arraylist of strings, because then you'd be doing the same thing and having to scan through the entire array. If you store them in a tree, ordered by alphabetical order or perhaps length - whatever you want to do- then you reduce your search complexity from O(n) to Olog(n). Keep in mind when using custom objects, if you intend to pass them between Activities, you will need to implement Parcelable or another method.
Third you could use an Sql database, but in your case I don't believe this would speed anything up, so I wouldn't recommend it.
Keep the urls in the text file sorted alphabetically.
Load the file in a string array list.
Do a simple binary search in the sorted list to check for an specific url.
I am populating a listview with data(Text,image Urls etc) fetched from server.
This data might be updated at server side weekly/monthly.
What i want is , *when my application loads the listview first time, the data fetched from server is stored at application/client side, so as to be re-used later in redrawing the listview.*How can i acheive that.
One Possible Solution is:
Use Application class to download this data first time, store it in SQLlite db and always fetch results from DB while drawing a listview.Later i can use GCM Send-To-Sync tickles to update the sqldb data.
-Querying db everytime a listview is to be drawn, is it not a good practice.??
Any common practice to achieve this.
Any views about using Loaders/Loading Manager.
EDIT
The data in questions is a jsonpackage. With structure like(consider array size as 100 at max.)
[ // JSON Array
{ // JSON Object
"rank":1,"country":"China",
"population":"1,354,040,000",
"flag":"http://www.androidbegin.com/tutorial/flag/china.png"
}, (
]
Yes, you need to store the data fetched from server to database itself and then, bind the data from SQLiteDatabase to ListView
It cannot be said that Querying db everytime a listview is to be drawn, is it not a good practice.., because you need to fetch data only once when the activity is started or when the data within database is changed.
Re-Using already fetched data from server... means that you need a persistent storage and the storage should be updated periodically or on some user request.
It depends on what kind of data you have. Generally if it in a json package coming from the server then you can just keep that raw json and parse and populate the data on every app launch. But this will only be helpful if the amount of data is not too large. If the amount of data you have at any given moment is huge then you will have to parse and store the formatted data to a SQL DB, query and fetch the data on every app launch.
Also in case of manageable amount of data you can keep the data in-memory for the duration of the app lifecycle using static data model instances.
There can be multiple ways in which you can solve this problem but it all depends on how much data do you have and in what format and accordingly you take a call on which methodology will fetch you the best performance.
Create a directory in sdcard/internal storage, whichever is available using this static method.
/**
* Cache class to create dir, save, retrieve and delete files
* File names are hashCode of the url json is downloaded from.
**/
public class FileCache {
static File cacheDir;
static final String DIRECTORY_ADDRESS = "/Android/data/<app_package>/.<directory name>";
public static createDirectory(Context context){
if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
cacheDir=new File(Environment.getExternalStorageDirectory(),DIRECTORY_ADDRESS);
else
cacheDir=context.getCacheDir();
if(!cacheDir.exists())
cacheDir.mkdirs();
}
public static File getFile(String url){
String filename=String.valueOf(url.hashCode());
File f = new File(cacheDir, filename);
return f;
}
public static void saveFile(InputStream is, File file){
try {
OutputStream os = new FileOutputStream(file);
copyStream(is, os);
os.close();
} catch (FileNotFoundException e) {
} catch (IOException e) {
}
}
/**
* Clear all files at app uninstall or application or on
* low memory or some other event, using a boradcastreceiver/alarmmanager
**/
public static void clear(){
File[] files=cacheDir.listFiles();
if(files==null)
return;
for(File f:files)
f.delete();
}
}
Now create directory when application starts:
FileCache.createDirectory(context);
Once this is set, you have to fetch your JSONArray using an AsyncTask, store store it in a file
/**
* Add this in doinBackground()
**/
private File getJson(Stirng url) {
File f = FileCache.getFile(url);
if(f != null && f.isFile()) {
//file exists in directory, no need to download.
return f;
}
try {
URL fileUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection) fileUrl.openConnection();
conn.setConnectTimeout(120000);
conn.setReadTimeout(120000);
conn.setInstanceFollowRedirects(true);
InputStream is = conn.getInputStream();
FileCache.saveFile(is, f);
return f;
} catch (Exception ex) {
return null;
}
}
Now you have to run this thread every time you need to load and bind the data into listview. I am excluding the details, of how you will parse the json from file and convert it into List for the Adapter.
This AsyncTask will automatically fetch the data if not available in file, and load into listview, in onPostExecute(). So, from the 2nd time, it wont take much time, and data will be persisted in storage. You can also add pagination in the ListView, i.e if you have 1000 json hashes, you can read data from this file in batches, that will also be a good optimization.
Now, whenever the data gets updated (monthly/weekly), send a push notification using GCM, run a service, clear the old file using FileCache.clear(), and start this AsyncTask from the service, all in the background, so if the app is opened afterwards, you will get updated data from file storage.
Putting everything in sqlite is a similar alternative, then you have to use DbAdapters, CursorLoaders and CursorAdapters, and then using the service to update the db.
But since you dont have large data sets, I think file caching is sufficient.
Hope that helps. :)
I implemented as below:
(If you see any issues in the implementation,performance wise or any please comment)
Fetch Data(Json Array with a length of 50 ) from server for first time, store it as a string(JsonArray.toString()) in sharedpreferences and populate the listview.
When required to display the listview again, get stored jsonarraystring from sharedpreferences ,convert it back to jsonarray and populate the listview.
To get updated data from server, send a GCM tickle to update stored sharedpreferences array with new value in background.
This approach is working fine so far. Do you guys see any issues i can face at anystage.
ThankYou
I have an application, and I am trying to set up a fairly large SQLite database (one table with roughly 5000 rows) into it. I have built the DB classes and everything, and my app works when I tested on a smaller scale (400 rows), but now when I want to import my database, I get the out of memory error which I can't seem to find a way to get around.
The database is initially on MySQL on my web server, and I couldn't convert it for some odd reason but I managed to generate a text file with the queries to add all 5000 rows, which is 11.5mb in size. I have this file in my assets folder, and I am trying this to put it into my DB:
public void onHandleIntent(Intent intent) {
DBAdapter db = new DBAdapter(getApplicationContext());
db.open();
try {
InputStream is = getAssets().open("verbs_sql.txt");
db.executeSQL(convertStreamToString(is));
} catch (IOException e) {}
db.close();
// Run main activity
Intent i = new Intent(DatabaseReceiver.this, BaseActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
DatabaseReceiver.this.startActivity(i);
}
public static String convertStreamToString(InputStream is) throws IOException {
Writer writer = new StringWriter();
char[] buffer = new char[2048];
try {
Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
int n;
while ((n = reader.read(buffer)) != -1) {
writer.write(buffer, 0, n);
}
} finally {
is.close();
}
String text = writer.toString();
return text;
}
}
The out of memory error occurs on the StringWriter(), so it looks likes it putting that big file on to the memory. How can I solve this? I have also tried looping through all 5000 rows but after maybe 30 seconds I got the out of memory again.
I had the same problem. I tried so many ways to solve it but failed. At last i found the reason and i wondered. In my case the reason for the error was i was printing the entire response string in log cat. That data was very huge and it took heap memory.
Take care of the following
Remove Log cat printing of bulk data.
Try to use only one J-SON Array for all Operation under one resonse(Reuse it for all).
Try to avoid array-list usage.
Insert item when each item iterate from J-Son Array. That means don't follow the method in which we are taking the item as object and put it in to an array-list and passing array-list to DB-helper and from there iterate the object and insert.
Sqlite databases are just files, when you're trying to run thousands of inserts on the phone you're hitting the SD card over and over for file access.
What you'll want to do is create the sqlite database on your desktop and include the already created database in the app. If you need to regularly update the information in the database you could post it on a website and have the app download it, just make sure to only do large downloads over Wifi.
Check out this Tech Talk for more information.
Edit: See this for more information on creating an sqlite database in windows and including it in your app.
I believe you could do this in the way you want. The problem with the code you posted is that you are trying to convert the entire file to a string. I am fairly certain that this would fail even on a desktop machine.
I believe that you would have better luck if you tried to read in one line at a time and execute the SQL. Then read the next line. You could also reduce the size of the file by passing it through zip.
If I can find a couple of minutes, I will attach some code.
I've read many topic on stackoverflow,and google, about this argument but i can't orient myself with writing custom ArrayList into files !
I've an ArrayList of type "CustomType". "CustomType" have two String variable : "name" and "description".
I want to save this ArrayList for reading it after closing and reopening my app.
Does anyone help me doing this? and does anyone explain me what happen when i save/read?
you could write it simply as a csv file, like this,
1,name,desc
2,name2,desc2
3,name3,desc3
...
or you could use gson library - http://code.google.com/p/google-gson/, to convert it into a json string and then write it to a file, that way when you read it you could directly get the object from json,
i would personally use the second method,
Edit: for writing to csv
try
{
FileWriter writer = new FileWriter("<path>/MyFile.csv");
while(lst.next())
{
for(int i = 0; i < columnSize; i++)
{
writer.append(i);
writer.append(',');
writer.append(lst.get(i).getName());
writer.append(',');
writer.append(lst.get(i).getDesc());
}
writer.append('\n');
}
}
catch(Exception e)
{
e.printStackTrace();
}
(assuming the object in side the ArrayList has methods getName() and getDesc() )
this might be helpful in reading it again,
Get and Parse CSV file in android
To save any kind of data you have to choose any of the available data storage tequinue
1)shared preferences
2)internal storage
3)external storage
4)Sqlite
5)internet server
detailed docs here
throgh any one of these way you can store ArrayList or other data which you are using to populate the screen , so every time user will open the app data will be loaded from stored location
You could use XML files too:
http://androidideasblog.blogspot.com/2010/01/read-write-and-parse-xml-file-in.html
http://www.ibm.com/developerworks/opensource/library/x-android/
how to create ,read & write xml file in android by using DocumentBuilderFactory android application
http://www.anddev.org/viewtopic.php?p=30363
This way you can use all kind of text and symbols, if you are using a CSV you can not use the 'separator' in your content.
I want to know a good way to create a list in my android app. I have all info in my DB and want to load data from it each time I start the app and make a list from it (id and title).
What is the best approach?
Should I make a PHP-script that responds with a JSON encoded array with all list items or should I make an XML-file that generates each time the data in the DB changes that I import to the app each time it starts? or any other good way to do it?
Since all stuff are made by XML-files in android it feels like importing a XML would be a good thing, is it? And how do I import an XML-file from a web server into the app?
// Daniel
You can use either JSON or XML.
You can use the web service approach or you can include your db with your application.
In fact, I most often choose to create a sqlite3 database of my data and include it in the assets folder, which can be copied to the app's data folder on startup.
As for copying your sqlite3 database from assets/ to the db data directory, I found these instructions helpful.
In your situation I would pick JSON over XML for all the reason's stated in the following post: http://ajaxian.com/archives/json-vs-xml-the-debate
Plus, in android, there are JSON Array's built in by default so you don't have to do any extra passing of the code.
return new JSONArray("my json string goes here...");
Since we are talking about a mobile device, I would always generate changes in your php script rather than have a full sync as this will be a lot smaller in size that a full sync. However, you will need to give your user a option to do a full re-sync if this is applicable to your app. I would use a SQLite database to store the data and only update the changes in that.
To also make the stream smaller, you can gzip compress your output from php as this can be natively read by the android device. In my app, I compress 500kb down to ~110kb before transmitting, a huge saving on performance. Here a partial example of how to read the stream:
InputStream in = null;
HttpURLConnection httpConn = null; // you will have to write your on code for this bit.
if (httpConn.getContentEncoding() != null)
{
String contentEncoding = httpConn.getContentEncoding().toString();
if (contentEncoding.contains("gzip"))
{
in = new GZIPInputStream(httpConn.getInputStream());
}
}
else
{
in = httpConn.getInputStream();
}
I hope that this all makes sense, it's been a long day programming :)
Stu