I have a Service that creates AsyncTasks for downloading files. In activities, we create Runnables or Threads that we pass to Activity.runOnUiThread(). I can't access that method from a service, so how do I use AsyncTask correctly, (do heavy work without blocking the UI Thread)?
If your service is only called from your application, and you can make it a singleton, then try this:
public class FileDownloaderService extends Service implements FileDownloader {
private static FileDownloaderService instance;
public FileDownloaderService () {
if (instance != null) {
throw new IllegalStateException("This service is supposed to be a singleton");
}
}
public static FileDownloaderService getInstance() {
// TODO: Make sure instance is not null!
return instance;
}
#Override
public void onCreate() {
instance = this;
}
#Override
public IBinder onBind(#SuppressWarnings("unused") Intent intent) {
return null;
}
#Override
public void downloadFile(URL from, File to, ProgressListener progressListener) {
new Thread(new Runnable() {
#Override
public void run() {
// Perform the file download
}
}).start();
}
}
Now you can directly call methods on your service. So just call downloadFile() to put the service to work.
About your real question of how to update the UI. Notice that this method receives a ProgressListener instance. It could look like this:
public interface ProgressListener {
void startDownloading();
void downloadProgress(int progress);
void endOfDownload();
void downloadFailed();
}
Now you just update the UI from the activity (not from the service, which remains unaware of how the UI looks like).
Related
How to continuously get the results from a service to UI or MainActivity.?
I am copying some large file using a service,and i want to update the progress bar in Foreground.
Simplest way to do this is to use Singleton object with listener.
Subscribe for update in activity and just set values in Service.
public class ProgressSingleton {
private static ProgressSingleton instance;
private ProgressListener progressListener = null;
public static void initInstance() {
if (instance == null) {
instance = new ProgressSingleton();
}
}
public static ProgressSingleton getInstance() {
return instance;
}
private ProgressSingleton() {
}
public void setProgress(int progress) {
if (progressListener != null) {
progressListener.onProgressUpdated(progress);
}
}
public interface ProgressListener {
void onProgressUpdated(int progress);
}
}
Make shure that you handle updates from listener in UI thread.
Also you can start your service in foreground, best option for big files.
I have one doubt about using services. I have a service that initializes an object, is it a bad practice to pass an instance of the service to the object so it can be used for that object? A simplified object would be:
public class MyService extends Service {
MyObject myObject = new MyObject(this);
...
}
public MyObject {
private MyService myService;
public MyObject(MyService myService) {
this.myService = myService;
}
...
private void exampleMethod() {
myService.method();
}
}
What do you think? Is it a bad practice? How could I solve that issue without passing the service's instance?
The fact is that I want to split the code in two classes because the features are different, the websocket is connected from the service class, but the methods to parse/send events through the websocket are in the second class. I want to do this way in order to avoid having one class with 2000 lines of code, and by splitting the code by features. The service handles the websocket connection, while the other class handles the other features. As everything is asynchronous, the second class needs an instance of the service class. For instance: if an error is received and parsed (on the second class), this second class must call the service class to update its status and do a reconnection.
EDIT:
I'm thinking about implementing the following solution:
public class MyService extends Service {
MyObject myObject = new MyObject() {
protected void onSuccess() {
...
}
};
...
}
public abstract class MyObject {
public MyObject() {
}
protected abstract void onSuccess();
...
private void exampleMethod() {
...
onSuccess()
}
}
The more I think about it, the better solution I think it is. What do you think?
Thank you very much in advance!
This makes no sense at all. I suggest you to use a interface if you need to pass a callback to a dao (the websocket controller). The thing is that you should use your service to implement your websocket controller.
Please add the websocket code, so we can suggest more changes.
EDIT:
public interface onGetData {
public void onSuccess(Object response) // here you pass the obj type you need in your service
public void onError(Object error) // same, but if things fail
}
public class MyService extends Service implements onGetData {
#Override
public void OnCreate() {
MyObject myObject = new MyObject(this);
}
#Override
public void onSuccess(Object response) {
}
#Override
public void onError(Object error) {
}
}
public MyObject {
private OnGetData onGetData ;
public MyObject(OnGetData onGetData) {
this.onGetData = onGetData;
}
private void onRequestSuccess(Object response) {
onGetData.onSuccess(response)
}
private void onRequestError(Object error) {
onGetData.onError(error)
}
}
I'm trying to create an app that makes HTTP requests through an intentservice. I need the app to wait for the service to finish its run (aka, have the request be returned with some data) before it continues its operations, as its operations involve manipulation of the data I hope to receive from the HTTP requests. I've tried numerous means of doing so - Semaphore, CountDownLatch, but it seems that for all of them, I need some method of passing in the waiting/counting object into the intentservice so that it can tell the main thread where that object is waiting that it is done processing. How do I go about doing that? Basically, I want a synchronous, blocking call to an http server to work conveniently with an Intent Service, since an intent service makes multi threading easy.
Again to reiterate just to make sure i'm not misusing terminology: What I mean by Synchronous and blocking/what I want: I make a call to the http server by sending an intent to my intentservice that makes the request. My UI thread, or thread from which this intent was sent, now waits until the request has been processed and a result has been returned before continuing to run.
If you think that I am going about this process (making http calls in a blocking, synchronous way) all wrong, what is another way you might choose to go about it? Thanks!
I am sorry, but I think your architecture is not right or I may understand it wrong. IntentService is built to do thing serial way on separate thread. Now you say you want it to be synchronous and blocking. You cannot block UI thread!
In order to create notification system from your IntentService to Activity/Fragment/etc. you have few choices: singleton, broadcast message (receiver, resultReceiver), others?
Based on assumption that service and other parts of the application are working in same process. Best option would be to create manager to do this job. Something like this can be built to start service as well as listen for completion event:
public class MyNetworkManager {
static MyNetworkManager sInstance;
Context mContext;
LinkedList<OnCompletionListener> mListeners;
private MyNetworkManager(Context context) {
mContext = context;
mListeners = new LinkedList<>();
}
public static MyNetworkManager getInstance(Context context) {
if (sInstance == null) {
synchronized (MyNetworkManager.class) {
if (sInstance == null) {
sInstance = new MyNetworkManager(context.getApplicationContext());
}
}
}
return sInstance;
}
// add listener to listen for completion event
public void addListener(OnCompletionListener listener) {
synchronized (mListeners) {
mListeners.add(listener);
}
}
// remove listener to stop listening for completion event
public void removeListener(OnCompletionListener listener) {
synchronized (mListeners) {
mListeners.remove(listener);
}
}
// call from UI to start service operation
public void startNetworkOperation() {
Intent service = new Intent();
mContext.startService(service);
}
// call from service to notify UI (still on different thread, can use Handler to make call on main thread)
public void notifyCompletion() {
synchronized (mListeners) {
for (OnCompletionListener listener : mListeners) {
listener.onCompleted(this);
}
}
}
public static interface OnCompletionListener {
void onCompleted(MyNetworkManager manager);
}
}
Use this pattern
public interface SynchronizationListener {
//void onStart(int id); not requered
//void onProgress(int id, long updateTime); not requered
void onFinish(Object data); // replace Object with your data type
}
In your service add end call this
private void startSynchronization() {
SynchronizationManager.getInstance().startSynchronizing();
}
Your Singleton Manager
public class SynchronizationManager {
private static SynchronizationManager instance;
private Object synRoot = new Object();
private boolean synchronizing = false;
private List<SynchronizationListener> synchronizationListeners;
public SynchronizationManager() {
synchronizationListeners = new ArrayList<SynchronizationListener>();
}
static {
instance = new SynchronizationManager();
}
public static SynchronizationManager getInstance() {
return instance;
}
public boolean isSynchronizing() {
synchronized (synRoot) {
return synchronizing;
}
}
public void startSynchronizing() {
synchronized (synRoot) {
if (synchronizing) {
return;
}
synchronizing = true;
}
Object data; // <-- replace Object with your data type
if (ConnectivityReceiver.hasGoodEnoughNetworkConnection()) { // check connection
data = sync();
}
synchronized (synRoot) {
synchronizing = false;
}
onSynchronizationFinish(data); // use listener for send data tu Observer Activity
}
public void stopSynchronizing() {
synchronized (synRoot) {
synchronizing = false;
}
}
public synchronized void registerSynchronizationListener(
SynchronizationListener listener) {
if (!synchronizationListeners.contains(listener)) {
synchronizationListeners.add(listener);
}
}
public synchronized void unregisterSynchronizationListener(
SynchronizationListener listener) {
if (synchronizationListeners.contains(listener)) {
synchronizationListeners.remove(listener);
}
}
public void onSynchronizationStart(int id) {
for (SynchronizationListener listener : synchronizationListeners) {
listener.onStart(id);
}
}
protected void onSynchronizationProgress(int id, long updateTime) {
for (SynchronizationListener listener : synchronizationListeners) {
listener.onProgress(id, updateTime);
}
}
protected void onSynchronizationFinish(Object data) {
for (SynchronizationListener listener : synchronizationListeners) {
listener.onFinish(data);
}
}
protected int sync) {
// code for load your data your HttpRequest
}
}
In your activity
private SynchronizationListener synchronizationListener = new SynchronizationListener() {
/*public void onStart(int id) {
}
public void onProgress(int id, long updateTime) {
}*/
public void onFinish(Object data) {
//elaborate data
}
};
#Override
protected void onResume() {
super.onResume();
SynchronizationManager.getInstance().registerSynchronizationListener(
synchronizationListener);
}
#Override
protected void onPause() {
super.onPause();
SynchronizationManager.getInstance().unregisterSynchronizationListener(
synchronizationListener);
}
See this code for example UnivrApp
A ContentProvider would be a better choice than an IntentService in my thinking. You can trigger each network call with a query and then return a MatrixCursor with details about the results of your background work. Android already has lots of good plumbing around running queries in background tasks and waiting for the results before triggering ui updates.
in ContentProvider query() method :
MatrixCursor cursor = new MatrixCursor(new String[]{"_id","uri", "status", "last_modified", "result"});
String lastModified=null;
int id =1;
// do your work here
// ..
// report your work here
cursor.addRow(new Object[]{id++, uri.toString(), HttpStatus.SC_OK, lastModified, "" });
// set uri for data observers to register
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
What you try to do is just communication between IntentService and Activity/Fragment.
You can try send broadcast at the end of onHandleIntent and catch it in registered receiver or use ResultReceiver - read more how to implement here.
Edit:
Try this:
Handle all background operations at once in onHandleIntent
On every step send new data using ResultReceiver
// operation 1
Bundle b1 = new Bundle();
b1.putParcelable("data", data1);
resultReceiver.send(0, b1);
// operation 2
Bundle b2 = new Bundle();
b2.putParcelable("data", data2);
resultReceiver.send(1, b2);
Handle it in ResultReceiver
public void onReceiveResult(int resultCode, Bundle resultData) {
if (resultCode == 0) { // handle step 1 }
else if (resultCode == 1) { // handle step 2 }
}
I seem to be having one of two issues here. Either an error like:
java.lang.IllegalStateException: Event bus [Bus "default"] accessed from non-main thread Looper
Or, if I've managed to "address" that, I simply never receive the event in my subscriber.
Currently, I have a class, cobbled from a few sources, sub-classing Bus:
public class MainThreadBus extends Bus {
private static Bus _bus;
private Handler _handler = new Handler(Looper.getMainLooper());
public MainThreadBus() {
if (_bus == null) {
_bus = new Bus();
}
}
#Override public void register(Object obj) {
_bus.register(obj);
}
#Override public void unregister(Object obj) {
_bus.unregister(obj);
}
#Override public void post(final Object event) {
if (Looper.myLooper() == Looper.getMainLooper()) {
_bus.post(event);
} else {
_handler.post(new Runnable() {
#Override public void run() {
_bus.post(event);
}
});
}
}
}
Which is used in an Activity like this:
#Subscribe
public void requestProgressAvailable(RESTRequestProgress progress) {
processProgress(progress);
}
#Override
protected void onResume() {
super.onResume();
_bus = new MainThreadBus();
_bus.register(this);
}
#Override
protected void onPause() {
super.onPause();
_bus = new MainThreadBus();
_bus.unregister(this);
}
And publishing from an IntentService like this:
_bus = new MainThreadBus();
_bus.post(request.createRESTRequestProgress(RESTRequest.STATUS_STARTED));
And the messages are not received. An alternate configuration had me receiving the thread error, so I'm going with this, for now.
So, what am I missing, or doing wrong?
EDIT: Thanks to Andy below for pointing out that my code wasn't acting as I thought it was. The code above now reflects modifications based on that feedback.
Aside from the fact this implementation isn't a Singleton, when getting this error, you can use the threadEnforcer.ANY option in the constructor:
private static final Bus BUS = new Bus(ThreadEnforcer.ANY);
The problem is that your code is not interacting with the same bus instance. Instead of creating a new MainThreadBus in each case, you should access the same bus, for example a singleton obtained from a factory or via injection.
I'm having a problem writing a service, that should work with multiple activities.
I wrote a simple service and a mediator class the makes the bind and can return a service object. this is the simple service class:
public class ServerConnectionService extends Service{
private static final String TAG = "ServerConnectionService";
private final Binder binder=new LocalBinder();
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onDestroy(){
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent) {
return binder;
}
public class LocalBinder extends Binder {
ServerConnectionService getService() {
return ServerConnectionService.this;
}
}
}
this is the mediator class:
public class ServiceConnectionBinder{
private ServerConnectionService m_SrvConnection=null;
private ServiceConnection m_OnService;
private boolean m_IsBound;
private Activity m_Client;
public ServiceConnectionBinder(Activity i_Activity)
{
m_IsBound = false;
this.m_Client = i_Activity;
this.m_OnService=new ServiceConnection() {
public void onServiceConnected(ComponentName className,IBinder rawBinder) {
m_SrvConnection=((ServerConnectionService.LocalBinder)rawBinder).getService();
}
public void onServiceDisconnected(ComponentName className) {
m_SrvConnection=null;
}
};
doBindService();
Log.d("ServiceConnectionBinder", "finished Ctor");
}
private void doBindService() {
if(!m_IsBound)
{
m_Client.bindService(new Intent(m_Client, ServerConnectionService.class), m_OnService, Context.BIND_AUTO_CREATE);
m_IsBound = true;
}
if(m_SrvConnection == null)
{
Log.d("ServiceConnectionBinder",".doBindService cannot bind " + ServerConnectionService.class.toString() + " to " + this.toString());
}
}
public void doUnbindService() {
if (m_IsBound) {
// Detach our existing connection.
m_Client.unbindService(m_OnService);
m_IsBound = false;
}
}
public ServerConnectionService getServerConnectionService()
{
if(m_IsBound)
{
Log.d("ServiceConnectionBinder", "getServerConnectionService m_IsBound = " + m_IsBound);
}
return m_SrvConnection;
}
}
The client Activity has the following data members:
private ServiceConnectionBinder m_SrvcConnectionBinder=null;
private ServerConnectionService m_SrvConnection=null;
And in onCreate() the following code:
m_SrvcConnectionBinder = new ServiceConnectionBinder(this);
m_SrvConnection = m_SrvcConnectionBinder.getServerConnectionService();
problem is that after the onCreate(), the m_SrvConnection is always null.
If you have any other ways to implement this you are more than welcome to share..
problem is that after the onCreate(), the m_SrvConnection is always null.
Of course. The binding request will not even begin until the main application thread gets control again (i.e., you return control to the OS).
You cannot use m_SrvConnection until onServiceConnected() is called.
Resurrecting the old post as I had the similar question, but there is no clear answer here.
One of the ways to address this is like this:
protected void onCreate(Bundle savedInstanceState){
//... some stuff #1...
new AsyncTask<Void, Void, Integer>(){
protected void onPreExecute() { }
protected Integer doInBackground(Void... params) {
while(m_SrvConnection==null);
return new Integer(1);
}
protected void onPostExecute(Integer result) {
// service is up, m_SrvConnection is set
// what you wanted to do with the service in onCreate() goes here
}
}.execute();
//...some stuff #2...
}
Note that "some stuff #1" will run right when onCreate() is called, "some stuff #2" will be executed almost right after that, but what you put in onPostExecute() will be run much later.
The reason for doing it this way and not just putting the code into onServiceConnected() is that the ServiceConnectionBinder can now be put outside of the Activity (in some singleton, or Application for example) and be used by multiple activities without the need for each of them to bind to the service.
Note, it may not be obvious, but things in onPostExecute() may (will) actually be run after all other standard callbacks (like onResume() etc.).