My app relies on a Service which stays in sync with external hardware in the background. Because the service operates on the main Thread, it does any heavy work asynchronously using an IntentService. Here is a minimalized example of the code to explain the control flow:
public class MyService extends Service {
private final Handler handler = new Handler();
void someMethodDoesCallBarAsynchronously(){
Intent i = new Intent(this, MyIntentService.class);
i.putAction(ACTION_FOO);
i.putExtra(EXTRA_RECEIVER, new MyResultReceiver(handler));
startService(toGetScannerStatus);
}
void receivedFooResult(String bar){
//...
}
public class MyResultReceiver extends ResultReceiver{
public MyResultReceiver(Handler handler){
super(handler);
}
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
super.onReceiveResult(resultCode, resultData);
switch(resultCode){
case RESULT_CODE_FOO:
receivedFooResult(resultData.getString(FOO_RESULT));
break;
}
}
}
}
public class MyIntentService extends IntentService {
public MyIntentService(){super("MyIntentService");}
#Override
protected void onHandleIntent(Intent intent) {
switch (intent.getAction()) {
case ACTION_FOO:
ResultReceiver receiver = intent.getParcelableExtra(EXTRA_RECEIVER);
Bundle bundle = new Bundle()
bundle.putString(FOO_RESULT, foo());
receiver.send(RESULT_CODE_FOO, b);
break;
}
}
Now, after half a year and some updates, Android Studio complains that MyResultReceiver implements Parcelable but does not provide a CREATOR field. Problem one, MyResultReceiver is an inner class, so I have to put it into an own class. I cannot make it static because it must hold a reference to MyService. That is still quite easy:
public class MyService extends Service {
private final Handler handler = new Handler();
void someMethodDoesCallBarAsynchronously(){
Intent i = new Intent(this, MyIntentService.class);
i.putAction(ACTION_FOO);
i.putExtra(EXTRA_RECEIVER, new MyResultReceiver(this, handler));
startService(toGetScannerStatus);
}
void receivedFooResult(String bar){
//...
}
}
public class MyResultReceiver extends ResultReceiver{
private final MyService myService;
public MyResultReceiver(MyService s, Handler handler){
super(handler);
this.myService = myService;
}
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
super.onReceiveResult(resultCode, resultData);
switch(resultCode){
case RESULT_CODE_FOO:
myService.receivedFooResult(resultData.getString(FOO_RESULT));
break;
}
}
}
Now I can add a public static field. But how to implement CREATOR correctly?
public class MyResultReceiver extends ResultReceiver {
public static final Parcelable.Creator<MyResultReceiver> CREATOR
= new Parcelable.Creator<MyResultReceiver>() {
public MyResultReceiver[] newArray(int size) {
return new MyResultReceiver[size];
}
public MyResultReceiver createFromParcel(Parcel in) {
// ???
}
};
If you want to: Why is this necessary? What will it be used for?
your current architecture is too complex imho: you could easily do that in one Service but it is up to you, when it comes to your problem: you don't need to create any CREATOR as what you got is a lint warning, just add SuppressLint annotation like this:
#SuppressLint("ParcelCreator")
public class MyResultReceiver extends ResultReceiver {
Related
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().
In my Application I have to notify my Activity from IntentService class .
For that purpose I am using LocalBroadcastManager. But I am not receiving anything in the onReceive of my Broadcast Receiver. Here is what I have written.
In my BaseActivity I have registered my receiver.
public class BaseActivity extends FragmentActivity implements App {
#Override
protected void onCreate(Bundle arg0) {
super.onCreate(arg0);
}
#Override
protected void onResume() {
super.onResume();
LocalBroadcastManager.getInstance(this).registerReceiver(
mMessageReceiver, new IntentFilter(custom-event-name));
}
#Override
protected void onPause() {
super.onPause();
LocalBroadcastManager.getInstance(this).unregisterReceiver(
mMessageReceiver);
}
// Our handler for received Intents. This will be called whenever an Intent
// with an action named "custom-event-name" is broadcasted.
BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getExtras();
System.out.println("Overlay Message" +bundle.getString("message"));
}
};
}
I am sending a local broadcast from my RegisterAlarmIntentService class.
public class RegisterAlramIntentService extends WakefulIntentService implements
APIConstants {
public RegisterAlramIntentService() {
super("AlarmService");
}
#Override
public String getTag() {
return "AlarmService";
}
#Override
protected void onHandleIntent(Intent intent) {
System.out.println("Working till here fine In RegisterAlarm");
Bundle bundle = intent.getExtras();
Intent localIntent = new Intent(custom-event-name);
localIntent.putExtras(bundle );
LocalBroadcastManager.getInstance(this).sendBroadcast(
localIntent);
}
}
onHandleIntent() method is called. But nothing is received in onReceive() of my receiver.
Please Help. Thanks in advance!!
Try
public class RegisterAlramIntentService extends WakefulIntentService implements
APIConstants {
Intent localIntent;
public RegisterAlramIntentService() {
super("AlarmService");
localIntent = new Intent(custom-event-name);
}
#Override
public String getTag() {
return "AlarmService";
}
#Override
protected void onHandleIntent(Intent intent) {
System.out.println("Working till here fine In RegisterAlarm");
Bundle bundle = intent.getExtras();
Thread.sleep(5000); // For Testing only because it is in whole new thread (which may not wait for your reciever to setup)
localIntent.putExtras(bundle );
LocalBroadcastManager.getInstance(this).sendBroadcast(
localIntent);
}
}
Also in manifest :
<service android:name="com.commonsware.android.localcast.RegisterAlramIntentService"/>
See this
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.
I'm using a ResultReceiver to allow a service to pass data through to an activity. I'm experiencing some difficulties with comparing intents that have been sent through IPC, it looks like the objects are changing, and thus can't be compared using a standard hashcode(.equals) comparison. I've created some sample code that will reproduce the scenario:
MyActivity.java:
public class MyActivity extends Activity {
private final Handler mHandler = new Handler();
private Intent serviceIntent;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
MyReceiver receiver = new MyReceiver(mHandler);
serviceIntent = new Intent(this, MyService.class);
serviceIntent.putExtra("receiver", receiver);
startService(serviceIntent);
}
public class MyReceiver extends ResultReceiver {
public MyReceiver(Handler handler) {
super(handler);
}
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
Intent intent = resultData.getParcelable("intent");
if(intent.equals(serviceIntent)) {
Log.d("TEST", "Same intent!");
} else {
Log.d("TEST", "Different intents!");
}
}
}
}
MyService.java
public class MyService extends Service {
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
ResultReceiver receiver = intent.getExtras().getParcelable("receiver");
Bundle b = new Bundle();
b.putParcelable("intent", intent);
receiver.send(100, b);
stopSelf();
return Service.START_NOT_STICKY;
}
}
Don't forget to register the service in the manifest if you want to run this.
So the intent is sent back and forth, no change is made in the process and yet my activity insists that the two references differ. What's going on here?
The intent created by Intent intent = resultData.getParcelable("intent") and private Intent serviceIntent are not the same object, even if they are created to contain the exact same data. The current .equals() function simply checks if the intents are the same object; you will have to write your own .equals() function to determine if the intents are the same by whatever definition fits your application. See here.
Fundamentally, I would like to establish a callback to an Activity from an IntentService. My question is very similar to the one answered here:
Restful API service
However, in the answer code, the activity code is seen as implementing a ResultReceiver. Unless I'm missing something, ResultReceiver is actually a class, so it cannot perform this implementation.
So essentially, I'm asking what would be the correct way to wire up a ResultReceiver to that service. I get confused with Handler and ResultReceiver concepts with respect to this. Any working sample code would be appreciated.
You need to make custom resultreceiver class extended from
ResultReceiver
then implements the resultreceiver interface in your activity
Pass custom resultreceiver object to intentService and in
intentservice just fetch the receiver object and call
receiver.send() function to send anything to the calling activity in
Bundle object.
here is customResultReceiver class :
public class MyResultReceiver extends ResultReceiver {
private Receiver mReceiver;
public MyResultReceiver(Handler handler) {
super(handler);
// TODO Auto-generated constructor stub
}
public interface Receiver {
public void onReceiveResult(int resultCode, Bundle resultData);
}
public void setReceiver(Receiver receiver) {
mReceiver = receiver;
}
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
if (mReceiver != null) {
mReceiver.onReceiveResult(resultCode, resultData);
}
}
}
implements the Myresultreceiver.receiver interface in you activity, create a class variable
Public MyResultReceiver mReceiver;
initialize this variable in onCreate:
mReceiver = new MyResultReceiver(new Handler());
mReceiver.setReceiver(this);
Pass this mReceiver to the intentService via:
intent.putExtra("receiverTag", mReceiver);
and fetch in IntentService like:
ResultReceiver rec = intent.getParcelableExtra("receiverTag");
and send anything to activity using rec as:
Bundle b=new Bundle();
rec.send(0, b);
this will be received in onReceiveResult of the activity. You can view complete code at:IntentService: Providing data back to Activity
Edit: You should call setReceiver(this) in onResume and setReceiver(null) in onPause() to avoid leaks.
You override a method by subclassing. It doesn't have to be an interface to do that.
For example:
intent.putExtra(StockService.REQUEST_RECEIVER_EXTRA, new ResultReceiver(null) {
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
if (resultCode == StockService.RESULT_ID_QUOTE) {
...
}
}
});
I have created a simple example that demonstrates how to use ResultReceiver.
MainActivity:
public class MainActivity extends AppCompatActivity {
private final static String TAG = MainActivity.class.getSimpleName();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent serviceIntent = new Intent(this, MyService.class);
serviceIntent.putExtra("logName", "MAIN_ACTIVITY");
serviceIntent.putExtra(MyService.BUNDLED_LISTENER, new ResultReceiver(new Handler()) {
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
super.onReceiveResult(resultCode, resultData);
if (resultCode == Activity.RESULT_OK) {
String val = resultData.getString("value");
Log.i(TAG, "++++++++++++RESULT_OK+++++++++++ [" + val + "]");
} else {
Log.i(TAG, "+++++++++++++RESULT_NOT_OK++++++++++++");
}
}
});
startService(serviceIntent);
}
}
MyService:
public class MyService extends Service {
private final static String TAG = MyService.class.getSimpleName();
public final static String BUNDLED_LISTENER = "listener";
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
String logName = intent.getStringExtra("logName");
ResultReceiver receiver = intent.getParcelableExtra(MyService.BUNDLED_LISTENER);
Bundle bundle = new Bundle();
bundle.putString("value", "30");
receiver.send(Activity.RESULT_OK, bundle);
return Service.START_NOT_STICKY;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
for use Resulteceiver in android
Create SomeResultReceiver extends from resultReceiver
Create interface someReceiver with on method for example onReceivResult(int resultCode,Bundle resultData);
3.use someReceiver in someResultreceiver
create someService extends IntentService and use someresultReceiver.send() method for send result from service to someOne class (ex: MyActivity)
Implement somereceiver on Activity
6.instantiation someResultReceiver in MyActivity class and setreceiver
startService with Intent and putExtra someResultreceiver instanse
for more details ResultReceiver Class see enter link description here