i've an activity with a button and a label.
On button click my app must download several files ( about 9000 ).
If user clicks again on button, the download must stop and on another click it must start from the beginning.
So this is what i do:
In activity:
file.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Button b = (Button)v;
if(canFile){
b.setText("Stop download");
changeLabelInfo("Getting file list...");
labelFile.setVisibility(View.VISIBLE);
fileTask.start();
}else{
b.setText("Download files");
if(fileTask.isAlive()){
fileTask.interrupt();
fileTask = null;
fileTask = new UpdateFilesThread(this);
}
labelFile.setVisibility(View.INVISIBLE);
Kernel.setManualUpdate("file",false);
}
canFile = !canFile;
}
});
The thread that must download files is UpdateFilesThread:
public class UpdateFilesThread extends Thread{
private MainActivity activity;
private final String rootPath = "/mnt/local/";
public UpdateFilesThread(MainActivity activity){
this.activity = activity;
}
public void run(){
String json = getFilesURL();
JSONObject a = (JSONObject)JSONValue.parse(json);
boolean isZip = false,canDownload = true;
String[] keys = new String[]{"key1","key2","key3","key4"};
for(String key:keys){
Object folder = (Object)a.get(key);
if(folder instanceof JSONObject){
JSONObject fold = (JSONObject)folder;
for(Object path_o:fold.keySet()){
path = path_o.toString().replace(" ", "%20");
if(local.endsWith(".php")){
isZip = true;
try {
Jsoup.connect(mywebserviceURL).data("path",path).timeout(0).post(); // If php generate zip containing php file
} catch (IOException e) {
canDownload = false;
}
}
if(canDownload){
try{
if(downloadFromUrl(path,isZip))
//SAVE URL DOWNLOADED
}catch(Exception e){
e.printStackTrace();
}
}
canDownload = true;
isZip = false;
}
}
}
a.remove(key);
}
private String getFilesURL(){
try {
MultipartEntity entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
entity.addPart("type", new StringBody("all"));
HttpPost post = new HttpPost("mywebserviceURL");
post.setEntity(entity);
HttpClient client = new DefaultHttpClient();
HttpResponse response = client.execute(post);
return EntityUtils.toString(response.getEntity());
} catch (UnsupportedEncodingException e) {
Support.writeError(e, null);
e.printStackTrace();
return "";
} catch (ClientProtocolException e) {
Support.writeError(e, null);
e.printStackTrace();
return "";
} catch (ParseException e) {
Support.writeError(e, null);
e.printStackTrace();
return "";
} catch (IOException e) {
Support.writeError(e, null);
e.printStackTrace();
return "";
}
}
public boolean downloadFromUrl(String path,boolean isZip){
InputStream is = null;
FileOutputStream fos = null;
String localFilename = rootPath+path;
String local = isZip?rootPath+"tmp.zip":localFilename;
boolean return_ = false;
try {
URL url = new URL(isZip?mywebserviceURLZip:mywebserviceURLZip+path);
URLConnection urlConn = url.openConnection();
urlConn.setReadTimeout(0);
is = urlConn.getInputStream();
fos = new FileOutputStream(local);
byte[] buffer = new byte[51200];
int len;
while ((len = is.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
fos.close();
is.close();
if(isZip){
ZipFile zip = new ZipFile(local);
zip.extractAll(rootPath);
new File(local).delete();
}
return_= true;
}catch(Exception e){
e.printStackTrace();
return false;
}
return return_;
}
}
My problem borns when user clicks two time the button ( stop downloading and start again ).
The prompt error says that the thread is already startend and in running.. how can i solve it? I know that asyncTask should be better but i've problem cause in my application there are so many thread running and the device is so poorly peforming..
It's possible to stop definitelly a thread? are there other better solution?
Try implementing an AsyncTask. When the user first taps the button call the task's execute (Params... params). On the second tap call the task's cancel (boolean mayInterruptIfRunning). Put the download functionality in the task's doInBackground (Params... params)
Your run thread needs to occasionally check isInterrupted() and exit if it returns true. Otherwise the thread will never be canceled.
I think your entire architecture is wrong though. Downloading 9000 files onto a mobile phone? Even if each file is only 1KB, that's a huge amount of memory for a mobile app. And at the very least you ought to zip up that data and download a zip, for your own sanity.
Related
I will like the ControlFx MaskerPane to be showing while the request is going on. I have this code on a button action to make the network request.
if(mp==null){
mp=new MaskerPane();
stackPane.getChildren().add(mp);
mp.setVisible(false);
}
try {
mp.setVisible(true);
JSONObject verifyKey = new Network().verifyKey(pinF.getText(), phoneF.getText(), mp);
if (verifyKey != null) {
String string = verifyKey.getString("ans");
mp.setVisible(false);
if (string.equals("1")) {
// Thanks for buying
} else {
error.setText("Key already used");
error.setVisible(true);
}
}
} catch (JSONException ex) {
mp.setVisible(false);
Logger.getLogger(Buy.class.getName()).log(Level.SEVERE, null, ex);
}
However the verify method in my Network class looks like
public JSONObject verifyKey(String key, String phone, MaskerPane mp){
this.mp=mp;
String url = "http://theUrl";
String httpcall = httpcall(url, "func","verify","key",key,"phone",phone,"type","eDesk");
try {
return new JSONObject(httpcall);
} catch (JSONException ex) {
Logger.getLogger(Network.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
public String httpcall(String url,String ...args) {
try {
// String charset = "UTF-8"; // Or in Java 7 and later, use the constant: java.nio.charset.StandardCharsets.UTF_8.name()
String charset = java.nio.charset.StandardCharsets.UTF_8.name();
String formatin = "";
Object values[] = new String[args.length/2];
int valCount =0;
for (int i = 0; i < args.length; i+=2) {
formatin+=args[i]+"=%s&";
values[valCount]=URLEncoder.encode(args[i+1], charset);
valCount++;
}
String query = String.format(formatin,values);
query=query.substring(0, query.length()-1);
//Remember to remove proxy lines before production
//SocketAddress addr = new InetSocketAddress("127.0.0.1", 8080);
//Proxy proxy = new Proxy(Proxy.Type.HTTP, addr);
URLConnection connection = new URL(url).openConnection(proxy);
connection.setDoOutput(true); // Triggers POST.
connection.setRequestProperty("Accept-Charset", charset);
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=" + charset);
try (OutputStream output = connection.getOutputStream()) {
output.write(query.getBytes(charset));
}
InputStream response = connection.getInputStream();
//return getStringFromInputStream(response);
} catch (UnsupportedEncodingException ex) {
if(mp!=null)mp.setVisible(false);
Logger.getLogger(Buy.class.getName()).log(Level.SEVERE, null, ex);
}catch (MalformedURLException ex) {
if(mp!=null)mp.setVisible(false);
Logger.getLogger(Buy.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
//if(mp!=null)mp.setVisible(false);
Logger.getLogger(Buy.class.getName()).log(Level.SEVERE, null, ex);
//return "{\"ans\":\"1\"}";//xpectd on success
//return "{\"ans\":\"0\",\"err\":\"1\"}";//xpectd on failure
}
return null;
}
The problem is the maskerpane wont show until the network request is called is complete.
I found out I need to execute the network operation in another thread. One can use Executor services. These can be found in javafx concurrency package
if (mp == null) {
mp = new MaskerPane();
stackPane.getChildren().add(mp);
mp.setVisible(false);
}
mp.setVisible(true);
Runnable task = () -> {
try {
JSONObject verifyKey = new Network().verifyKey(pinF.getText(), phoneF.getText(), mp);
if (verifyKey != null) {
String string = verifyKey.getString("ans");
//Updating ui from another thread need you to use Platform.runLatter... So wrapping the below with runLatter to avoid exceptions
mp.setVisible(false);
if (string.equals("1")) {
// Thanks for buying
} else {
error.setText("Key already used");
error.setVisible(true);
}
}
Thread back = new Thread(task);
back.setPriority(Thread.MAX_PRIORITY);
back.setDaemon(true);
back.start();
} catch (JSONException ex) {
mp.setVisible(false);
Logger.getLogger(Buy.class.getName()).log(Level.SEVERE, null, ex);
}
}
// Run the task in a background thread
Thread back = new Thread(task);
back.setPriority(Thread.MAX_PRIORITY);
back.setDaemon(true);
back.start();
https://docs.oracle.com/javase/8/javafx/interoperability-tutorial/concurrency.htm
So I have an app which downloads certain files, dedicated to a client of mine who is hosting his files on a remote location, and i'm doing so using the code below:
public class DownloadService extends IntentService {
private int result = Activity.RESULT_CANCELED;
public DownloadService() {
super("DownloadService");
}
#Override
protected void onHandleIntent(Intent intent) {
String urlPath = intent.getStringExtra(URL);
String fileName = intent.getStringExtra(FILENAME);
File output = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
fileName);
if (output.exists()) {
output.delete();
}
URLConnection streamConnection = null;
InputStream stream = null;
FileOutputStream fos = null;
try {
URL url = new URL(urlPath);
streamConnection = url.openConnection();
stream = streamConnection.getInputStream();
streamConnection.connect();
long lengthofFile = streamConnection.getContentLength();
InputStream reader = stream;
bis = new BufferedInputStream(reader);
fos = new FileOutputStream(output.getPath());
int next = -1;
int progress = 0;
int bytesRead = 0;
byte buffer[] = new byte[1024];
while ((bytesRead = bis.read(buffer)) > 0) {
fos.write(buffer, 0, bytesRead);
progress += bytesRead;
int progressUpdate = (int)((progress * 100) / lengthofFile);
Intent testIntent = new Intent(".MESSAGE_INTENT");
testIntent.putExtra(PERCENTAGE, progressUpdate);
sendBroadcast(testIntent);
}
result = Activity.RESULT_OK;
fos.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (stream != null) {
try {
stream.close();
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
publishResults(output.getAbsolutePath(), result);
}
private void publishResults(String outputPath, int result) {
Intent intent = new Intent(".MESSAGE_INTENT");
intent.putExtra(FILEPATH, outputPath);
intent.putExtra(RESULT, result);
sendBroadcast(intent);
}
}
and to call this service i would use:
Intent intent = new Intent(MainActivity.getAppContext(), DownloadService.class);
intent.putExtra(DownloadService.FILENAME, downloadFileName[item]);
intent.putExtra(DownloadService.URL, urlDownload[item]);
MainActivity.getAppContext().startService(intent);
now this allows user to download one file at a time, however if the user downloads another file, the second file will have to wait till the first file is done downloading.
now what is happening in my case is:
1- First download FILE_1 is downloading, and in the status is says FILE_1.
2- User clicks a new file download, the status changes the first file name to the second file name, and waits till FILE_1 finishes download to start with FILE_2 however the active download is changed from FILE_1 to FILE_2.
questions:
is there a way to call DownloadService multiple times for multiple files?
is it possible to fix the problem i'm facing? treating download intent services as two different intents?
UPDATE
I managed to solve this issue by assigning a unique Int ID per file, each ID will point to a position in the listview which displays the files being downloaded or queued, then i work with each file on it's own.
Following code uses commons-io-2.4.jar library to make your work easy by handling low level data movements as you would focus on method in hand
URL someUrl = new URL("your url String"); //
File someFile = new File("your file, where you want to save the data");
FileUtils.copyURLToFile(someUrl, someFile );
if you want to call this statement few time to download different files from the server following code might give you an idea what you might want to do, but you will have to write it's equivalent code to run in android which you want to probably AsyncTask
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import org.apache.commons.io.FileUtils;
public class DownloadTest {
public static void main(String[] args) {
Thread thread = new Thread(){
#Override
public void run() {
// TODO Auto-generated method stub
super.run();
try {
dowanloadFile(new URL("some url"), new File("some file"));
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
thread.start();
}
private static void dowanloadFile(URL url, File file){
try {
FileUtils.copyURLToFile(url, file );
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
I try to fetch stringBuilder object to the my main activity. I check my json file or parsing codes they works well. However when I try to fetch stringbuilder it gave an error:
A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks.
java.lang.Throwable: Explicit termination method 'close' not called
code for Server.java is as below
`
public class Server extends Activity {
static StringBuilder stringBuilder = new StringBuilder();
public Server(){
try {
JSONObject obj = new JSONObject(loadJSONFromAsset());
JSONArray project = obj.getJSONArray("project");
for (int i = 0; i < project.length(); i++) {
JSONObject ss = project.getJSONObject(i);
stringBuilder.append(ss.getString("title") + "\n");
JSONArray post = ss.getJSONArray("posts");
for(int j = 0; j < post.length();j++){
JSONObject posts = post.getJSONObject(j);
stringBuilder.append(posts.getString("id") +"\n");
JSONArray tag = posts.getJSONArray("tags");
for(int k = 0; k < tag.length();k++){
stringBuilder.append(tag.getString(k) +"\n");
}
}
}
}
catch (JSONException e) {
stringBuilder.append("error");
e.printStackTrace();
}
}
public String getString(){
return stringBuilder.toString();
}
public String loadJSONFromAsset() {
String json = null;
try {
InputStream is = getAssets().open("cat.json");
int size = is.available();
byte[] buffer = new byte[size];
is.read(buffer);
is.close();
json = new String(buffer, "UTF-8");
} catch (IOException ex) {
ex.printStackTrace();
return null;
}
return json;
}}
and here is my MainActivity.java
public class MainActivity extends Activity {
TextView jsonDataTextView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
jsonDataTextView = (TextView) findViewById(R.id.textView);
Server s = new Server();
jsonDataTextView.setText(s.stringBuilder.toString());} }
Is there any solution?
The InputStream may not be closed in the try block of your loadJSONFromAsset method, and it should.
public String loadJSONFromAsset() {
String json = null;
InputStream is = null;
try {
is = getAssets().open("cat.json");
int size = is.available();
byte[] buffer = new byte[size];
is.read(buffer);
json = new String(buffer, "UTF-8");
}
catch (IOException ex) {
ex.printStackTrace();
}
finally {
if (is != null) {
try {
is.close();
}
catch (IOException ex) {
// Do you want to handle this exception?
}
}
}
return json;
}
Note: Something bothers me in your Server constructor, in the catch block you try to append "error" to your StringBuilder. Do you know that here, the StringBuilder may not be empty. Indeed in the try block, one (or more) attempt(s) to append some string to it may work before something goes wrong at some point.
Note 2: Server as a non Activity
public class Server {
private Context mContext;
public Server(Context context) {
mContext = context;
...
}
...
public String loadJSONFromAsset() {
...
mContext.getAssets().open("cat.json");
}
}
then in your MainActivity
Server s = new Server(this);
I know this has been asked a few times here, but I'm not sure which way I should go. This code downloads the html file okay, but I get an IOException when trying to write the html to a file. I've tried many suggestions on sof, but none seem to work for me and I'm at a loss as it seems it should be working.
class Downloader extends AsyncTask<URL, Void, Void> {
String site = getResources().getString(R.string.sched_hd_url);
File sdCard = Environment.getExternalStorageDirectory();
File dir = new File(sdCard.getAbsolutePath() + "/directory/");
File file = new File(dir, "file.html");
#Override
protected Void doInBackground(URL... urls) {
try {
URL url = new URL(site);
URLConnection yc = url.openConnection();
BufferedInputStream in = new BufferedInputStream(new URL(site).openStream());
OutputStream out = new FileOutputStream(file);
int total = 0;
int count;
byte data1[] = new byte[1024];
while ((count = in.read(data1)) != -1) {
out.write(data1);
total += count;
}
in.close();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
protected void onPostExecute(Void result) {
progress.setVisibility(View.INVISIBLE);
finish();
}
}
I run this code and no file appears in the directory that I specified. The directory already exists, and I do have the permissions in my manifest. Any suggestions would be greatly appreciated.
So my problem was a couple of things. First, I want to thank those that commented. In my question, I did neglect to put in the out.close(); method. When that didn't work, I was looking at the string which held the URL I wanted to use. That had errors in it. This fixed the download problem, but I wanted to download from a place where the .html file was not in the URL (example: http://www.example.com/ instead of http://www.example.com/index.html). It worked for the latter but not the former. So instead of using URLConnection I used HttpURLConnection. Here is my working code:
class Downloader extends AsyncTask<URL, Void, Void> {
String site = getResources().getString(R.string.sched_hd_url);
File sdCard = Environment.getExternalStorageDirectory();
File dir = new File(sdCard.getAbsolutePath() + "/directory/");
File file = new File(dir, "file.html");
#Override
protected Void doInBackground(URL... uri) {
FileOutputStream out = null;
if (file.exists()) {
try {
file.delete();
file.createNewFile();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try {
URL url = new URL(site);
HttpURLConnection yc = (HttpURLConnection) url.openConnection();
BufferedInputStream in = new BufferedInputStream(
new URL(site).openStream());
out = new FileOutputStream(file);
int total = 0;
int count;
byte data1[] = new byte[1024];
while ((count = in.read(data1)) != -1) {
out.write(data1);
total += count;
}
in.close();
out.close();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
protected void onPostExecute(Void result) {
progress.setVisibility(View.INVISIBLE);
finish();
}
}
Also another error in my question was in regards to no implementation checking to see if the file had already existed. Thanks again for the help.
am writing a downloadManager and i need a hint. there i could create an event an fire it when ever i wanted. but i cant seem to find how to do that in java. what i want to do is i want to create an event for my class and then fire it inside one of the classes' member methods. now when ever this Class is called and sees that the download is finish(i.e. some variable has reached 100 for example) it fires an event indicating the situation. how can i create that in java?
public class DownloadManager
{
static Queue<AvailableGame> downloadQueue;
static Integer currenProgress;
static String currentDownload;
static Boolean isRunning;
/**
* Start download
*/
public void startDownloading()
{
AsyncTask task = new AsyncTask()
{
#Override
protected Object doInBackground(Object[] objects)
{
downloadNextFile();
return null;
private static String downloadFile(String downloadUrl)
{
String toDownload = downloadUrl;
String fileName = getFileNameFromUrl(toDownload);
// take CPU lock to prevent CPU from going off if the user
// presses the power button during download
PowerManager pm = (PowerManager)
MainActivity.getContext().getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"DownloadManager");
wl.acquire();
try
{
InputStream input = null;
OutputStream output = null;
HttpURLConnection connection = null;
try
{
URL url = new URL(toDownload);
connection = (HttpURLConnection) url.openConnection();
connection.connect();
// expect HTTP 200 OK, so we don't mistakenly save error report
// instead of the file
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK)
{
return "Server returned HTTP " + connection.getResponseCode() + " "
+ connection.getResponseMessage();
}
int fileLength = connection.getContentLength();
// download the file
input = connection.getInputStream();
output = new FileOutputStream("/sdcard/" + fileName);
byte data[] = new byte[4096];
long total = 0;
int count;
while ((count = input.read(data)) != -1)
{
total += count;
// publishing the progress....
if (fileLength > 0)
{
currenProgress = ((int) (total * 100 / fileLength));
currentDownload = "Downloading " + fileName;
}
output.write(data, 0, count);
}
}
catch (Exception e)
{
return e.toString();
}
finally
{
try
{
if (output != null)
{
output.close();
}
if (input != null)
{
input.close();
}
}
catch (IOException ignored)
{
}
if (connection != null)
{
connection.disconnect();
}
}
}
finally
{
wl.release();
}
return null;
}
}
How can i save information when my download is finish,the second probleem is how to a
First I think you should be using Android's DownloadManager for this. If you are, you can register a BroadcastReceiver that detects when a download is finished.
There is a full example you can download and run on your phone with full source code for you to see how this can be done: https://github.com/commonsguy/cw-omnibus/tree/master/EmPubLite/T16-Update
Check specifically the files DownloadCheckService.java and DownloadCompleteReceiver.java.
Hope it helps.