I'm designing an android app that will listen to the incoming SMSs and will handle them in a specific way.
I have a broadcast receiver that receives the message and sends it to an intent service:
Intent serviceIntent = new Intent(context, SMSIntentService.class);
serviceIntent.putExtras(intent.getExtras());
context.startService(serviceIntent);
The purpose of the intent service is to save the SMS to my own DB and then send that message to a server via HTTP POST, evaluate the result and update the app's DB and eventually reply to the sender.
So far everything is good but as there is a chance that a lot of SMS arrive at the same time, I want to decouple the communication with the server putting it in another thread.
So what I'm doing so far is this:
SmsDto sms = smsDataSource.saveSms(new SmsDto(originator, body, timestamp));
SMSProcessingTask task = new SMSProcessingTask(this.getApplicationContext(), sms);
Thread t = new Thread(task);
t.start();
And so far so good, but I don't trust this implementation with a big amount of messages.
So, my question is:
In an intent service, is it recommended to use a ThreadPoolExecutor?
I would end up with something like this:
//in IntentService's onCreate
this.executor = Executors.newCachedThreadPool();
//in onHandleIntent()
executor.execute(task);
What happens if for a period of time no messages are received and the IntentService stops. Will the threads created by it continue running?
I don't know if this approach is the best way to deal with what I'm trying to accomplish.
Thanks
Update:
There is not UI activity at all in this app.
Since the communication with the server can take quite a long time, I want to minimize the processing time of a message, so the next sms in queue is picked up quickly and start being processed.
Ni
No you shouldn't use one. The main reason being that SQlite access is not thread safe so you don't want multiple threads writing to the database at the same time.
Also, if you task happens to update the UI it's not going to work that way.
I really don't understand why you have those tasks : the IntentService already processes its messages off the UI thread.
What you can do is use the submit(Callable) method instead of the execute one.
that way you can get a future object with the data you want to write in the DB and no thread will actually touch it as it not safe like Phillippe said
I used it on a similar way when I needed multiple httprquests to send.
I managed them using SQL DB, so the writing only occur on the onHandleIntent.
while(helper.requestsExists()){
ArrayList<String> requestArr = helper.getRequestsToExcute(3);
//checks if the DB requests exists
if(!requestArr.isEmpty()){
//execute them and delete the DB entry
for(int i=0;i<requestArr.size();i++){
file = new File(requestArr.get(i));
Log.e("file",file.toString());
Future<String> future = executor.submit(new MyThread(file,getApplicationContext()));
Log.e("future object", future.toString());
try {
long idToDelete = Long.parseLong(future.get());
Log.e("THREAD ANSWER", future.get() + "");
helper.deleteRequest(idToDelete);
} catch (InterruptedException e) {
Log.e("future try", "");
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
executor.shutdown();
secondly the intentService will not stop until the onHandleIntent is done and even if so, the threads will continue running until they've done their job
Related
My android app starts a service that opens a websocket to communicate to a remote server. The service spawns a thread whose run method looks like this.
public void run() {
try {
super.run();
for(int i = 1; i < 1000; i++) {
Log.d(TAG, String.format(" *** Iteration #%d.", i));
Thread.sleep(3000); // Dummy load.
mWebsocket.sendTextMessage("Test");
}
}
catch (Exception exc) {
Log.d(MY_TAG, "MyThread.run - Exception: " + exc.getMessage());
}
}
When I turn off the screen or send the app to the background, logcat shows that the loop is running, but the remote server stops receiving the test messages. Apparently, the messages are pooling somewhere because once the app is back to the foreground, the server will received a bunch of test messages. Is this the expected behavior on Android? I've tried different Websocket packages (Autobahn, okhttp3, ...) and the result is the same.
If you want this function to be guaranteed to continue to run while your app's UI is in the background, you will need to make that service run as a foreground service. There are some restrictions/guidelines on the use of foreground services, see the documentation at https://developer.android.com/guide/components/services.html.
Alternatively, if this is work that needs to occur on a periodic recurring basis and does not need to run continuously, you may be able to utilize JobScheduler; see https://developer.android.com/topic/performance/scheduling.html.
Currently, in my main app, I am sending multiple texts to status bar object.
My status bar object, is going to display multiple texts sequentially, with sleep time of N seconds for each display interval.
Here's my implementation in my main app.
public synchronized void setNextText(final CharSequence text) {
if (executor == null) {
executor = Executors.newSingleThreadExecutor();
}
executor.execute(new Runnable() {
#Override
public void run() {
Fragment fragment = getTargetFragment();
if (fragment instanceof OnStatusBarUpdaterListener) {
((OnStatusBarUpdaterListener)fragment).setNextText(text);
try {
// Allow 1 seconds for every text.
Thread.sleep(Constants.STATUS_BAR_UPDATER_TIME);
} catch (InterruptedException ex) {
Log.e(TAG, "", ex);
}
}
}
});
}
Now, I would like to have the same behavior in app widget. I was wondering, is using Executor being recommended in app widget environment? If not, what class I should use to achieve the similar objective?
I do have experience in using HandlerThread + AlarmManager in app widget. It works good so far. However, the operation done by the runnable is one time. It doesn't sleep and wait.
The following is the code which I use to update stock price in fixed interval.
// This code is trigger by AlarmManager periodically.
if (holder.updateStockPriceHandlerThread == null) {
holder.updateStockPriceHandlerThread = new HandlerThread("updateStockPriceHandlerThread" + appWidgetId);
holder.updateStockPriceHandlerThread.start();
holder.updateStockPriceWorkerQueue = new Handler(holder.updateStockPriceHandlerThread.getLooper());
holder.updateStockPriceWorkerQueue.post(getUpdateStockPriceRunnable(...
}
However, I have a feeling that, for use case "display multiple texts sequentially, with sleep time of N seconds for each display interval", AlarmManager might not be a good solution. Imagine I have 100 texts. Having to set 100 alarms for 100 texts doesn't sound good...
An AppWidgetProvider is a subclass of BroadcastReceiver. Once your callback (e.g., onUpdate()) returns, your process can be terminated at any point.
If that is not a problem — if you fail to finish the semi-animation that you are doing, that's OK — using an Executor from onUpdate() could work.
If you want to make sure that the text changes go to completion, delegate the app widget updating to a Service, where you use your Executor. Call stopSelf() on the Service when you are done, so it can go away and not artificially keep your process around.
Well the singleThread instance work creates an Executor that uses a single worker thread. meaning only thread to process your operation. But in you case use at least two. Your operations sounds expensive.
To conclude your question stick with the executor service as it thread safe.
I'm making image processor app. I need to scan the phone for pictures and list them with their number of pixels. So that's gonna be a a large impact on performance and as I understood, I need to make it work on background thread.
So my question is, what is the best approach for this? I understand that IntentService may be the best solution, but I'm not sure how I will implement progress bar with it, and I need to return Picture objects and later update the UI on shuffle button. I'm doing update with Glide library so that's gonna go smooth.
Reading about Asynctasks, I stumbled about comments how it's bad and leads to leaks in memory and should avoid using it. rXJava is too complicated at the moment.
This is my code:
Main activity:
#OnClick(R.id.shuffle)
public void shuffleList() {
Collections.shuffle(listOfImageFiles);
recyclerViewAdapter = new PictureRecycleViewAdapter(listOfImageFiles, this);
recyclerView.swapAdapter(recyclerViewAdapter, false);
recyclerViewAdapter.notifyDataSetChanged();
}
#OnClick(R.id.scan)
public void processImages() {
//progress bar
listOfPictures = new ArrayList<>();
//Gets data from default camera roll directory. Note that some of the phone companies have different file paths. So instead of hardcoding string paths, I used this instead.
String path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getPath();
File filePath = new File(path);
listOfImageFiles = scanPhotos(filePath);
// async?
for (File file : listOfImageFiles
) {
Bitmap bitmap = BitmapFactory.decodeFile(file.getPath());
//int is sufficient for most today's pixels. long would be overkill - 4 vs 8 bytes
int pixels = bitmap.getHeight() * bitmap.getWidth();
listOfPictures.add(new Picture(file.getPath(), pixels));
}
}
public List<File> scanPhotos(File directory) {
List<File> listOfPictures = new ArrayList<>();
try {
File[] files = directory.listFiles();
for (File file : files
) {
if (file.isDirectory() && !file.isHidden()) {
listOfPictures.addAll(scanPhotos(file));
} else {
if (file.getName().endsWith(".jpg") || file.getName().endsWith(".jpeg") || file.getName().endsWith(".png")) {
listOfPictures.add(file);
}
}
}
} catch (Exception e) {
Log.e(e.getMessage(), e.getMessage());
}
return listOfPictures;
}
IntentService
IntentService is definitely a valid approach. You can use Broadcasts to return your result to another component of the app, be it Activity or another Service, for example:
Start the IntentService - if you need some parameters, place them in the Extras of the service intent.
Your IntentService runs on the background thread until the computation is finished.
Upon finishing, send a broadcast with computation result placed in intent extras.
In your activity, register a BroadcastReceiver that will listen for your computation result broadcast.
Upon getting the broadcast in your Activity, retrieve the computation result from intent extras.
You might also implement broadcasts received by your Service for things like cancellation of the computation or updating the parameters.
One of the advantages of IntentService is that you can easily integrate it with the JobScheduler API to defer execution until certain system conditions are met.
Alternatives
You can use a bus library, such as https://github.com/greenrobot/EventBus to communicate between Activity and Service - the only problem is, EventBus won't work with remote services (running in a separate process).
Like you've mentioned, using RxJava with IO and computation schedulers is also a good idea.
AsyncTask is fine as long as you not tie it with a hard reference to an activity - don't implement it as an inner class of Activity and if you want to communicate the result back, do it through a WeakReference<T>
AsyncTask is fine, you just need to be careful with its implementation.
However, for longer running tasks there are better options. IntentService is a good option.
When it comes to a responsive UI when using an IntentService you could add two things.
Notifications
Create an ongoing notification that indicates that your App is working on something. This lets users know that their CPU cycles are being eaten by something in the background and they are less likely(?) to be confused and cranky about their device running slower.
Additionally, it gives your App more of an allowance for staying alive when Android is looking for background Apps to kill to release memory.
EventBus
You can make UI reporting extremely simple by using an EventBus library. I am personally a fan of greenbot/EventBus, but there are others.
Example
In your Activity:
#Subscribe(threadMode = ThreadMode.MAIN)
public void onProgressEvent(ProgressEvent event) {
mProgressBar.setProgress(event.value);
}
In your IntentService:
EventBus.getDefault().post(new ProgressEvent(5000));
I am connecting to an XMPP server in Android using Smack. Here is my code:
static void openConnection() {
try {
if (null == connection || !connection.isAuthenticated()) {
XMPPTCPConnectionConfiguration.Builder configuration = XMPPTCPConnectionConfiguration.builder();
configuration.setHost(SERVER_HOST);
configuration.setPort(SERVER_PORT);
configuration.setServiceName(SERVICE_NAME);
configuration.setUsernameAndPassword(new TinyDB(context.getApplicationContext()).getString("username"), new TinyDB(context.getApplicationContext()).getString("password"));
configuration.setDebuggerEnabled(true);
connection = new XMPPTCPConnection(configuration.build());
connection.setUseStreamManagement(true);
connection.setUseStreamManagementResumption(true);
ReconnectionManager reconnectionManager = ReconnectionManager.getInstanceFor(connection);
reconnectionManager.enableAutomaticReconnection();
reconnectionManager.setReconnectionPolicy(ReconnectionManager.ReconnectionPolicy.RANDOM_INCREASING_DELAY);
connection.connect();
connection.login();
}
} catch (XMPPException xe) {
xe.printStackTrace();
} catch (SmackException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
So when I call openConnection() should I do that in an AsyncTask or is that not necessary? I am a little confused.
You should manage your XMPP(TCP)Connection within an Android Service. The service state (running/stopped) should reassemble the connection state: When the service is running the connection should be established or the service should try to establish the connection (if data connectivity is available). If the service stops, then also disconnect the connection.
When i call openConnection() should i do that in an asynctask or that is not neccesary?
Shortly, YES. Everything related with networking should be moved to another thread to avoid blocking main thread. Hence doInBackground() of AsyncTask runs on another thread, which is where you should call that function.
Yes, as the official documentation points it out:
AsyncTask enables proper and easy use of the UI thread. This class
allows to perform background operations and publish results on the UI
thread without having to manipulate threads and/or handlers.
I chose not to use AsyncTask for my smack project after searching around.
its threading model have been quite different between Android version and need to take care about, also after honeycomb, it is single thread, long blocking this will cause issue on the whole device that also use AsyncTask , xmpp and bosh can cause long blocking up to seconds/minutes
AsyncTask has implicit reference to activity and such a long operation will cause memory issues, or easy memory leakage when exception handling is not proper
AsyncTask 's result will be lost if reference activity got reset, but activity in Android can be reset as easy as a simple device rotation or network configuration change, too many save and restore instance to make this usable as every xmpp operation may be long task
I am coding an application where a remote service has to run at all time and to perform these taks :
Create and keep a bluetooth connection to another device
Ask this device for informations periodically (1 second)
Get GPS Location periodically (1 second)
Write previous datas in a text file every 1 second
For this, I created from my remote service 2 Threads : one for the data request (loopThread) and one for the GPS Location (gpsThread). The loopThread, after getting the datas from the blueTooth Device should ask the gpsThread for the location. It has to be very quick, that's why I am using a Thread, so i can store the Location in a variable which can be sent.
The remote serviceand the 2 threads should communicate through handlers.
The problem is : I can make each Handlers communicate with the remote service, but not with each other.
I create Threads like this :
myGPSThread = new GPSThread(mainServiceHandler,locationManager);
myLoopThread = new AcquisitionThread(mainServiceHandler, sockIn, sockOut);
I tried sending the Handler of one to the other by message, but Handlers seem not to be parcelable.
Does anyone have the solution to this?
If you want to stick to your Handler based approach, you can set up your two Threads as follows.
For your Threads, subclass HandlerThread instead of Thread. Also, make them implement Handler.Callback and don't start() them right away.
final class GPSThread extends HandlerThread implements Handler.Callback {
private Handler otherThreadHandler;
public void setOtherThreadHandler(Handler otherThreadHandler) {
this.otherThreadHandler = otherThreadHandler;
}
#Override
public void handleMessage(Message msg) {
// like in your comment
}
}
myGPSThread = new GPSThread(locationManager);
myLoopThread = new AcquisitionThread(sockIn, sockOut);
myGPSThreadHandler = new Handler(myGPSThread.getLooper(), myGPSThread);
myLoopThreadHandler = new Handler(myLoopThread.getLooper(), myLoopThread);
myGPSThread.setOtherThreadHandler(myLoopThreadHandler);
myLoopThread.setOtherThreadHandler(myGPSThreadHanlder);
myGPSThread.start();
myLoopThread.start();
If you want low latency and your event-driven code is short and friendly, you may want to create the HandlerThreads with a better-than-default priority; see here.
As already mentioned, you can as well set up two "ordinary" Threads which operate on two LinkedBlockingQueues; these Threads would block in their run() methods upon waiting for a message (aka Object) from the other Thread.