Despite similar question was asked, I have differnet situation:
My app consists mostly of a background Service. I want to start external activities and get results back.
I see several options:
Create dummy Activity and keep reference to it for using its startActivityForResult. This consumes quite a lot of memory, as we know.
Use Broadcast Intents instead of Android's results infrastructure: ask client activities to broadcast their results before closing. This kind of breaks the idea and not so performance-efficient.
Use Instrumentation directly - try to copy code from startActivityForResult into my Service.
Use Service interfaces - serialize and add AIDL connection to the Intent for starting an Activity. In this case Activity should call Service directly instead of providing result.
The third approach feels closer to Android for me, but I'm not sure if it's possible to do - Service does not have its Instrumentation, and default implementation seems to always return null.
Maybe you have any other ideas?
I’ve been thinking about this recently when implementing account authenticators with three-legged authorisation flows. Sending a result back to the service for processing performs better than processing it in the activity. It also provides a better separation of concerns.
It’s not that clearly documented, but Android provides an easy way to send and receive results anywhere (including services) with ResultReceiver.
I’ve found it to be a lot cleaner than passing activities around, since that always comes with the risk of leaking those activities. Additionally, calling concrete methods is less flexible.
To use ResultReceiver in a service, you’ll need to subclass it and provide a way to process the received result, usually in an inner class:
public class SomeService extends Service {
/**
* Code for a successful result, mirrors {#link Activity.RESULT_OK}.
*/
public static final int RESULT_OK = -1;
/**
* Key used in the intent extras for the result receiver.
*/
public static final String KEY_RECEIVER = "KEY_RECEIVER";
/**
* Key used in the result bundle for the message.
*/
public static final String KEY_MESSAGE = "KEY_MESSAGE";
// ...
/**
* Used by an activity to send a result back to our service.
*/
class MessageReceiver extends ResultReceiver {
public MessageReceiver() {
// Pass in a handler or null if you don't care about the thread
// on which your code is executed.
super(null);
}
/**
* Called when there's a result available.
*/
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
// Define and handle your own result codes
if (resultCode != RESULT_OK) {
return;
}
// Let's assume that a successful result includes a message.
String message = resultData.getString(KEY_MESSAGE);
// Now you can do something with it.
}
}
}
When you start an activity in the service, create a result receiver and pack it into the intent extras:
/**
* Starts an activity for retrieving a message.
*/
private void startMessageActivity() {
Intent intent = new Intent(this, MessageActivity.class);
// Pack the parcelable receiver into the intent extras so the
// activity can access it.
intent.putExtra(KEY_RECEIVER, new MessageReceiver());
startActivity(intent);
}
And finally, in the activity, unpack the receiver and use ResultReceiver#send(int, Bundle) to send a result back.
You can send a result at any time, but here I've chosen to do it before finishing:
public class MessageActivity extends Activity {
// ...
#Override
public void finish() {
// Unpack the receiver.
ResultReceiver receiver =
getIntent().getParcelableExtra(SomeService.KEY_RECEIVER);
Bundle resultData = new Bundle();
resultData.putString(SomeService.KEY_MESSAGE, "Hello world!");
receiver.send(SomeService.RESULT_OK, resultData);
super.finish();
}
}
I think option 2 is the most idiomatic way on android. Using startActivityForResult from an Activity is a synchronous/blocking call, i.e., the parent activity waits and does not do anything until the child is done. When working from a Service and interacting with activities your primarily doing asynchronous/non-blocking calls, i.e., the service calls out for some work to be done and then waits for a signal to tell it that it can continue.
If you are using the android local service pattern then you can have your activities acquire a reference of the Service and then call a specific function after it has performed its work. Attempting your option 3 would be counter to what the framework provides for you.
Related
I need to run the following when i get a message from my GCM listner:
public void GetInfo1 (Bundle data){
Log.d("Get Messages", "Data: " + String.valueOf(data));
final String uid = data.getString("uid");
final String infoid = data.getString("infoid");
GetInfo(uid, infoid); // This is another activity that does a Json Post and returns a array that i use to populate my Listview
}
This is how i call the function :
if (activityName.equals(MyActivity.class.getName())) {
// Execute that special method from ActivityListView
Log.d(TAG, "Activity is running");
MyActivity myActivity = new MyActivity();
myActivity.Getinfo1(data);
} else {
// Show the notification
sendNotification(message);
}
but i am always getting this error :
Can't create handler inside thread that has not called
Looper.prepare()
I have tried everything, and am out of ideas now,
I need to call the function and pass the vars but if i make it static i get a error that you cant call non static methods from a static ....
Please any help will be fantastic
T-
In general, to start a new activity, you should not instantiate the activity directly but rather use an Intent to trigger the activity, using either startActivity() or startActivityForResult(). However, in this case, it would appear that you are doing an operation that is intended to run in the background (without user interaction). Activity is an application component intended for direct interaction with the user. For background processing, you should be using a Android Service, instead. As with an Activity, you do not create services directly, but rather invoke them by firing off appropriate Intents.
I am learning Android. I have made a simple Activity then in the Activity start the Service that do some high network operation now I want when high network load call complete then I want to update to my Activity.
Is it possible to update the Activity from the Service ?
Thanks in advance.
This is code taken and modified from a now depreciated library called DataDroid but it is relevant to what you are attempting to do.
private final class RequestReceiver extends ResultReceiver {
RequestReceiver() {
super(new Handler(Looper.getMainLooper()));
}
#Override
public void onReceiveResult(int resultCode, Bundle resultData) {
/*
* Depending on how you implement this either update activity from here
* or instantiate it with an interface that your activity implements and
* call that here.
*/
}
}
Create service with somethinglike this:
RequestReceiver requestReceiver = new RequestReceiver();
Intent i = new Intent(mContext, Service);
i.putExtra(RequestService.INTENT_EXTRA_RECEIVER, requestReceiver);
mContext.startService(i);
You could also just use the previously mentioned library and modify it to fit your current use case or use one of the many other similar libraries available.
I have a broadcast receiver which is listening to the WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.
In that receiver I filter all the available networks and return a list of networks with a specific SSID. I nned to pass that list back to the MainActivity of my application.
I know I can create an inner class for my Broadcast receiver but I prefer to create a separate class for better organization.
I am thinking in creating a static variable in my mainActivity class and then set that value.
Is this a good practice?
A good way of sharing and access information a cross of Activites and other classes is by using the application object. You can access the application object from all your classes as long as you have the application context.
See this tutorial about application object: How to use application object
Usage from activities:
MyApplicationObject app = (MyApplicationOjbject)getApplicationContext();
app.setMyVariable(variable);
From other classes outside activity:
MyApplicationObject app = (MyApplicationOjbject)context.getApplicationContext();
app.setMyVariable(variable);
Stefan is right, this static link is not pretty. You can sometimes have multiple instance of the same activity (when recreated, until Garbage collector collect it). Or multiple broadcast happening, overwriting your static variable value.
If you don't want to use an anonymous inner class, you can override the constructor and pass a reference to your current activity that you will be able to use to send the results when processing onReceive(). Just clean up this reference when you are done to avoid leaking your activity.
I've used the same technique with success. The one time this bit me was when I did not consider that the user could tilt the screen and the activity would be recreated. I failed to check if the static variable was already set and replaced it repeatedly. Watch out for that.
One more technique I can think of is to share a callback between the activity and the broadcast receiver. The receiver makes a call to the callback which stores a reference to the right activity and calls runOnUiThread(action) to make UI updates. References should be updated onStart() and onStop(). I've never really used this pattern. Thought about it in the shower :)
I recommend to not use a static variable to deliver the information. If your main activity is the only object receiving the information from the receiver make the BroadcastReceiver local to the main activity. Doing so groups those elements which share a responsibility.
This is how I get data from broadcasts, little bit of more code but its way simpler to read
for future, in case of complex stuff get going.
Intent intent = new Intent(context, BroadcastReciever.class);
SimpleResultReciever resultReciever = new SimpleResultReciever(new Handler())
.setCallback(new OnLocationCallback() {
#Override
public void onRecieverLocation(Location location) {
if(location != null) {
MainActivity.this.location = location;
}
}
});
intent.putExtra(SimpleResultReciever.KEY_RESULT_RECIEVER, resultReciever);
//call intent or create pending intent you will use broadcast stuff.
public class SimpleResultReciever extends ResultReceiver {
public final static String KEY_RESULT_RECIEVER = "bundle.broadcast.reciever.callback";
private OnLocationCallback callback;
public LocationResultReciever setCallback(OnLocationCallback callback) {
this.callback = callback;
return this;
}
/**
* Create a new ResultReceive to receive results. Your
* {#link #onReceiveResult} method will be called from the thread running
* <var>handler</var> if given, or from an arbitrary thread if null.
*
* #param handler
*/
public LocationResultReciever(Handler handler) {
super(handler);
}
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
super.onReceiveResult(resultCode, resultData);
Location location = resultData.getParcelable(LocationManager.KEY_LOCATION_CHANGED);
if(callback != null) {
callback.onRecieverLocation(location);
}
}
}
public class LocationBroadcastReciever extends BroadcastReceiver {
public LocationBroadcastReciever() {
super();
}
#Override
public void onReceive(Context context, Intent intent) {
Bundle extra = intent.getExtras();
Location location = extra.getParcelable(LocationManager.KEY_LOCATION_CHANGED);
ResultReceiver res = extra.getParcelable(LocationResultReciever.KEY_RESULT_RECIEVER);
if(res != null) {
Bundle data = new Bundle();
data.putParcelable(LocationManager.KEY_LOCATION_CHANGED, location);
res.send(Activity.RESULT_OK, data);
}
}
}
if u r launching an Main activity form the receiver and then u can pass the list in by using putextra(), and then you can get that in the main activity.
some thing like this.
Intent intent = new Intent(ctx.getApplicationContext(), targetActivity);
intent.putCharSequenceArrayListExtra(name, value);
I would like to achieve the following behaviour, but I'm not sure how:
User start an activity
Activity starts an AsyncTask
User performs some action that creates a new activity
The AsyncTask finishes and somehow returns the result to the new activity
Is there a way of achieving this behaviour?
Thank you
Create a Service that itself spawns its own thread and does your background processing. You can bind your activities to the service so you can call back into an activity when your processing is complete.
I've been using a variation of what was suggested by Chris:
Start by creating an IntentService, which is the easiest kind of Service to create. Then use SharedPreferences to indicate the state of your IntentService and share values between your Service and Activities. Your Activity can register itself as an OnSharedPreferenceChangeListener in order to know when your Service is done doing work and/or another SharedPreference it cares about has changed.
With IntentService, all you need to do is override the onHandleIntent method. Everything inside onHandleIntent will run on a background thread.
This is a way to do exactly what you want, assuming that the result is an int. You can extend this property, using a parcelable object. Probably, using a Service is still the best choice.
1) Create a class, called Result, that is a wrapper for your result. It must implement the Parcelable interface:
public class Result implements Parcelable {
private int result;
public Result(int i) {
super();
result = i;
}
public void setResult(int result) {
this.result = result;
}
public int getResult() {
return result;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(result);
}
public static final Parcelable.Creator<Result> CREATOR = new Parcelable.Creator<Result>() {
public Result createFromParcel(Parcel in) {
return new Result(in);
}
public Result[] newArray(int size) {
return new Result[size];
}
};
public Result(Parcel in) {
result = in.readInt();
}
}
2) Now, you can use a Result object as a private variable of the first activity:
public class FirstActivity extends Activity {
private Result result;
....
}
3) In your firstActivity, you can start an AsyncTask with a line like this:
new MyAsyncTask(result).execute();
4) Your AsyncTask can be made in this way:
class MyAsyncTask extends AsyncTask<Void, Void, Void> { // you can modify types as you want
Result result;
public MyAsyncTask(Result result) {
this.result = result;
}
....
public mySetResult() {
result.setResult(...); //set your value
}
....
}
5) When you start the second Activity, you can pass your result object to the second activity:
Intent i = new Intent(getApplicationContext(), SecondActivity.class);
i.putExtra("parc", result);
startActivity(i);
6) Finally, from the second activity, you can obtain the result using this code:
Result res = (Result) getIntent().getParcelableExtra("parc");
For more details about parcelable object, can see Android Developer
A Service is a component that allows some code to have a separate lifetime outside of activities without interacting with the user. As others have mentioned, that's certainly one option to consider. If you go with that, IntentService is the easiest way to make the work asynchronous.
However, you could continue to use AsyncTask and just add some code to signal that it's "complete". This is the case when the background work no longer matters if your application is killed, and you're OK with your app being killed before this work completes if the user leaves the application. Another way to see this is if the result of the AsyncTask only matters to either/both of these two activities and not outside. This is an important difference in requirements from needing a Service which again, provides a lifetime outside of activities.
To pass the data, take a look at this doc. There are a lot of ways you could tackle this, but for this kind of thing I prefer a pseudo-singleton approach. (I don't like to use SharedPreferences to pass data, because frankly I don't think that's what the class is for. I prefer this pseudo-singleton approach over a pure singleton because it's more testable. Android uses the singleton approach all over the place though.) I'd create a reference to some sort of AsyncTask registrar class in the Application object. As the Application object is accessible from both activities, the first one can register your AsyncTask with the registrar and the second one can get that AsyncTask and register to listen for completion if it hasn't already finished.
I am attempting to create an enhanced Intent class (I call it DataIntent) by giving it the ability to hold a "payload" of type Object (versus using it's built-in facility for Uri's). DataIntent extends Android's Intent class.
My Activity creates the extended object without any problems and the invocation of the startActivityForResult() goes off without any problems also. But, in my responding Activity when I call the getIntent() method, and attempt to cast it to my DataIntent, I'll throw the ClassCastException.
I realize this is probably a very dumb question - a 1,000 appologies in advance - but does anyone know why I cannot cast it to the DataIntent since that's what was used to start the new Activity, and DataIntent is a child of Intent?
DataIntent dataIntent = (DataIntent)getIntent();
// invoked inside the responding Activity instance - throws a ClassCastException
You can't do that, sorry. You need to place your data inside of the Intent. The Intent object is moved across processes and thus the one you get back is not the same instance as the one you created.
I did the same thing CirrusFlyer. I also looked for the final keyword before I started to implement it. Google should mark Intent class as final.
You can and should extend an Intent, but you must understand the purpose of an Intent.
#1 an intent must be parcelable to support persisting the intent-data across an app restart (collapsed due to memory limitations, trimMemory etc).
#2 Understand that the Intent constructed by the caller, is not the Intent provided to the activity. This is due to item #1. So any object references would be lost and are a bad idea -- it needs to be parcelable remember.
#3 Intents should only contain data-context for the activity (or whatever). So you should not place pages of data into an intent, rather, it should contain ids or keys, or whatever contextual data is necessary to re-obtain the data for the activity (or whatever).
Now... Why should you extend an Intent? For good contracts!
Intents by themselves are terrible contracts, way too loose. Some people create static method helpers but there is a better way.
If an ABCActivity requires "A" "B" and "C" to perform properly. A generic intent cannot describe that, we'd rely on documentation that no one will read.
Instead we can create a ABCIntent whose constructor demands A,B & C. This creates a clear contract on what is required to load the activity. We can do that with a static method, but a an ABCIntent can also provide getters for A B C making it a clean packaged contract for describing requirements to load the activity and how to obtain the data.
There is one caveat, we need a private constructor to construct our ABCIntent from a generic intent to inherit the extras.
public ABCActivity extends Activity {
private ABCIntent intent;
public static ABCIntent extends Intent {
private ABCIntent(Intent intent) {
super(intent);
}
public ABCIntent(A a, B b, C c) {
putExtra("EXTRA_A", A.serialize(a));
putExtra("EXTRA_B", B.serialize(b));
putExtra("EXTRA_C", C.serialize(c));
}
public A getA() { return A.deserialize(getExtra("EXTRA_A")); }
public B getB() { return B.deserialize(getExtra("EXTRB_B")); }
public C getC() { return C.deserialize(getExtra("EXTRC_C")); }
}
#Override
protected ABCIntent getIntent() {
return intent == null ? (intent = new ABCIntent(super.getIntent())) : intent;
}
#Override
protected void onCreate( ... ) {
A a = getIntent().getA();
B b = getIntent().getB();
C c = getIntent().getC();
// TODO: re-obtain activity state based on A, B, C then render
}
}
Notice that we construct ABCIntent from intent.
The ABCIntent inherits the intent extras.
Now you have a nicely packaged class who's job it is to define the contract for the activity and to provide the contractual data to the activity.
If you're a new engineer on the project, there is no way for you to misunderstand how to use this. No docs to read.