Learning to use the BroadcastReceiver class in Android, I have written a small program to receive the battery charge state and write it to three TextView fields in an activity.
However, I have made the BroadcastReceiver as a separate class to make it more simple and separate from the activity. Therefore I have to find a method to tell my Activity class that the battery data has been updated, or, which is my solution, to pass in references to the TextView fields from the Activity to the BroadcastReceiver class.
Does anyone know whether it is possible to make a callback method from the BroadcastReceiver to start a function, f.ex. updateTextViews(); in the Activity?
Here is the source code - note there are two java files:
http://pastebin.com/qjCTsSuH
Regards, Niels.
What worked a charm for me is simply declaring the interface objects as static. Bear in mind though that statics can cause as many problems as they solve as statics persist therir values accross instances.
public class MainActivity extends AppCompatActivity implements SocketMessageReceiver.ISocketMessageReceiver {
//Declare the cb interface static in your activity
private static SocketMessageReceiver.ISocketMessageReceiver iSocketMessageReceiver;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
//Assign this
iSocketMessageReceiver = this;
socketMessageReceiver.registerCallback(iSocketMessageReceiver);
}
#Override
public void sendSocketMessage(String socketMessage) {
lblEchoMessage.setText(socketMessage);
}
}
And in your Receiver ....
public class SocketMessageReceiver extends BroadcastReceiver {
interface ISocketMessageReceiver {
void sendSocketMessage(String socketMessage);
}
//Also declare the interface in your BroadcastReceiver as static
private static ISocketMessageReceiver iSocketMessageReceiver;
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals("com.WarwickWestonWright.SocketExample.RECEIVE")) {
iSocketMessageReceiver.sendSocketMessage(intent.getBundleExtra("DATA").getString("DATA"));
}
}
public void registerCallback(ISocketMessageReceiver iSocketMessageReceiver) {
this.iSocketMessageReceiver = iSocketMessageReceiver;
}
}
I have made the BroadcastReceiver as a separate class to make it more simple
IMHO, you made it more complex.
Therefore I have to find a method to tell my Activity class that the battery data has been updated, or, which is my solution, to pass in references to the TextView fields from the Activity to the BroadcastReceiver class.
Option #1: Just go back to using an inner class for the BroadcastReceiver. ACTION_BATTERY_CHANGED can only be used via registerReceiver() anyway. Just have onReceive() call some method on the activity to do the work of updating the UI.
Option #2: Pass your activity into the constructor of the BroadcastReceiver, and call the method as in option #1.
Option #3: Use an event bus, like Square's Otto or greenrobot's EventBus.
Related
Previously, I was using activities in my project and was sending data using Intent from one activity to another which works perfectly fine.
Now requirement changes, and I have to show all things on Dialogs, instead of activities, so there will separate 3-4 dialog class and single activity.
Now I want the same flow on dialog also, but there is a problem to pass data temporarily exactly how intent works!
I tried with Singleton, but the problem is it remains data until the whole lifecycle, but I don't want that.
I can't use the interface also because there are lots of things to pass.
Also, I can't use bundle fundle n all those, because this all depends on runtime, I meant it depends upon if user fill input
Question: How can I pass data from one class to another class or activity? and it should not save value for the whole lifecycle.
statically sending data is an option but its not good way, because memory to static variables is assigned at Application level and can be cleared when memory needed. The best way is to use
Object Oriented approach
For example if you have a class, You can send data in class constructor, or can send it through function call
class class1
{
public class1(Object data) { // constructor
// you can use this data
}
//// Or through function call
public void func(Object data) { // this method can be called by other classes which has its object
// you can use this data
}
}
Now lets assume you have another class
class class2
{
class1 obj = new class1(your_data_object); // if you want to send through constructor
void someMethod() {
obj.func(your_data_object); // send data whatever you want to send
}
}
Obviously your case will not be as simple as my example, but to handle complex cases you can implement interfaces.
Interface Example
define an interface
interface myListener {
public void listen(Object data);
}
now lets say you want to call class2 method from class1. then class2 must implement this interface.
public class class2 implements myListener {
#override
public void listen(Object data)
{
/// you got data here, do whatever you want to do that with that data.
}
}
Now in class1 if you have interface object you can call class2 method
interfaceRef.listen(your_data);
Try with EventBus or BroadCastReceivers to pass data accordingly in local variables.
EventBus is a publish/subscribe event bus for Android and Java. EventBus... simplifies the communication between components. decouples event senders and receivers. performs well with Activities, Fragments, and background threads.
http://greenrobot.org/eventbus
First Register to EventBus in your Activity
#Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
#Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
Now pass the data from anywhere ,whether it is activity/fragment/background service etc etc etc like :
EventBus.getDefault().postSticky(new MessageEvent("your data here");
Now in your activity receive this message like :
#Subscribe(sticky = true,threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
Log.e("TAG","Event Received");
Log.e("TAG",event.getData);
}
Hi I am kind of new to android, still learning. And my problem is that, for example I have a method which was created in the MainActivity and I need to call it from another class.
Is it a good practice to get the instance of the MainActivity so that I may be able to call the method in the MainActivity from another class?
This is an example:
public class MainActivity extends AppCompatActivity {
private static MainActivity inst;
public static MainActivity instances()
{
return inst;
}
#Override
public void onStart() {
super.onStart();
inst = this;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void showToast (String text){
Toast.makeText(inst, text, Toast.LENGTH_SHORT).show();
}
Then this is the other class:
public class broadcastReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
MainActivity instance = new MainActivity();
instance.showToast(AnyText);
}
}
I saw this type of coding while looking at tutorials and wondered if it's a good practice or maybe there might be a better way? Since I get the warning of Do not place Android Context Classes in static classes
Thanks in advance for any insight or help! :D
I guess You want to make A singleton of Activity Class
but as Mention in All Pattern Design
using Singleton
If and Only If its only way to Make A Global Variable
Singleton is based on Lazing Initialing and Load On Memory
so I guess If you cant to Interact With Activiy You can Use
BroadCast Or Intents
You can call method from another class like this:
MainActivity instance = new MainActivity();
String data = instance.data();
and create data method in that class:
public String data() {
return mangaId;
}
Is it a good practice to get the instance of the MainActivity so that
I may be able to call the method in the MainActivity from another
class?
You totally can do this but you don't need to make it static and use a constructor. Just create a new instance like follows and you'll access the public methods
MainActivity mainActivity = new MainActivity();
mainActivity.showToast(text);
About the warning
It suggests avoiding having context fields defined as static. The warning itself explains why: It's a memory leak. If you make it static it will be accessible anywhere in your app and some methods can hold the reference to this context for a really long time and it won't be garbage collected. It will lead to a outofmemory exception and the app could crash. But here you're trying to invoke showToast() from broadcastreceiver so you can just get rid of static references. And it you need them in the future you safe ways to inject context
You cannot create instances of an Activity using the new operator.
You have to use an Intent to let an Activity to be created.
So you cannot get a reference to an instance of your activity.
The only methods you can use of your activity class are static ones.
I seem to be stuck with a problem with an object communicating with my activity class. The object is a view object with an onClick method that when called I would like it to notify my activity class so that it can perform said action. Below is some example code of my situation (assume all conventional setup operations have already been made):
public class MainActivity extends AppCompatActivity{
//...other global methods and objects
//Does not have access to instantiated Entry object(s)
public void entryObjectWasClicked(){
//perform said action
}
}
public class Entry extends View implements View.OnClickListener{
//...other global methods and objects
//Does not have access to the MainActivity object
#Override
public void onClick(View v){
//send a message to the MainActivity to
//somehow call the entryObjectWasClicked() method
}
}
The only way (off the top of my head) that I could think about dealing with this problem is by creating a static method in MainActivity and then calling it from an anonymous MainActivity object in the onClick method of Entry. The problem with the static method approach is that any subsequent method/object/primitive usages in the static method force those methods/objects/primitives to be static. This defeats the purpose of then being able to have two different instances of the MainActivity object.
After some looking I came across using Broadcast messages, specifically using the LocalBroadcastManager to send an intent to the activity. This code example works for my model, but I want to know: is this the best way for me to go about sending messages to my MainActivity from my Entry object?
If there is a more effective way of doing all this, what would it be?
You're overcomplicating things. Don't override onClick for this. Instead, have your activity call setOnClickHandler on your view, which sets a callback that's called when the view is clicked. Then use the default implementation.
Since you extend view, i guess you want to use it inside a layout. That means you may want to create a Listener for that. Example:
public class Entry extends View implements View.OnClickListener{
private OnClickListener listener;
public void setListener(OnClickListener listener) {
this.listener = listener;
}
#Override
public void onClick(){
if (this.listener != null) this.listener.onClick(this);
}
}
How you can inflate your layout in your Activity and access your custom view.
public class MainActivity extends AppCompatActivity{
public void onCreate( ...) {
Entry entry = findViewById(R.id.entry);
entry.setListener(new OnClickListener(...));
}
}
Inside my activity I have a broadcast receiver that I initialize as such:
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
updateViews();
}
};
updateViews() is a function inside the activity that is only used within the broadcast receiver. How can I create this receiver as a separate class that can play with the views (rotate, delete etc.) of my activity?
In addition, I have a compass within the activity. It works, however I would also like to make the compass a separate class that can send data to the activity. It will not change the views of the activity but only update certain double/float values.
#Override
public void onSensorChanged(SensorEvent event) { }
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) { }
How can I create this receiver as a separate class...?
This answer assumes that "separate class" means you want a BroadcastReceiver that is defined in its own source file and is not an inner class of an activity. Before offering a solution, I'll ask, what do you expect to gain by this? Do you have multiple activities that will use the receiver? If not, it's best to leave it as an inner class of the single activity that uses it. If you don't like using the anonymous inner class in the code you posted, you can declare is as an inner class:
private class MyBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// As an inner class of the activity, this receiver has
// access to all activity members.
updateViews();
}
}
If you really want it as a standalone receiver, the example code below is a starting point. Be sure to register/unregister the receiver in your activity's onResume/onPause callbacks. The example includes code for a less safe approach, using a plain activity reference, and a safer approach using a weak reference. Only one is needed.
public class MyBroadcastReceiver extends BroadcastReceiver {
// For less safe use
private MyClientActivity mActivity;
// For more safe use
private WeakReference<MyClientActivity> mActivityRef;
public MyBroadcastReceiver(MyClientActivity activity) {
mActivity = activity;
mActivityRef = new WeakReference<MyClientActivity>(activity);
}
#Override
public void onReceive(Context context, Intent intent) {
// Less safe
mActivity.findViewById(R.id.action_bar);
mActivity.someActivityMemberMethod();
// etc
// More safe. Guards against failure to unregister
// this receiver when activity is paused.
MyClientActivity act = mActivityRef.get();
if (act != null && !act.isDestroyed()) {
mActivity.findViewById(R.id.action_bar);
mActivity.someActivityMemberMethod();
// etc
} else {
// Error: Activity failed to unregister this receiver
}
}
}
I would also like to make the compass a separate class that can send
data to the activity
Same assumption and question as above: Will the compass be used by multiple activities? If not, it's probably best to make it an inner class of the single activity. If there are multiple client activities, consider using a started-service. It could notify activities of sensor events using a local broadcast or an event bus such as greenrobot.
I need to get a reference to the main Activity from a Service.
This is my design:
MainActivity.java
public class MainActivity extends Activity{
private Intent myIntent;
onCreate(){
myIntent=new Intent(MainActivity.this, MyService.class);
btnStart.setOnClickListener(new OnClickListener(){
public void onClick(View V){
startService(myIntent);
});
}}
MyService.java
class MyService extends Service{
public IBinder onBind(Intent intent) {
return null;
}
onCreate(){
//Here I need to have a MainActivity reference
//to pass it to another object
}
}
How can I do this?
[EDIT]
Thanks to all for the answers!
This app is a web server, that at this moment works only with threads, and I want to use a service instead, to make it work also in the background.
The problem is that I have a class that is responsible for getting the page from assets, and to do this operation I need to use this method:
InputStream iS =myActivity.getAssets().open("www/"+filename);
At this moment my project has only one Activity and no services, so I can pass the main activity's reference directly from itself:
WebServer ws= new DroidWebServer(8080,this);
So, in order to make this app work with a service, what should I change in my design?
You didn't explain why you need this. But this is definitely bad design. Storing references to Activity is the first thing you shouldn't do with activities. Well, you can, but you must track Activity lifecycle and release the reference after its onDestroy() is called. If you are not doing this, you'll get a memory leak (when configuration changes, for example). And, well, after onDestroy() is called, Activity is considered dead and is most likely useless anyway.
So just don't store the reference in Service. Describe what you need to achieve instead. I'm sure there are better alternatives out there.
UPDATE
Ok, so you do not actually need reference to Activity. Instead you need reference to Context (which in your case should be ApplicationContext to not keep reference to Activity or any other component for that matter).
Assuming you have a separate class that handles WebService request:
class WebService
{
private final Context mContext;
public WebService(Context ctx)
{
//The only context that is safe to keep without tracking its lifetime
//is application context. Activity context and Service context can expire
//and we do not want to keep reference to them and prevent
//GC from recycling the memory.
mContext = ctx.getApplicationContext();
}
public void someFunc(String filename) throws IOException
{
InputStream iS = mContext.getAssets().open("www/"+filename);
}
}
Now you can create & use WebService instance from Service (which is recommended for such background tasks) or even from Activity (which is much trickier to get right when web service calls or long background tasks are involved).
An example with Service:
class MyService extends Service
{
WebService mWs;
#Override
public void onCreate()
{
super.onCreate();
mWs = new WebService(this);
//you now can call mWs.someFunc() in separate thread to load data from assets.
}
#Override
public IBinder onBind(Intent intent)
{
return null;
}
}
To communicate between your service and activity you should use AIDL.
More info on this link:
EDIT: (Thanks Renan Malke Stigliani)
http://developer.android.com/guide/components/aidl.html
The AIDL is overkill unless the activity and service are in seperate apks.
Just use a binder to a local service.
(full example here: http://developer.android.com/reference/android/app/Service.html)
public class LocalBinder extends Binder {
LocalService getService() {
return LocalService.this;
}
}
Agree with inazaruk's comments. But, In terms of communicating between an Activity and a Service, you have a few choices - AIDL (as mentioned above), Messenger, BroadcastReicever, etc. The Messenger method is similar to AIDL but doesn't require you to define the interfaces. You can start here:
http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/app/MessengerService.html