I am developing a android app, which will update device location after 4 seconds interval and depending on the response received from the server it will open specific activity.
Problem 1) In some case it will open up a activity like incoming phone call with sound. I am facing problem when I am removing the app from recent app. I noticed the poll function is running twice at the same time, and multiple media is playing at the same time.
Problem 2) I am using Service intead of IntentService(I am a beginner and not sure which will be better). The background service should run even the phone goes to sleep mode, just like WhatsApp or other messenger run.
As the file is big enough, I am attaching only important part
public class TaxiNorrService extends Service implements LocationListener {
...
...
final Handler poll_handler = new Handler();
private NotificationManager mNM;
private final Actions actions = new Actions();
public Ringtone r;
private String newtext;
private Runnable BreakRunnable;
private Runnable poll_runnable;
private Handler BreakHandler;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
poll_runnable = new Runnable() {
#Override
public void run() {
if(!App.isAutoBreak()){
if(BreakHandler !=null){
BreakHandler.removeCallbacks(BreakRunnable);
}
if(r != null) {
if (r.isPlaying()) {
r.stop();
}
}
}
if (actions.checkPermission(getApplicationContext())) {
checkGPS();
if(isNetworkAvailable()){
if(App.isPollOn()){
poll(latitude, longitude);
}
}else{
if(BreakHandler !=null){
BreakHandler.removeCallbacks(BreakRunnable);
}
boolean foregroud = false;
try {
foregroud = new ForegroundCheckTask().execute(getApplication()).get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
boolean background = isMyServiceRunning(TaxiNorrService.class);
if(foregroud == true && background == true && App.isAppForground()){
if(!App.isLoadingVisible()){
Intent intent = new Intent(TaxiNorrService.this, Loading_activity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}
}
}
poll_handler.postDelayed(this, 4000);
}
};
return Service.START_STICKY;
}
private void poll(double lat, double lon){
//Connected to API endpoint
}
...
...
#Override
public void onDestroy() {
if(r != null) {
if (r.isPlaying()) {
r.stop();
}
}
poll_handler.removeCallbacks(poll_runnable);
super.onDestroy();
}
}
I found the answer for my questions. The code written in the onStartCommand should be within onCreate function. This is because onCreate will execute when service starts first time, and onStartCommand will execute every time when you start the app. Please follow this topic,
Android - running a method periodically using postDelayed() call
Not sure how should I phrase it, but In my app sometimes a single button click generates multiple events and hence it ends up sending multiple similar transactions to the server which is causing data integrity problem. Here is the code flow, Do note that this code runs on users phone which may have weaker connections at times, hence I store the data in SQLite and there is a separate sync service which sends data (fetching from SQLite tables) to Server when Internet is connected.
This is where the click is captured and event is posted for Main Activity (Landing Page)
#OnClick(R.id.btn_add_attempt)
public void onAttemptClick() {
try{
btnAddAttempt.setEnabled(false);
EventBus.getDefault().post(new ExampleActionEvent());
moveToHomePage();
} catch (Exception e){
e.printStackTrace();
Crashlytics.logException(e);
}
}
Here is the Event subscription from my main Activity.
#Subscribe()
public void onExampleAction(ExampleActionEvent exampleActionEvent)
{
if(application == null) application = (ExApplication) getApplication();
final Thread thread = new Thread(new Runnable() {
#Override
public void run() {
ArrayList<Something> somethings = application.getArray().getsomethings();
if (!ExUtils.validatesomethings(somethings)){
runOnUiThread(new Runnable() {
#Override
public void run() {
RiderUtils.showSimplePopup(
MainLandingActivity.this,
getString(R.string.title_alert),
getString(R.string.alert_message),
getString(R.string.done),
null,
false,
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
showPendingSomething();
ExUtils.moveToPending(MainLandingActivity.this);
}
},
null,
false);
}
});
return;
}
status = application.getArray().getStatus();
imageLocalPath = application.getArray().getsomethings().get(0).getPhotoPath();
Log.d(Const.TAG, "image path is: "+imageLocalPath);
try {
ExUtils.pushExampleActionEvent(application.getArray(), MainLandingActivity.this, xyz, abc);
} catch (Exception e) {
e.printStackTrace();
}
FileWriteEvent fileWriteEvent = null;
String currentTimestamp = application.getArray().getTimestamp();
switch (status) {
case Const.UPDATE_SUCCESS:
fileWriteEvent = new FileWriteEvent(Const.EVENT_CSV_FILE_SUCCESS_VALUE, application.getArray(), "",
cachedLocation, currentTimestamp, null);
break;
case Const.UPDATE_ATTEMPTED:
fileWriteEvent = new FileWriteEvent(Const.EVENT_CSV_FILE_ATTEMPTED_VALUE, riderApplication.getBulkArray(), "",
cachedLocation, currentTimestamp, null);
ExUtils.showSnackbar(holder, getString(R.string.Example), Snackbar.LENGTH_LONG);
break;
}
if(fileWriteEvent != null)
{
EventBus.getDefault().post(fileWriteEvent);
}
createAll(); //SQLite DB call for Inserting some data
runOnUiThread(new Runnable() {
#Override
public void run() {
try {
switch (status) {
case Const.UPDATE_SUCCESS:
RiderUtils.showSnackbar(holder, getString(R.string.Success), Snackbar.LENGTH_LONG);
break;
case Const.UPDATE_ATTEMPTED:
RiderUtils.showSnackbar(holder, getString(R.string.Failed), Snackbar.LENGTH_LONG);
break;
}
}catch (Exception e) {
e.printStackTrace();
}
}
});
}
});
thread.start();
}
I am not able to figure out why is it misbehaving sometimes randomly otherwise it runs smoothly, Hence I am not able to regenrate the issue while debugging.
So after sometimes I looked at the problem again and found out the developer who worked before me had not declared the Main Activity as SingleInstance and hence Every time if the app was opened from Notification Drawer as we were showing it in Notification drawer if app is running, then it was creating a new Activity hence registering on EventBus which was causing the issue by creating duplicate events.
I need my app to wait on the loading screen so that a connection to the server may be made, and then enter the users saved credentials automatically using SaveSharedPreference and then move to the next page in MainActivity.
The connection is made through a service with a call in the onCreate blocks with:
startService(new Intent(LoggingIn.this, MessagingService.class));
and the users credentials are taken from SaveSharedPreference and sent to the server within try/catch blocks as:
result = imService.authenticateUser(
SaveSharedPreference
.getUserName(getApplicationContext()),
SaveSharedPreference
.getPassword(getApplicationContext()));
At present, the app simply boots up and then crashes. I tried using Thread.sleep(500) but that simply shuts the app down, and posts this error:
How can I get the functionality as outlined above and make my app wait for a connection to the server prior to sending authenticateUser
Wait for the successful logging in with authenticateUser before my app tries to launch MainActivity?
setContentView(R.layout.loading_splash_page);
// If already logged in, pull the information and send to server
// to
// auto log in
Thread loginThread = new Thread() {
private Handler handler = new Handler();
#Override
public void run() {
// String result = null;
try {
result = imService.authenticateUser(
SaveSharedPreference
.getUserName(getApplicationContext()),
SaveSharedPreference
.getPassword(getApplicationContext()));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
if (result == null || result.equals(AUTHENTICATION_FAILED)) {
/*
* Authenticatin failed, inform the user
*/
handler.post(new Runnable() {
public void run() {
Toast.makeText(
getApplicationContext(),
R.string.make_sure_username_and_password_correct,
Toast.LENGTH_LONG).show();
// showDialog(MAKE_SURE_USERNAME_AND_PASSWORD_CORRECT);
}
});
} else {
/*
* if result not equal to authentication failed, result
* is equal to friend and group list of the user 0: is
* for friends, 1: is for groups
*/
handler.post(new Runnable() {
public void run() {
Intent i = new Intent(LoggingIn.this,
MainActivity.class);
startActivity(i);
LoggingIn.this.finish();
}
});
}
}
};
loginThread.start();
ADDITION The service merhods for imService:
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with the service has been
// established, giving us the service object we can use to
// interact with the service. Because we have bound to a explicit
// service that we know is running in our own process, we can
// cast its IBinder to a concrete class and directly access it.
imService = ((MessagingService.IMBinder) service).getService();
if (imService.isUserAuthenticated() == true) {
// Intent i = new Intent(LoggingIn.this, ListOfFriends.class);
Intent i = new Intent(LoggingIn.this, MainActivity.class);
startActivity(i);
LoggingIn.this.finish();
}
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
// Because it is running in our same process, we should never
// see this happen.
imService = null;
Toast.makeText(LoggingIn.this, R.string.local_service_stopped,
Toast.LENGTH_SHORT).show();
}
};
I have created a Login activity which uses another class - LoginService which is an AsyncTask for the network communication.
public void onClick(View view) {
if (editTextPassword.getText().toString() != null & editTextUsername.getText().toString() != null){
new LoginService(editTextUsername.getText().toString(), editTextPassword.getText().toString()).execute();
if(loginSuccess!=false){
//Used to move to the Cases Activity
Intent casesActivity = new Intent(getApplicationContext(), CasesActivity.class);
startActivity(casesActivity);
}else{
Toast.makeText(getApplicationContext(),"Incorrect Details", Toast.LENGTH_LONG).show();
}
}
else{
//Display Toaster for error
Toast.makeText(getApplicationContext(),"Please enter your details", Toast.LENGTH_LONG).show();
}
}
Before the LoginService has finished executing, the activity has already moved to another activity via the Intent variable. I do not understand why. The idea of the LoginService is to validate the credentials of the user. If it returns true, then it can switch to the other activity.
You do not want to do this in this way. The .execute() will begin as soon as possible, but there is no guarantee (and perhaps guaranteed not to) that it will get your loginSuccess value back to you in time.
Everything after new LoginService(...).execute(); should be moved into onPostExecute():
private Context mContext = null;
public void setContext(Context context) {
mContext = context;
}
#Override
protected void onPostExecute(Void result) {
if(loginSuccess!=false){
//Used to move to the Cases Activity
Intent casesActivity = new Intent(mContext, CasesActivity.class);
startActivity(casesActivity);
}else{
Toast.makeText(mContext,"Incorrect Details", Toast.LENGTH_LONG).show();
}
}
Then, you have to call setContext() like so:
LoginService service = new LoginService(editTextUsername.getText().toString(), editTextPassword.getText().toString());
service.setContext(getApplicationContext());
service.execute();
You should move
Intent casesActivity = new Intent(getApplicationContext(), CasesActivity.class);
startActivity(casesActivity);
}else{
Toast.makeText(getApplicationContext(),"Incorrect Details", Toast.LENGTH_LONG).show();
}
into LoginService's onPostExecute.
In this way you are sure the asynctask has finished its work.
In any case it's quite strange the other activity gets started, it might be because of an old assignement of loginSuccess to true
How to return the result from the asynctask?
Catch the result of AsyncTask from onPostExecute().
#Override
public void onPostExecute(Boolean result)
{
boolean loginSuccess = result;
if(loginSuccess!=false) {
Intent casesActivity = new Intent(getApplicationContext(), CasesActivity.class);
startActivity(casesActivity);
}
else {
Toast.makeText(getApplicationContext(),"Incorrect Details", Toast.LENGTH_LONG).show();
}
}
The data type of result in AsyncTask depends on the 3rd Type parameter.
Sometimes we think execute() method of AsyncTask will return a result which is wrong. It will return an AsyncTask itself
I have several SeekBar and onSeekBarProgressStop(), I want to show a Toast message.
But if on SeekBar I perform the action rapidly then UI thread somehow blocks and Toast message waits till UI thread is free.
Now my concern is to avoid the new Toast message if the Toast message is already displaying. Or is their any condition by which we check that UI thread is currently free then I'll show the Toast message.
I tried it in both way, by using runOnUIThread() and also creating new Handler.
I've tried a variety of things to do this. At first I tried using the cancel(), which had no effect for me (see also this answer).
With setDuration(n) I wasn't coming to anywhere either. It turned out by logging getDuration() that it carries a value of 0 (if makeText()'s parameter was Toast.LENGTH_SHORT) or 1 (if makeText()'s parameter was Toast.LENGTH_LONG).
Finally I tried to check if the toast's view isShown(). Of course it isn't if no toast is shown, but even more, it returns a fatal error in this case. So I needed to try and catch the error.
Now, isShown() returns true if a toast is displayed.
Utilizing isShown() I came up with the method:
/**
* <strong>public void showAToast (String st)</strong></br>
* this little method displays a toast on the screen.</br>
* it checks if a toast is currently visible</br>
* if so </br>
* ... it "sets" the new text</br>
* else</br>
* ... it "makes" the new text</br>
* and "shows" either or
* #param st the string to be toasted
*/
public void showAToast (String st){ //"Toast toast" is declared in the class
try{ toast.getView().isShown(); // true if visible
toast.setText(st);
} catch (Exception e) { // invisible if exception
toast = Toast.makeText(theContext, st, toastDuration);
}
toast.show(); //finally display it
}
The following is an alternative solution to the most popular answer, without the try/catch.
public void showAToast (String message){
if (mToast != null) {
mToast.cancel();
}
mToast = Toast.makeText(this, message, Toast.LENGTH_SHORT);
mToast.show();
}
A clean solution that works out of the box. Define this on your Activity:
private Toast toast;
/**
* Use this to prevent multiple Toasts from spamming the UI for a long time.
*/
public void showToast(CharSequence text, int duration)
{
if (toast == null)
toast = Toast.makeText(this, text, duration);
else
toast.setText(text);
toast.show();
}
public void showToast(int resId, int duration)
{
showToast(getResources().getText(resId), duration);
}
My solution is:
public class Utils {
public static Toast showToast(Context context, Toast toast, String str) {
if (toast != null)
toast.cancel();
Toast t = Toast.makeText(context, str, Toast.LENGTH_SHORT);
t.show();
return t;
}
}
and the caller should have a Toast member for this method's parameter, or
class EasyToast {
Toast toast;
Context context;
public EasyToast(Context context) {
this.context = context;
}
public Toast show(String str) {
if (toast != null)
toast.cancel();
Toast t = Toast.makeText(context, str, Toast.LENGTH_SHORT);
t.show();
return t;
}
}
have a helper class like this.
keep track of the last time you showed the toast, and make re-showing it a no-op if it falls within some interval.
public class RepeatSafeToast {
private static final int DURATION = 4000;
private static final Map<Object, Long> lastShown = new HashMap<Object, Long>();
private static boolean isRecent(Object obj) {
Long last = lastShown.get(obj);
if (last == null) {
return false;
}
long now = System.currentTimeMillis();
if (last + DURATION < now) {
return false;
}
return true;
}
public static synchronized void show(Context context, int resId) {
if (isRecent(resId)) {
return;
}
Toast.makeText(context, resId, Toast.LENGTH_LONG).show();
lastShown.put(resId, System.currentTimeMillis());
}
public static synchronized void show(Context context, String msg) {
if (isRecent(msg)) {
return;
}
Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
lastShown.put(msg, System.currentTimeMillis());
}
}
and then,
RepeatSafeToast.show(this, "Hello, toast.");
RepeatSafeToast.show(this, "Hello, toast."); // won't be shown
RepeatSafeToast.show(this, "Hello, toast."); // won't be shown
RepeatSafeToast.show(this, "Hello, toast."); // won't be shown
this isn't perfect, since the length of LENGTH_SHORT and LENGTH_LONG are undefined, but it works well in practice. it has the advantage over other solutions that you don't need to hold on to the Toast object and the call syntax remains terse.
The enhanced function from above thread, which will show toast only if not visible with same text message:
public void showSingleToast(){
try{
if(!toast.getView().isShown()) {
toast.show();
}
} catch (Exception exception) {
exception.printStackTrace();
Log.d(TAG,"Toast Exception is "+exception.getLocalizedMessage());
toast = Toast.makeText(this.getActivity(), getContext().getString(R.string.no_search_result_fou`enter code here`nd), Toast.LENGTH_SHORT);
toast.show();
}
}
A combined solution
For my case, I needed to cancel the current toast if it shown and display another one.
This was to solve the scenario when the user asks for a service while it is still loading or not available I need to show a toast (might me different if the requested service is different). Otherwise, the toasts will keep showing in order and it will take a very long time to hide them automatically.
So basically I save the instance of the toast am creating and the following code is how to cancel it safly
synchronized public void cancel() {
if(toast == null) {
Log.d(TAG, "cancel: toast is null (occurs first time only)" );
return;
}
final View view = toast.getView();
if(view == null){
Log.d(TAG, "cancel: view is null");
return;
}
if (view.isShown()) {
toast.cancel();
}else{
Log.d(TAG, "cancel: view is already dismissed");
}
}
And to use it I can now not worry about cancelling as in:
if (toastSingleton != null ) {
toastSingleton.cancel();
toastSingleton.showToast(messageText);
}else{
Log.e(TAG, "setMessageText: toastSingleton is null");
}
The showToast is up to you how to implement it as I needed a custom look for my toast.
Good for stopping stacking e.g. click driven toast. Based off #Addi's answer.
public Toast toast = null;
//....
public void favsDisplay(MenuItem item)
{
if(toast == null) // first time around
{
Context context = getApplicationContext();
CharSequence text = "Some text...";
int duration = Toast.LENGTH_SHORT;
toast = Toast.makeText(context, text, duration);
}
try
{
if(toast.getView().isShown() == false) // if false not showing anymore, then show it
toast.show();
}
catch (Exception e)
{}
}
Check for showing toast message on screen either it is displayed or not.
For Showing a toast message Make a separate class. And use the method of this class which display the toast message after checking the visibility of the toast message. Use This Snippet of code:
public class AppToast {
private static Toast toast;
public static void showToast(Context context, String message) {
try {
if (!toast.getView().isShown()) {
toast=Toast.makeText(context, message, Toast.LENGTH_SHORT);
toast.show();
}
} catch (Exception ex) {
toast=Toast.makeText(context,message,Toast.LENGTH_SHORT);
toast.show();
}
}
}
I hope this solution will help you.
Thanks
added timer to remove the toast after 2 seconds.
private Toast toast;
public void showToast(String text){
try {
toast.getView().isShown();
toast.setText(text);
}catch (Exception e){
toast = Toast.makeText(mContext, text, Toast.LENGTH_SHORT);
}
if(toast.getView().isShown()){
new Timer().schedule(new TimerTask() {
#Override
public void run() {
toast.cancel();
}
}, 2000);
}else{
toast.show();
}
}
showToast("Please wait");
Solution for Android 11+ (API level 30 and above)
Toast.getView() is deprecated since API level 30:
This method was deprecated in API level 30. Custom toast views are
deprecated. Apps can create a standard text toast with the
makeText(android.content.Context, java.lang.CharSequence, int) method,
or use a Snackbar when in the foreground. Starting from Android
Build.VERSION_CODES#R, apps targeting API level Build.VERSION_CODES#R
or higher that are in the background will not have custom toast views
displayed.
If you want to avoid Toast overlapping, you could save the time the last Toast was shown using System.currentTimeMillis().
Here's a an example of use case where Toast is instantly overlapped only if the text of the new one is different from the last one, otherwise, it waits a certain amount of time before overlapping it (i.e SAME_TOAST_DURATION_BEFORE_OVERLAP):
public class SingleToast {
private static Toast _toast;
private static String _text;
private static long _lastToast;
private static final int SAME_TOAST_DURATION_BEFORE_OVERLAP = 2000; // in ms
public static void show(Context context, String text, int duration) {
if (_toast == null) {
_toast = Toast.makeText(context.getApplicationContext(), text, duration);
_text = text;
} else {
if (_text.equals(text)) {
if (System.currentTimeMillis() - _lastToast > SAME_TOAST_DURATION_BEFORE_OVERLAP) {
_toast.cancel();
} else {
return;
}
} else {
_text = text;
_toast.cancel();
_toast.setText(_text);
}
}
_lastToast = System.currentTimeMillis();
_toast.show();
}
}