First, I extended IntentService to build my DonwloadService class. It was OK, but a download queue had been created and 2nd download had to wait for the 1st to finished etc.
So I decided to extend Service class (following the docs). I've just added download function and function for publishing results (listened by receiver in activity):
public class DownloadService extends Service
{
private static final String TAG = DownloadService.class.getSimpleName();
public static final String EXTRA_DOWNLOAD = "EXTRA_DOWNLOAD";
public static final String EXTRA_POSITION = "EXTRA_POSITION";
public static final String INTENT_NOTIFICATION = "INTENT_NOTIFICATION";
public static final String EXTRA_RESULT = "EXTRA_RESULT";
public static final String EXTRA_PROGRESS = "EXTRA_PROGRESS";
public static final String EXTRA_PATH = "EXTRA_PATH";
public static final String EXTRA_INDETERMINABLE = "EXTRA_INDETERMINABLE";
public static final int RESULT_PROGRESS = 123;
private int mResult = Activity.RESULT_CANCELED;
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
private final class ServiceHandler extends Handler
{
public ServiceHandler(Looper looper)
{
super(looper);
}
#Override
public void handleMessage(Message message)
{
// Download file
download(message.getData());
// Stop the service using the startId, so that we don't stop
// the service in the middle of handling another job
stopSelf(message.arg1);
}
}
#Override
public void onCreate()
{
// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block. We also make it
// background priority so CPU-intensive work will not disrupt our UI.
HandlerThread handlerThread = new HandlerThread("DownloadServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
handlerThread.start();
// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper = handlerThread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
// For each start request, send a message to start a job and deliver the
// start ID so we know which request we're stopping when we finish the job
Message message = mServiceHandler.obtainMessage();
message.setData(intent.getExtras());
message.arg1 = startId;
mServiceHandler.sendMessage(message);
return START_REDELIVER_INTENT;
}
#Nullable
#Override
public IBinder onBind(Intent intent)
{
// No binding provided
return null;
}
#Override
public void onDestroy()
{
Log.d(TAG, "Service done");
}
/**
* Handles file download
*
* #param bundle
*/
private void download(Bundle bundle)
{
if (bundle != null) {
return;
}
Download download = bundle.getParcelable(EXTRA_DOWNLOAD);
int position = bundle.getInt(EXTRA_POSITION, -1);
File downloadedFile = new File(Environment.getExternalStorageDirectory(), position + ".jpg");
if (downloadedFile.exists()) {
downloadedFile.delete();
}
FileOutputStream fileOutputStream = null;
int filesize = -1;
try {
fileOutputStream = new FileOutputStream(downloadedFile.getPath());
URL url = new URL(download.getUrl());
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.connect();
filesize = connection.getContentLength();
InputStream reader = new BufferedInputStream(connection.getInputStream());
byte data[] = new byte[1024];
int next = -1;
int total = 0;
while ((next = reader.read(data)) != -1) {
mResult = RESULT_PROGRESS;
total += next;
publishResult(downloadedFile.getAbsolutePath(),
(filesize > 0) ? Math.round(total * 100 / filesize) : Math.round(total / 1024),
position, filesize <= 0);
fileOutputStream.write(data, 0, next);
}
mResult = Activity.RESULT_OK;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
publishResult(downloadedFile.getAbsolutePath(), 100, position, filesize <= 0);
}
private void publishResult(String downloadPath, int progress, int positionInList,
boolean indeterminable)
{
Intent intent = new Intent(INTENT_NOTIFICATION);
intent.putExtra(EXTRA_PATH, downloadPath);
intent.putExtra(EXTRA_PROGRESS, progress);
intent.putExtra(EXTRA_POSITION, positionInList);
intent.putExtra(EXTRA_INDETERMINABLE, indeterminable);
intent.putExtra(EXTRA_RESULT, mResult);
sendBroadcast(intent);
}
}
But still there is a download queue, no parallel downloads.
If you want to run a task repeatedly on different sets of data, but you only need one execution running at a time, an IntentService suits your needs. To automatically run tasks as resources become available, or to allow multiple tasks to run at the same time (or both), you need to provide a managed collection of threads. To do this, use an instance of ThreadPoolExecutor, which runs a task from a queue when a thread in its pool becomes free. To run a task, all you have to do is add it to the queue.
Reference:
Creating a Manager for Multiple Threads
Related
I have good concept of starting and using the basic service. I mean not to complicated. In My app I want a service which should not be killed in any situation and should download some files from the server then it should call stopSelf. I have made my service in the following way. But before sharing its whole code just let me tell you what I am doing
In Service I am passing the series of url (string array) which has to download all files from the server.
I am using the async task to download from the server.
Under this whole process I am getting a 1st response that is in xml then I parse it , and get the JSON string (sorry about that my web service designer is a numb like me). so after these two conversion I store the data in the database and then starts downloading files and saving them to device and store their path in the database. (this all works fine)
I am calculating and updating progress in the notification bar. (showing user how much the files has been downloaded)
what I really want
I want that my service should not be killed when user removes it from the recent app list , so that it should continue to download and continue to update the status in notification bar. I am using Notification manager to update the progress.
What is really happening
When I close my app from recent app tray, I think my service gets killed and the downloading process stops, and It also stops updating the progress of notification in notification bar, Where As I want it to continue to run until the download process is finished.
Here is my code it is simplified as some methods are really not worthy
to be discussed here Such as Parsing the xml or JSON
Here is the Code
public class MyDemoService extends Service {
private static final String TAG = "MyDemoService";
private static final int NOTIFICATION_ID = 1;
private LocalBinder m_binder = new LocalBinder();
private NotificationManager mNotifyManager;
private NotificationCompat.Builder mBuilder;
myAsyncTask myWebFetch;
// Timer to update the ongoing notification
private final long mFrequency = 100; // milliseconds
private final int TICK_WHAT = 2;
public class LocalBinder extends Binder {
MyDemoService getService() {
return MyDemoService.this;
}
}
private Handler mHandler = new Handler() {
public void handleMessage(Message m) {
updateNotification();
sendMessageDelayed(Message.obtain(this, TICK_WHAT), mFrequency);
}
};
#Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "bound");
return m_binder;
}
#Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "created");
mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return Service.START_STICKY;
}
#Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
Log.d(TAG, "Removed");
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "Destroyed");
}
public void updateNotification() {
// Log.d(TAG, "updating notification");
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
}
public void hideNotification() {
Log.d(TAG, "removing notification");
mNotifyManager.cancel(NOTIFICATION_ID);
mHandler.removeMessages(TICK_WHAT);
}
public void start() {
Log.d(TAG, "start");
mBuilder =
new NotificationCompat.Builder(MyDemoService.this)
.setSmallIcon(R.drawable.download)
.setContentTitle("SMU")
.setContentText("Downloading Images");
Intent targetIntent = new Intent(MyDemoService.this, MainActivity.class);
PendingIntent contentIntent = PendingIntent.getActivity(MyDemoService.this, 0, targetIntent, PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(contentIntent);
mNotifyManager.notify(NOTIFICATION_ID, mBuilder.build());
myWebFetch = new myAsyncTask();
myWebFetch.execute();
}
class myAsyncTask extends AsyncTask<String, Integer, Void> {
MyDB myDB;
myAsyncTask() {
myDB = new MyDB(MyDemoService.this);
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
mBuilder.setContentText("Download complete");
// Removes the progress bar
mBuilder.setProgress(0, 0, false);
mNotifyManager.notify(NOTIFICATION_ID, mBuilder.build());
}
#Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
mBuilder.setProgress(100, values[0], false);
mNotifyManager.notify(NOTIFICATION_ID, mBuilder.build());
}
#Override
protected Void doInBackground(String... params) {
//set the download URL, a url that points to a file on the internet
getJSON("http://*****", 1000000);
return null;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
mBuilder.setProgress(100, 0, false);
mNotifyManager.notify(NOTIFICATION_ID, mBuilder.build());
}
public void getJSON(String url, int timeout) {
HttpURLConnection c = null;
try {
URL u = new URL(url);
c = (HttpURLConnection) u.openConnection();
c.setRequestMethod("GET");
c.setUseCaches(false);
c.setAllowUserInteraction(false);
c.setConnectTimeout(timeout);
c.setReadTimeout(timeout);
c.setInstanceFollowRedirects(false);
c.connect();
int status = c.getResponseCode();
if (status == 200) {
String readStream = readStream(c.getInputStream());
if (readStream != null) {
JsonParser mJsonParser = new JsonParser(MyDemoService.this);
mJsonParser.parseJaSon(readStream);
ArrayList<SuitDetails> mImageList = new ArrayList<>(myDB.GetAllData());
if (mImageList != null) {
//NOW HERE DOWNLOADING IMAGES FROM URL WE GOT SAVED IN DB AFTER PARSING
downloadImages(mImageList);
}
}
}
} catch (MalformedURLException ex) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex);
} finally {
if (c != null) {
try {
c.disconnect();
} catch (Exception ex) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex);
}
}
}
}
#TargetApi(Build.VERSION_CODES.KITKAT)
private String readStream(InputStream in) {
//parsing my input stream and sending back string
return jsonString.toString();
}
void downloadImages(ArrayList<SuitDetails> arrayList) {
try {
ArrayList<SuitDetails> imageUrl = arrayList;
URL url;
float progressImages = 0;
HttpURLConnection urlConnection = null;
for (int i = 0; i < imageUrl.size(); i++) {
progressImages += 100 / imageUrl.size();
publishProgress((int) progressImages);
url = new URL(imageUrl.get(i).getPath().toString());
//create the new connection
urlConnection = (HttpURLConnection) url.openConnection();
//set up some things on the connection
urlConnection.setRequestMethod("GET");
urlConnection.setDoOutput(false);
urlConnection.setUseCaches(false);
urlConnection.setAllowUserInteraction(false);
urlConnection.setConnectTimeout(60000);
urlConnection.setReadTimeout(60000);
urlConnection.setInstanceFollowRedirects(false);
//and connect!
urlConnection.connect();
File storagePath = new File(MyDemoService.this.getExternalFilesDir("TEST") + "/Mytest");
storagePath.mkdirs();
String finalName = imageUrl.get(i).getImageName();
File myImage = new File(storagePath, finalName + ".png");
FileOutputStream fileOutput = new FileOutputStream(myImage);
InputStream inputStream = urlConnection.getInputStream();
int totalSize = urlConnection.getContentLength();
int downloadedSize = 0;
byte[] buffer = new byte[1024];
int bufferLength = 0;
while ((bufferLength = inputStream.read(buffer)) > 0) {
//add the data in the buffer to the file in the file output stream (the file on the sd card
fileOutput.write(buffer, 0, bufferLength);
//add up the size so we know how much is downloaded
downloadedSize += bufferLength;
//this is where you would do something to report the prgress, like this maybe
}
//close the output stream when done
ContentValues contentValues = new ContentValues();
contentValues.put("Status", "1");
contentValues.put("Path", myImage.getPath().toString());
myDB.UpdateDownloadStatus(contentValues, imageUrl.get(i).getSImageID());
fileOutput.close();
}
myDB.closeDb();
//catch some possible errors...
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
I Know this is length code but sharing if You want to analyse it deeply.
I will provide how I am using and calling this service in MainActivity if you demand it
why are you not using an IntentService if you want to do network stuff?
you should consider adding setIntentRedelivery(true); in your constructor
from the documentation
Sets intent redelivery preferences. Usually called from the
constructor with your preferred semantics.
If enabled is true, onStartCommand(Intent, int, int) will return
START_REDELIVER_INTENT, so if this process dies before
onHandleIntent(Intent) returns, the process will be restarted and the
intent redelivered. If multiple Intents have been sent, only the most
recent one is guaranteed to be redelivered.
If enabled is false (the default), onStartCommand(Intent, int, int)
will return START_NOT_STICKY, and if the process dies, the Intent dies
along with it.
I am making an inhouse navigation application with google glass. I am sending orientation data from the google glass to the android phone true WIFI (SOCKETS). My code is running fine and ok, but after about 25 seconds the receiving thread (on the phone) stops for 5 seconds and then continues, this is very annoying.
I tested this by closing the connection (when i see the thread becoming unresponsive) and it takes 5 seconds for the debugger to hit the breakpoint that i set in the application (obviously this is the thread that is causing the problem)
The weird thing is that the UI thread is still responsive i can move every object in the activity, it is just the specific thread containing the connection (Socket) that is getting blocked. I will post the code (i suspect memory leak) so that maybe someone can give me a indication why this could be happening, any advice is welcome.
CHANGED CODE AFTER COMMENT:
public class GlassOrientationSocket implements Runnable {
private final static int PORT = 6604;
private static Socket mClientSocket;
private static String mResult;
private static Handler handler = null;
private static boolean threadIsRunning;
private static BufferedReader inputReader;
public GlassOrientationSocket(Handler handler) {
this.handler = handler;
this.threadIsRunning = true;
}
public void terminateThread() {this.threadIsRunning = false;}
#SuppressWarnings("deprecation")
#Override
public void run() {
//Replace this with ip-address of android server (aka Google glass) // put ip on NFC TAG for easier configuration
String ServerIP = SECRET;
//port should be same as ServerSocketActivity
try {
mClientSocket = new Socket(serverIP, PORT);
inputReader = new BufferedReader(new InputStreamReader(mClientSocket.getInputStream()));
int angleCutter;
int orientationCutter;
float[] results = new float[2];
while(threadIsRunning)
{
if (((mResult = inputReader.readLine()) != null)) {
angleCutter = mResult.indexOf("|", 0);
orientationCutter = mResult.indexOf("|", angleCutter + 1);
results[0] = Float.valueOf(mResult.substring(0, angleCutter));
results[1] = Float.valueOf(mResult.substring(angleCutter + 1, orientationCutter));
Message msg = new Message();
msg.arg1 = 1;
msg.obj = results;
handler.sendMessage(msg);
} else {
if (mClientSocket != null) mClientSocket.close();
Message msg = new Message();
msg.arg1 = 2;
handler.sendMessage(msg);
threadIsRunning = false;
}
}
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}}
////
public class CompassMonitor {
static protected ArrayList<CompassListener> listeners=new ArrayList<CompassListener>();
static protected GlassOrientationSocket monitor=null;
private static Handler msgHandler = new Handler();
private static CompassListener orientationListener;
private static float[] dataReturned = new float[2];
static public synchronized void registerListener(Context context,CompassListener listener){
if(listeners.size()==0){
orientationListener = listener;
monitor=new GlassOrientationSocket(msgHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.arg1) {
case 1:
dataReturned = (float[])msg.obj;
notifyListeners(dataReturned[0],dataReturned[1],"Useless Parameter");
break;
case 2:
unregisterListener(orientationListener);
monitor.terminateThread();
}
}
});
Thread t = new Thread(monitor);
t.start();
}
listeners.add(listener);
}
static synchronized public void unregisterListener(CompassListener listener){
if (listeners != null && listener != null)
listeners.remove(listener);
}
static synchronized protected void notifyListeners(float azimuth,float angle, String direction){
for(CompassListener l:listeners){
try{
l.onCompassChanged(azimuth,angle,direction);
}catch(Exception ex){}
}
}}
I have a service that I'm using to send SOAP Webservice calls. Everything is working perfectly and it never crashes, but I kinda think it should.
My problem is that when I have long running queries (10-50 sec.) onDestroy() is called before my workerthread is done (and I call stopSelfResult). Could it be that System.out.println isn't executed right away/out of sync (cached) in the LogCat window?
The is how a start the service through QueryBase class:
QueryBase someService = new QueryBase(myActivity);
someService.execute(...);
My QueryBase Class
public class QueryBase {
private WeakReference<Activity> currentActivity = null;
private static class ResponseHandler extends Handler {
private QueryBase mQueryBase;
public ResponseHandler(QueryBase vQueryBase) {
mQueryBase = vQueryBase;
};
public void handleMessage(Message message) {
Bundle extras = message.getData();
mQueryBase.handleResult(message.arg1,message.arg2,extras.getInt("FRAMEID"),extras.getString("RESPONSE"));
mQueryBase=null;
};
};
public QueryBase(Activity vActivity) {
currentActivity = new WeakReference<Activity>(vActivity);
}
/***************************************************************************
* Start the service
**************************************************************************/
public boolean execute(Activity vActivity, int cmdID, int frameID, String serverAddress, int requestType, String request) {
// Valid activity
if (vActivity==null) return false;
// Test to see if network is connected
if (!isOnline(vActivity)) return false;
Intent webService = new Intent(vActivity, WebService.class);
final ResponseHandler responseHD = new ResponseHandler(this);
Messenger messenger = new Messenger(responseHD);
webService.putExtra("QUERYRESULT_MESSENGER",messenger);
webService.putExtra("CMDID", cmdID);
webService.putExtra("FRAMEID",frameID);
webService.putExtra("SERVER_ADDRESS",serverAddress);
webService.putExtra("REQUEST_TYPE",requestType);
webService.putExtra("REQUEST",request);
vActivity.startService(webService);
return true;
}
/***************************************************************************
* Is my Android connected?
**************************************************************************/
private Boolean isOnline(Activity vActivity) {
ConnectivityManager connMgr = (ConnectivityManager) vActivity.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.isConnected()) return true;
else return false;
}
/***************************************************************************
* Get current Activity
**************************************************************************/
public Activity getCurrentActivity() {
Activity ac = currentActivity.get();
if (ac!=null) {
if ((ac.isFinishing()) || (ac.activityDestroyed)) {
return null;
};
}
return ac;
};
/***************************************************************************
* XML result from webservice
**************************************************************************/
public void handleResult(int resultCode, int cmdID, int frameID, String response) {
System.out.println("DEFAULT HANDLER: ResultCode: " + resultCode);
};
}
My WebService Class
public class WebService extends Service {
public static final int WS_RT_BLOOSOAP = 0;
public static final int WS_RT_RSS = 1;
public static final int WS_RESULT_OK = 0;
public static final int WS_RESULT_UNABLE_TO_CONNECT = 2;
public static final int WS_RESULT_INVALID_REQUEST = 3;
public static final int WS_RESULT_UNKNOWN_ERROR = 999;
static private SparseBooleanArray workList=null; // Only one job with the same frameID is allowed to run
#Override
public void onCreate() {
System.out.println("#### WebService onCreate");
if (workList==null) workList = new SparseBooleanArray();
}
#Override
public void onDestroy() {
System.out.println("#### WebService onDestroy");
}
/***************************************************************************
* Start working
**************************************************************************/
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.out.println("WebService Start ID=" + startId);
final int currentID = startId;
final Intent currentIntent = intent;
Runnable workerRunnable = new Runnable() {
public void run() {
System.out.println("WebService Thread Start - ID=" + currentID);
int resultCode;
Bundle responseExtras = new Bundle();
resultCode = serverRequest(currentIntent,responseExtras);
sendResponse(currentIntent,resultCode,responseExtras);
System.out.println("WebService Thread End - ID=" + currentID);
Bundle extras = currentIntent.getExtras();
if (extras != null) {
int frameID = extras.getInt("FRAMEID");
System.out.println(">>>>>>> PUT FALSE " + frameID);
workList.put(frameID, false);
};
stopSelfResult(currentID);
}
};
if (intent!=null) {
Bundle extras = intent.getExtras();
if (extras != null) {
int frameID = extras.getInt("FRAMEID");
Boolean found = workList.get(frameID,false);
if (!found) {
System.out.println(">>>>>>> PUT TRUE FRAMEID=" + frameID);
workList.put(frameID, true);
Thread workerThread = new Thread(workerRunnable);
workerThread.start();
} else {
System.out.println(">>>>>>> Allready running FRAMEID=" + frameID);
}
};
};
return Service.START_STICKY;
};
/***************************************************************************
* No binding
**************************************************************************/
#Override
public IBinder onBind(Intent intent) {
return null;
}
/***************************************************************************
* Send webservice request and return result in responseExtras
**************************************************************************/
private int serverRequest(Intent intent, Bundle responseExtras) {
...
};
/***************************************************************************
* Send response back to service caller using Messenger.send()
**************************************************************************/
private boolean sendResponse(Intent intent, int resultCode, Bundle responseExtras) {
...
};
Your service is stopped if you call stopSelfResult() with the latest startId. So if the service gets started with an intent for startId=1 and another intent with startId=2 and the second is finished before the first, you call stopSelfResult(2) before you finished for startId=1. The service gets destroyed immediately if you call stopSelfResult() with the latest startId and no other intents are pending.
Hold the latest startId. Add all startIds you wish to process in an array (e.g. List<Integer> runningStartIds) and remove them when you've finished processing them. After removing on finishing, compare the current startId with the latest one and do not call stopSelfResult() if runningStartIds is not empty. So you will end up calling stopSelfResult() only for the latest startId, when all intents were processed and no more intents are pending.
Should work, although I haven't posted an example.
.:EDIT:.
Explenation:
The next Intent may come in as fast as you return from onStartCommand() regardless of what you're doing in the background.
.:EDIT:.
Not an Improvement(Improvement:
Thinking about that, in fact you only have to keep the mLastStartId. Just skip calling stopSelfResult() until the finished startId matches mLastStartId.)
Unfortunately, it always can be happen. Actually, android application components' life cycle aren't synchronized w/ any type of worker threads as default.
So, you may need to check the status of Service manually, for example you can have one boolean flag to indicate if a service is working or not. Another handy approach is using IntentService instead of using normal service, it handles worker thread and life-cycle features by itself.
public class MyIntentService extends IntentService {
public MyIntentService() {
super("MyIntentService ");
}
#Override
protected void onHandleIntent(Intent intent) {
// This callback-method is called inside worker thread,
// so you can do some long-time network job here.
SystemClock.sleep(30000); // 30 seconds
// In this timing the service will be stopped automatically.
}
}
I am trying to download a file from a URL. If the download fails (regardless of the reason) I want the application to retry an hour later.
Since the download is in it's own thread (not the main thread), I can't seem to start a new CountDownTimer.
I don't want to block the download thread so I'm trying to use the CountDownTimer.
Is there another way to do this?
Thanks in advance!
private class Downloader extends AsyncTask<Object, Void, File>
{
#Override
protected File doInBackground(Object... params)
{
Context context = (Context) params[0];
String strUrl = (String) params[1];
String strFileName = (String) params[2];
return download(context, strUrl, strFileName);
}
/**
* Downloads files in a separate thread. Adds notification of download to status bar.
*/
public File download(final Context context, final String url, final String fileName)
{
boolean isVideoLoaded=false;
try
{
URL urlObj = new URL(url);
URLConnection con = urlObj.openConnection();
BufferedInputStream bis = new BufferedInputStream(con.getInputStream(), BUFFER_SIZE);
FileOutputStream fos = new FileOutputStream(retFile);
byte[] bArray = new byte[BUFFER_SIZE];
int current = 0;
int read = 0;
while(current != -1)
{
fos.write(bArray,0,current);
current = bis.read(bArray, 0, BUFFER_SIZE);
read = read + current;
}
fos.close();
bis.close();
isVideoLoaded = true;
strFileName = retFile.getAbsolutePath();
}
catch(Exception ioe)
{
Log.d("Downloader.download", "Error: " + ioe.toString(), ioe);
}
}
if (!isVideoLoaded) // if video is still not loaded
{
// sleep then try again
new CountDownTimer(15000, 2000)
{
public void onFinish()
{
Downloader dh = new Downloader();
Object params[] = new Object[3];
params[0] = context;
params[1] = url;
params[2] = fileName;*/
dh.execute(params);
}
#Override
public void onTick(long millisUntilFinished) {
// TODO Auto-generated method stub
}
}.start();
}
return retFile;
}
protected void onPostExecute(File file) {
// display downloaded file
}
You can create a Timer that will start a download on your UI thread.
In your UI class, use this function
private StartOneHourTimer()
{
new CountDownTimer(10000 * 3600, 1000) {
public void onTick(long millisUntilFinished) {
mTextField.setText("seconds remaining: " + millisUntilFinished / 1000);
}
public void onFinish() {
m_downloader = new Downloader(..);
m_downloader.execute(..);
}
}.start();
}
In your Downloader onPostExecute(),
start a new Timer that will expire in 1 hour.
protected void onPostExecute(File file)
{
if (!isVideoLoaded)
startOneHourTimer();
else
//Call your normal UI code here
}
Change your onPostExecute so that it calls back out to the main activity to signify that the download failed (should be able to get to it by MyMainActivity.this.someMethod().) The main activity can then make a new AsyncTask (you can't reuse the old one) and signify that it should delay start.
One option for doing this is to add a constructor to your AsyncTask so you can pass a delay parameter to it, then your AsyncTask can use the delay parameter as part of the doInBackground method.
Inside doInBackground simply wait with a Thread.sleep()
I am doing an Android service that gives content to other apps that can register as callback.
I am not 100% sure about how the Android Handler class works, so can someone confirm me that this code is thread safe?
public class MyService extends Service {
private static final String MESSAGE = "message";
private final RemoteCallbackList<IMyCallback> readerCallbacks = new RemoteCallbackList<IMyCallback>();
private static final int REPORT_MSG = 1;
private Thread readerThread;
#Override
public void onCreate() {
readerThread = new Thread(readerRunnable);
readerThread.setDaemon(true);
readerThread.start();
}
private Runnable readerRunnable = new Runnable() {
#Override
public void run() {
while (!Thread.interrupted()) {
// Blocking call
byte[] message = JniCommunicator.readMessage();
if (message == null || message.length == 0) {
continue;
}
Bundle b = new Bundle();
b.putByteArray(MESSAGE, message);
Message m = readHandler.obtainMessage(REPORT_MSG);
m.setData(b);
readHandler.sendMessage(m);
}
}
};
private final Handler readHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case REPORT_MSG:
byte[] message = msg.getData().getByteArray(MESSAGE);
// Broadcast the new message to all clients
final int N = readerCallbacks.beginBroadcast();
for (int i = 0; i < N; i++) {
try {
readerCallbacks.getBroadcastItem(i).newMessage(message);
} catch (RemoteException e) {
// The RemoteCallbackList will take care of removing
// the dead object for us.
}
}
readerCallbacks.finishBroadcast();
break;
}
}
};
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
private final IService.Stub mBinder = new IService.Stub() {
public void registerCallback(IMyCallback cb) {
if (cb != null)
readerCallbacks.register(cb);
}
public void unregisterCallback(IMyCallback cb) {
if (cb != null)
readerCallbacks.unregister(cb);
}
};
}
In particular, if someone calls unregisterCallback() while the Handler is in the for loop, will it crash?
From my understanding, the Handler run in the same thread, so it is thread safe, but I am not sure.
Thanks
Handlers are thread safe, that is their entire purpose.
I'll agree that the documentation on the thread safety of handlers isn't the best but it would be very ironic if a class designed to communicate between thread weren't thread safe.
About the remote callbacks, they are also designed to be thread safe, you should read the documentation on this, it states clearly:
Performs locking of the underlying list of interfaces to deal with multithreaded incoming calls, and a thread-safe way to iterate over a snapshot of the list without holding its lock
All you have to make sure is that all variables multiple thread access are thread safe (which they are in your case) and that they aren't being changed (yours are final so no worries there either)