How should I decompress a large data file in AsyncTask.doInBackground? - android

I have a large data file, which is zipped, and approximately 20MB. When it's unzipped, it's up to about 50MB. The following source code works fine. I found the original on the web somewhere else and modified it a bit. And this method is called within the AsyncTask.doInBackground.
So, what I want to know is, how can I save the on going status(? sorry, I don't know the proper English word) and resume the procedure later? I mean, this method takes a bit long time (about a minute on an emulator), and I know there is no way since the data is kind of huge. So, if a main activity of this method gets killed, I want to save the current status of decompressing the file, and when the activity gets active, I want to resume decompressing from the last point. Hope my explanation clears my intent.
I was thinking using a service, but I also want to interact with UI, such as showing a progress or whatever. I can't find good information to do that in the service when I roughly scan the reference, but is there a way to do that in the service? And do you think I should use it?
Anyway, my main point is how to resume decompressing a file.
private final static int CHUNK_SIZE = 32 * 1024;
byte[] _fileIOBuffer = new byte[CHUNK_SIZE];
public void unzipFile(DBFileDownloader downloader, File zipFile, String directory)
throws IOException
{
ZipInputStream in = null;
FileOutputStream os = null;
try
{
in = new ZipInputStream (new FileInputStream(zipFile));
ZipEntry entry = null;
while ((entry = in.getNextEntry ())!= null)
{
String entryName = entry.getName();
if (entry.isDirectory ()) {
File file = new File (directory, entryName);
file.mkdirs();
}
else {
File file = new File(directory, entryName);
if (file.exists()){
file.delete(); // I don't know how to append, so delete it always
}
os = new FileOutputStream (file);
int bytesRead = 0;
while ((bytesRead = in.read (_fileIOBuffer))!= -1) {
os.write(_fileIOBuffer, 0, bytesRead);
// progress procedure
}
os.close();
}
}
}
catch (FileNotFoundException e) {
Log.v("unzip", e.getMessage());
}
catch (IOException e) {
Log.v("unzip", e.getMessage());
}
finally{
if (in != null ){
in.close();
}
if (os != null ){
os.close();
}
}
}
Thanks in advance,
yokyo

So, if a main activity of this method
gets killed, I want to save the
current status of decompressing the
file, and when the activity gets
active, I want to resume decompressing
from the last point.
That will be extremely difficult, if not impossible.
I was thinking using a service, but I
also want to interact with UI, such as
showing a progress or whatever.
This is a fine plan. Just have the activity register a listener with the service, and the service calls that listener for "a progress or whatever".

Related

Does passing the Activity Context to an singleton affect the getContentResolver()?

**Problem:**I'm updating an app to Android 11 (API 30) and I cannot get the getContentResolver() to work in the following instance though it works elsewhere.
With help from this forum and reading the Scoped Storage docs, elsewhere in the app, the following code works within the Actiities they reside; which includes inputting and outputting from the shared download folder using the getContentResolver(). The file types are a variety.
What I'm trying to do: This app also allows a user (with approved permissions) to attach a variety of files (e.g., docs, audio, video, etc.) to notes in a database. In this case, a PDF. However, the file information gets passed to a Singleton class created to avoid redundancy in the same code within other activities, so I just pass the Context if needed.
As I mentioned, this code works fine when contained within the Activity and the only difference I see is the context is passed to a singleton class. I was hoping someone would see something I'm not.
I do realize the URI is not passed (see the resultLauncher code, but I don't believe that really matters, I can take the file path and change it to an URI, which I have successfully multiple times. Aside from passing to a singleton class, this is the only difference (I can see) compared to my other code.
I could be way off here, the first app and all. A user needs to have the option to attach multiple file types.
**INTENT**
btnAddFile.setOnClickListener(v -> {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
resultLauncher.launch(intent);
});
RESULT LAUNCHER
resultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null){
Uri uri = result.getData().getData();
String util = UriUtils.getPathFromUri(this, uri);
tableLayoutFiles.addView(BuildTableLayout.setupFilesTableRow(EditNote.this,tableLayoutFiles, util,false));
}
});
CODE WITHIN SINGLETON CLASS METHOD (a TableLayout of files and file paths TextViews are passed. So the following code resides in a loop iterating the TextView (i.e., tv) file paths.
File f = new File(tv.getText().toString());
String n = f.getName();
try {
InputStream fis = context.getContentResolver().openInputStream(Uri.fromFile(new File(f.getPath())));
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
for (int read; (read = fis.read(buf)) != -1; ) {
bos.write(buf, 0, read);
}
fis.close();
noteFiles.add(new Files(0, n, bos.toByteArray()));
} catch (Exception e) {
Log.e("getContentResolver", e.toString());
e.printStackTrace();
}
Well I solved my own problem and as I suspected, I had to pass the Uri directly to the getContentResolver().openInputStream() and could not convert a path using Uri.fromFile(). Another learners experience.
Adding a new variable HashMap<String, Uri>() called fileURIs to my method, I passed the file Uri to my method. I used a file path as the key and the file's Uri as the key value. I updated the getContentResolver().openInputStream() within the for loop to retrieve the correct file Uri.
Update:
InputStream fis = context.getContentResolver().openInputStream(fileURIs.get(f.getPath()));
File f = new File(tv.getText().toString());
String n = f.getName();
try {
InputStream fis = context.getContentResolver().openInputStream(fileURIs.get(f.getPath()));
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
for (int read; (read = fis.read(buf)) != -1; ) {
bos.write(buf, 0, read);
}
fis.close();
noteFiles.add(new Files(0, n, bos.toByteArray()));
} catch (Exception e) {
Log.e("getContentResolver", e.toString());
e.printStackTrace();
}
And I should clarify, the use of Uri.fromFile() does work on file/directories that are not "access denied", so passing a cache file path can be passed to the ContextResolver() without issue in this fashion. In my case, when files are imported from an xml, they get moved to the cache directory which eliminates the issue and why Uri.fromFile() works. That was part of my confusion and not being fully eductated on scoped storage.

I am working on an android app that need to create a text file and write in it. I use the following code

I am working on an android app that need to create a text file and write in it. I use the following code:
public void onButtonClick(View view) {
writeInFile("Hello world");
}
public void writeInFile(String string) {
String dir = android.os.Environment.getExternalStorageDirectory().getAbsolutePath();
File file = new File(dir, "MyFile.txt");
try {
FileWriter fw = new FileWriter(file,true);
//fw.append(string);
fw.write(string);
Toast.makeText(this, "You wrote in the file", Toast.LENGTH_SHORT).show();
} catch(Exception e){
e.printStackTrace();
}
}
When I run the app, the file is created, I can see the toast message, but nothing in written in it. I have tried fw.append and fw.write. In my manifest I have yet written the necessary permission to write on external storage. I want to write directly in the external storage of my Samsung Galaxy Tab 4 (Marshmallow), not on the SD card.
First, always close(). You are not closing your FileWriter.
Second, always flush(). You are not flushing your FileWriter.
Third, even if you flush() and close(), due to the way buffered filesystems work, the bytes may not yet be written to disk. You're flushing from the Android app to the OS, but the writes to the disk may be buffered and delayed by the OS. To deal with this, I don't use FileWriter, but FileOutputStream (sometimes wrapped in an OutputStreamWriter), so you can call getFd().sync() on the FileOutputStream after calling flush() and before calling close().
Fourth, you won't be able to see this file and its contents in your desktop OS file manager until it gets indexed by MediaStore. Either test your results using other means (e.g., adb pull), or use MediaScannerConnection and its scanFile() method to tell the MediaStore to index your newly-created file.
The following code demonstrates everything but the MediaScannerConnection bit:
private static class SaveThread extends Thread {
private final String text;
private final File fileToEdit;
SaveThread(String text, File fileToEdit) {
this.text=text;
this.fileToEdit=fileToEdit;
}
#Override
public void run() {
try {
fileToEdit.getParentFile().mkdirs();
FileOutputStream fos=new FileOutputStream(fileToEdit);
Writer w=new BufferedWriter(new OutputStreamWriter(fos));
try {
w.write(text);
w.flush();
fos.getFD().sync();
}
finally {
w.close();
}
}
catch (IOException e) {
Log.e(getClass().getSimpleName(), "Exception writing file", e);
}
}
}
(from this sample project)

Android app writes to file and seems to be deleted when activity is finished

I have an android app that is writing a values to a file that the app also creates. I am able to write to the file and then again read from the file. However, as soon as that activity is finished, it seems that the file is now gone, or loses it's values.
I know you can't browse the files through explorer unless you root your phone and/or run the adb server as a specific user.
Here is my code for writing to the file:
public void savePrices(View view) {
FileOutputStream outputStream;
File getFilesDir = this.getFilesDir();
File filePathOne = new File(getFilesDir, filename);
try {
outputStream = openFileOutput(filename, Context.MODE_PRIVATE);
for (int i = 0; i < priceArray.length; i++) {
outputStream.write(String.format("%.2f\n", priceArray[i]).getBytes());
}
Toast.makeText(this, "Prices saved successfully!", Toast.LENGTH_SHORT).show();
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
Here is my code that reads the file:
public void loadPrices(View view) {
int i = 0;
final InputStream file;
BufferedReader reader;
try{
file = getAssets().open(filename);
reader = new BufferedReader(new InputStreamReader(file));
String line = reader.readLine();
while(line != null){
line = reader.readLine();
priceArray[i] = Double.parseDouble(line);
i++;
}
} catch(IOException ioe){
ioe.printStackTrace();
hamburgerPriceText.setText(String.format("%.2f", priceArray[0]));
hotDogPriceText.setText(String.format("%.2f", priceArray[1]));
chipsPriceText.setText(String.format("%.2f", priceArray[2]));
beerPriceText.setText(String.format("%.2f", priceArray[3]));
popPriceText.setText(String.format("%.2f", priceArray[4]));
Toast.makeText(this, "Prices loaded successfully!", Toast.LENGTH_SHORT).show();
}catch (NumberFormatException e) {
Log.e("Load File", "Could not parse file data: " + e.toString());
}
}
After I call the save method which sets the values in the array and saves the values to the file, I run a clear method that removes all the values on the activity fields and in the array. So when I run the read method and it populates the fields on the activity, I know the values are coming from reading the file. This is the only way that I know that I'm saving and reading from the file successfully.
My question is how do I make it permanent? If I close the activity that saves the values and then immediately run the read method, all the values are 0.
Is there something that I am missing? How can I write to a file so if the activity is closed, or the app is completely closed, I can still retain the values?
Here is my code that reads the file:
There is nothing in that code that reads a file. It is reading some stuff out of the your app's assets. Also, for some reason, it is only updating the UI if you have an exception.
So when I run the read method and it populates the fields on the activity, I know the values are coming from reading the file.
No, they are coming from your app's assets, and you are only populating the fields if you have an IOException.
My question is how do I make it permanent?
Step #1: Actually read from the file. Since you are using openFileOutput() to write to the file, use openFileInput() to read from the file.
Step #2: Update the UI when you successfully read in the data, not in the catch block for the IOException.

android copy /dev/log/* to SD card failer

GUYS
I'm new to android and this is my first post in StackOverflow.English as my second language,I'm not that good at it.I just looked for some answers here before,Now I think it's time for me to get involved in it.
There have been a problem occured when I try to copy the system log which located in /dev/log/* to my SD card.After some search on the answers here,I came across Copy file (image) from CacheDir to SD Card.So I had my code below:
private final String srcLocation = "/dev/log/radio";
private final String desLocation = "/mnt/sdcard/radio";
FileInputStream src;
FileOutputStream dst;
FileChannel mFCsrc;
FileChannel mFCdst;
public boolean copyFile(String sourceLocation, String destLocation) throws IOException {
try {
File sd = Environment.getExternalStorageDirectory();
if(sd.canWrite()){
File source=new File(sourceLocation);
File dest=new File(destLocation);
if(!dest.exists()){
dest.createNewFile();
}
if(source.exists()){
src = new FileInputStream(source);
dst = new FileOutputStream(dest);
mFCsrc = src.getChannel();
mFCdst = dst.getChannel();
mFCsrc.transferTo(0, mFCsrc.size(), mFCdst);
}
}
return true;
} catch (Exception ex) {
ex.printStackTrace();
return false;
} finally {
if (mFCsrc != null) {
mFCsrc.close();
}
if (mFCdst != null) {
mFCdst.close();
}
}
}
I do have the file in my SD card which I can see it from my DDMS window,but it's size is 0.So,anyone gets a clue? Thanks in advance.(I try to give you a picture of my DDMS window,but since my reputation is not enough,I cann't use a picture.I'm sorry about that!!)
You should debug. Step trough your code with the debugger and check what is happening.
Random thing that might be happening, but we have no way of knowing for sure: If "source" doesn't exist, you will create dest, but because source.exists() will return false,you don't do anything after that. You'll end up with the current behaviour, a newly created file without contents.

Copy a text file on SD card at the time of application installation?

I am working on an android game. I want to copy a text file to external SD card when the user installs the game for the first time. The text file is important for properly running the game.
How can I do that? Where should i place the text file in eclipse source project so that when i build the apk file, my text file also gets bundled in it and when a use installs application from that apk file the text file gets copied to "SDcard\data" folder.?
What code should i write and where, so that it gets executed only once at installation time.
Thanks in advance
This is the methods I use to copy a file to the sd card when the app is first installed:
public class StartUp extends Activity {
/**
* -- Called when the activity is first created.
* ==============================================================
**/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FirstRun();
}
private void FirstRun() {
SharedPreferences settings = this.getSharedPreferences("YourAppName", 0);
boolean firstrun = settings.getBoolean("firstrun", true);
if (firstrun) { // Checks to see if we've ran the application b4
SharedPreferences.Editor e = settings.edit();
e.putBoolean("firstrun", false);
e.commit();
// If not, run these methods:
SetDirectory();
Intent home = new Intent(StartUp.this, MainActivity.class);
startActivity(home);
} else { // Otherwise start the application here:
Intent home = new Intent(StartUp.this, MainActivity.class);
startActivity(home);
}
}
/**
* -- Check to see if the sdCard is mounted and create a directory w/in it
* ========================================================================
**/
private void SetDirectory() {
if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) {
extStorageDirectory = Environment.getExternalStorageDirectory().toString();
File txtDirectory = new File(extStorageDirectory + "/yourAppName/txt/");
// Create
// a
// File
// object
// for
// the
// parent
// directory
txtDirectory.mkdirs();// Have the object build the directory
// structure, if needed.
CopyAssets(); // Then run the method to copy the file.
} else if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED_READ_ONLY)) {
AlertsAndDialogs.sdCardMissing(this);//Or use your own method ie: Toast
}
}
/**
* -- Copy the file from the assets folder to the sdCard
* ===========================================================
**/
private void CopyAssets() {
AssetManager assetManager = getAssets();
String[] files = null;
try {
files = assetManager.list("");
} catch (IOException e) {
Log.e("tag", e.getMessage());
}
for (int i = 0; i < files.length; i++) {
InputStream in = null;
OutputStream out = null;
try {
in = assetManager.open(files[i]);
out = new FileOutputStream(extStorageDirectory + "/yourAppName/txt/" + files[i]);
copyFile(in, out);
in.close();
in = null;
out.flush();
out.close();
out = null;
} catch (Exception e) {
Log.e("tag", e.getMessage());
}
}
}
private void copyFile(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
}
For this target the best way is make SharedPreferences or your file must be added in "assets" directory in android project.
as per link
There is the ACTION_PACKAGE_ADDED Broadcast Intent, but the application being installed doesn't receive this.
So it looks using SharedPreferences is the easiest way...
SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(this);
boolean firstRun = p.getBoolean(PREFERENCE_FIRST_RUN, true);
p.edit().putBoolean(PREFERENCE_FIRST_RUN, false).commit();
Put the file in the assets folder. Then, using whatever logic you come up with, when your app launches determine if it is the first run of the app.
If it is you can use getAssets() from an Activity to access the asset file and just copy it to wherever necessary.
Since the file on the sdcard is something that could be accidentally deleted by the user, you should probably check directly for its presence (and possibly verify contents) rather than trying to use something independent such as a shared preference to tell if this is the first run of the activity.
For purposes of potential app upgrades, you should probably put a version number in the file, and check that.
If the file is something you want to let power users manually edit (to change expert options) then you may have a little bit of a challenging situation to handle on upgrade.

Categories

Resources