Communication between Activity and GCMListenerService Android - java

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);
}

Related

Variable is showing value as null even when I had set already value of it

I have created background service to connect my socket server, it works background while app is off, when user open the app MainActivity join to my service class and it looks fine, my service can change fragment in main activity, but when it get disconnected and want to change fragment in main activity then app crash
check my MainActivity
public class MainActivity extends AppCompatActivity {
private clientService mclientService;
private Intent mServiceIntent;
private final FragmentManager fm = getFragmentManager();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mclientService = new clientService(this);
mServiceIntent = new Intent(this, mclientService.getClass());
if (!isMyServiceRunning(mclientService.getClass())) {
startService(mServiceIntent);
}
}
public Fragment changeFragment (Fragment cls) {
FragmentTransaction ft = this.fm.beginTransaction();
ft.replace(R.id.bodyFrame, cls);
ft.commit();
return cls;
}
private boolean isMyServiceRunning(Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
Log.i ("isMyServiceRunning?", true+"");
return true;
}
}
Log.i ("isMyServiceRunning?", false+"");
return false;
}
}
and the clientService:
public class clientService extends Service {
private Socket mSocket;
{
try {
IO.Options opts = new IO.Options();
opts.query = "_d=jakistakiid";
mSocket = IO.socket("http://10.0.2.2:3000", opts);
} catch (URISyntaxException e) {}
}
public clientService(MainActivity main) {
super();
mainAttach(main);
}
public clientService() {
}
public MainActivity mMain;
public void startIt(){
Log.i("eroapp", "Service Started");
if(mSocket != null) {
mSocket.on(Socket.EVENT_CONNECT, new Emitter.Listener() {
#Override
public void call(Object... args) {
Log.i("eroapp", "connected");
}
}).on(Socket.EVENT_DISCONNECT, new Emitter.Listener() {
#Override
public void call(Object... args) {
Log.i("eroapp", "dc:"+mMain);
onDC();
}
});
mSocket.connect();
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
startIt();
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
Intent broadcastIntent = new Intent(this, restartReceiver.class);
sendBroadcast(broadcastIntent);
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
// CLIENT FUNCTIONS //
private void onDC(){
Log.i("eroapp", "DC:"+mMain);
}
private void mainAttach(MainActivity m) {
mMain = m;
Log.i("eroapp","Main created!");
if(!mSocket.connected()) {
mMain.changeFragment(new offlineFragment());
}else{
mMain.changeFragment(new singFragment());
}
}
}
looks fine, when i close app activity my service restarts and run in background, it says in logcat:
2019-11-17 02:41:22.686 17458-17458/com.example.secmsg I/eroapp: Service Started
2019-11-17 02:41:22.803 17458-17483/com.example.secmsg I/eroapp: connected
when i open app again, my service is already running, so its run only function mainAttach in clientService, and then service run changeFragment function in main activity, works great, but
when I get disconnected from server it says mMain is null ; < and logcat output:
2019-11-17 02:41:31.654 17458-17499/com.example.secmsg I/eroapp: dc:null
2019-11-17 02:41:31.654 17458-17499/com.example.secmsg I/eroapp: DCnull
The mclientService you created on the MainActivity is not the same as the clientService that was started by the Android system when you called startService(intent). All services created and started by the Android system by using the service class' empty constructor, therefore, clientService#mainAttach will never be called. Take a look at this answer for more information.
If you want to directly interact with the service on the MainActivity, you might want to bind the service to your activity. Check out the documentation about bound services here.
Another thing... It looks like you're planning to directly control the MainActivity from the service using the reference to the activity. Please never do that and use broadcasts instead. Good luck!

How to detect application runs foreground or background using Single Activity or Java class

I want to sent report to the server which means how long user Use the application in single day..I can achieve using this to method
#Override
protected void onResume() {
super.onResume();
//commonclassMethod.getInstance(UserForground);
}
#Override
protected void onStop() {
super.onStop();
//commonclassMethod.getInstance(UserBackground);
}
What happen i need to call call this method in every activity....
What i need,is there any possible to find user forground background method in single java class or activity..
Thanks in Advance.
You can can achieve this just by adding an method isAppIsInBackground(Context context) in class which is extending Application class
In that class define that method:
public static boolean isAppIsInBackground(Context context) {
boolean isInBackground = true;
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT_WATCH) {
List<ActivityManager.RunningAppProcessInfo> runningProcesses = am.getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo processInfo : runningProcesses) {
if (processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
for (String activeProcess : processInfo.pkgList) {
if (activeProcess.equals(context.getPackageName())) {
isInBackground = false;
}
}
}
}
} else {
List<ActivityManager.RunningTaskInfo> taskInfo = am.getRunningTasks(1);
ComponentName componentInfo = taskInfo.get(0).topActivity;
if (componentInfo.getPackageName().equals(context.getPackageName())) {
isInBackground = false;
}
}
return isInBackground;
}
It will true if app is in background
Or another better approach would be just extend your each Activity by an BaseActivity, in this BaseActivity's override methods
protected void onResume() {
super.onResume();
//commonclassMethod.getInstance(UserForground);
}
protected void onStop() {
super.onStop();
//commonclassMethod.getInstance(UserBackground);
}
You can implement callback method to solve your case.
For example:
You create an interface first, then define a method, which would act as a callback. In this example we would have two classes, one classA and another classB
Interface:
public interface OnCustomEventListener{
public void onEvent(); //method, which can have parameters
}
the listener itself in classB (we only set the listener in classB)
private OnCustomEventListener mListener; //listener field
//setting the listener
public void setCustomEventListener(OnCustomEventListener eventListener) {
this.mListener=eventListener;
}
in classA, how we start listening for whatever classB has to tell
classB.setCustomEventListener(new OnCustomEventListener(){
public void onEvent(){
//do whatever you want to do when the event is performed.
}
});
how do we trigger an event from classB (for example on button pressed)
if(this.mListener!=null){
this.mListener.onEvent();
}
Here is some nice tutorials link1,link2,link3 which describes callbacks and the use-case well.
Create a class extending application and use registerActivityLifecycleCallbacks() to get the activity lifecycle
public class MyApp extends Application {
#Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
#Override
public void onActivityCreated(Activity activity, Bundle bundle) {
}
#Override
public void onActivityStarted(Activity activity) {
}
#Override
public void onActivityResumed(Activity activity) {
}
#Override
public void onActivityPaused(Activity activity) {
if(activity.getClass().getSimpleName().equalsIgnoreCase(MainActivity.class.getSimpleName())){
//Do the required thing here
}
}
#Override
public void onActivityStopped(Activity activity) {
}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
#Override
public void onActivityDestroyed(Activity activity) {
}
});
}
}
Also dont forget to register activity in manifest
<application
android:name=".MyApp"
You have two options.
1) Do a abstract BaseActivity and make all Activities extend it. This way you only write the code in one BaseActivity and all children activities consume it.
2) Use a custom counter class to monitor app to foreground. Here is my implementation of it if you would like to copy it.
/**
* Created by App Studio 35 on 6/23/17.
*/
public class AppLifeCycleTracker implements Application.ActivityLifecycleCallbacks {;
/*///////////////////////////////////////////////////////////////
// METHODS
*////////////////////////////////////////////////////////////////
private static final String TAG = Globals.SEARCH_STRING + AppLifeCycleTracker.class.getSimpleName();
private static AppLifeCycleTracker INSTANCE;
private static int numActivitiesInMemory = 0;
private ArrayList<IAppToForegroundListener> mAppToForegroundListeners;
private boolean isRefreshing;
private Object lockAccess = new Object();
private AlertDialog mAlertDialog = null;
/*///////////////////////////////////////////////////////////////
// PROPERTIES
*////////////////////////////////////////////////////////////////
private ArrayList<IAppToForegroundListener> getAppToForegroundListeners(){
return mAppToForegroundListeners == null ? mAppToForegroundListeners = new ArrayList<IAppToForegroundListener>() : mAppToForegroundListeners;
}
public boolean getIsRefreshing(){
return isRefreshing;
}
public boolean getAppIsInBackground(){
return numActivitiesInMemory < 1;
}
/*///////////////////////////////////////////////////////////////
// CONSTRUCTOR
*////////////////////////////////////////////////////////////////
private AppLifeCycleTracker(){
}
public synchronized static AppLifeCycleTracker getInstance(){
if(INSTANCE == null){
INSTANCE = new AppLifeCycleTracker();
}
return INSTANCE;
}
/*///////////////////////////////////////////////////////////////
// LIFE CYCLE OVERRIDES
*////////////////////////////////////////////////////////////////
#Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
#Override
public void onActivityStarted(final Activity activity) {
//App went into background, so set a flag to avoid loading while we refresh
if(numActivitiesInMemory == 0 && !(activity instanceof SplashScreenActivity) && !(activity instanceof CreateAccountActivity)){
A35Log.v(TAG, "App Returned to Foreground, refreshing Token");
//first load on splash it goes from 0 to 1 so hold off on splash
synchronized (lockAccess) {
isRefreshing = true;
}
if (DeviceInfo.getInstance(activity).getIsConnectedToInternet()) {
CognitoManager.refreshToken(activity, new GenericHandler() {
#Override
public void onSuccess() {
A35Log.v(TAG, "Token Refresh Complete, notifying listeners");
//we are good, keep going
for(IAppToForegroundListener listener : getAppToForegroundListeners()){
listener.onRefreshTokenComplete();
}
synchronized (lockAccess) {
isRefreshing = false;
}
}
#Override
public void onFailure(Exception exception) {
//boot them to login screen
if(activity instanceof LoginActivity || activity instanceof SplashScreenActivity){
return;
}
startLoginActivity(activity);
synchronized (lockAccess) {
isRefreshing = false;
}
}
});
} else {
showInternetRequiredDialog(activity);
}
}
numActivitiesInMemory++;
}
#Override
public void onActivityResumed(Activity activity) {
}
#Override
public void onActivityPaused(Activity activity) {
}
#Override
public void onActivityStopped(Activity activity) {
numActivitiesInMemory--;
//if numActivities == 0 then you are in the background
}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
#Override
public void onActivityDestroyed(Activity activity) {
}
/*///////////////////////////////////////////////////////////////
// METHODS
*////////////////////////////////////////////////////////////////
public void addAppToForegroundListener(IAppToForegroundListener listener){
getAppToForegroundListeners().add(listener);
}
public void removeAppToForegroundListener(IAppToForegroundListener listener){
getAppToForegroundListeners().remove(listener);
}
private void startLoginActivity(final Activity activity){
((AMApplication) activity.getApplication()).logoutCurrentUser(activity, false, false, null, true, null);
}
/*///////////////////////////////////////////////////////////////
// INTERFACES
*////////////////////////////////////////////////////////////////
public interface IAppToForegroundListener {
/*///////////////////////////////////////////////////////////////
// METHODS
*////////////////////////////////////////////////////////////////
void onRefreshTokenComplete();
}
private void showInternetRequiredDialog(Activity activity){
final AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setTitle("Error").setMessage("Internet is required to use this app").setNegativeButton(R.string.ok, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
if(mAlertDialog != null && mAlertDialog.isShowing()) {
mAlertDialog.dismiss();
}
}
});
mAlertDialog = builder.create();
mAlertDialog.show();
}
}
Of course this does a little more then you are looking for as mine manages refreshing the token with cognito and forcing refresh on returning from background and things like that, so just disregard that piece. But the rest is all the same still. Hope that helps.
I'm assuming you don't need an example of a BaseActivity so I won't patronize you by pasting that.
Startup in Application class
#Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(AppLifeCycleTracker.getInstance());
}
Then you ONLY need to access from BaseActivity or BaseFragment IF you need to be notified when the app is in foreground or background at an Activity or fragment level. Which for your situation is not the case.
But if you ever wanted to use it, simply do this:
#Override
public void onAttach(Context context) {
super.onAttach(context);
AppLifeCycleTracker.getInstance().addAppToForegroundListener(this);
}
#Override
public void onDetach() {
super.onDetach();
AppLifeCycleTracker.getInstance().removeAppToForegroundListener(this);
}
But again, I must emphasize, this part is ONLY if you care to make your activity or fragment be aware of when the app comes back to foreground to force refresh or other behaviors. Replace onDetach with onDestroy if using Activity, but for your scenario you can skip that whole last section of code, you don't need it.

Android: Wearable not receiving Message Api message

I am trying to send a message from an android phone to a wearable, but the onMessageReceived method in the WearableListenerService is never called. I have verified that the message from the phone sends successfully and to the right node, so the problem appears to be on the receiving end in the wearable app. Here is my code for the listener class in the wearable project:
public class MessageListener extends WearableListenerService implements GoogleApiClient.ConnectionCallbacks {
#Override
public void onMessageReceived(MessageEvent messageEvent) {
if (messageEvent.getPath().equals("/message_path")) {
final String message = new String(messageEvent.getData());
// Broadcast message to wearable activity for display
Intent messageIntent = new Intent();
messageIntent.setAction(Intent.ACTION_SEND);
messageIntent.putExtra("message", message);
LocalBroadcastManager.getInstance(this).sendBroadcast(messageIntent);
}
else {
super.onMessageReceived(messageEvent);
}
}
#Override
public void onPeerConnected(Node peer) {
Log.v("myTag", "Peer connected: " + peer.getDisplayName());
}
#Override
public void onConnected(Bundle bundle) {
Wearable.MessageApi.addListener(MainActivityWear.client, this);
}
#Override
public void onConnectionSuspended(int i) {}
}
By setting breakpoints, I found that neither the onMessageReceived method nor the onConnected method were called.
Here is the MainActivity for the wearable project:
public class MainActivityWear extends Activity implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
private TextView mTextView;
private MessageReceiver messageReceiver;
public static GoogleApiClient client;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_wear);
final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
#Override
public void onLayoutInflated(WatchViewStub stub) {
mTextView = (TextView) stub.findViewById(R.id.text);
}
});
client = new GoogleApiClient.Builder(this)
.addApi(Wearable.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
// Register the local broadcast receiver
IntentFilter messageFilter = new IntentFilter(Intent.ACTION_SEND);
messageReceiver = new MessageReceiver();
LocalBroadcastManager.getInstance(this).registerReceiver(messageReceiver, messageFilter);
}
// Connect to the data layer when the Activity starts
#Override
protected void onStart() {
super.onStart();
client.connect();
}
#Override
public void onStop() {
super.onStop();
unregisterReceiver(messageReceiver);
}
#Override
public void onConnected(#Nullable Bundle bundle) {}
#Override
public void onConnectionSuspended(int i) {}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {}
public class MessageReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String message = intent.getStringExtra("message");
// Display message in UI
mTextView.setText(message);
}
}
}
The MessageReceiver is a nested class.
Everything compiles correctly, but the message is never received. Thank you in advance!
Check GoogleServicesAPI's version inside application and your devices. They must be the same.
Check whether your wear and handheld devices have the same version as your app. If not - use oldest one.
PS
do not use onPeerConnected and onConnectionSuspended. Those methods are deprecated and are not fired.

Android AIDL: Service-to-Activity communication

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");
}
});
}

Notify Activity Class

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);

Categories