i'm new in android.
i made an app with service and thread to show a Toast every 5 seconds:
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;
public class MyService extends Service {
#Override
public IBinder onBind(Intent intent) {
return null;
}
Handler mHandler = new Handler();
#Override
public void onCreate() {
super.onCreate();
final Runnable RunnableUpdateResults = new Runnable() {
public void run() {
Toast.makeText(getBaseContext(), "Hello", Toast.LENGTH_SHORT).show();
}
};
new Thread() {
public void run() {
try {
mHandler.postDelayed(RunnableUpdateResults);
sleep(5000);
} catch (InterruptedException e) {e.printStackTrace();}
}
}.start();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
return START_STICKY;
}
public void onStart(final Context context,Intent intent, int startId)
{
}
}
but my Toast shown only once. with no crash.
i used Handler's postDelayed function for this purpose. It will run your code with specified delay on the main UI thread, so you will be able to update UI controls but it dose not work too.
any solution to do a task repetitively ?
call Thread.sleep inside while loop as:
boolean isThreadRunning=true;
new Thread() {
public void run() {
try {
while(isThreadRunning){
mHandler.postDelayed(RunnableUpdateResults);
sleep(5000);
}
} catch (InterruptedException e) {e.printStackTrace();}
}
}.start();
To stop Thread make isThreadRunning=false;
You can achieve same using handler.postDelayed
mHandler.postDelayed(RunnableUpdateResults,5000);
and in RunnableUpdateResults call mHandler.postDelayed :
final Runnable RunnableUpdateResults = new Runnable() {
public void run() {
mHandler.postDelayed(RunnableUpdateResults,5000);
Toast.makeText(getBaseContext(),
"Hello", Toast.LENGTH_SHORT).show();
}
};
To stop Handler call removeCallbacks method:
mHandler.removeCallbacks(RunnableUpdateResults);
Use a Timer with a TimerTask
new Timer().schedule(new TimerTask() {
#Override
public void run() {
mHandler.post(RunnableUpdateResults);
}
}, 0, 5000);
Related
I am trying to create a service class with a inner class which is a Handler class , unfortunately I am not able to access handler.obtainMessage() in this class .. Can any one give suggestions on this ?
Source code for the Service class:
public class MyService extends Service {
private MyHandler myHandler;
private final class MyHandler extends Handler {
public MyHandler(Looper looper) {
super(looper);
}
public void handleMessage(Message msg) {
try {
Thread.sleep(5000);
// use the unique startId so you don't stop the
// service while processing other requests
stopSelfResult(msg.arg1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
#Override
public void close() {
}
#Override
public void flush() {
}
#Override
public void publish(LogRecord record) {
}
}
#Override
public void onCreate() {
Toast.makeText(this, "Service Created", Toast.LENGTH_SHORT).show();
// Create a new HandlerThread with a specified priority
HandlerThread thread = new HandlerThread("MyHandlerThread",Thread.NORM_PRIORITY);
// Start the handler thread so that our Handler queue will start
// processing messages
thread.start();
// Run the handler using the new HandlerThread
myHandler = new MyHandler(thread.getLooper());
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Message msg = myHandler.obtainMessage();
msg.arg1 = startId;
myHandler.sendMessage(msg);
return START_STICKY;
}
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public void onDestroy() {
Toast.makeText(this, "Service Done", Toast.LENGTH_SHORT).show();
}
}
You've got the wrong Handler class imported. It should be android.os.Handler, not java.util.logging.Handler.
i'm basicaly tying to open up a service, that every 10 seconds, will show up a toast to say "10 seconds passed"
this is what i'm trying to do,
and after many research ive found out that to loop a service i'm
going to need to use while (true) - sleep... method...
but the service or my app crashes every time i start the service
(or to be exact every time the timer runs out)
what is my problem ?
my guess is that maybe the contaxt i'm passing to the toast is wrong ?
maybe there is another way to show toast every 10 seconds in loop (inside a serivice) ?
here is my service code >
package com.greenroad.candidate.mywallpaperchanger;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.widget.Toast;
/**
* Created by pitsponet on 31/08/2015.
*/
public class myService extends Service {
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
Toast.makeText(getApplicationContext(), "service created",
Toast.LENGTH_LONG).show();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//try to run loop for showing up a toast
new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
while(true)
{
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//REST OF CODE HERE//
Toast.makeText(getApplicationContext(), "service started",
Toast.LENGTH_LONG).show();
}
}
}).start();
return super.onStartCommand(intent, flags, startId);
}
#Override
public void onDestroy() {
super.onDestroy();
Toast.makeText(getApplicationContext(), "service stoped",
Toast.LENGTH_LONG).show();
}
}
The reason for the crash, as explained by Brad, is because you are trying to perform UI operation from a non-UI Thread.
To achieve what you're trying to do, use the code below in your service. First of all remove your Thread in onStartCommand()
public class MyService extends Service {
private Handler mHandler;
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
mHandler = new Handler();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
mHandler.postDelayed(ToastTask, 10000); // Starts the loop here
super.onStartCommand(intent, flags, startId);
}
#Override
public void onDestroy() {
// Stop the loop
mHandler.removeCallbacks(ToastTask);
super.onDestroy();
}
private Runnable ToastTask = new Runnable() {
#Override
public void run() {
Toast.makeText(MyService.this, "10 Seconds have passed", Toast.LENGTH_SHORT).show();
// Schedule this Runnable to run again after 10 sec
mHandler.postDelayed(this, 10000);
}
}
}
The reason that the service is crashing is because you're trying to run UI tasks (Toasts) outside of the main thread. Since you are creating a secondary thread for the infinite while loop, you'll need to post your Toast calls to the main looper as follows:
final Handler mainHandler = new Handler(getApplicationContext().getMainLooper());
mainHandler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), "Text to display", Toast.LENGTH_LONG).show();
}
});
That being said, I highly discourage using Thread.sleep() in any code that will run on a device, as this could lead to some serious issues. You should be able to accomplish the same thing (and also get rid of the infinite while-loop) using a Timer instead.
To use a Timer, you should be able to do something like the following:
// Schedules a TimerTask to execute every 10 seconds after a 10 second delay.
final Timer timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
// Your Toast code here.
}
}, 10000, 10000);
Here's a complete example:
public class MyService extends Service {
private Handler mainHandler;
private Timer timer;
public void onStartCommand(final Intent intent, final int flags, final int startId) {
mainHandler = new Handler(getApplicationContext().getMainLooper());
timer = new Timer();
timer.schedule(new MyTimerTask(), 10000, 10000);
}
public void onDestroy() {
timer.cancel();
}
private class MyTimerTask extends TimerTask {
#Override
public void run() {
mainHandler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), "Text to display", Toast.LENGTH_LONG).show();
}
});
}
}
}
Background:
I've created a custom service as:
public class FloatingViewService extends Service {
public static FloatingViewService self;
onCreate() {
self = this;
addView(....)
}
...
...
public void updateText ( String newText) { this.textView.setText(newText) };
}
OnCreate event of this service, it sets a view using WindowManager.addView(...) and also set an instance pointer in self variable for future use.
Now this view is just a textview, that stays on the top of activities, regardless.
What I want to achieve:
I want to send some data from a static method that runs using ExecutorService instance, which should update textview text.
How I use this service:
Inside of an activity, I make a call to a static method that logs some values:
public class MyActivity: Activity
{
public void log() {
LogUtil.log(new Runnable() {
#Override
public void run() {
//log api call
FloatingViewService.self.updateText("New Text");
}
}) ;
}
}
Now you can see that I am making a call to an updateText method present in service, from different thread.
Here is how the LogUtil is:
public class LogUtil {
private static ExecutorService taskExecutorService = ThreadUtils.createTimedExecutorService(TASK_POOL_SIZE, TASK_POOL_IDLE_ALIVE_SECONDS,
TimeUnit.SECONDS, new LowPriorityThreadFactory());
public static log(Runnable runnable) {
taskExecutorService.submit(new Runnable() {
#Override
public void run() {
try {
runnable.run();
} catch (Exception ex) {
../
}
}
});
Now the problem is, it cannot update textview text. I can understand it is due to thread. But I have no clue on how to achieve it - is there any UIthread for service ?
Here is my code for example .. you shuold be able to pick the necesary parts from it. as Selvin said you have to create an Incoming handler on both sides to send information from one thread to the other...
Here is my service code
import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;
import com.pekam.myandroidtheme.*;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
public class MyService extends Service {
private NotificationManager nm;
private Timer timer = new Timer();
private int counter = 0, incrementby = 1;
private static boolean isRunning = false;
ArrayList<Messenger> mClients = new ArrayList<Messenger>(); // Keeps track of all current registered clients.
int mValue = 0; // Holds last value set by a client.
static final int MSG_REGISTER_CLIENT = 1;
static final int MSG_UNREGISTER_CLIENT = 2;
static final int MSG_SET_INT_VALUE = 3;
static final int MSG_SET_STRING_VALUE = 4;
final Messenger mMessenger = new Messenger(new IncomingHandler()); // Target we publish for clients to send messages to IncomingHandler.
#Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
class IncomingHandler extends Handler { // Handler of incoming messages from clients.
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REGISTER_CLIENT:
mClients.add(msg.replyTo);
break;
case MSG_UNREGISTER_CLIENT:
mClients.remove(msg.replyTo);
break;
case MSG_SET_INT_VALUE:
incrementby = msg.arg1;
break;
default:
super.handleMessage(msg);
}
}
}
private void sendMessageToUI(int intvaluetosend) {
for (int i=mClients.size()-1; i>=0; i--) {
try {
// Send data as an Integer
mClients.get(i).send(Message.obtain(null, MSG_SET_INT_VALUE, intvaluetosend, 0));
//Send data as a String
Bundle b = new Bundle();
b.putString("str1", "ab" + intvaluetosend + "cd");
Message msg = Message.obtain(null, MSG_SET_STRING_VALUE);
msg.setData(b);
mClients.get(i).send(msg);
} catch (RemoteException e) {
// The client is dead. Remove it from the list; we are going through the list from back to front so this is safe to do inside the loop.
mClients.remove(i);
}
}
}
#Override
public void onCreate() {
super.onCreate();
Log.i("MyService", "Service Started.");
showNotification();
timer.scheduleAtFixedRate(new TimerTask(){ public void run() {onTimerTick();}}, 0, 100L);
isRunning = true;
}
private void showNotification() {
nm = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
// In this sample, we'll use the same text for the ticker and the expanded notification
CharSequence text = getText(R.string.service_started);
// Set the icon, scrolling text and timestamp
Notification notification = new Notification(R.drawable.ic_launcher, text, System.currentTimeMillis());
// The PendingIntent to launch our activity if the user selects this notification
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, TabBarActivity.class), 0);
// Set the info for the views that show in the notification panel.
notification.setLatestEventInfo(this, getText(R.string.service_label), text, contentIntent);
// Send the notification.
// We use a layout id because it is a unique number. We use it later to cancel.
nm.notify(R.string.service_started, notification);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("MyService", "Received start id " + startId + ": " + intent);
return START_STICKY; // run until explicitly stopped.
}
public static boolean isRunning()
{
return isRunning;
}
private void onTimerTick() {
Log.i("TimerTick", "Timer doing work." + counter);
try {
counter += incrementby;
sendMessageToUI(counter);
} catch (Throwable t) { //you should always ultimately catch all exceptions in timer tasks.
Log.e("TimerTick", "Timer Tick Failed.", t);
}
}
#Override
public void onDestroy() {
super.onDestroy();
if (timer != null) {timer.cancel();}
counter=0;
nm.cancel(R.string.service_started); // Cancel the persistent notification.
Log.i("MyService", "Service Stopped.");
isRunning = false;
}
}
here is my android form app code
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.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import com.pekam.myandroidtheme.*;
public class MyServiceControllerActivity extends Activity {
Button btnStart, btnStop, btnBind, btnUnbind, btnUpby1, btnUpby10;
TextView textStatus, textIntValue, textStrValue;
Messenger mService = null;
boolean mIsBound;
final Messenger mMessenger = new Messenger(new IncomingHandler());
class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MyService.MSG_SET_INT_VALUE:
textIntValue.setText("Int Message: " + msg.arg1);
break;
case MyService.MSG_SET_STRING_VALUE:
String str1 = msg.getData().getString("str1");
textStrValue.setText("Str Message: " + str1);
break;
default:
super.handleMessage(msg);
}
}
}
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mService = new Messenger(service);
textStatus.setText("Attached.");
try {
Message msg = Message.obtain(null, MyService.MSG_REGISTER_CLIENT);
msg.replyTo = mMessenger;
mService.send(msg);
} catch (RemoteException e) {
// In this case the service has crashed before we could even do anything with it
}
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been unexpectedly disconnected - process crashed.
mService = null;
textStatus.setText("Disconnected.");
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.exampleservice);
btnStart = (Button)findViewById(R.id.btnStart);
btnStop = (Button)findViewById(R.id.btnStop);
btnBind = (Button)findViewById(R.id.btnBind);
btnUnbind = (Button)findViewById(R.id.btnUnbind);
textStatus = (TextView)findViewById(R.id.textStatus);
textIntValue = (TextView)findViewById(R.id.textIntValue);
textStrValue = (TextView)findViewById(R.id.textStrValue);
btnUpby1 = (Button)findViewById(R.id.btnUpby1);
btnUpby10 = (Button)findViewById(R.id.btnUpby10);
btnStart.setOnClickListener(btnStartListener);
btnStop.setOnClickListener(btnStopListener);
btnBind.setOnClickListener(btnBindListener);
btnUnbind.setOnClickListener(btnUnbindListener);
btnUpby1.setOnClickListener(btnUpby1Listener);
btnUpby10.setOnClickListener(btnUpby10Listener);
restoreMe(savedInstanceState);
CheckIfServiceIsRunning();
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("textStatus", textStatus.getText().toString());
outState.putString("textIntValue", textIntValue.getText().toString());
outState.putString("textStrValue", textStrValue.getText().toString());
}
private void restoreMe(Bundle state) {
if (state!=null) {
textStatus.setText(state.getString("textStatus"));
textIntValue.setText(state.getString("textIntValue"));
textStrValue.setText(state.getString("textStrValue"));
}
}
private void CheckIfServiceIsRunning() {
//If the service is running when the activity starts, we want to automatically bind to it.
if (MyService.isRunning()) {
doBindService();
}
}
private OnClickListener btnStartListener = new OnClickListener() {
public void onClick(View v){
startService(new Intent(MyServiceControllerActivity.this, MyService.class));
}
};
private OnClickListener btnStopListener = new OnClickListener() {
public void onClick(View v){
doUnbindService();
stopService(new Intent(MyServiceControllerActivity.this, MyService.class));
}
};
private OnClickListener btnBindListener = new OnClickListener() {
public void onClick(View v){
doBindService();
}
};
private OnClickListener btnUnbindListener = new OnClickListener() {
public void onClick(View v){
doUnbindService();
}
};
private OnClickListener btnUpby1Listener = new OnClickListener() {
public void onClick(View v){
sendMessageToService(1);
}
};
private OnClickListener btnUpby10Listener = new OnClickListener() {
public void onClick(View v){
sendMessageToService(10);
}
};
private void sendMessageToService(int intvaluetosend) {
if (mIsBound) {
if (mService != null) {
try {
Message msg = Message.obtain(null, MyService.MSG_SET_INT_VALUE, intvaluetosend, 0);
msg.replyTo = mMessenger;
mService.send(msg);
} catch (RemoteException e) {
}
}
}
}
void doBindService() {
bindService(new Intent(this, MyService.class), mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
textStatus.setText("Binding.");
}
void doUnbindService() {
if (mIsBound) {
// If we have received the service, and hence registered with it, then now is the time to unregister.
if (mService != null) {
try {
Message msg = Message.obtain(null, MyService.MSG_UNREGISTER_CLIENT);
msg.replyTo = mMessenger;
mService.send(msg);
} catch (RemoteException e) {
// There is nothing special we need to do if the service has crashed.
}
}
// Detach our existing connection.
unbindService(mConnection);
mIsBound = false;
textStatus.setText("Unbinding.");
}
}
#Override
protected void onDestroy() {
super.onDestroy();
try {
doUnbindService();
} catch (Throwable t) {
Log.e("TabBarActivity", "Failed to unbind from the service", t);
}
}
}
You need to stay on UIThread to update UI. A solution may be:
Create a static reference of activity. Remember to set it on resume method of activity and to unset it on pause method.
On the service side you can invoke a method of activiy to update UI.
Translating those operations in pseudocode. The activity will become :
public class MainActivity extends Activity {
public static MainActivity reference;
...
public onResume() {
reference=this;
}
public onPause() {
reference=null;
}
public void needToUpdateText(final String text)
{
runOnUiThread(new Runnable() {
public void run() {
Log.d("UI thread", "I am the UI thread with text "+text);
});
}
}
}
And the service class:
public class FloatingViewService extends Service {
...
public void updateText ( String newText)
{
if (MainActivity.reference!=null)
{
MainActivity.reference.needUpdateText(newText);
}
};
}
I need to write thread in service . But I'm not sure how to do this exactly. There must be more than one thread.
Can you help me please.
You start a thread in a service the same way you start a thread in anything. Either use Java threads, timers, or async task.
Java threads are usually started like so:
private Thread yourThread;
private NewRunnable yourRunnable;
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
... code...
yourThread = new Thread(yourRunnable);
... code...
}
private final class NewRunnable extends Runnable
{
#Override
public void run()
{
... Code here will be run in new thread....
}
}
This way works great.
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class Servicio extends Service {
#Override
public void onCreate() {
super.onCreate();
initialize();
}
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
public void initialize(){
Thread th = new Thread(new Runnable() {
#Override
public void run() {
//Your code ......
}
});
th.start();
}
}
Just get The context in the Service and Use AsyncTask to create another Thread... Or you can also do
new Thrad(){
public void Run(){
//your implementation..
}
}
I'm trying to have my IntentService show a Toast message,
but when sending it from the onHandleIntent message, the toast shows but gets stuck and the screen and never leaved.
I'm guessing its because the onHandleIntent method does not happen on the main service thread, but how can I move it?
Has anyone has this issue and solved it?
in onCreate() initialize a Handler and then post to it from your thread.
private class DisplayToast implements Runnable{
String mText;
public DisplayToast(String text){
mText = text;
}
public void run(){
Toast.makeText(mContext, mText, Toast.LENGTH_SHORT).show();
}
}
protected void onHandleIntent(Intent intent){
...
mHandler.post(new DisplayToast("did something"));
}
Here is the full IntentService Class code demonstrating Toasts that helped me:
package mypackage;
import android.app.IntentService;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
import android.widget.Toast;
public class MyService extends IntentService {
public MyService() { super("MyService"); }
public void showToast(String message) {
final String msg = message;
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_LONG).show();
}
});
}
#Override
protected void onHandleIntent(Intent intent) {
showToast("MyService is handling intent.");
}
}
Use the Handle to post a Runnable which content your operation
protected void onHandleIntent(Intent intent){
Handler handler=new Handler(Looper.getMainLooper());
handler.post(new Runnable(){
public void run(){
//your operation...
Toast.makeText(getApplicationContext(), "hello world", Toast.LENGTH_SHORT).show();
}
});