I have 2 classes which are GcmMessageHandler and Control (its an activity class, shows some graphics). When i handle the gcm message, i want to refresh control class (but if its front)
public class GcmMessageHandler extends IntentService {
String mes;
private Handler handler;
public GcmMessageHandler() {
super("GcmMessageHandler");
}
#Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
handler = new Handler();
}
#Override
protected void onHandleIntent(Intent intent) {
Bundle extras = intent.getExtras();
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
// The getMessageType() intent parameter must be the intent you received
// in your BroadcastReceiver.
String messageType = gcm.getMessageType(intent);
mes = extras.getString("title");
showToast();
Log.i("GCM", "Received : (" +messageType+") "+extras.getString("title"));
GcmBroadcastReceiver.completeWakefulIntent(intent);
}
public void showToast(){
handler.post(new Runnable() {
public void run() {
if(mes.equals("Control")){
}else{
Toast.makeText(getApplicationContext(),mes , Toast.LENGTH_LONG).show();
}
}
});
}
}
In this part:
if(mes.equals("Control")){ }
if the control activity class is resume, i want to refresh it. How can i do this?
You can use a BroadcastReceiver in order to notify your activity about any changes. So register a BroadcastReceiver in your activity first:
public class MainActivity extends Activity {
public static String REFRESH_ACTIVITY = "com.domain.action.REFRESH_UI"
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// do UI updates
}
};
#Override
public void onResume() {
super.onResume();
// do UI updates
IntentFilter filter = new IntentFilter();
filter.addAction(REFRESH_ACTIVITY);
this.registerReceiver(broadcastReceiver, filter);
}
#Override
public void onPause() {
super.onPause();
this.unregisterReceiver(broadcastReceiver);
}
...
}
Then send the broadcast to perform the UI update from any location:
if (mes.equals("Control")) {
Intent intent = new Intent();
intent.setAction(MainActivity.REFRESH_ACTIVITY);
sendBroadcast(intent);
}
Maybe you could use an observer design pattern.
Let the GcmMessageHandler hold the Control activity as an observer and then notify it when needed.
public class GcmMessageHandler extends IntentService {
String mes;
private Handler handler;
private Control mObserver
public GcmMessageHandler() {
super("GcmMessageHandler");
}
public void attachObserver(Control ctrl) {
mObserver = ctrl;
}
Then you just add a method to the Control class that can be called from the GcmMessageHandler class.
if(mes.equals("Control")){
mObserver.update(); // example
}else ...
It would be more slick if you first defined an observer interface:
public interface IObserver {
public abstract void update();
}
and had your Control class implement that. This way your GcmMessageHandler class could have a list of observers:
public class GcmMessageHandler extends IntentService {
String mes;
private Handler handler;
private List<IObserver> mObservers;
public GcmMessageHandler() {
super("GcmMessageHandler");
}
#Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
handler = new Handler();
mObservers = new ArrayList<IObserver>();
}
public void attachObserver(Control ctrl) {
mObservers.add(ctrl);
}
private void notify() {
for(IObserver observer : mObservers)
observer.update();
}
And of course if the Control class is the one holding the GcmMessageHandler object your just call the attach method from Control like this:
myGcmMessageHandler.attachObserver(this);
Related
I require a service to send messages to the Cloud while receiving data from the device as a result I have the following code:
public class MessageService extends Service {
private int mAlert = 0;
private PanicReceiver mPanicReceiver;
#Override
public void onCreate() {
super.onCreate();
mPanicReceiver = new PanicReceiver();
IntentFilter panicFilter = new IntentFilter();
panicFilter.addAction(Constants.PANIC_ON_RECEIVER_ACTION);
panicFilter.addAction(Constants.PANIC_OFF_RECEIVER_ACTION);
registerReceiver(mPanicReceiver, panicFilter);
}
#Override
public void onDestroy() {
unregisterReceiver(mPanicReceiver);
super.onDestroy();
}
private class PanicReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
switch (intent.getAction()) {
case Constants.PANIC_ON_RECEIVER_ACTION:
mAlert = 2;
break;
case Constants.PANIC_OFF_RECEIVER_ACTION:
mAlert = 0;
break;
}
}
}
}
I would like to seperate the broadcast receiver to a seperate file. How can you do that?
MessageService.class
public class MessageService extends Service {
private PanicReceiver mPanicReceiver;
#Override
public void onCreate() {
super.onCreate();
mPanicReceiver = new PanicReceiver();
IntentFilter panicFilter = new IntentFilter();
panicFilter.addAction(Constants.PANIC_ON_RECEIVER_ACTION);
panicFilter.addAction(Constants.PANIC_OFF_RECEIVER_ACTION);
registerReceiver(mPanicReceiver, panicFilter);
}
#Override
public void onDestroy() {
unregisterReceiver(mPanicReceiver);
super.onDestroy();
}
private int getAlert() {
return mPanicReceiver.getAlert();
}
}
PanicReceiver.java
private class PanicReceiver extends BroadcastReceiver {
private int mAlert = 0;
#Override
public void onReceive(Context context, Intent intent) {
switch (intent.getAction()) {
case Constants.PANIC_ON_RECEIVER_ACTION:
this.setAlert(2);
break;
case Constants.PANIC_OFF_RECEIVER_ACTION:
this.setAlert(0);
break;
}
}
public int getAlert() {
return mAlert;
}
public void setAlert(int mAlert) {
this.mAlert = mAlert;
}
}
Just move mAlert from MessageService to PanicReceiver, than you can use the IDE to assist you to refactor out the class pressing F6 with the cursor upside the class name, or with a right click:
I am developing an app to monitor changes in proximity sensor value. In app there should be two separate buttons to start a service and then to start monitoring proximity sensor.
This is my service class
public class MyService extends Service{
Sensor proxSensor;
SensorManager sm;
public static MyService instance;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
instance = this;
return Service.START_STICKY;
}
public void startScan(){
sm=(SensorManager)getSystemService(SENSOR_SERVICE);
proxSensor=sm.getDefaultSensor(Sensor.TYPE_PROXIMITY);
SensorEventListener eventListener = new SensorEventListener() {
#Override
public void onSensorChanged(SensorEvent sensorEvent) {
Log.e("Sensor","Value "+sensorEvent.values[0]);
}
#Override
public void onAccuracyChanged(Sensor sensor, int i) {
}
};
sm.registerListener(eventListener, proxSensor, SensorManager.SENSOR_DELAY_NORMAL);
}
I am starting service from my main activity
public void viewNotification(View view){
startService(new Intent(this,MyService.class));
}
public void viewNotification2(View view){
MyService.instance.startScan();
}
The Log output is printed correctly while the app is running but when I close the activity and remove it from previous apps list the output is not given. But if I call startScan() within onStartCommand it goes on running even after I close the app.
Why doesn't it keep on giving the output?
Is there any other method instead of using static MyService to achieve this?
First of all - use service binding or aidl approaches for working with your Service. (see: https://developer.android.com/guide/components/bound-services.html)
For example:
Suppose, we have service named MyService.
In this class you need write next
private final IBinder mBinder = new LocalServiceBinder();
#Nullable
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public class LocalServiceBinder extends Binder {
public MyService getBinder() {
return MyService.this;
}
}
Next in your Activity:
private MyService mService;
boolean isBounded;
private ServiceConnection mServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "onServiceConnected");
MyService.LocalServiceBinder binder = (MyService.LocalServiceBinder) service;
mService = binder.getBinder();
isBounded = true;
}
#Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "onServiceDisconnected");
isBounded = false;
}
};
And
#Override
protected void onStart() {
super.onStart();
bindService(new Intent(this, MyService.class), mServiceConnection, BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
if (isBounded) {
unbindService(mServiceConnection);
isBounded = false;
}
}
Now you can call your service methods like:
private void activityMethod(){
if (isBounded){
mService.someMethod();
}
}
Second, for working in foreground, call startForeground (int id, Notification notification) method.
I try to implement a Android AIDL communication strategy.
I have an Activity and a Service.
My Activity can successfully "talk" to my Service, but the reverse process does not seem to work.
To summarize, as the Activity and the Service run in different processes, they cannot share any data throw the IBinder interface.
So the onServiceConnected() method receive an AIDL interface instead.
This interface is implemented Service-side and is aimed at being used (called) Activity-side.
I use this interface to register() another AIDL.
This new AIDL is implemented Activity-side and called Service-side through the AIDL interface.
It act like a listener.
Unfortunatly, the method of this new AIDL does not seem to be called.
The Service run in its own process thanks to the following line in AndroidManifest.xml:
AndroidManifest.xml
<service android:name=".DemoService" android:process=":DemoServiceProcess" />
I have 2 AIDL files, one knowing the other.
IAidlActivity.aidl
package app.test.aidldemo;
interface IAidlActivity {
void publish(int count);
}
IAidlService.aidl
package app.test.aidldemo;
import app.test.aidldemo.IAidlActivity;
interface IAidlService {
void startCounter();
void register(IAidlActivity activity);
}
The Service implements onBind() and run a handler in charge of incrementing a counter.
DemoService.java
package app.test.aidldemo;
import [...]
public class DemoService extends Service
{
protected IAidlActivity aidlActivity;
#Override
public IBinder onBind(Intent intent)
{
return new IAidlService.Stub() {
#Override
public void startCounter() {
DemoService.this.startJob();
}
#Override
public void register(IAidlActivity activity) {
DemoService.this.aidlActivity = activity;
}
};
}
public void startJob() {
final Handler handler = new Handler();
handler.post(new Runnable() {
protected int count = 0;
#Override
public void run() {
if (count < 500) {
count++; // increment counter
try { // then publish it to view
DemoService.this.aidlActivity.publish(count); // interface, implemented activity-side
} catch (RemoteException e) {}
handler.postDelayed(this, 2000); // 2sec.
}
}
});
}
}
The Activity only consist of a TextView.
It start the bounding with the Service and update the view from time to time.
It is also supposed to update the view when publish() is called.
But that does not happen.
MainActivity.java
package app.test.aidldemo;
import [...]
public class MainActivity extends Activity {
protected TextView view;
protected ServiceConnection connection;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
view = new TextView(this);
setContentView(view);
appendToView("Let's go!");
connection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
IAidlService aidlService = IAidlService.Stub.asInterface(service);
appendToView("IAidlService accessed");
IAidlActivity.Stub aidlActivity = new IAidlActivity.Stub() {
#Override
public void publish(int count) {
appendToView("*** Hey, new count is: " + count + "!! ***");
}
};
appendToView("IAidlActivity created");
try {
aidlService.register(aidlActivity);
aidlService.startCounter(); // interface, implemented service-side
}
catch (RemoteException e) { appendToView(e.toString()); }
}
#Override
public void onServiceDisconnected(ComponentName name) {}
};
Intent intent = new Intent(MainActivity.this, DemoService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
#Override
public void onDestroy() {
unbindService(connection);
super.onDestroy();
}
public void appendToView(String text) {
view.append(text + "\n");
}
}
I also try some variations like:
run the appendToView("*** Hey... into runOnUiThread()
delay the bindService() by using another handler + postDelayed()
My fallback technique would be to only use IAidlService and have a "watcher" Activity-side to constantly check the counter.
But I would rather understand why it is not working, and what is the correct way to use AIDL.
Summary
2 statements to change.
DemoService.java
final Handler handler = new Handler(DemoService.this.getMainLooper());
MainActivity.java
public void appendToView(String text) {
view.post(new Runnable() {
#Override
public void run() {
view.append(text + "\n");
}
});
}
I need to execute some tasks when my application is closed.
I have made a service for do this and tried many things, but i don't have the good result.
If someone have a tutorial or some path to follow, it would be great
This is my service:
public class TrackersImporter extends Service {
private static TrackersImporter instance;
private static long refreshDelay = 1; // Minutes
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
private boolean isInit = false;
public ArrayList<Tracker> trackers = new ArrayList<>();
public static TrackersImporter getInstance(){
if (instance == null)
instance = new TrackersImporter();
return instance;
}
#Override
public void onCreate() {
HandlerThread thread = new HandlerThread("TrackersImporter",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
try {
Toast.makeText(this, "onStartCommand", Toast.LENGTH_SHORT).show();
Message message = mServiceHandler.obtainMessage();
message.arg1 = startId;
mServiceHandler.sendMessage(message);
} catch (Exception e) {
Log.w("TrackersImporter", e.getMessage());
}
return START_STICKY;
}
public void addTracker(Tracker tracker) {
trackers.add(tracker);
}
protected void showToast(final String msg){
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
});
}
// Object responsible for
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message msg) {
addTracker(Runkeeper.getInstance(MainActivity.getActivity()));
addTracker(Strava.getInstance(MainActivity.getActivity()));
startImport(MainActivity.getActivity().getBaseContext(), MainActivity.getActivity().getAppUser(), trackers);
stopSelf(msg.arg1);
}
/**
* Perform data imports.
* Imports are performed only 1 time.
* Additional calls to this method are equivalent to no-op.
* Call init() then performImport() for each TrackerImportable
* #param user user receiving the datas
*/
public void startImport(Context context, User user, ArrayList<Tracker> trackers) {
Context ctx = MainActivity.getActivity().getApplicationContext();
LocalDateTime now = new LocalDateTime();
if (Preferences.getPref(ctx, "tracker_import_date") == "")
Preferences.setPref(ctx, "tracker_import_date", now.toString());
LocalDateTime past = LocalDateTime.parse(Preferences.getPref(ctx, "tracker_import_date"));
long duration = new Duration(past.toDateTime(), now.toDateTime()).getStandardMinutes();
if (isInit)
return;
if (duration > refreshDelay) {
Preferences.setPref(ctx, "tracker_import_date", now.toString());
for (Tracker tracker : trackers) {
if (tracker.isEnabled() && Tracker.isUserEnabled(context, tracker.getName())) {
tracker.init();
tracker.performImport(user);
}
}
}
isInit = true;
}
}
}
This is my mainActivity
public class MainActivity extends BaseActivity {
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
if (ConnectivityUtil.isConnected(this.getApplicationContext())) {
initGoogleFit();
initTrackers(appUser);
}
}
private void initTrackers(User user) {
Intent trackersIntentService = new Intent(this, TrackersImporter.class);
trackersIntentService.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
this.startService(trackersIntentService);
}
#Override
protected void onResume() {
...
if (ConnectivityUtil.isConnected(this.getApplicationContext())) {
initTrackers(appUser);
}
}
}
First Create one launcher Activity which is like your Main Activity.
In Activity "onCreate" Method you need to start Service and Do Some thing if you wont in Service "onStartCommand" Method.
public class MainActivity extends Activity {
ArrayList<Integer> list;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService(new Intent(MainActivity.this,TrackersImporter.class);
}
public class TrackersImporter extends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// do something
Log.v(TAG ,"Service is started");
}
}
And also Register this Service at manifest.xml like this.
<service android:name=".TrackersImporter"></service>
if you like stop service
stopService(new Intent(MainActivity.this,TrackersImporter.class);
I am working on an android application with push notification feature using GCM. I have created a class called PushNotificationService which extends GCMListenerService. Inside the onMessageReceived(String from, Bundle data) I am able to get the message in the push notification.
Now, I want to access a method inside my MainActivity class whenever a particular message is received in the push.
Below is my code :-
PushNotificationService.java
public class PushNotificationService extends GcmListenerService {
#Override
public void onMessageReceived(String from, Bundle data) {
// TODO Auto-generated method stub
super.onMessageReceived(from, data);
String message = data.getString("message");
if(message.equalsIgnoreCase("Begin Task"))
{
//call method from MainActivity.class
}
}
}
MainActivty.java
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void beginTask()
{
Log.d("GCM","Message Received from Server");
finish();
}
}
I want the beginTask() method to execute whenever the message "Begin Task" is received.
I know one approach is via Service->Interface->Activity architecture but I am not able to use this as I never create an object of PushNotificationService.
Please help.
UPDATE :-
I am now using Otto Library and below is my code.
Added new MyBus.java
public class MyBus extends Bus {
private static Bus bus;
//isRegistered is used to track the current registration status
private static boolean isRegistered;
private Handler handler = new Handler(Looper.getMainLooper());
public MyBus() {
if (bus == null) {
//ANY will allow event bus to run even with services
//and broadcast receivers
bus = new Bus(ThreadEnforcer.ANY);
}
}
#Override
public void register(Object obj) {
//The bus is registered when an activity starts
bus.register(obj);
isRegistered = true;
}
#Override
public void unregister(Object obj) {
//The bus is unregistered when an activity goes to background
bus.unregister(obj);
isRegistered = false;
}
#Override
public void post(final Object event) {
if (Looper.myLooper() == Looper.getMainLooper()) {
//post the event in main thread or background thread
bus.post(event);
} else {
handler.post(new Runnable() {
#Override
public void run() {
bus.post(event);
}
});
}
}
public boolean isRegistered(){
return isRegistered;
}
}
PushNotificationService.java
public class PushNotificationService extends GcmListenerService {
#Override
public void onMessageReceived(String from, Bundle data) {
// TODO Auto-generated method stub
super.onMessageReceived(from, data);
MyBus myBus = new MyBus();
myBus.register(myBus);
String message = data.getString("message");
if(message.equalsIgnoreCase("Begin Task"))
{
myBus.post(message);
}
}
}
MainActivity.java
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Subscribe
public void beginTask()
{
Log.d("GCM","Message Received from Server");
}
}
The problem is still not solved. The beginTask() inside MainActivity.java is still not getting called.
Use eventBus libraries to facilitate this process...
I use Otto for this process
http://square.github.io/otto/
Here is an another eventBus library https://greenrobot.github.io/EventBus/
Steps:
1.Create an event from the service
2.Add a listener in the activity
3.If the activity is running the method will be executed
**EDIT 1 : **
I have abstracted the otto bus like this.
package com.mypackage.eventBus;
import android.os.Handler;
import android.os.Looper;
import com.squareup.otto.Bus;
import com.squareup.otto.ThreadEnforcer;
/**
* Created by gowtham on 10/6/15.
*/
public class MyBus extends Bus {
private static Bus bus;
//isRegistered is used to track the current registration status
private static boolean isRegistered;
private Handler handler = new Handler(Looper.getMainLooper());
public MyBus() {
if (bus == null) {
//ANY will allow event bus to run even with services
//and broadcast receivers
bus = new Bus(ThreadEnforcer.ANY);
}
}
#Override
public void register(Object obj) {
//The bus is registered when an activity starts
bus.register(obj);
isRegistered = true;
}
#Override
public void unregister(Object obj) {
//The bus is unregistered when an activity goes to background
bus.unregister(obj);
isRegistered = false;
}
#Override
public void post(final Object event) {
if (Looper.myLooper() == Looper.getMainLooper()) {
//post the event in main thread or background thread
bus.post(event);
} else {
handler.post(new Runnable() {
#Override
public void run() {
bus.post(event);
}
});
}
}
public boolean isRegistered(){
return isRegistered;
}
}
create an instance of the above object and try posting event
EDIT 2 for Jcarlo's comment
Follow these steps to find the state of the activity.
In your activity's onResume call MyBus.getInstance().register(this).
In your activity's onPause call MyBus.getInstance().unregister(this).
In your GCM IntentService before posting the message
if(MyBus.getInstance().isRegistered()){
//app is alive
//post data
}else{
//show notification
}
Hope this helps
You can use LocalBroadcastManager. Create a LocalBroadcastManager object mBroadcaster = LocalBroadcastManager.getInstance(this); on onCreate of your GCMListener and send broadcast with
#Override
public void onCreate() {
super.onCreate();
mBroadcaster = LocalBroadcastManager.getInstance(this);
}
#Override
public void onMessageReceived(String from, Bundle data) {
super.onMessageReceived(from, data);
String message = data.getString("message");
if(message.equalsIgnoreCase("Begin Task")) {
Intent i = new Intent();
i.setAction("yourPackageName");
i.putExtra("DATA", yourData);
mBroadcaster.send(i);
}
}
Then you can receive message in MainActivity using a BroadcastReceiver.
BroadCastReceiver mBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
beginTask();
}
};
Also you need to register and unregister the receiver in onStart and onStop of your activity
#Override
protected void onStart() {
super.onStart();
IntentFilter filter = new IntentFilter();
filter.addAction("yourPackageName);
LocalBroadcastManager.getInstance(this).registerReceiver((mBroadcastReceiver), filter);
}
#Override
protected void onStop() {
super.onStop();
LocalBroadcastManager.getInstance(this).unregisterReceiver(mBroadcastReceiver);
}