Using ResultReceiver in Android - android

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

Related

ResultReceiver from Service to Activity

I have a service running in background which is started from an activity and does its work without the activity. With a ResultReceiver I can communicate to the activity, but just as long as the activity is alive. Once destroid and started again there is no communication back to the activity. What did I wrong?
// Activity
public void onReceiveResult(int resultCode, Bundle resultData) {
String test = resultData.getString("Event");
}
public MyResultReceiver mReceiver;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mReceiver = new MyResultReceiver(new Handler());
mReceiver.setReceiver(this);
Intent i=new Intent(this, AppService.class);
i.putExtra(AppService.TIME, spinner_time.getSelectedItemPosition());
startService(i);
// Service
public class AppService extends Service {
public static final String TIME="TIME";
int time_loud;
Notification note;
Intent i;
private boolean flag_silencemode = false;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
time_loud = intent.getIntExtra(TIME, 0);
resultReceiver = intent.getParcelableExtra(RESULT_RECEIVER);
resultData = new Bundle();
resultData.putString("Event", "ysfgsdfgsdfg");
resultReceiver.send(55, resultData);
play(time_loud);
return(START_NOT_STICKY);
}
#SuppressLint("ParcelCreator")
public class MyResultReceiver extends ResultReceiver {
private Receiver mReceiver;
public MyResultReceiver(Handler handler) {
super(handler);
}
public void setReceiver(Receiver receiver) {
mReceiver = receiver;
}
public interface Receiver {
void onReceiveResult(int resultCode, Bundle resultData);
}
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
if (mReceiver != null) {
mReceiver.onReceiveResult(resultCode, resultData);
}
}
}
for simplicity use a local broadcast manager, and register and unregister it in your activity onResume , onPause lifecycles

Android Service extends ResultReceiver for IntentService, how to implement CREATOR?

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 {

BroadcastReceiver with ResultReceiver and lifetime

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.

IntentService responding to dead ResultReceiver

An activity instantiates a ResultReceiver and overrides onReceiveResult. The activity then sends an Intent to an IntentService and includes the ResultReceiver as an extra. Once the IntentService is finished processing it sends a message back to the ResultReceiver and processes it in onReceiveResult.
The issue is if the user navigates away from the Activity then the result is still sent back to the ResultReceiver which causes all types of issues. Is there not a way to stop this behavior? I've tried stopping the IntentService in the activity's onDestroy() but the result is still sent back.
Here is a sample Activity
public class Foo extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent i = new Intent(this, SampleIntentService.class);
i.putExtra("receiver", mReceiver);
startService(i);
}
#Override
public void onDestroy() {
stopService(new Intent(this, SampleIntentService.class));
mReceiver = null;
super.onDestroy();
}
ResultReceiver mReceiver = new ResultReceiver(new Handler()) {
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
// Handle response from IntentService here
}
};
}
Here is a sample IntentService
public class SampleIntentService extends IntentService {
public SampleIntentService() {
super(SampleIntentService.class.getName());
}
#Override
protected void onHandleIntent(Intent intent) {
ResultReceiver rec = intent.getParcelableExtra("receiver");
if (rec != null) {
rec.send(200, null);
}
}
}
I solved this issue by creating a custom ResultReceiver as follows.
public class SampleResultReceiver extends ResultReceiver {
private Receiver mReceiver;
public SampleResultReceiver(Handler handler) {
super(handler);
}
public void setReceiver(Receiver receiver) {
mReceiver = receiver;
}
public interface Receiver {
void onReceiveResult(int resultCode, Bundle resultData);
}
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
if (mReceiver != null) {
mReceiver.onReceiveResult(resultCode, resultData);
}
}
}
Then in my Activity I do the following:
public class Foo extends Activity implements Receiver {
private SampleResultReceiver mReceiver;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mReceiver = new SampleResultReceiver(new Handler());
mReceiver.setReceiver(this);
Intent i = new Intent(this, SampleIntentService.class);
i.putExtra("receiver", mReceiver);
startService(i);
}
#Override
public void onDestroy() {
if (mReceiver != null) {
mReceiver.setReceiver(null);
}
super.onDestroy();
}
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
// Handle response from IntentService here
}
}
This will cause any messages sent to your custom ResultReceiver to end up nowhere after the Activity has been destroyed :)

Do intents change when they are sent through IPC?

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.

Categories

Resources