I'm trying to run a background service in React-Native. From what I've heard I need to write it in native Java and connect it to the react-native code. When I try to emit an event I get an error:
Tried to access a JS module before the React instance was fully set up. Calls to should only happen once initialize() has been called on your native module.
So I added a check to see if the Module is running:
if (reactContext.getLifecycleState() == LifecycleState.RESUMED)
But it always returns false. The lifecycle is stuck on BEFORE_CREATE. How should I emit my event.
Service:
public class TestService extends Service {
double distance = 0.0;
ReactContext reactContext;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
reactContext = new ReactContext(getApplicationContext());
new Timer().scheduleAtFixedRate(new TimerTask(){
#Override
public void run(){
WritableMap params = Arguments.createMap();
distance+= 0.7;
Log.d("LOG", "Trying to send distance: "+distance+" on lifecycle: "+reactContext.getLifecycleState());
params.putDouble("distance", distance);
sendEvent(reactContext, "updateDistance", params);
}
},0,1000);
return START_STICKY;
}
private void sendEvent(ReactContext reactContext, String eventName, #Nullable WritableMap params) {
if (reactContext.getLifecycleState() == LifecycleState.RESUMED) {
reactContext.getJSModule(DeviceEventManagerModule
.RCTDeviceEventEmitter.class)
.emit(eventName, params);
Log.d("LOG", "Sent distance: "+distance);
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
Module:
public class ServiceModule extends ReactContextBaseJavaModule {
ReactContext reactContext;
public ServiceModule(ReactApplicationContext reactContext) {
super(reactContext);
this.reactContext = reactContext;
this.initialize();
}
#ReactMethod
public void startTrackingService() {
Intent intent = new Intent(reactContext, TestService.class);
reactContext.startService(intent);
}
#Override
public String getName() {
return "ServiceModule";
}
}
Package:
public class ServicePackage implements ReactPackage {
#Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new ServiceModule(reactContext));
return modules;
}
#Override
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
#Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
MainApplication:
#Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new ReactNativePushNotificationPackage(),
new ServicePackage()
);
}
I solved it :)
In the service I was creating a new context from base context which is NOT the same object. The workaround was to broadcast the data from the service and then send them do javascript.
ServiceModule:
public class ServiceModule extends ReactContextBaseJavaModule {
public static String UPDATE = "updateDistance";
public static String DISTANCE = "distance";
private IntentFilter intentFilter;
private BroadcastReceiver receiver;
public ServiceModule(ReactApplicationContext reactContext) {
super(reactContext);
initializeBroadcastReceiver();
}
#ReactMethod
public void startTrackingService() {
Intent intent = new Intent(getReactApplicationContext(), TestService.class);
getReactApplicationContext().startService(intent);
}
#ReactMethod
public void stopTrackingService() {
Intent intent = new Intent(getReactApplicationContext(), TestService.class);
getReactApplicationContext().stopService(intent);
}
private void sendEvent(ReactContext reactContext, String eventName, #Nullable WritableMap params) {
if (reactContext.hasActiveCatalystInstance()) {
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, params);
}
}
private void initializeBroadcastReceiver() {
intentFilter = new IntentFilter();
intentFilter.addAction(UPDATE);
receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
WritableMap params = Arguments.createMap();
params.putDouble(DISTANCE, intent.getDoubleExtra(DISTANCE, 0));
sendEvent(getReactApplicationContext(), UPDATE, params);
}
};
getReactApplicationContext().registerReceiver(receiver, intentFilter);
}
#Override
public String getName() {
return "ServiceModule";
}
}
TestService:
public class TestService extends Service {
double distance = 0.0;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Timer().scheduleAtFixedRate(new TimerTask(){
#Override
public void run(){
Intent broadcastIntent = new Intent();
broadcastIntent.setAction(ServiceModule.UPDATE);
broadcastIntent.putExtra(ServiceModule.DISTANCE, distance);
sendBroadcast(broadcastIntent);
distance+= 0.7;
}
},0,1000);
return START_STICKY;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
Related
I'm developing an app that is going to require 2 services:
HostService - A foreground service that manages music playback using the Spotify app-remote SDK. It is necessary to run this service in the foreground as I need to detect playState changes and manage playback constantly. However, this service ONLY needs to run if a condition is met, we'll call that condition isHost. This service will be bound to a SessionActivity that displays the music playing and manages playback.
ApiService - A service that continuously polls a separate web API for updates. This must also run in the foreground if the isHost condition is met as the updates may influence the music playback. However, if the user is not a host, this can be a background process that only needs to poll while the user is actively using a particular Activity. I'm not sure if this service should also be bound to the activity or not.
My question is, how should I go about setting up the ApiService differently depending on this condition? I've started to implement these services below but I'm not entirely sure what I have done makes sense. Additionally, am I doing the right thing by using an event handler the way I am? I don't fully understand Intents and I'm not sure if I should be using broadcast messages and such instead.
SessionActivity.java
class SessionActivity extends AppCompatActivity implements HostEventHandler, ApiEventHandler {
private boolean isHost;
private String data;
private String UID;
private ApiService api;
private HostService host;
private boolean hostBound;
private boolean apiBound;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_session);
isHost = getIntent().getBooleanExtra("isHost", false);
data = getIntent().getStringExtra("data");
UID = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);
}
private ServiceConnection hostConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
Log.d(Constants.TAG, "Bound to HostService");
HostService.LocalBinder binder = (HostService.LocalBinder) service;
host = (HostService) binder.getService()
host.registerEventHandler(this);
hostBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
Log.e(Constants.TAG, "onServiceDisconnected");
hostService = null;
}
};
private ServiceConnection apiConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
Log.d(Constants.TAG, "Bound to ApiService");
HostService.LocalBinder binder = (ApiService.LocalBinder) service;
api = (ApiService) binder.getService()
api.registerEventHandler(this);
apiBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
Log.e(Constants.TAG, "onServiceDisconnected");
apiService = null;
}
};
protected void onStart() {
super.onStart();
Log.d(Constants.TAG, "Binding to services...");
Intent intent = new Intent(this, ApiService.class);
intent.putExtra("data", data);
intent.putExtra("user", Constants.UID);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
if (isHost) {
Intent intent = new Intent(this, HostService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
{
}
#Override
protected void onStop() {
super.onStop();
unbindService(apiConnection);
apiBound = false;
if (isHost) {
unbindService(hostService);
hostBound = false;
}
}
// HostEventHandler and ApiEventHandler methods...
}
HostService.java
public class HostService extends Service {
private final IBinder binder = new LocalBinder();
private HostEventHandler handler;
public class LocalBinder extends Binder {
HostService getService() {
return HostService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
Log.d(Constants.TAG, "onBind");
return binder;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
createNotificationChannel();
Intent notificationIntent = new Intent(this, SessionActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Foreground Service")
.setContentText("Foreground service is running...")
.setSmallIcon(R.drawable.logo)
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);
connect(); // This connects to spotify
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
disconnect();
}
public registerEventHandler(HostEventHandler handler) {
this.handler = handler;
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel serviceChannel = new NotificationChannel(
CHANNEL_ID,
"Foreground Service Channel",
NotificationManager.IMPORTANCE_DEFAULT
);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(serviceChannel);
}
}
}
ApiService.java
public class ApiService extends Service {
private final IBinder binder = new LocalBinder();
private ApiEventHandler handler;
private final Timer timer;
private boolean connected = false;
private String data;
private String UID;
private ApiState state;
public class LocalBinder extends Binder {
HostService getService() {
return ApiService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
Log.d(Constants.TAG, "onBind");
return binder;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (!connected) {
data = intent.getStringExtra("data");
UID = intent.getStringExtra("user");
connect();
}
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
disconnect();
}
public void registerEventHandler(ApiEventHandler handler) {
this.handler = handler;
}
public ApiState getState() {
return state;
}
private void connect() {
if (connected) return;
class Updater extends TimerTask {
#Override
public void run() {
state = fetchUpdatedState(); // This will implement the web request
handler.onStateUpdate(state);
Log.d(Constants.TAG, "Polling API...");
timer.schedule(new Updater(), Constants.REFRESH_DELAY);
}
}
timer.schedule(new Updater(), Constants.REFRESH_DELAY);
connected = true;
}
}
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:
Hello i had implement code of Music service i want to create demo of music player but it not start automatically from onCreate() i want to start player automatically when activity open. here below i put code for Activity and service please help me any help will be appreciate.
public class MainActivity extends Activity {
private ArrayList<Song> songList;
private ListView songView;
private MusicService musicSrv;
private Intent playIntent;
private boolean musicBound = false;
private MusicController controller;
private boolean paused = false, playbackPaused = false;
private ServiceConnection musicConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
MusicService.MusicBinder binder = (MusicService.MusicBinder) service;
musicSrv = binder.getService();
musicSrv.setList(songList);
musicBound = true;
}
#Override
public void onServiceDisconnected(ComponentName name) {
musicBound = false;
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
songView = (ListView) findViewById(R.id.song_list);
songList = new ArrayList<Song>();
getSongList();
Collections.sort(songList, new Comparator<Song>() {
public int compare(Song a, Song b) {
return a.getTitle().compareTo(b.getTitle());
}
});
SongAdapter songAdt = new SongAdapter(this, songList);
songView.setAdapter(songAdt);
songPicked();
}
#Override
protected void onStart() {
super.onStart();
if (playIntent == null) {
playIntent = new Intent(this, MusicService.class);
bindService(playIntent, musicConnection, Context.BIND_AUTO_CREATE);
startService(playIntent);
}
}
public void songPicked() {
musicSrv.setSong(0);
musicSrv.playSong();
}
#Override
protected void onPause() {
super.onPause();
paused = true;
}
#Override
protected void onResume() {
super.onResume();
if (paused) {
paused = false;
}
}
#Override
protected void onStop() {
controller.hide();
super.onStop();
}
#Override
protected void onDestroy() {
stopService(playIntent);
musicSrv = null;
super.onDestroy();
}
}
Here below i put service code also.
public class MusicService extends Service implements
MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener,
MediaPlayer.OnCompletionListener {
private final IBinder musicBind = new MusicBinder();
private MediaPlayer player;
private ArrayList<Song> songs;
private int songPosn;
private String songTitle = "";
private Random rand;
public void onCreate() {
super.onCreate();
songPosn = 0;
rand = new Random();
player = new MediaPlayer();
initMusicPlayer();
}
public void initMusicPlayer() {
player.setWakeMode(getApplicationContext(),
PowerManager.PARTIAL_WAKE_LOCK);
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
player.setOnPreparedListener(this);
player.setOnCompletionListener(this);
player.setOnErrorListener(this);
}
public void setList(ArrayList<Song> theSongs) {
songs = theSongs;
}
#Override
public IBinder onBind(Intent intent) {
return musicBind;
}
#Override
public boolean onUnbind(Intent intent) {
player.stop();
player.release();
return false;
}
public void playSong() {
player.reset();
Song playSong = songs.get(0);
songTitle = playSong.getTitle();
long currSong = playSong.getID();
Uri trackUri = ContentUris.withAppendedId(
android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
currSong);
try {
player.setDataSource(getApplicationContext(), trackUri);
} catch (Exception e) {
Log.e("MUSIC SERVICE", "Error setting data source", e);
}
try {
player.prepare();
} catch (IOException e) {
e.printStackTrace();
}
}
public void setSong(int songIndex) {
songPosn = songIndex;
}
#Override
public void onCompletion(MediaPlayer mp) {
if (player.getCurrentPosition() > 0) {
mp.reset();
}
}
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
mp.reset();
return false;
}
#Override
public void onPrepared(MediaPlayer mp) {
mp.start();
playSong();
}
#Override
public void onDestroy() {
stopForeground(true);
}
public class MusicBinder extends Binder {
MusicService getService() {
return MusicService.this;
}
}
}
I just want to start player when application start automatically without any click. but it display unfortunately stopped with null object reference of Media player. I had never work with service also with music player.
Null pointer exception throws because music service object returns null so.
Finally solved as per #vladMatvienko answer thanks for your support man.
private ServiceConnection musicConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
MusicService.MusicBinder binder = (MusicService.MusicBinder) service;
musicSrv = binder.getService();
musicSrv.setList(songList);
musicBound = true;
songPicked();
}
#Override
public void onServiceDisconnected(ComponentName name) {
musicBound = false;
}
};
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 trying to implement an Instant Messaging functionality into my app following this guide: https://www.sinch.com/tutorials/android-messaging-tutorial-using-sinch-and-parse/#start the sinch client
I have followed everything in the guide exactly, but when I attempt to send a message I get a NullPointerException. Here is the guilty code:
private void sendMessage() {
messageBody = messageBodyField.getText().toString();
if (messageBody.isEmpty()) {
Toast.makeText(getApplicationContext(), "Enter a message", Toast.LENGTH_LONG).show();
return;
}
Log.i(TAG, "messageService: " + messageService);
Log.i(TAG, "recipientId: " + recipientId);
Log.i(TAG, "messageBody: " + messageBody);
messageService.sendMessage(recipientId, messageBody);
messageBodyField.setText("");
}
From the logs, I see that the recipientId and messageBody are properly assigned, and that the messageService object is null. Below is the full code of the activity:
public class ChatActivity extends AppCompatActivity {
private static final String TAG = "ChatActivity";
private String recipientId;
private EditText messageBodyField;
private ListView messagesList;
private ChatAdapter chatAdapter;
private String messageBody;
private MessageService.MessageServiceInterface messageService;
private String currentUserId;
private ServiceConnection serviceConnection = new MyServiceConnection();
private MessageClientListener messageClientListener = new MyMessageClientListener();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.chat);
bindService(new Intent(this, MessageService.class), serviceConnection, BIND_AUTO_CREATE);
//Log.i("Tag", "bindService(): " + bindService(new Intent(this, MessageService.class), serviceConnection, BIND_AUTO_CREATE));
Intent intent = getIntent();
recipientId = intent.getStringExtra("RECIPIENT_ID");
currentUserId = ParseUser.getCurrentUser().getObjectId();
messagesList = (ListView) findViewById(R.id.listview_chat);
chatAdapter = new ChatAdapter(this);
messagesList.setAdapter(chatAdapter);
loadMessageHistory();
messageBodyField = (EditText) findViewById(R.id.edittext_messageBodyField);
findViewById(R.id.button_send).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
sendMessage();
}
});
}
private void loadMessageHistory() {
String[] userIds = {currentUserId, recipientId};
ParseQuery<ParseObject> query = ParseQuery.getQuery("ParseMessage");
query.whereContainedIn("senderId", Arrays.asList(userIds));
query.whereContainedIn("recipientId", Arrays.asList(userIds));
query.orderByAscending("createdAt");
query.findInBackground(new FindCallback<ParseObject>() {
#Override
public void done(List<ParseObject> messageList, ParseException e) {
if (e == null) {
for (int i = 0; i < messageList.size(); i++) {
WritableMessage message = new WritableMessage(messageList.get(i).get("recipientId").toString(), messageList.get(i).get("messageText").toString());
if (messageList.get(i).get("senderId").toString().equals(currentUserId)) {
chatAdapter.addMessage(message, ChatAdapter.DIRECTION_OUTGOING);
} else {
chatAdapter.addMessage(message, ChatAdapter.DIRECTION_INCOMING);
}
}
}
}
});
}
private void sendMessage() {
messageBody = messageBodyField.getText().toString();
if (messageBody.isEmpty()) {
Toast.makeText(getApplicationContext(), "Enter a message", Toast.LENGTH_LONG).show();
return;
}
Log.i(TAG, "messageService: " + messageService);
Log.i(TAG, "recipientId: " + recipientId);
Log.i(TAG, "messageBody: " + messageBody);
messageService.sendMessage(recipientId, messageBody);
messageBodyField.setText("");
}
#Override
public void onDestroy() {
messageService.removeMessageClientListener(messageClientListener);
unbindService(serviceConnection);
super.onDestroy();
}
private class MyServiceConnection implements ServiceConnection {
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
messageService = (MessageService.MessageServiceInterface) iBinder;
Log.i(TAG, "messageService::onServiceConnected: " + messageService);
messageService.addMessageClientListener(messageClientListener);
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
messageService = null;
Log.i(TAG, "messageService::onServiceDisconnected: " + messageService);
}
}
private class MyMessageClientListener implements MessageClientListener {
#Override
public void onMessageFailed(MessageClient client, Message message, MessageFailureInfo failureInfo) {
Toast.makeText(ChatActivity.this, "Failed to send message", Toast.LENGTH_LONG).show();
}
#Override
public void onIncomingMessage(MessageClient client, final Message message) {
if (message.getSenderId().equals(recipientId)) {
final WritableMessage writableMessage = new WritableMessage(message.getRecipientIds().get(0), message.getTextBody());
ParseQuery<ParseObject> query = ParseQuery.getQuery("ParseMessage");
query.whereEqualTo("sinchId", message.getMessageId());
query.findInBackground(new FindCallback<ParseObject>() {
#Override
public void done(List<ParseObject> messageList, ParseException e) {
if (e == null) {
if (messageList.size() == 0) {
ParseObject parseMessage = new ParseObject("ParseMessage");
parseMessage.put("senderId", currentUserId);
parseMessage.put("recipientId", writableMessage.getRecipientIds().get(0));
parseMessage.put("messageText", writableMessage.getTextBody());
parseMessage.put("sinchId", message.getMessageId());
parseMessage.saveInBackground();
chatAdapter.addMessage(writableMessage, chatAdapter.DIRECTION_INCOMING);
}
}
}
});
}
}
#Override
public void onMessageSent(MessageClient client, Message message, String recipientId) {
final WritableMessage writableMessage = new WritableMessage(message.getRecipientIds().get(0), message.getTextBody());
chatAdapter.addMessage(writableMessage, ChatAdapter.DIRECTION_OUTGOING);
}
#Override
public void onMessageDelivered(MessageClient client, MessageDeliveryInfo deliveryInfo) {
}
#Override
public void onShouldSendPushData(MessageClient client, Message message, List<PushPair> pushPairs) {
}
}
}
And the MessageService class code:
package com.example.aes.ctf;
public class MessageService extends Service implements SinchClientListener {
private static final String APP_KEY = "app-key";
private static final String APP_SECRET = "secret";
private static final String ENVIRONMENT = "";
private final MessageServiceInterface serviceInterface = new MessageServiceInterface();
private SinchClient sinchClient = null;
private MessageClient messageClient = null;
private String currentUserId;
//private Intent broadcastIntent = new Intent("com.example.aes.ctf.ListUsersActivity");
//private LocalBroadcastManager broadcaster;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
currentUserId = ParseUser.getCurrentUser().getObjectId();
if (currentUserId != null && !isSinchClientStarted()) {
startSinchClient(currentUserId);
}
//broadcaster = LocalBroadcastManager.getInstance(this);
return super.onStartCommand(intent, flags, startId);
}
public void startSinchClient(String username) {
sinchClient = Sinch.getSinchClientBuilder().context(this).userId(username)
.applicationKey(APP_KEY).applicationSecret(APP_SECRET)
.environmentHost(ENVIRONMENT).build();
sinchClient.addSinchClientListener(this);
sinchClient.setSupportMessaging(true);
sinchClient.setSupportActiveConnectionInBackground(true);
sinchClient.checkManifest();
sinchClient.start();
}
private boolean isSinchClientStarted() {
return sinchClient != null && sinchClient.isStarted();
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onClientStarted(SinchClient client) {
/*broadcastIntent.putExtra("success", true);
broadcaster.sendBroadcast(broadcastIntent);*/
client.startListeningOnActiveConnection();
messageClient = client.getMessageClient();
}
#Override
public void onClientStopped(SinchClient client) {
client = null;
}
#Override
public void onClientFailed(SinchClient client, SinchError sinchError) {
/*broadcastIntent.putExtra("success", false);
broadcaster.sendBroadcast(broadcastIntent);*/
client = null;
}
#Override
public void onRegistrationCredentialsRequired(SinchClient sinchClient, ClientRegistration clientRegistration) {
}
#Override
public void onLogMessage(int i, String s, String s1) {
}
public void sendMessage(String recipientUserId, String textBody) {
if (messageClient != null) {
WritableMessage message = new WritableMessage(recipientUserId, textBody);
messageClient.send(message);
}
}
public void addMessageClientListener(MessageClientListener listener) {
if (messageClient != null) {
messageClient.addMessageClientListener(listener);
}
}
public void removeMessageClientListener(MessageClientListener listener) {
if (messageClient != null) {
messageClient.removeMessageClientListener(listener);
}
}
#Override
public void onDestroy() {
sinchClient.stopListeningOnActiveConnection();
sinchClient.terminate();
}
public class MessageServiceInterface extends Binder {
public void sendMessage(String recipientUserId, String textBody) {
MessageService.this.sendMessage(recipientUserId, textBody);
}
public void addMessageClientListener(MessageClientListener listener) {
MessageService.this.addMessageClientListener(listener);
}
public void removeMessageClientListener(MessageClientListener listener) {
MessageService.this.removeMessageClientListener(listener);
}
public boolean isSinchClientStarted() {
return MessageService.this.isSinchClientStarted();
}
}
}
I can post more related code if needed. I've been stuck on this error for awhile.
EDIT:
I was able to fix it by changing
#Override
public IBinder onBind(Intent intent) {
return null;
}
to
#Override
public IBinder onBind(Intent intent) {
return serviceInterface;
}
Thanks guy!
well, this is because you never INITIALIZE the messageService object.
it seems like you wish to start a service and then - control it from your activity... are you sure you wish to send messages from a service rather from an AsyncTask?
AsyncTask also run in the background and much more easier to control for this purpose...
try to chek this link: http://developer.android.com/reference/android/os/AsyncTask.html
Your problem is here:
#Override
public IBinder onBind(Intent intent) {
return null;
}
You should implement a binder for your service and return it in the onBind method of your service, so in your MessageServiceInterface class declare following method:
public MessageService getService() {
Log.v("Serivce Bind", "Bind");
return MessageService.this;
}
}
And declare a field in MessageService class:
IBinder binder = new MessageSeviceInterface();
Return binder instance in onBind method and finally get the allocated instance of MessageService using following code:
Replace
messageService = (MessageService.MessageServiceInterface) iBinder;
With:
MessageService.MessageServiceInterface binder = (MessageService.MessageServiceInterface) iBinder;
messageService = binder.getService()`