I have an Activity class, in which I have a static flag, let's say
public static volatile flag = false;
Then in the class, I start a thread, which checks the flag and do different things.
I also have a broadcastreceiver, which sets the flag to true or false.
I though volatile will force the flag to the most recent value. But I can see my broadcastreceiver sets the static flag to true, but my thread is still getting it as false.
Am I missing something basic here? Any help would be appreciated!
Simplified Code (Updated) - So the flag is supposed to change to true after one minute. But it never did. But message from broadcast receiver shows it has been change to true
TestappActivity.java:
package com.test;
import java.util.Calendar;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
public class TestappActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Intent intent0 = new Intent(this, TestService.class);
this.startService(intent0);
Intent intent = new Intent(this, TestReceiver.class);
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
PendingIntent sender = PendingIntent.getBroadcast(this,
1, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
Calendar slot = Calendar.getInstance();
int min = slot.get(Calendar.MINUTE);
slot.set(Calendar.MINUTE, min+1);
am.set(AlarmManager.RTC_WAKEUP, slot.getTimeInMillis(), sender);
}
}
TestService.java:
package com.test;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
public class TestService extends Service {
private static final String TAG = "TestService";
public static volatile boolean flag = false;
private MyTopThread mTopThread;
public TestService() {
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
}
#Override
public void onDestroy() {
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
protect();
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}
/**
* Run protection
*
*/
private void protect() {
mTopThread = new MyTopThread();
mTopThread.start();
}
private class MyTopThread extends Thread {
#Override
public void run() {
while (true) {
try {
Thread.sleep(150);
Log.d(TAG, "Flag is " + TestService.flag);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
TestReceiver.java:
package com.test;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class TestReceiver extends BroadcastReceiver {
final static private String TAG = "TestReceiver";
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "onReceive is triggered ...");
TestService.flag = true;
Log.d(TAG, "flag is changed to " + TestService.flag);
}
}
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.test"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="8" />
<application
android:icon="#drawable/ic_launcher"
android:label="#string/app_name" >
<activity
android:name=".TestappActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".TestService" />
<receiver
android:name=".TestReceiver"
android:process=":remote" >
</receiver>
</application>
</manifest>
I think the problem is that you are running the receiver in its own process. From the docs for the android:process attribute of <receiver>:
If the name assigned to this attribute begins with a colon (':'), a new process, private to the application, is created when it's needed and the broadcast receiver runs in that process.
I think the receiver is modifying a process-local version of TestService.flag, not the one being used by TestService. Try removing the android:process attribute from the <receiver> tag in your manifest.
From this link
http://www.javamex.com/tutorials/synchronization_volatile.shtml
Essentially, volatile is used to indicate that a variable's value will
be modified by different threads.
I really hope your service thread is not this one (I don't see any other one):
private class MyTopThread extends Thread {
#Override
public void run() {
while (true) {
try {
Thread.sleep(150);
Log.d(TAG, "Flag is " + TestService.flag);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Because you have while(true) here, not while(!flag) as it should be.
Related
I have created a service which lives within a library. The library is then included in another project which attempts to launch the service. I am using AIDL to interact however when I attempt to connect to the service my onConnected method is never hit.
From the log files I can see the following error:
D/ActivityManager: bindService callerProcessName:com.something.services.dummyproject, calleePkgName: com.something.services.dummyproject, action: com.something.services.dummyservice.IDummyService
W/ActivityManager: Unable to start service Intent { act=com.something.services.dummyservice.IDummyService cmp=com.something.services.dummyproject/com.something.services.dummyservice.IDummyService } U=0: not found
D/ActivityManager: bindService callerProcessName:com.something.services.dummyproject, calleePkgName: com.something.services.dummyservice.service, action: null
W/ActivityManager: Unable to start service Intent { cmp=com.something.services.dummyservice.service/.DummyService } U=0: not found
The DummyService.java file is as follows:
package com.something.services.dummyservice.services;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import com.something.services.dummyservice.IDummyService;
public class DummyService extends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
private final IDummyService.Stub mBinder = new IDummyService.Stub() {
#Override
public String dummyHello() throws RemoteException {
return "Hello";
}
#Override
public void exit() throws RemoteException {
stopSelf();
}
};
}
The DummyService Manifest is as follows:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.something.services.dummyservice">
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<service
android:name=".services.DummyService"
android:exported="true"
android:label="#string/app_name"
tools:ignore="ExportedService" />
</application>
</manifest>
The IDummyService.aidl file is as follows:
// IDummyService.aidl
package com.something.services.dummyservice;
// Declare any non-default types here with import statements
interface IDummyService {
String dummyHello();
void exit();
}
I'm generating a .aar file with the above.
I've then included that library into my main project file.
The manifest in my main project is as follows:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.something.services.dummyproject">
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity
android:name=".MainActivity"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
The MainActivity.java file is as follows:
package com.something.services.dummyproject;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.something.services.dummyservice.IDummyService;
import butterknife.Bind;
import butterknife.ButterKnife;
public class MainActivity extends AppCompatActivity {
private IDummyService mDummyService;
#Bind(R.id.btnStartService)
Button btnStartService;
#Bind(R.id.btnEndService)
Button btnEndService;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
btnStartService.setOnClickListener(startServiceOnClickListener);
btnEndService.setOnClickListener(endServiceOnClickListener);
}
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
mDummyService = IDummyService.Stub.asInterface(service);
dummyHello();
}
#Override
public void onServiceDisconnected(ComponentName className) {
mDummyService = null;
}
};
private final View.OnClickListener startServiceOnClickListener = new View.OnClickListener() {
#Override
public void onClick(final View completeButton) {
Intent intent = new Intent(MainActivity.this, com.something.services.dummyservice.IDummyService.class);
intent.setAction(IDummyService.class.getName());
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
};
private final View.OnClickListener endServiceOnClickListener = new View.OnClickListener() {
#Override
public void onClick(final View completeButton) {
exitService();
}
};
private void dummyHello() {
try {
String response = mDummyService.dummyHello();
Toast.makeText(getApplicationContext(), response, Toast.LENGTH_LONG);
} catch (RemoteException e) {
e.printStackTrace();
}
}
private void exitService() {
try {
if (mDummyService != null) {
mDummyService.exit();
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
The dummyProject also contains the exact same AIDL file. In both cases the AIDL file lives in an aidl folder which lives alongside the src\JAVA folder. That contains a package folder which contains the AIDL file.
Any help with identifying why I'm unable to connect to the service is very much appreciated!
My target was Restarting the Service when app is in background or even killed from home page by sweeping. App & Service is working nice while app is in foreground and background but while I killed the app by force(sweeping out from home page), the Service stopped working. That's okay but I implemented a Broadcast Receiver to restart the Service but it seems like its (Broadcast Receiver) not even called itself or the Service while app was killed forcefully / sweeping from home page.
My device is : Xiaomi Redmi Note 4
I included my codes here :
MainActivity.java
package com.turzo.servicetest;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
public class MainActivity extends AppCompatActivity {
private String TAG = "ServiceTest";
Intent mServiceIntent;
private SensorService mSensorService;
Context ctx;
public Context getCtx() {
return ctx;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ctx = this;
registerRec();
setContentView(R.layout.activity_main);
mSensorService = new SensorService(getCtx());
mServiceIntent = new Intent(getCtx(), mSensorService.getClass());
if (!isMyServiceRunning(mSensorService.getClass())) {
startService(mServiceIntent);
}
}
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 (TAG, true+"");
return true;
}
}
Log.i (TAG, false+"");
return false;
}
#Override
protected void onDestroy() {
stopService(mServiceIntent);
Log.i(TAG, "onDestroy!");
super.onDestroy();
}
public void registerRec(){
SensorRestarterBroadcastReceiver myreceiver = new SensorRestarterBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
registerReceiver((BroadcastReceiver) myreceiver, intentFilter);
}
}
SensorService.java
package com.turzo.servicetest;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;
import java.util.Timer;
import java.util.TimerTask;
public class SensorService extends Service {
public int counter=0;
private String TAG = "ServiceTest";
public SensorService(Context applicationContext) {
super();
Log.i(TAG , "here I am!");
}
public SensorService() {
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
startTimer();
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG , "ondestroy!");
Intent broadcastIntent = new Intent("com.turzo.servicetest.ActivityRecognition.RestartSensor");
sendBroadcast(broadcastIntent);
stoptimertask();
}
private Timer timer;
private TimerTask timerTask;
long oldTime=0;
public void startTimer() {
//set a new Timer
timer = new Timer();
//initialize the TimerTask's job
initializeTimerTask();
//schedule the timer, to wake up every 1 second
timer.schedule(timerTask, 1000, 1000); //
}
/**
* it sets the timer to print the counter every x seconds
*/
public void initializeTimerTask() {
timerTask = new TimerTask() {
public void run() {
Log.i(TAG , "in timer ++++ "+ (counter++));
}
};
}
/**
* not needed
*/
public void stoptimertask() {
//stop the timer, if it's not already null
if (timer != null) {
timer.cancel();
timer = null;
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
SensorRestarterBroadcastReceiver.java
package com.turzo.servicetest;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class SensorRestarterBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.i(SensorRestarterBroadcastReceiver.class.getSimpleName(), "Service Stops! Oooooooooooooppppssssss!!!!");
context.startService(new Intent(context, SensorService.class));
}
}
AndroidManifext.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.turzo.servicetest">
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name="com.turzo.servicetest.SensorService"
android:enabled="true" >
</service>
<receiver
android:name="com.turzo.servicetest.SensorRestarterBroadcastReceiver"
android:enabled="true"
android:exported="true"
android:label="RestartServiceWhenStopped">
<intent-filter>
<action android:name="com.turzo.servicetest.ActivityRecognition.RestartSensor"/>
</intent-filter>
</receiver>
</application>
</manifest>
You should restart Service in onTaskRemoved().
#Override
public void onTaskRemoved(Intent rootIntent) {
Intent restartService = new Intent(getApplicationContext(),
this.getClass());
restartService.setPackage(getPackageName());
PendingIntent restartServicePI = PendingIntent.getService(
getApplicationContext(), 1, restartService,
PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmService = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
alarmService.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + 1000, restartServicePI);
}
NOTE:- Starting from android O . You can not call startService.
The startService() method now throws an IllegalStateException if an app targeting Android 8.0 tries to use that method in a situation when it isn't permitted to create background services.
This does not apply to foreground services, which are noticeable to the user. It can run in background with a notification on top. By default, these restrictions only apply to apps that target Android 8.0 (API level 26) or higher. However, users can enable most of these restrictions for any app from the Settings screen, even if the app targets an API level lower than 26. So in case if user enables the restrictions for below API 26 your Service will not work.
Read Background Execution Limits.
So Try to avoid using Service if you can . Make use of WorkManager if it fits the requirements.
I'm attempting to send a string from wearable to mobile device using code below.
This implementation is based on https://github.com/twotoasters/Wear-MessageApiDemo/
Case there is an issue with the time delay in connecting to the device I've increased
CONNECTION_TIME_OUT_MS from 100 to 2000 (milliseconds).
To the mobile manifest I add :
<service
android:name=".ListenerService" >
<intent-filter>
<action android:name="com.google.android.gms.wearable.MESSAGE_RECEIVED" />
</intent-filter>
</service>
instead of
<service
android:name=".ListenerService" >
<intent-filter>
<action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
</intent-filter>
</service>
as com.google.android.gms.wearable.BIND_LISTENER is deprecated
The code compiles but the message is not received by phone.
The method
private void showToast(String message) {
Log.d(TAG, "received message : " + message);
}
Should fire within the listenerService when a message is received.
The issue is a message is never received. Have I implemented the message api correctly ?
API version : 23
Source :
Mobile component
Kick off listenerService :
----------------------------------- MainActivity.onCreate ---------------
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new ListenerService();
}
Define the Listener service to listen for messages
----------------------------------- ListenerService ------------------
import android.util.Log;
import android.widget.TextView;
import com.google.android.gms.wearable.MessageEvent;
import com.google.android.gms.wearable.WearableListenerService;
public class ListenerService extends WearableListenerService {
private static final String TAG = "ListenerService";
TextView mTextView;
#Override
public void onMessageReceived(MessageEvent messageEvent) {
MainActivity.mTextView.setText("got message");
showToast(messageEvent.getPath());
}
private void showToast(String message) {
Log.d(TAG, "received message : " + message);
}
}
Define the service in the manifest
----------------------------------- AndroidManifest.xml ----------------
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.runner">
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.BODY_SENSORS"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.GPS_PROVIDER" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".ListenerService" >
<intent-filter>
<action android:name="com.google.android.gms.wearable.MESSAGE_RECEIVED" />
</intent-filter>
</service>
</application>
</manifest>
Wear component
MainActivity :
package common;
import android.content.Context;
import android.location.Location;
import android.location.LocationManager;
import android.os.Bundle;
import android.support.wearable.activity.WearableActivity;
import android.support.wearable.view.BoxInsetLayout;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.wearable.Node;
import com.google.android.gms.wearable.NodeApi;
import com.google.android.gms.wearable.Wearable;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
public class MainActivity extends WearableActivity {
private static final long CONNECTION_TIME_OUT_MS = 2000;
private static final String MESSAGE = "Hello Wear!";
private GoogleApiClient client;
private String nodeId;
private static final String TAG = "MainActivity";
private BoxInsetLayout mContainerView;
/**
* Initializes the GoogleApiClient and gets the Node ID of the connected device.
*/
private void initApi() {
client = getGoogleApiClient(this);
retrieveDeviceNode();
}
/**
* Returns a GoogleApiClient that can access the Wear API.
* #param context
* #return A GoogleApiClient that can make calls to the Wear API
*/
private GoogleApiClient getGoogleApiClient(Context context) {
return new GoogleApiClient.Builder(context)
.addApi(Wearable.API)
.build();
}
/**
* Connects to the GoogleApiClient and retrieves the connected device's Node ID. If there are
* multiple connected devices, the first Node ID is returned.
*/
private void retrieveDeviceNode() {
new Thread(new Runnable() {
#Override
public void run() {
client.blockingConnect(CONNECTION_TIME_OUT_MS, TimeUnit.MILLISECONDS);
NodeApi.GetConnectedNodesResult result =
Wearable.NodeApi.getConnectedNodes(client).await();
List<Node> nodes = result.getNodes();
if (nodes.size() > 0) {
Log.d(TAG, "nodeId "+nodeId);
nodeId = nodes.get(0).getId();
}
client.disconnect();
}
}).start();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initApi();
sendToast();
}
/**
* Sends a message to the connected mobile device, telling it to show a Toast.
*/
private void sendToast() {
if (nodeId != null) {
new Thread(new Runnable() {
#Override
public void run() {
client.blockingConnect(CONNECTION_TIME_OUT_MS, TimeUnit.MILLISECONDS);
Wearable.MessageApi.sendMessage(client, nodeId, MESSAGE, null);
client.disconnect();
}
}).start();
}
}
}
Update :
Here is the class added to mobile module to listen for received messages :
package com.receivers;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class MessageListener extends BroadcastReceiver {
private static final String TAG = "MessageListener";
#Override
public void onReceive(Context context, Intent intent) {
String str = intent.getAction();
Log.i(TAG, "onReceive triggered : "+str);
}
}
Config of MessageListener in AndroidManifest.xml :
<receiver android:name="com.receivers.MessageListener">
<intent-filter>
<action android:name="com.google.android.gms.wearable.MESSAGE_RECEIVED" />
</intent-filter>
</receiver>
I've tried setting a breakpoint at line String str = intent.getAction(); but onReceive method does not appear to be invoked.
Within the wear module the method onNodeFound() does appear to send the message correctly as this line Wearable.MessageApi.sendMessage(googleApiClient, nodeId, MESSAGE_PATH, "Hello Wear!".getBytes(Charset.forName("UTF-8"))); is being invoked. Have i setup the MessageListener correctly ?
Update 2 :
ReceiverActivity :
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
public class ReceiverActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver,
new IntentFilter("custom-event-name"));
}
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Get extra data included in the Intent
String message = intent.getStringExtra("EXTRA_MESSAGE");
Log.d("receiver", "Got message: " + message);
}
};
#Override
protected void onDestroy() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
super.onDestroy();
}
}
Within the ListenerService , method onMessageReceived is being fired and here trying to broadcast the message :
#Override
public void onMessageReceived(MessageEvent messageEvent) {
super.onMessageReceived(messageEvent);
Log.d("tester", "received a message from wear: " + new String(messageEvent.getData()));
final String message = new String(messageEvent.getData());
final Intent messageIntent = new Intent();
messageIntent.putExtra("EXTRA_MESSAGE", message); // define your extra
LocalBroadcastManager.getInstance(this).sendBroadcast(messageIntent);
}
Starting the activity in AndroidManifest.xml :
<activity android:name=".ReceiverActivity">
</activity>
But ReceiverActivity does not appear to receive message, is ReceiverActivity setup correctly ?
Update 3 :
As per comment to start the activity I add :
Intent intent = new Intent(this, ReceiverActivity.class);
startActivity(intent);
to MainActivity.onCreate :
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = new Intent(this, ReceiverActivity.class);
startActivity(intent);
......
new ListenerService();
This is not how you start any Service. This just creates a Servie instance that will do nothing and will be collected after onCreate() exits.
This Service will be started by the system when you'll receive a message. You only need to define it in the manifest. Also, you might need to define a path for messages you receive, like
<service android:name=".ListenerService" >
<intent-filter>
<action android:name="com.google.android.gms.wearable.MESSAGE_RECEIVED" />
<data android:scheme="wear" android:host="*" android:pathPrefix="/message"/>
</intent-filter>
</service>
That's all you need for your Service to be set up. Also, keep in mind that in order to receive messages your Wear app and Handheld app should have the same package name (applicationId). Double check you don't have mismatching applicationId for flavors or buildTypes. So if you have applicationId in build.gradle, make sure they match for both wear and handhelp app projects
defaultConfig {
applicationId "com.runner"
About updating UI:
#Override
public void onMessageReceived(MessageEvent messageEvent) {
MainActivity.mTextView.setText("got message");
}
This is not a way to interact a Service with Activity.
Activity might or might not be running when Service runs. Only update Activtity UI from Activity. If you need to show user a message, use a BroadcastReceiver or an Observer.
Note that onMessageReceived() will not be ran on Main UI thread, so use a Handler before showing a Toast there.
So if you want to pass the Message from this Service to Activity, one of the ways is like
#Override
public void onMessageReceived(MessageEvent messageEvent) {
final byte[] data = messsageEvent.getData();
if (data != null) {
final String message = new String(data, Charset.forName("UTF-8"));
final Intent messageIntent = new Intent("custom-event-name");
intent.putExtra(EXTRA_MESSAGE, message); // define your extra
LocalBroadcastManager.getInstance(this).sendBroadcast(messageIntent);
// Register BroadcastReiver from LocalBroadcastManager in your Activity to receive this broadcast
}
}
Or if you want to start Activity if it's not running, you need a different approach:
<activity
android:name=".ReceiverActivity"
android:launchMode="singleTop"/>
Service:
#Override
public void onMessageReceived(MessageEvent messageEvent) {
final byte[] data = messsageEvent.getData();
if (data != null) {
final String message = new String(data, Charset.forName("UTF-8"));
final Intent activityIntent = new Intent(this, ReceiverActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("EXTRA_MESSAGE", message);
startActivity(intent);
}
}
// In this case, in Activity, if it's explicitly started, you don't need a BroadcastReceiver
// Instead, you can get the extra from Activity Intent
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handleIntent(getIntent());
}
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
handleIntent(intent);
}
private void handleIntent(Intent intent) {
String message = intent.getStringExtra("EXTRA_MESSAGE");
}
In Wear component
initApi();
sendToast();
you use different threads that may run simultaneously so when sendToast() runs you may actually have nodeId not resolved yet.
What I suggest doing is connecting GoogleApiClient in onCreate() with a Listener. Once the client connects, start getting the node. You don't need to spawn your own threads, the API is asynchronous if you use setResultCallback() instead of await()
Edit 14/02/2018: as Rajesh mentioned in the comments, Wearable.API is deprecated. The answer below refers to old API, which were new on the time of writing. I am leaving the old answer as is, but I don't have time to investigate how to do this with new APIs.
private static final String MESSAGE_PATH = "/message";
private GoogleApiClient googleApiClient;
#Override
protected void onCerate(Bundle state) {
super.onCreate(state);
googleApiClient = getGoogleApiClient(this);
googleApiClient.connect();
}
#Override
protected void onDestroy() {
super.onDestroy();
googleApiClient.disconnect();
}
private GoogleApiClient getGoogleApiClient(Context context) {
return new GoogleApiClient.Builder(context)
.addApi(Wearable.API)
.addConnectionCallbacks(mConnectionCallbacks)
.build();
}
private void findNodes() {
Wearable.NodeApi.getConnectedNodes(googleApiClient).setResultCallback(
new ResultCallback<NodeApi.GetConnectedNodesResult>() {
#Override
public void onResult(
#NonNull final NodeApi.GetConnectedNodesResult getConnectedNodesResult) {
List<Node> nodes = result.getNodes();
if (nodes != null && !nodes.isEmpty()) {
nodeId = nodes.get(0).getId();
Log.d(TAG, "nodeId "+ nodeId);
onNodeFound();
}
}
});
}
private void onNodeFound() {
if (nodeId != null) {
// Now you have your node, send a message, make sure the path starts like the path in manifest
// What you thought is a message is actually a path, and the actual message is the byte array.
// You may concat your message in path though, but keep in mind you will have to parse the string then
Wearable.MessageApi.sendMessage(client, nodeId, MESSAGE_PATH, "Hello Wear!".getBytes(Charset.forName("UTF-8")));
}
}
private final GoogleApiClient.ConnectionCallbacks mConnectionCallbacks
= new GoogleApiClient.ConnectionCallbacks() {
#Override
public void onConnected(#Nullable final Bundle bundle) {
findNodes();
}
#Override
public void onConnectionSuspended(final int i) {
}
};
I made a service that every 5 second he put on the screen a TAG (I think this is the name of this). When I make a boot it needs to put the TAG on the screen but he says that the app crashed. Why?
The code:
Android Manifest:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<receiver android:name="com.YuvalFatal.MyBroadcastReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<service android:enabled="true" android:name="com.YuvalFatal.MyService"/>
BroadcastReceiver:
package com.YuvalFatal.ineedhelp;
import java.util.Timer;
import java.util.TimerTask;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class MyBroadcastreceiver extends BroadcastReceiver {
#Override
public void onReceive(final Context arg0, Intent arg1) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
public void run() {
Intent startServiceIntent = new Intent(arg0, MyService.class);
arg0.startService(startServiceIntent);
}
}, 0, 5000);
}
}
IntentService:
package com.YuvalFatal.ineedhelp;
import android.app.IntentService;
import android.content.Intent;
import android.util.Log;
public class MyService extends IntentService {
private static final String TAG = "com.YuvalFatal.ineedhelp";
public MyService(String name) {
super(name);
// TODO Auto-generated constructor stub
}
#Override
protected void onHandleIntent(Intent intent) {
// TODO Auto-generated method stub
Log.i(TAG, "Intent Service started");
}
}
I think (yep, I am magician and have great intuition :) your Service constructor should be default:
public class MyService extends IntentService {
...
public MyService() { // Default constructor! Without params!
super("MyService"); // Or another string
}
...
}
Other code looks normal
So after 2-3 hours of failures i finally got my remote service to work somehow but it's acting real strange.
I'm using AIDL to send mediaPath string to my service and it starts playing music just fine, but the onStartCommand never gets called and the Service entry in Apps doesn't have the label/description I set in the manifest file. [http://i50.tinypic.com/344p349.png]
Also the Service terminates if I terminate the main Activity process, although it resides in a separate process. Is this how it's supposed to be? [http://i49.tinypic.com/16hpa86.png]
And I never get the "Service disconnected" Log which should happen when Service gets unbound from Activity.
Service code:
package com.example.randomserviceshitnot;
import java.io.IOException;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
public class MojPrviServis extends Service {
private final Servis.Stub binder = new Servis.Stub() {
public void execute(String mediaPath) throws RemoteException {
MediaPlayer mp = new MediaPlayer();
try {
mp.setDataSource(mediaPath);
mp.prepare();
} catch (IllegalArgumentException e) { e.printStackTrace(); } catch (SecurityException e) {
e.printStackTrace(); } catch (IllegalStateException e) { e.printStackTrace();
} catch (IOException e) { e.printStackTrace(); }
mp.start();
}
};
public void onCreate() {
super.onCreate();
Log.d("Filip", "Service onCreate called.");
}
public IBinder onBind(Intent intent) {
return binder;
}
public void onDestroy() {
super.onDestroy();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("Filip ", "Service onStart called.");
return START_STICKY;
}
}
Activity code:
package com.example.randomserviceshitnot;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
public class MainActivity extends Activity {
private Servis mBoundService;
private boolean mIsBound = false;
private static final String mediaPath = Environment.getExternalStorageDirectory().toString()+"/Music/Art Of The Dress(Archie Remix).mp3";
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d("Filip ", "Service connected.");
mBoundService = Servis.Stub.asInterface(service);
try {
mBoundService.execute(mediaPath);
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void onServiceDisconnected(ComponentName name) {
Log.d("Filip ", "Service disconnected.");
mBoundService = null;
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
protected void onStart() {
super.onStart();
doBindService();
}
void doBindService() {
Intent s = new Intent();
s.setAction("remote.servis");
bindService(s, mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
}
#Override
protected void onDestroy() {
super.onDestroy();
doUnbindService();
}
public void onPause() {
super.onPause();
doUnbindService();
}
void doUnbindService() {
if(mIsBound) {
unbindService(mConnection);
mIsBound=false;
}
}
}
AIDL:
package com.example.randomserviceshitnot;
interface Servis {
void execute(String s);
}
Manifest:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.randomserviceshitnot"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="15" />
<application
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="#string/title_activity_main" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".MojPrviServis"
android:label="#string/servis_koji_notifikuje"
android:description="#string/proces_desc"
android:icon="#drawable/ic_launcher"
android:process=":dep" >
<intent-filter>
<action android:name="remote.servis" />
</intent-filter>
</service>
</application>
</manifest>
http://developer.android.com/guide/components/services.html
Read the documentation of onStartCommand() and onBind()
When you call bindService() onStartCommand() is NOT called. But onBind() is called.
Also the service terminates if all the clients exited if the service was stared with bindservice().
For playing music you should use startService() instead of bind. If service is started this way then it does not stop when client exists. It has to stop itself when done.