Android: Instantiate a Handler in a TimerTask within a Service - android

I'm trying to use a service to make a regular call to my API. The asynchronous class I use to make external HTTP calls returns information to a handler which is passed in.
A simplified version below dies on the line where the Handler is instantiated (without a stack trace). Any idea why? Is there a better way I should be doing this?
package com.fred.services;
import java.util.Timer;
import java.util.TimerTask;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
public class NotificationService extends Service {
private static final String TAG = "com.fred.services NotificationService";
public long delay = 0;
public long period_in_minutes = 10;
public long period = 1000*60*period_in_minutes;
private Timer timer = null;
private TimerTask task = new TimerTask() {
#Override
public void run() {
Handler h;
Log.i(TAG, "now you see it");
h = new Handler();
Log.i(TAG, "now you don't");
}
};
#Override
public void onCreate(){
super.onCreate();
if (timer == null) startservice();
}
private void startservice() {
if (timer == null) timer = new Timer();
timer.scheduleAtFixedRate(task, delay, period);
}
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
}

Is there a better way I should be doing this?
Use AlarmManager and an IntentService. This allows your code to stay out of memory except during the moments when it is actually adding value to the user (i.e., accessing your Web service).

Related

How to run a Service always in background (Non-Stop) - Android

I have created a Service in my android application which starts running on BOOT_COMPLETE. I want to run my Service non-stop (run always), and for that I have used while(true) inside onStartCommand() method. So is this fine to use while(true) or there is any other better way to run a service always in background?
This is code of my Service:
package com.example.abc.project1;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import org.json.*;
import java.io.*;
import java.net.*;
import java.util.*;
public class HelloService extends Service {
private static final String TAG = "HelloService";
private boolean isRunning = false;
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
isRunning = true;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
#Override
public void run() {
while(true) {
/*non-stop work to be done in background always*/
}
}
}).start();
return Service.START_STICKY;
}
#Override
public void onDestroy() {
isRunning = false;
}
}
I have not tried this myself but if you change receiver to service it should work.

Android Messenger Application using services

I want to implement a simple messenger application for Android devices,I'm working with a web service which contains all the required methods for sending and receiving(by pressing the send button a record will be inserted in the DB and by calling the receive method all the rows related to this receiver(user) are retrieved).
I've written a service in a separate class and in onStart() I check the receive method of my .Net web service,I start the service in onCreate() of my activity ,so the service is in the background and receives the incoming messages perfectly,I can show the new message by using a toast directly in my service code,but I know that for accessing the views which are in my activity I should use pendingintent and maybe a BroadcastReceiver,so I can add the new messages to the main screen of my activity(for example a textview).
Now I want to find a way to access the textview of my activity and set the text of it through my service or anything else...
please help me on this issue,
Here is my activity:
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class MyOwnActivity extends Activity
{
Button btnSend;
Button btnExtra;
EditText txtMessageBody;
TextView lblMessages;
BerryService BS = new BerryService();
public void SetMessageHistory(String value)
{
txtMessageBody.setText(value);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnSend = (Button) findViewById(R.id.btnSend);
btnExtra = (Button) findViewById(R.id.btnExtraIntent);
txtMessageBody = (EditText) findViewById(R.id.txtMessageBody);
lblMessages = (TextView) findViewById(R.id.lblMessages);
/////////
//////////
startService(new Intent(this, IncomingMessageService.class));
btnSend.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// call webservice method to send
BS.SetSoapAction("http://tempuri.org/Send");
BS.SetMethodName("Send");
String a = BS.SendMessage(txtMessageBody.getText().toString());
lblMessages.setText(lblMessages.getText().toString() + "\n"
+ txtMessageBody.getText().toString());
txtMessageBody.setText("");
}
});
}
}
Here is my service:
import java.util.Timer;
import java.util.TimerTask;
import android.app.ActivityManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.SystemClock;
import android.widget.Toast;
public class IncomingMessageService extends Service
{
private static final int NOTIFY_ME_ID = 12;
BerryService BS = new BerryService();
String text = "";
#Override
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Bind Failed");
}
#Override
public void onCreate() {
Toast.makeText(this, "onCreate", 5000).show();
}
#Override
public void onStart(Intent intent, int startId) {
// ////////////////////////
Toast.makeText(this, "onStart ", 1000).show();
// Timer Tick
final Handler handler = new Handler();
Timer _t = new Timer();
TimerTask tt = new TimerTask() {
#Override
public void run() {
handler.post(new Runnable() {
public void run() {
Toast.makeText(getApplicationContext(), "tick ", 1000)
.show();
// here the receive method should be called
BS.SetSoapAction("http://tempuri.org/RecieveMessage");
BS.SetMethodName("RecieveMessage");
String receivedMsg = BS.ReceiveMessage("sh");
//Instead of toast I want to access the textview in my activity!!!!!
Toast.makeText(getApplicationContext(), receivedMsg, 5000).show();
}
});
}
};
_t.scheduleAtFixedRate(tt, 0, 1000);
}
// /
#Override
public void onDestroy() {
Toast.makeText(this, "onDestroy", 5000).show();
}
}
You need to understand the concept of Broadcast, in your case it is the correct solution.
Start Broadcast in its activity
public static final String ACTION = "com.yourapp.ACTION.TEXT_RECEIVED";
private BroadcastReceiver mReceiver;
#Override
public void onCreate(Bundle savedInstanceState) {
////////
mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String msg = intent.getStringExtra("msg");
yourTextView.setText(msg);
}
};
IntentFilter filter = new IntentFilter(ACTION);
filter.addCategory(Intent.CATEGORY_DEFAULT);
registerReceiver(mReceiver, filter);
////////
}
protected void onDestroy() {
// remember to unregister the receiver
super.onDestroy();
if (mReceiver != null) {
unregisterReceiver(mReceiver);
}
}
When you need to send the message of service you should use:
Intent i = new Intent();
i.setAction(MyOwnActivity.ACTION);
i.addCategory(Intent.CATEGORY_DEFAULT);
i.putExtra("msg", "the message received by webservice");
i.putExtras(b);
sendBroadcast(i);
Have a look here: http://developer.android.com/reference/android/content/BroadcastReceiver.html
Using a broadcast manager is great but I personally prefer to use square's Otto because it is just so easy to perform communication between components in an android application.
http://square.github.io/otto/
If you do choose to use otto, you are going to have to override the Bus's post method to be able to talk post messages to a bus on the foreground. Here is the code for that:
public class MainThreadBus extends Bus {
private final Handler handler = new Handler(Looper.getMainLooper());
#Override public void post(final Object event) {
if (Looper.myLooper() == Looper.getMainLooper()) {
super.post(event);
} else {
handler.post(new Runnable() {
#Override
public void run() {
post(event);
}
});
}
}
}

android activity/service works only if phone connected to pc

I know this sounds weird, but I created a simple timer with an activity and a service (started and bound).
In the activity I also implemented onStart and onStop just logging a message (Log.d(TAG,"activity started/stopped").
The fact is that if the phone is connected to the pc everything seems to work. I can start the timer, pause it, modify and restart it. Open other apps and it keeps working on the background. I can recall it and I see the actual countdown going down. If it finish I can recall the activity from a notification and stop the ringing. etc etc
If the phone it's detached from the pc, that it works like there is no service at all. So the activity runs and if I press the home button it goes on the background and keeps working for a couple of minutes than it stops.
I can see the process in the running applications and if I recall the activity it restart from the point where it paused. That is, I set 10 minutes, I click start and then the home button. After 2-3 minutes it stops working and if I recall the activity it continues counting down from 8-7 minutes...
Any idea?
The activity:
package com.sleone.cookingtimer;
import com.sleone.cookingtimer.TimerService.LocalBinder;
import android.os.Bundle;
import android.os.IBinder;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import kankan.wheel.widget.WheelView;
import kankan.wheel.widget.adapters.NumericWheelAdapter;
import android.util.Log;
public class TimerMainActivity extends Activity {
// private CookingTimer timer;
// suppressWarnings because is initialized binding to the service
private TimerService timerService;
private Intent timerServiceIntent;
private final String TAG = "TimerMainActivity";
private WheelView hoursWheel ;
private WheelView minutesWheel;
private WheelView secondsWheel;
/*
* Initialize the activity
*/
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_timer_main);
timerServiceIntent = new Intent(this, TimerService.class);
startTimerService();
// init the gui
hoursWheel = (WheelView) findViewById(R.id.hoursWheelView);
minutesWheel = (WheelView) findViewById(R.id.minutesWheelView);
secondsWheel = (WheelView) findViewById(R.id.secondsWheelView);
hoursWheel.setViewAdapter(new NumericWheelAdapter(this, 0, 6));
minutesWheel.setViewAdapter(new NumericWheelAdapter(this, 0, 59));
secondsWheel.setViewAdapter(new NumericWheelAdapter(this, 0, 59));
}
#Override
protected void onStop(){
super.onStop();
Log.d(TAG, "TimerMainActivity stopped");
}
#Override
protected void onStart(){
super.onStart();
Log.d(TAG, "TimerMainActivity started");
}
private void startTimerService() {
// connect to the service
// leave the service in background
Log.d(TAG, "Starting the TimerService");
startService(timerServiceIntent);
// interact with the service
Log.d(TAG, "Binding to the TimerService");
bindService(timerServiceIntent, mConnection, Context.BIND_AUTO_CREATE);
}
private void stopTimerService() {
unbindService(mConnection);
stopService(timerServiceIntent);
}
/*
* Disconnect from the service
*/
#Override
protected void onDestroy() {
Log.d(TAG, "Stopping TimerService");
super.onStop();
stopTimerService();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.timer_main, menu);
return true;
}
public void controlTimer(View view) {
Button controlButton = (Button) findViewById(R.id.controlTimerButton);
if (controlButton.getText().equals(
getResources().getText(R.string.startTimer))) {
if ((hoursWheel.getCurrentItem() == 0)
&& (minutesWheel.getCurrentItem() == 0)
&& (secondsWheel.getCurrentItem() == 0)) {
return;
}
controlButton.setText(R.string.stopTimer);
timerService.startTimer();
} else {
controlButton.setText(R.string.startTimer);
timerService.stopTimer();
}
}
/* Defines callbacks for service binding, passed to bindService() */
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
// We've bound to LocalService, cast the IBinder and get
// LocalService instance
LocalBinder binder = (LocalBinder) service;
timerService = binder.getService();
binder.createCookingTimer(TimerMainActivity.this);
Log.d(TAG, "onServiceConnected() finished");
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
Log.e(TAG, "TimerService unexpectedly disconnected!!");
}
};
}
The service:
package com.sleone.cookingtimer;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
public class TimerService extends Service{
// Binder given to clients
private final IBinder mBinder = new LocalBinder();
private CookingTimer timer;
//private int timerServiceId;
public class LocalBinder extends Binder {
public TimerService getService() {
// Return this instance of LocalService so clients can call public methods
return TimerService.this;
}
// when the client connects to the service instantiate the CookingImer
public void createCookingTimer(TimerMainActivity timerMainActivity){
timer = new CookingTimer(timerMainActivity);
}
}
public void startTimer(){
timer.startTimer();
}
public void stopTimer(){
timer.stopTimer();
}
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return mBinder;
}
}
I don't think you need the timer itself. It;s just a CountDownTimer which onTick it updates the hours/minutes/seconds Wheel and onFinish plays a sound and create a notification.
You might have some sort of race condition, that when connected to the PC, execution is a bit slower, but when not connected the timing is a bit different and the order of execution changes. It's hard to tell without the code.
Ok, I guess I figured it out.
Basically I did not understand exactly that a service could also be paused when the cpu goes to sleep.
So, my guess is that while on the emulator or with the cable connected the cpu never goes to sleep because there is no battery consumption.
To wake up the application even from the cpu sleep I used an AlarmManger with the AlarmManager.RTC_WAKEUP flag.

Creating an android service

I'm trying to create a service which will start by the user request in the application.
After the user will choose an update interval, the service will run in the operation system background, and will send a non-relevant message.
I've tried to write the service according to the example for Service class API.
For some reason, I figured in debug (when running doBindService() method) that mUpdateBoundService is getting null.
My second question is whether I can use "Toast" inform message outside an application ? (As kind of a desktop notification).
Can anyone help ? Here is my short code:
UpdateService.java
package android.update;
import java.util.Timer;
import java.util.TimerTask;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.widget.Toast;
public class UpdateService extends Service {
private NotificationManager mNM;
private final IBinder mBinder = new UpdateBinder();
private int updateInterval;
public class UpdateBinder extends Binder {
UpdateService getService() {
return UpdateService.this;
}
}
public void onCreate() {
mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
Timer timer = new Timer();
timer.schedule(new UpdateTimeTask(), 100, updateInterval);
}
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
class UpdateTimeTask extends TimerTask {
public void run() {
showNotification();
}
}
public void showNotification() {
Toast.makeText(this, "Hi", 10);
}
#Override
public IBinder onBind(Intent intent) {
updateInterval = intent.getExtras().getInt(getString(R.string.keyUpdateInterval));
return mBinder;
}
}
UpdateActivity.java
package android.update;
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.IBinder;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
public class UpdateActivity extends Activity {
private UpdateService mUpdateBoundService;
private boolean mIsBound = false;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
public void onClickStartUpdateService(View view) {
switch (view.getId()) {
case R.id.btnStartUpdateService:
doBindService();
//Toast.makeText(this,"Service Started",Toast.LENGTH_LONG).show();
mUpdateBoundService.showNotification();
break;
}
}
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mUpdateBoundService = ((UpdateService.UpdateBinder)service).getService();
}
public void onServiceDisconnected(ComponentName className) {
mUpdateBoundService = null;
}
};
private void doBindService() {
Intent updateActivityIntent = new Intent(UpdateActivity.this,
UpdateService.class);
EditText txtUpdateInterval = (EditText) findViewById(R.id.txtUpdateInterval);
int interval = Integer.parseInt(txtUpdateInterval.getText().toString());
updateActivityIntent.putExtra(getString(R.string.keyUpdateInterval), interval);
bindService(updateActivityIntent, mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
}
void doUnbindService() {
if (mIsBound) {
unbindService(mConnection);
mIsBound = false;
}
}
#Override
protected void onDestroy() {
super.onDestroy();
doUnbindService();
}
}
Your toast is not showing because you are not telling it to. Try:
public void showNotification() {
Toast.makeText(this, "Hi", 10).show();
}
For your service issue, I think that you do not properly understand how services & activities work together. A service can run independently of a service, or you can have a service whose lifecycle matches that of a given activity. From your code, it is not clear which of these models you are following. Your implementation will cause the service to wake periodically, but only while your activity is running. If the user switches to another activity, your service will no longer be woken.
If you want a service to wake periodically independently of the activity, then you need to run your timer event in the service itself. Better still use an Alarm to wake your service: Register an Alarm with AlarmManager which will fire an Intent at a future point (or regular intervals, if you prefer), and extend your service from IntentService, override onHandleIntent() and add the necessary Intent Filter to your Service entry in the manifest.

Possible memory leak in android. Might be using the wrong cleanup method, or missing something

I have a memory leak. Here's the code
package fourguys.testing.IntentTest;
import android.app.Activity; import android.media.MediaPlayer; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.media.MediaPlayer; import android.media.AudioManager; import android.content.Context;
public class CanvasDrawingActivity extends Activity {
private static final int FIRE = 0;
private int initVolume = 0;
private Handler handler;
private MyCanvas v;
private MediaPlayer mp;
private AudioManager am;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
am = (AudioManager)this.getSystemService(Context.AUDIO_SERVICE);
// this method gets the current volume setting for music
initVolume = am.getStreamVolume(AudioManager.STREAM_MUSIC);
am.setStreamVolume(AudioManager.STREAM_MUSIC,100,AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
mp = MediaPlayer.create(this, R.raw.test);
makeHandler();
v =new MyCanvas(this);
new Thread(new Runnable(){
#Override
public void run() {
while(true)
handler.sendEmptyMessage(FIRE);
}}).start();
setContentView(v);
mp.setLooping(true);
mp.start();
}
private void makeHandler()
{
handler = new Handler(){
#Override
public void handleMessage(Message msg) {
switch(msg.what)
{
case FIRE:
{
v.invalidate();
break;
}
}
}
};
}
protected void onPause() {
super.onPause();
mp.stop();
}
protected void onFinish() {
mp.stop();
}
}
and this:
package fourguys.testing.IntentTest;
import android.app.Activity; import android.content.Intent; import android.media.MediaPlayer; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.view.WindowManager;
public class IntentTest extends Activity { /** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
//reciever intentReceiver = new reciever();
// IntentFilter intentFilter = new IntentFilter("com.app.REC");
//registerReceiver(intentReceiver, intentFilter);
Button b = (Button)this.findViewById(R.id.endButton);
b.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent i = new Intent(IntentTest.this,CanvasDrawingActivity.class);
startActivity(i);
}
});
}
// the onPause method get called when the app is either being hidden or being closed so this the place where we would want to clean anything up like stoping the media player.
#Override
protected void onPause()
{
super.onPause();
}
}
I run the app and it gets wonky on exit. It locks the handset and causes the battery to run hot. I need to pull the battery physically to reboot. Any thoughts as to why that might be? It runs fantastically on the emulator. Should I be using onFinish instead, or am I not cleaning something up and I'm missing it?
It is this part of your code:
new Thread(new Runnable(){
#Override
public void run() {
while(true)
handler.sendEmptyMessage(FIRE);
}}).start();
You're doing three obvious things wrong here. 1) You're not killing it and/or pausing it in Activity#onPause. 2) You're not calling setDaemon(true); this will cause the process to continue and not die while this thread is running. 3) you're using a hot loop, i.e., you're not calling Thread#sleep() or some other type of equivalent method there to pause and stop fully using the cpu.

Categories

Resources