Actually in android want to run my job after 1 minutes whether app is active or not but it runs my job after around 3,4 minutes which I don't want please give me suggestion how can I run it according to my given time.
Thanks in advance.
Here is my code:
ComponentName componentName = new ComponentName(MainActivity.this, JobSchedularClass.class);
jobInfo = new JobInfo.Builder(code, componentName)
.setMinimumLatency(1000)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setOverrideDeadline(1000)
.setPersisted(true)
.build();
jobScheduler = (JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE);
}
public void Startjob(View view) {
int code_result = jobScheduler.schedule(jobInfo);
Toast.makeText(this, " Job scheduling...", Toast.LENGTH_SHORT).show();
if (code_result == JobScheduler.RESULT_SUCCESS) {
Toast.makeText(this, " Job scheduling done...", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, " Job scheduling failed...", Toast.LENGTH_SHORT).show();
}
}
public void Stopjob(View view) {
jobScheduler.cancel(code);
Toast.makeText(this, " Job cancel...", Toast.LENGTH_SHORT).show();
}
Here is my Job service class code:
public class JobSchedularClass extends JobService {
private static final String TAG = "JobSchedularClass";
private BackgroundJob backgroundJob;
#Override
public boolean onStartJob(final JobParameters params) {
Log.d(TAG, "onStartJob: ");
backgroundJob=new BackgroundJob(){
#Override
protected void onPostExecute(String s) {
Toast.makeText(getApplicationContext(),s,Toast.LENGTH_SHORT).show();
jobFinished(params,true);
}
};
backgroundJob.execute();
return true;
}
#Override
public boolean onStopJob(JobParameters params) {
Log.d(TAG, "onStopJob: ");
backgroundJob.cancel(true);
return false;
}
}
Here is my Async Task code:
public class BackgroundJob extends AsyncTask<Void,Void,String> {
private static final String TAG = "BackgroundJob";
#Override
protected String doInBackground(Void... voids) {
Log.d(TAG, "doInBackground: ");
return "BackGround Job is running";
}
}
Related
I have started working on a Freemarker Debugger using breakpoints etc. The supplied framework is based on java RMI. So far I get it to suspend at one breakpoint but then ... nothing.
Is there a very basic example setup for the serverpart and the client part other then the debug/imp classes supplied with the sources. That would be of great help.
this is my server class:
class DebuggerServer {
private final int port;
private final String templateName1;
private final Environment templateEnv;
private boolean stop = false;
public DebuggerServer(String templateName) throws IOException {
System.setProperty("freemarker.debug.password", "hello");
port = SecurityUtilities.getSystemProperty("freemarker.debug.port", Debugger.DEFAULT_PORT).intValue();
System.setProperty("freemarker.debug.password", "hello");
Configuration cfg = new Configuration();
// Some other recommended settings:
cfg.setIncompatibleImprovements(new Version(2, 3, 20));
cfg.setDefaultEncoding("UTF-8");
cfg.setLocale(Locale.US);
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
Template template = cfg.getTemplate(templateName);
templateName1 = template.getName();
System.out.println("Debugging " + templateName1);
Map<String, Object> root = new HashMap();
Writer consoleWriter = new OutputStreamWriter(System.out);
templateEnv = new Environment(template, null, consoleWriter);
DebuggerService.registerTemplate(template);
}
public void start() {
new Thread(new Runnable() {
#Override
public void run() {
startInternal();
}
}, "FreeMarker Debugger Server Acceptor").start();
}
private void startInternal() {
boolean handled = false;
while (!stop) {
List breakPoints = DebuggerService.getBreakpoints(templateName1);
for (int i = 0; i < breakPoints.size(); i++) {
try {
Breakpoint bp = (Breakpoint) breakPoints.get(i);
handled = DebuggerService.suspendEnvironment(templateEnv, templateName1, bp.getLine());
} catch (RemoteException e) {
System.err.println(e.getMessage());
}
}
}
}
public void stop() {
this.stop = true;
}
}
This is the client class:
class DebuggerClientHandler {
private final Debugger client;
private boolean stop = false;
public DebuggerClientHandler(String templateName) throws IOException {
// System.setProperty("freemarker.debug.password", "hello");
// System.setProperty("java.rmi.server.hostname", "192.168.2.160");
client = DebuggerClient.getDebugger(InetAddress.getByName("localhost"), Debugger.DEFAULT_PORT, "hello");
client.addDebuggerListener(environmentSuspendedEvent -> {
System.out.println("Break " + environmentSuspendedEvent.getName() + " at line " + environmentSuspendedEvent.getLine());
// environmentSuspendedEvent.getEnvironment().resume();
});
}
public void start() {
new Thread(new Runnable() {
#Override
public void run() {
startInternal();
}
}, "FreeMarker Debugger Server").start();
}
private void startInternal() {
while (!stop) {
}
}
public void stop() {
this.stop = true;
}
public void addBreakPoint(String s, int i) throws RemoteException {
Breakpoint bp = new Breakpoint(s, i);
List breakpoints = client.getBreakpoints();
client.addBreakpoint(bp);
}
}
Liferay IDE (https://github.com/liferay/liferay-ide) has FreeMarker template debug support (https://issues.liferay.com/browse/IDE-976), so somehow they managed to use it. I have never seen it in action though. Other than that, I'm not aware of anything that uses the debug API.
I am using sinch Video calling API for my android application. But when the app is in the background, whenever the user is offline i.e user cleared app from the recent tab, he was unable to get the call. I used Firebase for pushing notification whenever the user is offline, but it is not getting triggered all the time, it is getting triggered sometimes only. can anyone suggest me how to push notification of incoming call when the user cleared the app from recent and will sinch actually run in the background?
public class SinchService extends Service {
private static final String APP_KEY = "key";
private static final String APP_SECRET = "secret";
private static final String ENVIRONMENT = "clientapi.sinch.com";
public static final String CALL_ID = "CALL_ID";
static final String TAG = SinchService.class.getSimpleName();
private SinchServiceInterface mSinchServiceInterface = new SinchServiceInterface();
private SinchClient mSinchClient;
private String mUserId;
static Context context;
private StartFailedListener mListener;
#Override
public void onCreate() {
super.onCreate();
}
/* #Override
public void onDestroy() {
if (mSinchClient != null && mSinchClient.isStarted()) {
mSinchClient.terminate();
}
super.onDestroy();
}*/
private void start(String userName,String fcm_token) {
if (mSinchClient == null) {
mUserId = userName;
/*LooperThread looperThread = new LooperThread();
looperThread.start();*/
/* new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
}
});*/
mSinchClient = Sinch.getSinchClientBuilder().context(getApplicationContext()).userId(mUserId)
.applicationKey(APP_KEY)
.applicationSecret(APP_SECRET)
.environmentHost(ENVIRONMENT)
.build();
//Log.d("OnMsg","coming after getSinchServiceInterface()");
mSinchClient.setSupportMessaging(true);
mSinchClient.setSupportCalling(true);
mSinchClient.setSupportManagedPush(true);
mSinchClient.setSupportPushNotifications(true);
mSinchClient.startListeningOnActiveConnection();
/* mSinchClient.startListeningOnActiveConnection();
mSinchClient.setSupportActiveConnectionInBackground(true);*/
// mSinchClient.setSupportActiveConnectionInBackground(true);
// mSinchClient.startListeningOnActiveConnection();
/* mSinchClient.setSupportManagedPush(true);
mSinchClient.setSupportPushNotifications(true);*/
// Log.d("fcm:","fcm token in bytes "+ fcm_token.getBytes());
mSinchClient.checkManifest();
mSinchClient.addSinchClientListener(new MySinchClientListener());
mSinchClient.getCallClient().addCallClientListener(new SinchCallClientListener());
mSinchClient.start();
// mSinchClient.registerPushNotificationData(fcm_token.getBytes());
}
}
class LooperThread extends Thread {
public Handler mHandler;
#Override
public void run() {
// Initialize the current thread as a Looper
// (this thread can have a MessageQueue now)
Looper.prepare();
mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
// process incoming messages here
/* mSinchClient.setSupportMessaging(true);
mSinchClient.setSupportCalling(true);
mSinchClient.setSupportManagedPush(true);
mSinchClient.setSupportPushNotifications(true);
mSinchClient.startListeningOnActiveConnection();
*//* mSinchClient.startListeningOnActiveConnection();
mSinchClient.setSupportActiveConnectionInBackground(true);*//*
// mSinchClient.setSupportActiveConnectionInBackground(true);
// mSinchClient.startListeningOnActiveConnection();
*//* mSinchClient.setSupportManagedPush(true);
mSinchClient.setSupportPushNotifications(true);*//*
// Log.d("fcm:","fcm token in bytes "+ fcm_token.getBytes());
mSinchClient.checkManifest();
mSinchClient.addSinchClientListener(new MySinchClientListener());
mSinchClient.getCallClient().addCallClientListener(new SinchCallClientListener());
mSinchClient.start();*/
}
};
// Run the message queue in this thread
Looper.loop();
}
}
private void stop() {
if (mSinchClient != null) {
mSinchClient.terminate();
mSinchClient = null;
}
}
private boolean isStarted() {
return (mSinchClient != null && mSinchClient.isStarted());
}
#Override
public IBinder onBind(Intent intent) {
return mSinchServiceInterface;
}
public class SinchServiceInterface extends Binder {
public Call callUserVideo(String userId) {
return mSinchClient.getCallClient().callUserVideo(userId);
}
public String getUserName() {
return mUserId;
}
public boolean isStarted() {
return SinchService.this.isStarted();
}
public void startClient(String userName,String token) {
start(userName,token);
}
public void stopClient() {
stop();
}
public void setStartListener(StartFailedListener listener) {
mListener = listener;
}
public Call getCall(String callId) {
return mSinchClient.getCallClient().getCall(callId);
}
public VideoController getVideoController() {
if (!isStarted()) {
return null;
}
return mSinchClient.getVideoController();
}
public AudioController getAudioController() {
if (!isStarted()) {
return null;
}
return mSinchClient.getAudioController();
}
}
public interface StartFailedListener {
void onStartFailed(SinchError error);
void onStarted();
}
private class MySinchClientListener implements SinchClientListener {
#Override
public void onClientFailed(SinchClient client, SinchError error) {
if (mListener != null) {
mListener.onStartFailed(error);
}
/* mSinchClient.terminate();
mSinchClient = null;*/
}
#Override
public void onClientStarted(SinchClient client) {
Log.d(TAG, "SinchClient started");
if (mListener != null) {
mListener.onStarted();
mSinchClient.startListeningOnActiveConnection();
}
}
#Override
public void onClientStopped(SinchClient client) {
Log.d(TAG, "SinchClient stopped");
}
#Override
public void onLogMessage(int level, String area, String message) {
switch (level) {
case Log.DEBUG:
Log.d(area, message);
break;
case Log.ERROR:
Log.e(area, message);
break;
case Log.INFO:
Log.i(area, message);
break;
case Log.VERBOSE:
Log.v(area, message);
break;
case Log.WARN:
Log.w(area, message);
break;
}
}
#Override
public void onRegistrationCredentialsRequired(SinchClient client,
ClientRegistration clientRegistration) {
}
}
public class SinchCallClientListener implements CallClientListener {
#Override
public void onIncomingCall(CallClient callClient, Call call) {
Log.d("OnMsg", "Incoming call");
Intent intent = new Intent(SinchService.this, IncomingCallScreenActivity.class);
intent.putExtra(CALL_ID, call.getCallId());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
SinchService.this.startActivity(intent);
}
}
}
public class PlaceCallActivity extends BaseActivity implements SinchService.StartFailedListener/*,PushTokenRegistrationCallback*/{
private Button mCallButton;
private EditText mCallName;
Button stopButton;
String token;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//initializing UI elements
mCallName = findViewById(R.id.callName);
mCallButton = findViewById(R.id.callButton);
mCallButton.setEnabled(false);
mCallButton.setOnClickListener(buttonClickListener);
stopButton = findViewById(R.id.stopButton);
stopButton.setEnabled(false);
FirebaseInstanceId.getInstance().getInstanceId()
.addOnCompleteListener(new OnCompleteListener<InstanceIdResult>() {
#Override
public void onComplete(#NonNull Task<InstanceIdResult> task) {
if (!task.isSuccessful()) {
Log.w("test", "getInstanceId failed", task.getException());
return;
}
// Get new Instance ID token
token = task.getResult().getToken();
// Log and toast
//String msg = getString(R.string.msg_token_fmt, token);
Log.d("test", token);
Toast.makeText(PlaceCallActivity.this, token, Toast.LENGTH_SHORT).show();
}
});
stopButton.setOnClickListener(buttonClickListener);
/* try {
#SuppressLint("WrongThread") String regId = FirebaseInstanceId.getInstance().getToken("Your-Sender-ID", "FCM");
} catch (IOException e) {
e.printStackTrace();
}*/
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
getSinchServiceInterface().startClient(new PreferenceUtils(PlaceCallActivity.this).getName(),token);
}
}, 4000);
}
// invoked when the connection with SinchServer is established
#Override
protected void onServiceConnected() {
TextView userName = (TextView) findViewById(R.id.loggedInName);
getSinchServiceInterface().setStartListener(this);
userName.setText(new PreferenceUtils(PlaceCallActivity.this).getName());
mCallButton.setEnabled(true);
stopButton.setEnabled(true);
}
/* #Override
public void onDestroy() {
if (getSinchServiceInterface() != null) {
getSinchServiceInterface().stopClient();
}
super.onDestroy();
}*/
/*//to kill the current session of SinchService
private void stopButtonClicked() {
if (getSinchServiceInterface() != null) {
getSinchServiceInterface().stopClient();
}
finish();
}*/
//to place the call to the entered name
private void callButtonClicked() {
String userName = mCallName.getText().toString();
if (userName.isEmpty()) {
Toast.makeText(this, "Please enter a user to call", Toast.LENGTH_LONG).show();
return;
}
Call call = getSinchServiceInterface().callUserVideo(userName);
String callId = call.getCallId();
Log.d("test","call id is"+callId);
Intent callScreen = new Intent(this, CallScreenActivity.class);
callScreen.putExtra(SinchService.CALL_ID, callId);
callScreen.putExtra("TOKEN", token);
startActivity(callScreen);
}
private OnClickListener buttonClickListener = new OnClickListener() {
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.callButton:
callButtonClicked();
break;
case R.id.stopButton:
// stopButtonClicked();
break;
}
}
};
#Override
public void onStartFailed(SinchError error) {
Toast.makeText(this, error.toString(), Toast.LENGTH_LONG).show();
}
#Override
public void onStarted() {
}
}
I have to schedule a work to fetch user current location and update to server in a given interval (Even the app is not running).
I am trying to WorkManagerAPI to implement the functionality.
Is it possible to fetch the current location of the user from the doWork() method ?
locationManager.requestLocationUpdates(
provider, timeInterval, travelDistance, locationListener
);
When I request Location updates from the doWork() it throws below error.
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
As per my understanding, when implementing LocationManager.requestLocationUpdates() on a Worker thread, the call is being made on a non-UI, background thread created by WorkManager. LocationManager.requestLocationUpdates() is an asynchronous call possibly on another background thread. To handle the callbacks defined by the LocationListener, the calling thread must stay alive. Thats why the exception says,
Can't create handler inside thread that has not called Looper.prepare()
Check the code snippet below. Please consider this as pseudocode, I haven't tested this piece of code.
public class LocationWorker extends Worker {
String LOG_TAG = "LocationWorker";
private Context mContext;
private MyHandlerThread mHandlerThread;
public LocationWorker(#NonNull Context context, #NonNull WorkerParameters workerParams) {
super(context, workerParams);
mContext = context;
}
#NonNull
#Override
public Result doWork() {
Log.d(LOG_TAG, "doWork");
mHandlerThread = new MyHandlerThread("MY_THREAD");
mHandlerThread.start();
Runnable runnable = new Runnable() {
#Override
public void run() {
LocationManager locationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
String bestProvider = locationManager.getBestProvider(new Criteria(), true);
boolean permission = false;
if (PermissionChecker.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED &&
PermissionChecker.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
Log.e(LOG_TAG, "This app requires ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION permissions.");
permission = true;
}
Log.d(LOG_TAG, "permission: "+permission);
Log.d(LOG_TAG, "bestProvider: "+bestProvider);
if (permission && bestProvider != null) {
MyLocationListener locListener = new MyLocationListener();
locationManager.requestLocationUpdates(bestProvider, 500, 1, locListener, mHandlerThread.getLooper());
}
}
};
mHandlerThread.post(runnable);
return Result.success();
}
class MyHandlerThread extends HandlerThread {
Handler mHandler;
MyHandlerThread(String name) {
super(name);
}
#Override
protected void onLooperPrepared() {
Looper looper = getLooper();
if (looper != null)
mHandler = new Handler(looper);
}
void post(Runnable runnable) {
if (mHandler != null)
mHandler.post(runnable);
}
}
class MyLocationListener implements LocationListener
{
#Override
public void onLocationChanged(final Location loc)
{
Log.d(LOG_TAG, "Location changed: " + loc.getLatitude() +","+ loc.getLongitude());
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras)
{
Log.d(LOG_TAG, "onStatusChanged");
}
#Override
public void onProviderDisabled(String provider)
{
Log.d(LOG_TAG, "onProviderDisabled");
}
#Override
public void onProviderEnabled(String provider)
{
Log.d(LOG_TAG, "onProviderEnabled");
}
}
}
I have been calling this API long enough. But now, volley cannot read my URL string completely using the same code as before, except that I put sendmsgnow() function in my adapter. It's working when I put that URL in my browser.
P.s:-I have changed the password as it's a paid sms gateway.
The error:
//Adapter.java
public class Adapter extends RecyclerView.Adapter<Adapter.Viewholder> {
String contactdonor;
private static final String URL = "http://Lifetimesms.com/plain?username=opvg&password=mypassword&to=03365297078&from=ComapnyName&message=uyy kugtyf rtd ydrtjyty tku.";
private ArrayList<UserInformation> list;
private Context ctx;
public Adapter(ArrayList<UserInformation> list, Context ctx) {
this.list = list;
this.ctx = ctx;
}
#Override
public Viewholder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_user_layout, parent, false);
Viewholder myHolder = new Viewholder(view);
return myHolder;
}
#Override
public void onBindViewHolder(Viewholder holder, int position) {
final UserInformation val = list.get(position);
holder.edt_contact_no.setText(val.getContact_no());
holder.edt_blood_group.setText(val.getBlood_group());
holder.btn_approve.setText("approve");
holder.btn_decline.setText("decline");
holder.btn_approve.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(ctx, "bloodgrp got= " + val.getBlood_group(), Toast.LENGTH_SHORT).show();
sendmsg(val);
}
});
holder.btn_decline.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//code to delete data
FirebaseDatabase firebaseDatabase = FirebaseDatabase.getInstance();
DatabaseReference reference = firebaseDatabase.getReference("Patients");
reference.child(val.getContact_no()).removeValue();
}
});
}
public void sendmsg(final UserInformation val) {
//Toast.makeText(ctx, "sendmsg called", Toast.LENGTH_SHORT).show();
//Toast.makeText(ctx, "blood group received" + val.getBlood_group(), Toast.LENGTH_SHORT).show();
FirebaseDatabase firebaseDatabase = FirebaseDatabase.getInstance();
DatabaseReference reference = firebaseDatabase.getReference("Donors");
Query query = reference.orderByChild("blood_group_donor").equalTo(val.getBlood_group());
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
DonorInformation donor = null;
for (DataSnapshot data : dataSnapshot.getChildren()) {
donor = data.getValue(DonorInformation.class);
contactdonor = donor.getContact_no_donor();
Toast.makeText(ctx, "contactno is " + contactdonor, Toast.LENGTH_SHORT).show();
nowsendtxt();
}
// Toast.makeText(ctx, "contactno selected 0is= " + contact, Toast.LENGTH_SHORT).show();
}
private void nowsendtxt() {
//Toast.makeText(ctx, "contactno received" + contactdonor, Toast.LENGTH_SHORT).show();
StringRequest request = new StringRequest(URL, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
Toast.makeText(ctx, "successful", Toast.LENGTH_SHORT).show();
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(ctx, "error", Toast.LENGTH_SHORT).show();
}
}//
);
RequestQueue requestQueue = Volley.newRequestQueue(ctx);
requestQueue.add(request);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
/*public void nowsendtxt(String contactdonor,UserInformation p )
{
Toast.makeText(ctx, "contactno received" + contactdonor, Toast.LENGTH_SHORT).show();
UserInformation patient=p;
String patient_name,bloodgrp_common;
patient_name=patient.getPatient_name();
bloodgrp_common=patient.getBlood_group();
StringRequest request = new StringRequest(URL, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
Toast.makeText(ctx, "successful", Toast.LENGTH_SHORT).show();
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(ctx, "error", Toast.LENGTH_SHORT).show();
}
}
);
RequestQueue requestQueue = Volley.newRequestQueue(ctx);
requestQueue.add(request);
}*/
#Override
public int getItemCount() {
return list.size();
}
public static class Viewholder extends RecyclerView.ViewHolder {
EditText edt_blood_group;
EditText edt_contact_no;
Button btn_approve, btn_decline;
public Viewholder(View itemView) {
super(itemView);
edt_blood_group = (EditText) itemView.findViewById(R.id.edt_blood_group);
edt_contact_no = (EditText) itemView.findViewById(R.id.edt_contact_no);
btn_approve = (Button) itemView.findViewById(R.id.btn_approve);
btn_decline = (Button) itemView.findViewById(R.id.btn_decline);
}
}
}
I have implemented simple RxEventBus which starts emitting events, even if there is no subscribers. I want to cache last emitted event, so that if first/next subscriber subscribes, it receive only one (last) item.
I created test class which describes my problem:
public class RxBus {
ApplicationsRxEventBus applicationsRxEventBus;
public RxBus() {
applicationsRxEventBus = new ApplicationsRxEventBus();
}
public static void main(String[] args) {
RxBus rxBus = new RxBus();
rxBus.start();
}
private void start() {
ExecutorService executorService = Executors.newScheduledThreadPool(2);
Runnable runnable0 = () -> {
while (true) {
long currentTime = System.currentTimeMillis();
System.out.println("emiting: " + currentTime);
applicationsRxEventBus.emit(new ApplicationsEvent(currentTime));
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Runnable runnable1 = () -> applicationsRxEventBus
.getBus()
.subscribe(new Subscriber<ApplicationsEvent>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable throwable) {
}
#Override
public void onNext(ApplicationsEvent applicationsEvent) {
System.out.println("runnable 1: " + applicationsEvent.number);
}
});
Runnable runnable2 = () -> applicationsRxEventBus
.getBus()
.subscribe(new Subscriber<ApplicationsEvent>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable throwable) {
}
#Override
public void onNext(ApplicationsEvent applicationsEvent) {
System.out.println("runnable 2: " + applicationsEvent.number);
}
});
executorService.execute(runnable0);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
executorService.execute(runnable1);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
executorService.execute(runnable2);
}
private class ApplicationsRxEventBus {
private final Subject<ApplicationsEvent, ApplicationsEvent> mRxBus;
private final Observable<ApplicationsEvent> mBusObservable;
public ApplicationsRxEventBus() {
mRxBus = new SerializedSubject<>(BehaviorSubject.<ApplicationsEvent>create());
mBusObservable = mRxBus.cache();
}
public void emit(ApplicationsEvent event) {
mRxBus.onNext(event);
}
public Observable<ApplicationsEvent> getBus() {
return mBusObservable;
}
}
private class ApplicationsEvent {
long number;
public ApplicationsEvent(long number) {
this.number = number;
}
}
}
runnable0 is emitting events even if there is no subscribers. runnable1 subscribes after 3 sec, and receives last item (and this is ok). But runnable2 subscribes after 3 sec after runnable1, and receives all items, which runnable1 received. I only need last item to be received for runnable2. I have tried cache events in RxBus:
private class ApplicationsRxEventBus {
private final Subject<ApplicationsEvent, ApplicationsEvent> mRxBus;
private final Observable<ApplicationsEvent> mBusObservable;
private ApplicationsEvent event;
public ApplicationsRxEventBus() {
mRxBus = new SerializedSubject<>(BehaviorSubject.<ApplicationsEvent>create());
mBusObservable = mRxBus;
}
public void emit(ApplicationsEvent event) {
this.event = event;
mRxBus.onNext(event);
}
public Observable<ApplicationsEvent> getBus() {
return mBusObservable.doOnSubscribe(() -> emit(event));
}
}
But problem is, that when runnable2 subscribes, runnable1 receives event twice:
emiting: 1447183225122
runnable 1: 1447183225122
runnable 1: 1447183225122
runnable 2: 1447183225122
emiting: 1447183225627
runnable 1: 1447183225627
runnable 2: 1447183225627
I am sure, that there is RxJava operator for this. How to achieve this?
Your ApplicationsRxEventBus does extra work by reemitting a stored event whenever one Subscribes in addition to all the cached events.
You only need a single BehaviorSubject + toSerialized as it will hold onto the very last event and re-emit it to Subscribers by itself.
You are using the wrong interface. When you susbscribe to a cold Observable you get all of its events. You need to turn it into hot Observable first. This is done by creating a ConnectableObservable from your Observable using its publish method. Your Observers then call connect to start receiving events.
You can also read more about in the Hot and Cold observables section of the tutorial.