I'm not sure if my current approach is the proper way to use a Service: I would like to listen to a LocalBroadcastManager in the background (no Activity involved) and query some WebServices upon receiving an Intent. Could you please have a look at my code below and tell me if this is "good" or "bad" in regards of a robust code design? Of course I'd like to reduce the device resource utilisation to a minimum.
Originally, I had an IntentService in my mind but I didn't figure out how to start it from a BroadcastReceiver (you can't register a BroadcastReceiver in the manifest if it just listens to LocalBroadcasts).
public class WebRequestService extends Service {
#Override
public void onCreate() {
BroadcastReceiver mLocalMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
new getCurrentValues().execute();
}
};
IntentFilter messageFilter = new IntentFilter(Intent.ACTION_SEND);
LocalBroadcastManager.getInstance(this).registerReceiver(mLocalMessageReceiver, messageFilter);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
#Override
public void onDestroy() {}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
private class getCurrentValues extends AsyncTask<Void, Void, ResultDTO> {
#Override
protected ResultDTO doInBackground(Void... params) {
// do some magic
return resultDTO;
}
protected void onPostExecute(ResultDTO result) {
if (result != null) {
Intent messageIntent = new Intent();
messageIntent.setAction("currentValuesUpdated");
messageIntent.putExtra("result", result);
LocalBroadcastManager.getInstance(this).sendBroadcast(messageIntent);
}
}
} }
Thank you very much
Related
From an Android Service, I would like to call an Activity method, but only if the Activity is in the foreground.
I would like nothing to happen in case the Activity is not in the foreground.
Which is the simplest way to achieve that?
From a Service always better to broadcast events if the activity is listening to that broadcast will respond. If the activity is not listening then nothing will happen it will ignore.
This is the better solution than the one which you have asked.
I found a very simple solution, adapted from this previous answer:
On the Service:
Intent intent = new Intent(MainActivity.RECEIVER_INTENT);
intent.putExtra(MainActivity.RECEIVER_MESSAGE, myMessage);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
On the Activity:
public static final String RECEIVER_INTENT = "RECEIVER_INTENT";
public static final String RECEIVER_MESSAGE = "RECEIVER_MESSAGE";
Create a listener on onCreate():
public void onCreate(Bundle savedInstanceState) {
...
mBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String message = intent.getStringExtra(RECEIVER_MESSAGE);
// call any method you want here
}
};
}
register it in onStart():
#Override
protected void onStart() {
super.onStart();
LocalBroadcastManager.getInstance(this).registerReceiver((mBroadcastReceiver),
new IntentFilter(RECEIVER_INTENT)
);
}
unregister it in onStop():
#Override
protected void onStop() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(mBroadcastReceiver);
super.onStop();
}
You can do this using a Interface just to check if the activity is in background or in foreground.
I am sharing some code to have some idea.
public interface CheckAppInForeground {
boolean isAppInForGround();
}
In your Activity
public class MainActivity extends AppCompatActivity implements CheckAppInForeground {
Boolean isAppInForeGround;
#Override
protected void onResume() {
super.onResume();
isAppInForeGround = true;
}
#Override
protected void onStop() {
super.onStop();
isAppInForeGround = false;
}
#Override
public boolean isAppInForGround() {
return isAppInForeGround;
}
}
And your service class
public class MyService extends Service {
Activity activity;
public MyService(Activity activity) {
this.activity = activity;
}
#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) {
MainActivity mainActivity = (MainActivity) activity;
if (activity != null) {
boolean isActivityInForGround = mainActivity.isAppInForGround();
if (isActivityInForGround) {
// Do what you want to do when activity is in foreground
} else {
// Activity is in background
}
} else {
// Activity is destroyed
}
return super.onStartCommand(intent, flags, startId);
}
}
I think i am clear with the code. If you find something missing or unclear please let me know
What are the best practices for running worker threads in the background that periodically update UI elements in an activity. The goal here is to avoid any screen freezing on any kind of updates and if there are any specific guidelines/standards that should be followed.
Try Service for Background Work.
I have made an example for you.
Try this.
TestActivity.java
public class TestActivity extends AppCompatActivity {
private final String TAG = "TestActivity";
public final static String RECEIVER_ACTION = "com.action.MyReceiverAction";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test_work);
registerMyReceiver();
startService(new Intent(this, BackgroundService.class));
}
MyReceiver myReceiver = new MyReceiver();
private void registerMyReceiver() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(RECEIVER_ACTION);
registerReceiver(myReceiver, intentFilter);
}
class MyReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent) {
Log.e(TAG, "onReceive() called");
}
}
#Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(myReceiver);
}
}
BackgroundService.java
public class BackgroundService extends Service {
private String TAG = "BackgroundService";
#Override
public void onCreate() {
super.onCreate();
Log.e(TAG, "onCreate() called");
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
Log.e(TAG, "onBind() called");
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG, "onStartCommand() called");
notifyToUI();
return super.onStartCommand(intent, flags, startId);
}
/**
* This Methd will notify your Activity
*/
private void notifyToUI()
{
Intent myIntent = new Intent();
myIntent.setAction(TestActivity.RECEIVER_ACTION);
sendBroadcast(myIntent);
}
}
Now at the end register BackgroundService in AndroidManifest.xml file
<service android:name=".BackgroundService"/>
Use AlarmManager (or some other timer) to periodically start a service. That service then updates the model, and notifies UI thread with for example LocalBroadcastManager. UI thread can then use BroadcastReceiver to catch the Intent and update itself.
I have an activity. It will be receive two variable from an service. In the service, I will send two variable to the activity by
// Send first variable
sendBroadcast(new Intent("first_one"));
// Send second variable
sendBroadcast(new Intent("second_one"));
Now, In the activity, I used bellow code to receive the data. There are
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
registerReceiver(firstRec, new IntentFilter("first_one"));
registerReceiver(secondRec, new IntentFilter("second_one"));
}
#Override
protected void onPause() {
super.onPause();
if (firstRec != null) {
unregisterReceiver(firstRec);
firstRec = null;
}
if (secondRec != null) {
unregisterReceiver(secondRec);
secondRec = null;
}
}
private BroadcastReceiver firstRec = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.d("TAG","OK first");
}
};
private BroadcastReceiver secondRec = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.d("TAG","OK second");
}
};
However, I cannot print the log "OK second" when I called sendBroadcast(new Intent("second_one")); in the service. What is happen? How can I fix it? Thank you
UPDATE: my activity is an accept calling activity get from #notz
How can incoming calls be answered programmatically in Android 5.0 (Lollipop)?. Then I create an service as following
public class myService extends Service{
#Override
public void onCreate() {
super.onCreate();
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Intent answerCalintent = new Intent(getApplicationContext(), AcceptCallActivity.class);
answerCalintent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
answerCalintent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(answerCalintent);
//Send the second command after 10 second and make the calling in background
new CountDownTimer(10000, 100) {
public void onTick(long millisUntilFinished) {
}
public void onFinish() {
sendBroadcast(new Intent("second_one"));
}
}.start();
return START_STICKY;
}
}
You should unregister your reŃeiver in a method opposite to that in which you register it:
If you registered receiver in onCreate() - then you should unregister it in onDestroy().
But as i know, for most cases, the best practice is to register receiver in onResume() and unregister it in onPause().
I am new to Android development, and I try to get some practice with service and intentservice.
This is my service class:
public class MyBaseService extends Service {
private double[] returnData;
public MyBaseService() {
}
#Override
public void onCreate() {
returnData = new double[//dataSise];
}
/** The service is starting, due to a call to startService() */
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
try {
for (Map.Entry<Integer, Double[]> mapEntry : dataMap.entrySet()) {
doXYZ(mapEntry.getValue());
Arrays.sort(returnData);
}
} catch (IOException e) {
e.printStackTrace();
}
Intent intents = new Intent();
intents.setAction(ACTION_SEND_TO_ACTIVITY);
sendBroadcast(intents);
return START_STICKY;
}
/** A client is binding to the service with bindService() */
#Override
public IBinder onBind(Intent arg0) {
return mBinder;
}
public class MyBinder extends Binder {
public MyBaseService getService() {
return MyBaseService.this;
}
}
/** Called when a client is binding to the service with bindService()*/
#Override
public void onRebind(Intent intent) {
}
/** Called when The service is no longer used and is being destroyed */
#Override
public void onDestroy() {
super.onDestroy();
}
private void doXYZ(double[] data) {
int gallerySize = galleryFiles.length;
for (int i=0; i<data.length; ++i) {
Intent cfIntent = new Intent(this, MyIntentService.class);
compareFeatureIntent.putExtra(MyIntentService.COMPARING_INDEX, i);
startService(cfIntent);
}
}
BroadcastReceiver mReceiver;
// use this as an inner class like here or as a top-level class
public class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
int index = intent.getIntExtra(MyIntentService.COMPARING_INDEX, 0);
double scores = intent.getDoubleArrayExtra(MyIntentService.COMPARING_SCORE);
data[index] = scores[0];
}
// constructor
public MyReceiver(){
}
}
}
And this is intentservice class:
public class MyIntentService extends IntentService {
protected static final String ACTION_COMPARE_FEATURES = "CompareFeatures";
protected static final String COMPARING_SCORE = "Score";
protected static final String COMPARING_INDEX = "Index";
public MyIntentService() {
super("MyIntentService");
}
#Override
protected void onHandleIntent(Intent intent) {
int index = (int)intent.getLongExtra(COMPARING_INDEX, 0);
// This is long operation
double[] scores = getScores(index);
Intent intents = new Intent();
intents.setAction(ACTION_COMPARE_FEATURES);
intent.putExtra(COMPARING_SCORE, scores);
intent.putExtra(COMPARING_INDEX, index);
sendBroadcast(intents);
}
}
The scenario is that I want to start MyBaseService class inside main activity. Inside MyBaseService, I need to do a long run operation and need to iterate that operation many times. So, I put that long operation in MyIntentService, and start MyIntentService in a loop.
MyIntentService will produce some data, and I want to get that data back in MyBaseService class to do some further operations.
The Problem I am facing with communication between MyBaseService and MyIntentService. Because MyBaseService will start MyIntentSerice many times, my initial solution is to sendBroadcast() from MyIntentService, and register receiver in MyBaseService.
So, my questions are:
Is my design with MyBaseService MyIntentService efficient? If not, how should I do to archive the result I want?
If sendBroadcast() is a right direction, how should I register in MyBaseService?
Your architecture is fine. There are several ways to do this but this approach is OK.
You can register the BroadcastReceiver in MyBaseSerice.onStartCommand() and unregister it in MyBaseService.onDestroy().
You will need to determine how to shutdown MyBaseService. Either the Activity can do it or MyBaseService will need to keep track of the number of replies it is waiting for from the IntentService and as soon as it gets the last one it can shut itself down by calling stopSelf().
I have interrogation about the way to use a BroadcastReceiver with a ResultReceiver in it.
I know that if "A BroadcastReceiver hasn't finished executing within 10 seconds.", there is an ANR.
I have an application that respond to an Intent, declared in the Manifest.
It is a BroadcastReceiver that start a service because it needs to make some networks operations:
public class MyReceiver extends BroadcastReceiver {
private Context context = null;
private MyResultReceiver myResultReceiver = null;
#Override
public void onReceive(Context context, Intent intent) {
this.context = context;
myResultReceiver = new MyResultReceiver(new Handler());
Intent i = new Intent();
i.setClass(context, MyService.class);
i.putExtra(Constants.EXTRA_RESULT_RECEIVER, myResultReceiver);
context.startService(i);
}
public class MyResultReceiver extends ResultReceiver {
public MyResultReceiver(Handler handler) {
super(handler);
}
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
if (resultCode == 42) {
// Something
} else {
// Something else
}
}
}
}
My service looks like this:
public class MyService extends Service {
private Context context = null;
private ResultReceiver resultReceiver = null;
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
this.context = this;
resultReceiver = intent.getParcelableExtra(Constants.EXTRA_RESULT_RECEIVER);
MyTask myTask = new MyTask();
myTask.execute();
return super.onStartCommand(intent, flags, startId);
}
public class MyTask extends AsyncTask<Void, Void, Boolean> {
#Override
protected Boolean doInBackground(Void... params) {
// Network operation
return status;
}
#Override
protected void onPostExecute(final Boolean status) {
Bundle bundle = new Bundle();
if (status == true) {
if (resultReceiver != null) {
resultReceiver.send(42, null);
}
} else {
if (resultReceiver != null) {
resultReceiver.send(-1, null);
}
}
}
}
}
My question is, am I sure that the resultReceiver still exist and will do what it have to do if the network operation is longer than 10 seconds ?
Here's the relevant documentation from the SDK:
If this BroadcastReceiver was launched through a tag, then
the object is no longer alive after returning from this function. This
means you should not perform any operations that return a result to
you asynchronously -- in particular, for interacting with services,
you should use startService(Intent) instead of bindService(Intent,
ServiceConnection, int). If you wish to interact with a service that
is already running, you can use peekService(Context, Intent).
The Intent filters used in registerReceiver(BroadcastReceiver,
IntentFilter) and in application manifests are not guaranteed to be
exclusive. They are hints to the operating system about how to find
suitable recipients. It is possible for senders to force delivery to
specific recipients, bypassing filter resolution. For this reason,
onReceive() implementations should respond only to known actions,
ignoring any unexpected Intents that they may receive.
Bottom line:
If you start a service, use startService(Intent).
Don't do long running applications on onReceive.
AsyncTasks may be destroyed, your best bet is to use a Service. If you are using an AsyncTask inside of a Service, it should be fine.