Android perform ImageUpload in background and access intent data in callback - android

Well I've thinking through this problem and i need some help from community.
I want to upload image(s)(5 images) to server which is currently done in async task of app. Hence upload can be stopped when user closes the app so i want to perform this operation using IntentService.
So I have created my Intent Service to perform image upload.
public class ImageUploadService extends IntentService {
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* #param name Used to name the worker thread, important only for debugging.
*/
public ImageUploadService(String name) {super(name);}
public ImageUploadService(){
this(ImageUploadService.class.getName());
}
#Override
protected void onHandleIntent(Intent intent) {
//todo perform long running image upload operation
KbUserService kbUserService = JacksonRestRequestBuilder
.setupUploadRestService(RestUrls.BASE_URL_CUSTOMER, true,
KbUserService.class, getApplicationContext());
LinkedHashMap<String, Object> requestBody = new LinkedHashMap<>();
Call<LinkedHashMap<String,Object>> call = kbUserService.uploadFile(requestBody);
call.enqueue(new KbCallback() {
#Override
public void failure(Map<String, Object> serverErrorResponse, String genericErrorMessage) {
//todo send notification that image upload failed and also send broadcast event that image upload failed.
}
#Override
public void success(Map<String, Object> successResponse) {
Map<String, Object> modelDataFromResponseAsMap =
ClientModelUtils.getModelDataFromResponseAsMap(successResponse);
// Log.i(NAME,"Background Image upload completed for "+msg.arg1);
String fileUrl = ClientModelUtils.getString(modelDataFromResponseAsMap,
ModelConstants.UserConstants.IMAGE_URL);
//todo send notification that image upload success and send broadcast that image upload success.
// uploadFileSuccess(fileUrl, profName);
// bean.setUploadInProgress(false);
// view.uploadFileSuccess(fileUrl,profName);
}
#Override
public void networkOrUnexpectedError(String message) {
//todo send notification that image upload failed and also send broadcast event that image upload failed.
}
});
}
protected void showToast(final String msg){
//gets the main thread
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
// run this code in the main thread
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
});
}
}
So here i send a broadcast in success response to all registered activities.
I'm not able to solve the following problems,
1.Suppose when came out of app and came back to activity, how to know what are the currently uploading images?
2.How to access intent data in onHandleIntent() inside callback?, Because in intent data i have information about the imageType so when request is success i want to send an broadcast with imageUrl and imageType.

This might not be the ideal answer but I normally have broadcast receivers on both the service and the activity. This way they can communicate with each other. For example when the activity starts again I send a broadcast to the service notifying it. The service will then send a broadcast with its information.
public class SomeService extends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
registerReceiver(receiver, new IntentFilter(SOME_SERVICE_ACTIONS));
}
#Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(receiver);
}
private BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
switch (intent.getIntExtra(SERVICE_COMMAND, 0)) {
case ACTIVITY_STARTED:
sendCallback();
break;
}
};
}

Related

Data change in IntentService

I'm using IntentService for handling my messages from push notification from FCM. It works perfectly as required when the message comes one by one but when the device is not connected to the network and after when device again connected FCM send the bulk of messages at a time and at this scenario service causes some ambiguous while handling intent data which causes unexpected behavior in calling web services.
My push-notification message handler Class :
public class PushMessageHandler extends FirebaseMessagingService {
private final static String TAG = "PushMessageHandler";
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
if (remoteMessage.getData() != null){
Log.d(TAG, String.valueOf(remoteMessage.getData()));
Intent notificationService = new Intent(this, NotificationService.class);
notificationService.putExtra(ResponseConstants.NOTIFICATION_FIELD,remoteMessage.getData().get(ResponseConstants.NOTIFICATION_FIELD));
notificationService.putExtra(ResponseConstants.NOTIFICATION_DATA,remoteMessage.getData().get(ResponseConstants.NOTIFICATION_DATA));
notificationService.putExtra(ResponseConstants.NOTIFICATION_TYPE,remoteMessage.getData().get(ResponseConstants.NOTIFICATION_TYPE));
try {
notificationService.putExtra(ResponseConstants.NOTIFICATION_IMAGE,remoteMessage.getData().get(ResponseConstants.NOTIFICATION_IMAGE));
notificationService.putExtra(ResponseConstants.NOTIFICATION_TITLE, remoteMessage.getData().get(ResponseConstants.NOTIFICATION_TITLE));
} catch (Exception e){
Crashlytics.logException(e);
}
try {
notificationService.putExtra(ResponseConstants.DATASETS,remoteMessage.getData().get(ResponseConstants.DATASETS));
} catch (Exception e){
Crashlytics.logException(e);
}
startService(notificationService);
} else {
Log.d(TAG, "Notification data is null");
}
}
}
And my notification handler service class :
public class NotificationService extends IntentService implements NotificationContract.View {
#Inject
public NotificationPresenter mNotificationPresenter;
private NotificationContract.Presenter mPresenter;
private static final String TAG = "NotificationService";
private Intent mIntent;
public NotificationService() {
super("NotificationService");
}
#Override
protected void onHandleIntent(#Nullable Intent intent) {
mIntent = intent;
DaggerNotificationPresenterComponent.builder()
.notificationViewModule(new NotificationViewModule(this))
.remoteDataSourceComponent(MyApplication.getInstance().providesRemoteDataSource())
.localDataSourceComponent(MyApplication.getInstance().providesLocalDataSource())
.build().inject(this);
}
#Override
public synchronized void setPresenter(NotificationContract.Presenter presenter) {
this.mPresenter = presenter;
final String notificationField = mIntent.getStringExtra(ResponseConstants.NOTIFICATION_FIELD);
Log.d(TAG, notificationField);
Handler handler = new Handler(getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
switch (notificationField.trim()){
case Constants.NOTIFICATION_FIELD_CACHEHOMEFEEDS :
mPresenter.prefetchData(Integer.parseInt(
mIntent.getStringExtra(ResponseConstants.NOTIFICATION_DATA)),
new JSONObject(mIntent.getStringExtra(ResponseConstants.DATASETS)));
break;
case Constants.NOTIFICATION_FIELD_UPDATEFEEDS :
mPresenter.getPostDetailById(Integer.parseInt(
mIntent.getStringExtra(ResponseConstants.NOTIFICATION_DATA)),
new JSONObject(mIntent.getStringExtra(ResponseConstants.DATASETS)));
break;
case Constants.NOTIFICATION_FIELD_ARTICLES :
mPresenter.getPostDetailsPostUrl(mIntent.getStringExtra(ResponseConstants.NOTIFICATION_DATA));
break;
case Constants.NOTIFICATION_FIELD_POSTDELETED :
mPresenter.deleteFeed(Integer.parseInt(
mIntent.getStringExtra(ResponseConstants.NOTIFICATION_DATA)));
break;
}
}
});
}
}
In the case of bulk push messages, I'm getting the interchangeable value of NOTIFICATION_DATA i.e the value that I'm expected when notification field is "NOTIFICATION_FIELD_CACHEHOMEFEEDS" is "post: 1234" and for field "NOTIFICATION_FIELD_ARTICLES" is "post: 'post-URL'" but I'm getting "post:1234" for filed "NOTIFICATION_FIELD_ARTICLES", the value is getting interchangeable in any sequence depends on message calling of push notification.
According to the documentation of IntentService handles the requests one by one in queue manner. Then why this happening. Is any method to handle this perfectly.
IntentService -> onHandleIntent is executed on a background thread. If you have time consuming operation you should execute it there.
If not - just use normal Service.
Now in onHandleIntent you are injecting presenter multiple times from background thread - i think you should move the injection to the constructor.
Then in onHandleIntent call your presenter methods (mPresenter.prefetchData, mPresenter.getPostDetailById etc).

Android create thread for pusher to run at background

In my app, I am getting my messages instantly from my server via pusher. I have created a service designated to handle connections and firing broadcast messages to other activities in my app.
The problem that I face now is to have this service run in a new thread to have it still run even when my app goes to the background. I've found from this that I should create and connect it to the "service thread", but I cannot find examples for it with pusher.
If anyone can, could you please provide an example to do so? If not, insights to writing code with these "service threads" would be helpful as well. Thanks in advance for the help :D
PusherService.java
public class PusherService extends Service {
private static final String TAG = "PusherService";
private Pusher pusher = new Pusher("myKey");
private Channel channel = pusher.subscribe("cafe_channel");
private JSONObject pusherJSONObj;
private Order order;
public PusherService() {
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//this service will run until we stop it
setupPusher();
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
Toast.makeText(this, "Service Stopped", Toast.LENGTH_LONG).show();
}
private void setupPusher() {
Log.d(TAG, System.currentTimeMillis()+"");
channel.bind("customer_order", new SubscriptionEventListener() {
#Override
public void onEvent(String channelName, String eventName, final String data) {
Intent broadcastIntent = new Intent();
try {
pusherJSONObj = new JSONObject(data);
order = new Order(pusherJSONObj);
broadcastIntent.setAction("customer_order");
broadcastIntent.putExtra("message", "success");
broadcastIntent.putExtra("order", order);
} catch (JSONException e) {
e.printStackTrace();
Log.d("Pusher", "conversion failed");
broadcastIntent.setAction("customer_order");
broadcastIntent.putExtra("message", "JSON conversion error");
}
sendBroadcast(broadcastIntent);
}
});
pusher.connect();
}
}
OrdersActivity.java
private BroadcastReceiver pusherReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equalsIgnoreCase("customer_order")) {
adapter.newOrder((Order) intent.getParcelableExtra("order"));
}
}
};
It turns out that multithreading on one process does not solve my problem.
So instead, I split the service into a new process, which will keep the service running independent of the status of the main thread & process. Tested and found that service does not stall when my activities go background.

Android not able to stop service

In my app i am using a Service that periodically checks if there is a new personal message for the logged in user.
The service is started if the user enables the notification feature. Now if the user disables the notification feature i would like to stop the service.
I try to stop the service with the following lines of code.
Intent service = new Intent(getApplicationContext(), MessageService.class);
stopService(service);
The problem is that the service doesn't stop. It goes on working.
Here you can see my message service.
public class MessageService extends Service {
private int intervall;
public MessageService(){
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent,flags,startId);
Bundle intentData = intent.getExtras();
if(intentData != null) {
this.intervall = intentData.getInt("intervall");
}
final Handler handler = new Handler(){
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// async task for calling api otherwise we get an exeception here
new ServiceMessagesTask().execute(MessageService.this);
}
};
new Thread(new Runnable(){
public void run() {
while(true)
{
try {
Thread.sleep(intervall); // repeat after given intervall
handler.sendEmptyMessage(0);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
}
}
I have an activity where the user can edit his preferences. There it is also possible to activate the notification feature.
The notification service is started or stoped in the savePreferences() method:
public void savePreferences(View button) {
EditText login = (EditText)findViewById(R.id.txtbLogin);
EditText password = (EditText)findViewById(R.id.txtbPassword);
CheckBox enableNotification = (CheckBox) findViewById(R.id.cbNotifications);
Spinner spinner = (Spinner) findViewById(R.id.notificationInterval);
if(!login.getText().equals("") && !password.getText().equals("")){
Map<String, Object> preferences = new HashMap<String, Object>();
preferences.put("document_type", CouchbaseHelper.CB_VIEW_USER_PREFERENCES);
preferences.put("login", login.getText().toString());
preferences.put("password", password.getText().toString());
if(enableNotification.isChecked()){
preferences.put("enableNotification", true);
} else {
preferences.put("enableNotification", false);
}
preferences.put("notificationInterval", this.notificationInterval);
CouchbaseHelper couchbaseHelper = new CouchbaseHelper(getApplicationContext());
String documentId = couchbaseHelper.createDocUserPreferences(preferences);
couchbaseHelper.closeDb();
// start notification service if enabled
if(enableNotification.isChecked()){
Intent service = new Intent(getApplicationContext(), MessageService.class);
service.putExtra("intervall", Integer.valueOf(this.notificationInterval)*60*1000);
startService(service);
} else {
// TODO: this is not working!!! service doesnt stop
// try to stop running service
if(isMyServiceRunning()){
Intent service = new Intent(getApplicationContext(), MessageService.class);
stopService(service);
}
}
}
finish();
Intent main = new Intent(Preferences.this, Main.class);
startActivity(main);
}
I'm afraid you really don't get what a service is, service is just a component that do not require UI and is not linked to an activity life cycle, hence it runs in background, BUT background doesn't necessarily means in a separate thread, actually the service runs in the main thread, now that's one thing, killing a service doesn't mean you are killing all the working threads you create within, and in your code you are creating a Thread that is looping forever, that thread although created in the service is not linked in any way to the service life cycle.
So, if you want to stop the thread, get a reference to the thread you are creating in the startCommand method and in the onDestroy method just stop it, instead of having a while(true) validation, go for a flag and just change it to false in the onDestroy so it will stop the thread you created when started the service.
Regards!

Android - Best practice for retrying IntentService

My Android app sends a load of files to Amazon S3. Each file URI is passed in separate calls to IntentService which performs the upload.
However, I'm wondering what is the best way to handle failures... Should I detect the failure with my IntentService's onHandleIntent() method and retry within that same method, OR should I allow the failure to be handled outside of the method (and if so, how?)?
I'm personally leaning towards the first suggestion as I would prefer any file to be successfully uploaded before subsequent files are attempted to be uploaded, but I am not sure if detecting errors and performing retries within the onHandleIntent() method is good practice(?).
This is a very nice question. I was asked this in one interview and i had failed to answer it. But i will try and answer it here after some searching for the answer.
Step-1: You start an IntentService. You can start an IntentService either from an Activity or a Fragment.
/* Starting Download Service */
DownloadResultReceiver mReceiver = new DownloadResultReceiver(new Handler());
mReceiver.setReceiver(this);
Intent intent = new Intent(Intent.ACTION_SYNC, null, this, DownloadService.class);
/* Send optional extras to Download IntentService */
intent.putExtra("url", url);
intent.putExtra("receiver", mReceiver);
intent.putExtra("requestId", 101);
startService(intent);
Step-2: Make the class that extends IntentService.
public class DownloadService extends IntentService {
public static final int STATUS_RUNNING = 0;
public static final int STATUS_FINISHED = 1;
public static final int STATUS_ERROR = 2;
private static final String TAG = "DownloadService";
public DownloadService() {
super(DownloadService.class.getName());
}
#Override
protected void onHandleIntent(Intent intent) {
Log.d(TAG, "Service Started!");
final ResultReceiver receiver = intent.getParcelableExtra("receiver");
String url = intent.getStringExtra("url");
Bundle bundle = new Bundle();
if (!TextUtils.isEmpty(url)) {
/* Update UI: Download Service is Running */
receiver.send(STATUS_RUNNING, Bundle.EMPTY);
try {
String[] results = downloadData(url);//make your network call here and get the data or download a file.
/* Sending result back to activity */
if (null != results && results.length > 0) {
bundle.putStringArray("result", results);
receiver.send(STATUS_FINISHED, bundle);
}
} catch (Exception e) {
/* Sending error message back to activity */
bundle.putString(Intent.EXTRA_TEXT, e.toString());
receiver.send(STATUS_ERROR, bundle);
}
}
Log.d(TAG, "Service Stopping!");
this.stopSelf();
}
}
Step-3: To receive results back from IntentService, we can use subclass of ResultReciever. Once results are sent from Service the onReceiveResult() method will be called. Your activity handles this response and fetches the results from the Bundle. Once results are recieved, accordingly the activity instance updates the UI.
public class DownloadResultReceiver extends ResultReceiver {
private Receiver mReceiver;
public DownloadResultReceiver(Handler handler) {
super(handler);
}
public void setReceiver(Receiver receiver) {
mReceiver = receiver;
}
public interface Receiver {
public void onReceiveResult(int resultCode, Bundle resultData);
}
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
if (mReceiver != null) {
mReceiver.onReceiveResult(resultCode, resultData);
}
}
}
Step-4: In your MainActivity:
#Override
public void onReceiveResult(int resultCode, Bundle resultData) {
switch (resultCode) {
case DownloadService.STATUS_RUNNING:
//progress bar visible.
break;
case DownloadService.STATUS_FINISHED:
/* Hide progress & extract result from bundle */
/* Update ListView with result */
break;
case DownloadService.STATUS_ERROR:
/* Handle the error */
String error = resultData.getString(Intent.EXTRA_TEXT);
Toast.makeText(this, error, Toast.LENGTH_LONG).show();
/*It is here, i think, that you can again check (eg your net connection) and call the IntentService to restart fetching of data from the network. */
break;
}
}
I hope the above answer helps you. Any suggestions to improve the answer are most welcome. Thanks.

Android:Unable to receive data from Service to Activity through Broadcastreceiver

I have an activity and and a service. I am running my service in background in a time interval through AlaramManager. What I want is to receive periodically data from the service inside activity. For this I'm using broadcastreceiver, but it does not showing any data.
In my service I'm using this method for sending data:
private final void sendServiceActiveBroadcast(final boolean pActivate) {
final Intent _intent = new Intent();
_intent.setAction(BROADCAST_ACTION);
_intent.addCategory("com.monday.worker_android.android.CATEGORY");
_intent.putExtra("isactive", pActivate);
NewService.this.sendBroadcast(_intent);
}
And use it inside an AsyncTask class like:
#Override
protected void onPostExecute(String result) {
Log.d("Post Execute", "Executed");
super.onPostExecute(result);
float[] arr = new float[30];
if (round(distance(LATITUDE, LONGITUDE, lati, longi)) < 200) {
Log.d("OnPostExecute", "In");
sendServiceActiveBroadcast(true);
}
}
And try to receive this in my activity like:
private BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
boolean value = intent.getBooleanExtra("isactive", false);
if (value == true) {
Toast.makeText(getApplicationContext(), "received",
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getApplicationContext(), " not received",
Toast.LENGTH_SHORT).show();
}
}
};
I resister it in my onResume() and unresister it in my onPause() like:
#Override
protected void onResume() {
IntentFilter filter = new IntentFilter();
filter.addAction(NewService.BROADCAST_ACTION);
registerReceiver(receiver, filter);
super.onResume();
}
#Override
protected void onPause() {
unregisterReceiver(receiver);
super.onPause();
}
I just check some tutorials and code it. My application is working fine with service and also it excutes the onPostExecute() perfectly. But it does't show any broadcast data.
Can any one please suggest me how to receive periodically data from service and why I fail to receive data here and about my mistakes.
Thank You
addCategory
is the problem in your code. Because, in your activity you didn't set the category attribute, so the reference can not be found. Also, without having the category you can send this to multiple receivers at different locations with action attribute.

Categories

Resources