I just wanted to learn how to do some Android App development, so I started with some simple demos and now doing something harder (I guess ;-)).
Using: Eclipse + min SDK 8 + Android 2.2. Debugging and testing on emulator and my SGS I9000:
I managed to use AsyncTask to send some Data (Picture which I get from Camera or over the Gallery) to a server which works great. So, now that things works great, I decided to add a Custom Notification to the Status bar with a progress bar and text. Well, that worked too, but then I noticed:
1) as soon ad I call the publishProgress() from doInBackground() which will call the onProgressUpdate() where I update my notification. I noticed by "opening" and "closing" the Notification bar during the update, that it is not smooth. It freezes or sometime you'll see that the notification bar is not responding anymore (If opened it won't close anymore or if it was closed and try to open it won't).
2) I also noticed when I start x Notifications, then my System crashes and it looks as my system did a new start which it didn't!
well, I thought I did everything as it stands in the documentations, but I am sure I did something wrong, cause I am sure it's possible what I am looking for since Facebook for Android does the same as I soon as I choose a picture to share.
I create the notification in my onPreExecute() call (so that means on the UI thread).
here my code if anybody can tell me where my problem is: (I hope that's Okay to post the code here)
NotificationHelper manges the Notification by generating and updating the progressbar/text
public class NotificationHelper {
private Context mContext;
private Notification mNotification;
private NotificationManager mNotificationManager;
private PendingIntent mContentIntent;
private RemoteViews contentView;
private static Random randomGenerator = new Random();
private int Id = -1;
public NotificationHelper(Context context)
{
mContext = context;
Id = randomGenerator.nextInt(1000);
}
public void CreateNotification(String text)
{
mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
mNotification = new Notification(R.drawable.icon, "MyTestApp", System.currentTimeMillis());
contentView = new RemoteViews(mContext.getPackageName(), R.layout.notification);
contentView.setProgressBar(R.id.progressBar, 100, 0, false);
contentView.setTextViewText(R.id.text, Html.fromHtml("<b>TEST </b>" + text));
contentView.setTextViewText(R.id.textStatus, String.format(mContext.getString(R.string.uploading), ""));
mNotification.contentView = contentView;
Intent notificationIntent = new Intent(mContext, main.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContentIntent = PendingIntent.getActivity(mContext, 0, notificationIntent, 0);
mNotification.contentIntent = mContentIntent;
mNotificationManager.notify(Id, mNotification);
}
public void UpdateProgress(int Percent)
{
if (mNotification == null) return;
mNotification.contentView.setProgressBar(R.id.progressBar, 100, Percent, false);
contentView.setTextViewText(R.id.textStatus, String.format(mContext.getString(R.string.uploading), Integer.toString(Percent) + "%"));
mNotificationManager.notify(Id, mNotification);
}
public void Completed(boolean Status)
{
if (contentView != null) {
//mNotificationManager.cancel(Id);
contentView.setViewVisibility(R.id.progressbarContainer, View.INVISIBLE);
if (Status)
{
contentView.setTextViewText(R.id.textStatus, mContext.getString(R.string.upload_done));
}else
{
contentView.setTextViewText(R.id.textStatus, mContext.getString(R.string.upload_failed));
}
mNotificationManager.notify(Id, mNotification);
}
}
}
here is my Task:
public class MyTask extends AsyncTask<Void, Integer, Integer> {
private Context context;
private NotificationHelper pbNotificationHelper;
public String TextToShow;
public MyTask(Context context, String text) {
this.context = context;
TextToShow = text;
}
#Override
protected Integer doInBackground(Void... params) {
int xfileSize = 1048576 * 4;
if (true){
int i = 0;
while (true)
{
if (i > xfileSize) break;
i += 32 * 1024;
publishProgress( (i * 100) / xfileSize);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
return 0;
}
#Override
protected void onPreExecute() {
// TODO Auto-generated method stub
super.onPreExecute();
pbNotificationHelper = new NotificationHelper(context);
pbNotificationHelper.CreateNotification(TextToShow);
}
#Override
protected void onProgressUpdate(Integer... values) {
// TODO Auto-generated method stub
super.onProgressUpdate(values);
pbNotificationHelper.UpdateProgress(values[0]);
}
#Override
protected void onPostExecute(Integer result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
pbNotificationHelper.Completed(true);
}
}
and here is my test activity:
public class main extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
public void Test1Click(View view) {
new MyTask(getApplicationContext(), "This is a test message").execute();
}
}
I am sorry if that is too much code, but I am really at end of my understandings!
Interesting is that, when I don't call the publishProgress() nothings freezes and looks everything smooth! Any help? any Idea what I do wrong?
Many thanks in advance,
Cheers Gohlool
I think where ever you calling your async task code it should be in separate thread.
Related
I have good concept of starting and using the basic service. I mean not to complicated. In My app I want a service which should not be killed in any situation and should download some files from the server then it should call stopSelf. I have made my service in the following way. But before sharing its whole code just let me tell you what I am doing
In Service I am passing the series of url (string array) which has to download all files from the server.
I am using the async task to download from the server.
Under this whole process I am getting a 1st response that is in xml then I parse it , and get the JSON string (sorry about that my web service designer is a numb like me). so after these two conversion I store the data in the database and then starts downloading files and saving them to device and store their path in the database. (this all works fine)
I am calculating and updating progress in the notification bar. (showing user how much the files has been downloaded)
what I really want
I want that my service should not be killed when user removes it from the recent app list , so that it should continue to download and continue to update the status in notification bar. I am using Notification manager to update the progress.
What is really happening
When I close my app from recent app tray, I think my service gets killed and the downloading process stops, and It also stops updating the progress of notification in notification bar, Where As I want it to continue to run until the download process is finished.
Here is my code it is simplified as some methods are really not worthy
to be discussed here Such as Parsing the xml or JSON
Here is the Code
public class MyDemoService extends Service {
private static final String TAG = "MyDemoService";
private static final int NOTIFICATION_ID = 1;
private LocalBinder m_binder = new LocalBinder();
private NotificationManager mNotifyManager;
private NotificationCompat.Builder mBuilder;
myAsyncTask myWebFetch;
// Timer to update the ongoing notification
private final long mFrequency = 100; // milliseconds
private final int TICK_WHAT = 2;
public class LocalBinder extends Binder {
MyDemoService getService() {
return MyDemoService.this;
}
}
private Handler mHandler = new Handler() {
public void handleMessage(Message m) {
updateNotification();
sendMessageDelayed(Message.obtain(this, TICK_WHAT), mFrequency);
}
};
#Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "bound");
return m_binder;
}
#Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "created");
mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return Service.START_STICKY;
}
#Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
Log.d(TAG, "Removed");
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "Destroyed");
}
public void updateNotification() {
// Log.d(TAG, "updating notification");
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
}
public void hideNotification() {
Log.d(TAG, "removing notification");
mNotifyManager.cancel(NOTIFICATION_ID);
mHandler.removeMessages(TICK_WHAT);
}
public void start() {
Log.d(TAG, "start");
mBuilder =
new NotificationCompat.Builder(MyDemoService.this)
.setSmallIcon(R.drawable.download)
.setContentTitle("SMU")
.setContentText("Downloading Images");
Intent targetIntent = new Intent(MyDemoService.this, MainActivity.class);
PendingIntent contentIntent = PendingIntent.getActivity(MyDemoService.this, 0, targetIntent, PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(contentIntent);
mNotifyManager.notify(NOTIFICATION_ID, mBuilder.build());
myWebFetch = new myAsyncTask();
myWebFetch.execute();
}
class myAsyncTask extends AsyncTask<String, Integer, Void> {
MyDB myDB;
myAsyncTask() {
myDB = new MyDB(MyDemoService.this);
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
mBuilder.setContentText("Download complete");
// Removes the progress bar
mBuilder.setProgress(0, 0, false);
mNotifyManager.notify(NOTIFICATION_ID, mBuilder.build());
}
#Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
mBuilder.setProgress(100, values[0], false);
mNotifyManager.notify(NOTIFICATION_ID, mBuilder.build());
}
#Override
protected Void doInBackground(String... params) {
//set the download URL, a url that points to a file on the internet
getJSON("http://*****", 1000000);
return null;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
mBuilder.setProgress(100, 0, false);
mNotifyManager.notify(NOTIFICATION_ID, mBuilder.build());
}
public void getJSON(String url, int timeout) {
HttpURLConnection c = null;
try {
URL u = new URL(url);
c = (HttpURLConnection) u.openConnection();
c.setRequestMethod("GET");
c.setUseCaches(false);
c.setAllowUserInteraction(false);
c.setConnectTimeout(timeout);
c.setReadTimeout(timeout);
c.setInstanceFollowRedirects(false);
c.connect();
int status = c.getResponseCode();
if (status == 200) {
String readStream = readStream(c.getInputStream());
if (readStream != null) {
JsonParser mJsonParser = new JsonParser(MyDemoService.this);
mJsonParser.parseJaSon(readStream);
ArrayList<SuitDetails> mImageList = new ArrayList<>(myDB.GetAllData());
if (mImageList != null) {
//NOW HERE DOWNLOADING IMAGES FROM URL WE GOT SAVED IN DB AFTER PARSING
downloadImages(mImageList);
}
}
}
} catch (MalformedURLException ex) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex);
} finally {
if (c != null) {
try {
c.disconnect();
} catch (Exception ex) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex);
}
}
}
}
#TargetApi(Build.VERSION_CODES.KITKAT)
private String readStream(InputStream in) {
//parsing my input stream and sending back string
return jsonString.toString();
}
void downloadImages(ArrayList<SuitDetails> arrayList) {
try {
ArrayList<SuitDetails> imageUrl = arrayList;
URL url;
float progressImages = 0;
HttpURLConnection urlConnection = null;
for (int i = 0; i < imageUrl.size(); i++) {
progressImages += 100 / imageUrl.size();
publishProgress((int) progressImages);
url = new URL(imageUrl.get(i).getPath().toString());
//create the new connection
urlConnection = (HttpURLConnection) url.openConnection();
//set up some things on the connection
urlConnection.setRequestMethod("GET");
urlConnection.setDoOutput(false);
urlConnection.setUseCaches(false);
urlConnection.setAllowUserInteraction(false);
urlConnection.setConnectTimeout(60000);
urlConnection.setReadTimeout(60000);
urlConnection.setInstanceFollowRedirects(false);
//and connect!
urlConnection.connect();
File storagePath = new File(MyDemoService.this.getExternalFilesDir("TEST") + "/Mytest");
storagePath.mkdirs();
String finalName = imageUrl.get(i).getImageName();
File myImage = new File(storagePath, finalName + ".png");
FileOutputStream fileOutput = new FileOutputStream(myImage);
InputStream inputStream = urlConnection.getInputStream();
int totalSize = urlConnection.getContentLength();
int downloadedSize = 0;
byte[] buffer = new byte[1024];
int bufferLength = 0;
while ((bufferLength = inputStream.read(buffer)) > 0) {
//add the data in the buffer to the file in the file output stream (the file on the sd card
fileOutput.write(buffer, 0, bufferLength);
//add up the size so we know how much is downloaded
downloadedSize += bufferLength;
//this is where you would do something to report the prgress, like this maybe
}
//close the output stream when done
ContentValues contentValues = new ContentValues();
contentValues.put("Status", "1");
contentValues.put("Path", myImage.getPath().toString());
myDB.UpdateDownloadStatus(contentValues, imageUrl.get(i).getSImageID());
fileOutput.close();
}
myDB.closeDb();
//catch some possible errors...
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
I Know this is length code but sharing if You want to analyse it deeply.
I will provide how I am using and calling this service in MainActivity if you demand it
why are you not using an IntentService if you want to do network stuff?
you should consider adding setIntentRedelivery(true); in your constructor
from the documentation
Sets intent redelivery preferences. Usually called from the
constructor with your preferred semantics.
If enabled is true, onStartCommand(Intent, int, int) will return
START_REDELIVER_INTENT, so if this process dies before
onHandleIntent(Intent) returns, the process will be restarted and the
intent redelivered. If multiple Intents have been sent, only the most
recent one is guaranteed to be redelivered.
If enabled is false (the default), onStartCommand(Intent, int, int)
will return START_NOT_STICKY, and if the process dies, the Intent dies
along with it.
I have an App that Monitors room noise levels, I initially got the Code from Github, in the original code, the programmer was monitoring noise levels from Main Activity and displaying the results in textviews, but I want to monitor using a service, I have implemented everything and its working but the textviews seem to be lagging behind, lets say I make a bit of noise and the noise level reach 5, it sticks at 5 even when there is no noise in the room, but in the original app, it was so sensitive that it would go back to 0 or another value depending on the noise levels, I do not know where I have gone wrong but below is my code:
Main Activity
public class StartingPoint extends Activity {
private String volumeBars;
private String volumeLevel;
private TextView volumeBarView;
private TextView volumeLevelView;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Toast.makeText(getBaseContext(), "Loading...", Toast.LENGTH_LONG).show();
setContentView(R.layout.activity_starting_point);
//starting Service
startService(new Intent(this, VolumeListerner.class));
volumeBarView = (TextView) findViewById(R.id.volumeBars);
volumeLevelView = (TextView) findViewById(R.id.volumeLevel);
}
#Override
public void onResume() {
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, new IntentFilter("UI_UPDATER"));
super.onResume();
// Sound based code
}
#Override
public void onPause() {
super.onPause();
}
public void updateTextView() {
volumeBarView.setText(volumeBars);
volumeLevelView.setText(volumeLevel);
return;
}
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
volumeBars = intent.getStringExtra("VolumeBars");
volumeLevel = intent.getStringExtra("volumeLevel");
Log.d("receiver", "Got message: " + volumeBars + " : " + volumeLevel);
updateTextView();
}
};
Service:
public class VolumeListerner extends Service {
private static String volumeVisual = "";
private static int volumeToSend;
private Handler handler;
private SoundMeter mSensor;
/** interface for clients that bind */
IBinder mBinder;
/** indicates whether onRebind should be used */
boolean mAllowRebind;
/** The service is starting, due to a call to startService() */
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
soundLevelCheck();
return super.onStartCommand(intent, flags, startId);
}
private void soundLevelCheck()
{
mSensor = new SoundMeter();
try {
mSensor.start();
Toast.makeText(getBaseContext(), "Sound sensor initiated.", Toast.LENGTH_SHORT).show();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
handler = new Handler();
final Runnable r = new Runnable() {
public void run() {
// Get the volume from 0 to 255 in 'int'
double volume = 10 * mSensor.getTheAmplitude() / 32768;
volumeToSend = (int) volume;
volumeVisual = "";
for( int i=0; i<volumeToSend; i++){
volumeVisual += "|";
updateUI();
}
handler.postDelayed(this, 250); // amount of delay between every cycle of volume level detection + sending the data out
}
};
// Is this line necessary? --- YES IT IS, or else the loop never runs
// this tells Java to run "r"
handler.postDelayed(r, 250);
}
private void updateUI()
{
Intent intent = new Intent( "UI_UPDATER" );
intent.putExtra("VolumeBars", "Volume Bars: " + String.valueOf(volumeVisual));
intent.putExtra("volumeLevel","Volume Levels: " + String.valueOf(volumeToSend));
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
I recommmand you to use an enhanced event bus with emphasis on Android support. you have a choice between :
1- Otto
2- Event Bus
so im building this service for a application locker. it runs fine for the most part.but when i try to run the service to lock my own application(ie the app locker itself) there's a lag for like 4-5 seconds and then the lock activity launches. The logcat displays that it has skipped 600 frames and is doing too much work on the main thread. can anyone tell him how do i fix this or optimize this code
the AppActivities contains the name of activities that are to be ignored from launching the locker again when they are on top of the stack.eg the lockscreen activity to be shown to the user. The allowedapp is the last app verified by the user
public class LockerService extends Service {
String LockedApps[];
String allowedapp = null;
DataBaseHandler handler;
Intent pwdIntent = null;
ActivityManager am;
String[] AppActivities = { "com.packagename.Locker",
"com.packagename.Compare_Pattern",
"com.packagename.Captcha_Verfication",
"com.haibison.android.lockpattern.LockPatternActivity" };
private final static Handler servicehandler = new Handler() {
#Override
public void handleMessage(Message msg) {
}
};
#Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
handler = new DataBaseHandler(this);
am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
pwdIntent = new Intent(LockerService.this, Locker.class);
pwdIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
private Runnable checkforeground = new Runnable() {
public void run() {
handler.open();
LockedApps = handler.getPackages();
handler.close();
String packname = am.getRunningTasks(1).get(0).topActivity
.getPackageName();
String activityname = am.getRunningTasks(1).get(0).topActivity
.getClassName();
SharedPreferences sp = PreferenceManager
.getDefaultSharedPreferences(LockerService.this);
allowedapp = sp.getString("allowedapp", "anon");
// check if top application is mylocker application
if ((packname.equals("com.packagename"))
&& (allowedapp.equals("com.packagename"))) {
// do nothing
}
// check if top application is mylocker application and prevent relaunching the lockeractivity every 1.5 seconds
else if ((packname.equals("com.packagename"))
&& !(Arrays.asList(AppActivities).contains(activityname))) {
try {
Editor edit = sp.edit();
edit.putString("current_app", packname);
edit.commit();
startActivity(pwdIntent);
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else if ((Arrays.asList(LockedApps).contains(packname))
&& (allowedapp.equals(packname))) {
// do nothing
} else if ((Arrays.asList(LockedApps).contains(packname))) {
Editor edit = sp.edit();
edit.putString("current_app", packname);
edit.commit();
startActivity(pwdIntent);
}
servicehandler.postDelayed(this, 1500); // 1.5 seconds
}
};
#Override
public void onStart(Intent intent, int startId) {
servicehandler.removeCallbacks(checkforeground);
servicehandler.postDelayed(checkforeground, 1500);// 1.5 second
}
#Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
servicehandler.removeCallbacks(checkforeground);
stopSelf();
}
}
first of all as Gabe mentioned, a runnable runs on the main Thread.To solve the frames issue You'll need to create another new thread to run your code in the background.
Try this initialize executorService and LcThread and a boolean running_statusin your service.
The running_status variable is used to break the while loop of your thread so that stops looping in the back
#Override
public void onStart(Intent intent, int startId) {
running_status = true;
executorService = Executors.newSingleThreadExecutor();
servicehandler.removeCallbacks(LcThread);
LcThread = new LockerThread();
executorService.submit(LcThread);
}
create the following class
class LockerThread implements Runnable {
#Override
public void run() {
while(running_status){
//copy code from your old Runnable run method here
}
}
}
next modify the onDestroy method
#Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
if (executorService != null) {
executorService.shutdown();
}
running_status = false;
servicehandler.removeCallbacks(LcThread);
stopSelf();
}
hope this solves your problem
A runnable still happens on the main thread. Services do not have their own thread by default, they run on the UI thread. If you want to do heavy processing in a service, you need to use a Thread or AsyncTask, so the processing does not occur on the UI thread.
how can i prevent this service with thread to dont be killed from android, i need this notifications always runnig, but when is mobile locked, nothing will happen. I think android kill service or thread or something like that
MainActivity in onCreate
startService(new Intent(this, NotifyService.class));
My service
public class NotifyService extends Service {
private DatabaseOp mDbHelper;
public Vibrator vibrator;
String username;
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate ()
{
mDbHelper = new DatabaseOp(this);
final boolean cyklus = true;
Thread vlakno = new Thread (new Runnable()
{
#Override
public void run()
{
while (cyklus)
{
try {
Thread.sleep(60000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String sysDate = getSysDate();
String sysDate2 = getSysDate2();
String time = getSysTime();
mDbHelper.open();
Log.v( "sysDate", sysDate );
Cursor cursorU = mDbHelper.fetchUlohaS(0, sysDate);
if (cursorU.getCount() > 0)
{
String idU = cursorU.getString(cursorU.getColumnIndexOrThrow(DatabaseOp.KEY_ID));
String dbDateU = cursorU.getString(cursorU.getColumnIndexOrThrow(DatabaseOp.KEY_DATE));
String menoU = cursorU.getString(cursorU.getColumnIndexOrThrow(DatabaseOp.KEY_NAZOV));
String mHodina = getResources().getString(R.string.cas)+" "+cursorU.getString(cursorU.getColumnIndexOrThrow(DatabaseOp.KEY_HODINA));
Log.v( "task", dbDateU+"/"+sysDate );
if (dbDateU.equals(sysDate))
{
Notify(menoU, mHodina, idU, 0);
}
}
Cursor cursorS = mDbHelper.fetchSviatokS(3, sysDate2);
if (cursorS.getCount() > 0)
{
String idS = cursorS.getString(cursorS.getColumnIndexOrThrow(DatabaseOp.KEY_ID));
String dbDateS = cursorS.getString(cursorS.getColumnIndexOrThrow(DatabaseOp.KEY_DATUM));
String menoS = cursorS.getString(cursorS.getColumnIndexOrThrow(DatabaseOp.KEY_NAZOV));
if (dbDateS.equals(sysDate2) && time.equals("09:00"))
{
Notify(menoS,getResources().getString(R.string.title_section4), idS, 3);
}
}
mDbHelper.close();
}
}
});
vlakno.start();
}
}
Have you tried to use ForgroundService?
Checkout this repo for an example - https://github.com/supercurio/foreground-service-sample-app
I think you should consider AlarmManager. See http://developer.android.com/reference/android/app/AlarmManager.html.
To tip the system to keep your Service alive as long as possible (i.e. before RAM is very short or user kills the service by hand through application info screen), you need to run it as a foreground service -- by using startForeground() method.
If you're looking for a way to run when the device is turned off, read the Keeping The Device Awake training page and consider using AlarmManager instead as suggested by #khris if your task is not very critical in terms of timing precision.
I've been using Android open source service example. I just need to use it to send notification to user, but strange, it allocates lots of memory. I checked in Running Services, and it is almost 20MB (if i set ACTION_BACKGROUND) or 30MB (if i set ACTION_FOREGROUND)...
What should i do to reduce this memory usage?
I've already read this discussion I have no bitmap or webview.
Here's my service:
/**
* This is an example of implementing an application service that can
* run in the "foreground". It shows how to code this to work well by using
* the improved Android 2.0 APIs when available and otherwise falling back
* to the original APIs. Yes: you can take this exact code, compile it
* against the Android 2.0 SDK, and it will against everything down to
* Android 1.0.
*/
public class NotificationService extends Service {
static final String ACTION_FOREGROUND = "com.example.android.apis.FOREGROUND";
static final String ACTION_BACKGROUND = "com.example.android.apis.BACKGROUND";
private static final Class<?>[] mSetForegroundSignature = new Class[] {
boolean.class};
private static final Class<?>[] mStartForegroundSignature = new Class[] {
int.class, Notification.class};
private static final Class<?>[] mStopForegroundSignature = new Class[] {
boolean.class};
// protected NotificationManager mNM;
private Method mSetForeground;
private Method mStartForeground;
private Method mStopForeground;
private Object[] mSetForegroundArgs = new Object[1];
private Object[] mStartForegroundArgs = new Object[2];
private Object[] mStopForegroundArgs = new Object[1];
void invokeMethod(Method method, Object[] args) {
try {
method.invoke(this, args);
} catch (InvocationTargetException e) {
// Should not happen.
Log.w("ApiDemos", "Unable to invoke method", e);
} catch (IllegalAccessException e) {
// Should not happen.
Log.w("ApiDemos", "Unable to invoke method", e);
}
}
/**
* This is a wrapper around the new startForeground method, using the older
* APIs if it is not available.
*/
void startForegroundCompat(int id, Notification notification) {
// If we have the new startForeground API, then use it.
if (mStartForeground != null) {
mStartForegroundArgs[0] = Integer.valueOf(id);
mStartForegroundArgs[1] = notification;
invokeMethod(mStartForeground, mStartForegroundArgs);
return;
}
// Fall back on the old API.
mSetForegroundArgs[0] = Boolean.TRUE;
invokeMethod(mSetForeground, mSetForegroundArgs);
// mNM.notify(id, notification);
}
/**
* This is a wrapper around the new stopForeground method, using the older
* APIs if it is not available.
*/
void stopForegroundCompat(int id) {
// If we have the new stopForeground API, then use it.
if (mStopForeground != null) {
mStopForegroundArgs[0] = Boolean.TRUE;
invokeMethod(mStopForeground, mStopForegroundArgs);
return;
}
// Fall back on the old API. Note to cancel BEFORE changing the
// foreground state, since we could be killed at that point.
// mNM.cancel(id);
mSetForegroundArgs[0] = Boolean.FALSE;
invokeMethod(mSetForeground, mSetForegroundArgs);
}
#Override
public void onCreate() {
// mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
try {
mStartForeground = getClass().getMethod("startForeground",
mStartForegroundSignature);
mStopForeground = getClass().getMethod("stopForeground",
mStopForegroundSignature);
return;
} catch (NoSuchMethodException e) {
// Running on an older platform.
mStartForeground = mStopForeground = null;
}
try {
mSetForeground = getClass().getMethod("setForeground",
mSetForegroundSignature);
} catch (NoSuchMethodException e) {
throw new IllegalStateException(
"OS doesn't have Service.startForeground OR Service.setForeground!");
}
}
#Override
public void onDestroy() {
// Make sure our notification is gone.
stopForegroundCompat(1);
}
// This is the old onStart method that will be called on the pre-2.0
// platform. On 2.0 or later we override onStartCommand() so this
// method will not be called.
#Override
public void onStart(Intent intent, int startId) {
handleCommand(intent);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
handleCommand(intent);
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}
#Override
public void onRebind(Intent intent) {
super.onRebind(intent);
handleCommand(intent);
}
void handleCommand(Intent intent) {
if (intent == null)
return;
if (ACTION_FOREGROUND.equals(intent.getAction())) {
DBHelper db = new DBHelper(this);
String lastTime = db.getLastVisitTime();
if(!lastTime.equals("-1")) {
new Notifications(this).InviteUser();
}
String target = db.getTargetValue();
if(target.equals("")) {
new Notifications(this).TargetlessNotification();
}
db.close();
/*
// In this sample, we'll use the same text for the ticker and the expanded notification
CharSequence text = getString(R.string.app_name);
CharSequence description = getString(R.string.recall_user);
// Set the icon, scrolling text and timestamp
Notification notification = new Notification(R.drawable.icon, text, System.currentTimeMillis());
// The PendingIntent to launch our activity if the user selects this notification PendingIntent
contentIntent = PendingIntent.getActivity(this, 1, new Intent(this, YKEYarinaSaklaActivity.class), 0);
// Set the info for the views that show in the notification panel.
notification.setLatestEventInfo(this, text, description, contentIntent);
// Set properties of notification
notification.flags = Notification.FLAG_INSISTENT | Notification.FLAG_AUTO_CANCEL;
notification.defaults |= Notification.DEFAULT_ALL;
startForegroundCompat(1, notification);
*/
} else if (ACTION_BACKGROUND.equals(intent.getAction())) {
stopForegroundCompat(1);
}
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
P.S.: I don't know if it's relevant or not but i'm starting this service onDestroy of my app, so it'll send notification to user on a specific time with AlarmManager. (So it should not be killed to avoid AlarmManager deleting my notification.)
I've tried to simplfy my service as possible as i can, but the situation is still the same... Then i realize that somehow, usage of memory decrease by itself... So, if i have no option, i could except that.
public class NotificationService2 extends Service{
private String target, lastTime, notifCheck, notifCheck2;
#Override
public void onStart(Intent intent, int startId) {
Bundle extras = intent.getExtras();
if(extras != null) {
this.lastTime = extras.getString("lastTime");
this.target = extras.getString("target");
this.notifCheck = extras.getString("notifCheck");
this.notifCheck2 = extras.getString("notifCheck2");
}
handleCommand(intent);
super.onStart(intent, startId);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Bundle extras = intent.getExtras();
if(extras != null) {
this.lastTime = extras.getString("lastTime");
this.target = extras.getString("target");
this.notifCheck = extras.getString("notifCheck");
this.notifCheck2 = extras.getString("notifCheck2");
}
handleCommand(intent);
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
handleCommand(intent);
return null;
}
#Override
public void onRebind(Intent intent) {
super.onRebind(intent);
handleCommand(intent);
}
void handleCommand(Intent intent) {
if (intent == null)
return;
String lastTime = this.lastTime;
String notifCheck = this.notifCheck;
String target = this.target;
String notifCheck2 = this.notifCheck2;
if(lastTime != null && notifCheck != null) {
if(!lastTime.equals("-1") && !notifCheck.equals("1"))
new Notifications(this).InviteUser();
} else this.stopSelf();
if(target != null && notifCheck2 != null) {
if(target.equals("") && !notifCheck2.equals("1"))
new Notifications(this).TargetlessNotification();
} else this.stopSelf();
}
}