im making an Android app, i have a service, that update my local DB in background, by downloading data from the remote DB.
i have to put a thread on the service, because i dont know why, when i use a simple handle style bucle on the service it frozen my app during some secs when it is updating the local db. (i have my local db in a dbAdapter on MyApplication class)
OK, then i put a thread on the service, but i dont know why, if i start the service, the thread of the service is frezzing my APP :S. it's suposed that when u use services and threads code is executed in background and doesnt froze nothing, but in this case is frezzing my app. ¿how to avoid it?
this is the code of my service:
public class MyServiceLocalDB extends Service implements Runnable{
RemoteConnection con; //conexion remota
//para almacenar la config local de mi app
static SharedPreferences settings;
static SharedPreferences.Editor configEditor;
boolean serviceStopped;
private static MyDbAdapter mDb;
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
serviceStopped=false;
settings = PreferenceManager.getDefaultSharedPreferences(this.getApplicationContext());
configEditor = settings.edit();
con = new RemoteConnection();
mDb = new MyDbAdapter(this);
mDb.open();
}
#Override
public void onDestroy() {
//player.stop();
serviceStopped=true;
}
#Override
public void onStart(Intent intent, int startid) {
//player.start();
this.run();
}
public void updateDB()
{
mDb.clearDB();
List<Friend> myFriends=con.RetrieveFriends(settings.getString("login",""));
List<Permission> myPermissions=con.RetrievePermissions(settings.getString("login",""));
Permission p1 = null;
for (int i=0;i<myFriends.size();i++)
{
mDb.createUser(myFriends.get(i).getEmail(),myFriends.get(i).getFullName(),myFriends.get(i).getMovilephone(),myFriends.get(i).getMovileOperatingSystem(),myFriends.get(i).getPermission());
//p1=con.RetrievePermissionWithUser("pablo#upv.es", myFriends.get(i).getEmail());
}
for (int i=0;i<myPermissions.size();i++)
{
p1=myPermissions.get(i);
String hour1=formatHourFromTime(p1.getHour1());
String hour2=formatHourFromTime(p1.getHour2());
mDb.createPermission(p1.getFk_email1(),p1.getFk_email2(),""+p1.getValidated(),hour1,hour2,p1.getDate1Formated(),p1.getDate2Formated(),""+p1.getWeekend(),p1.getFk_type());
p1=null;
}
//MyApplication.getDatabaseAdapter().clearDB();
MyApplication.setDatabaseAdapter(mDb);
}
public String formatHourFromTime(Time time)
{
String hour1;
if (time.getHours()<10)
hour1="0"+time.getHours();
else
hour1=""+time.getHours();
if (time.getMinutes()<10)
hour1=hour1+":0"+time.getMinutes()+":00";
else
hour1=hour1+":"+time.getMinutes()+":00";
return hour1;
}
public void run() {
while (serviceStopped==false)
{
//handler.sendEmptyMessage(0);
try {
Thread.sleep(25000);// sleeps
} catch (InterruptedException e) {}
updateDB();
}
}
}
The onStart is called by the OS on a main UI thread, that's why you got stuck there (you block the main UI thread in the run()). Instead of this.run(); you should start a new Thread here - new Thread(this).start();.
BTW, onStart is deprecated. Implement onStartCommand instead.
Related
I'm working on an app that can check web data every half an hour and I need to ensure it keeps running as long as the power is on.
For now, the structure of my app is like this:
main_activity:
AlarmManager in onCreate()
alarm_receiver:
start_service
acquire partial_wl for the service
service:
get network data using StrictMode
pop activity_2 if the data is expected
activity_2:
vibration
button to exit(activity_2.this.finish())
But in testing I find the service will stop(be killed) after the first 30 mins. In addition, if I start a thread for networking in service instead of using StrictMode, it will be killed in 5mins after the screen is locked.
Hope someone could give a suggestion for this. It's truly disturbing.
Many thanks.
common service lives no metter what is happen with activity. if you want it start periodically check out mine service:
https://bitbucket.org/kvrus/ocs-android/raw/036de7f0d3579b2a193bcb82309f7f82819508e6/app/src/main/java/koss/ru/oneclickrate/network/EcbEuropeService.java
/**
* Loads exchange rates form network periodically
* Returns results in broadcast message.
* Created by koss on 19.02.16.
* */
public class EcbEuropeService extends Service {
public static final String ECB_URL = "http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml";
public static final int UPDATE_PERIOD = 30000;
public static final int UPDATE_TICK = 1000;
public static final String NOTIFICATION = "koss.ru.oneclickrate.receiver";
public static final String EXTRA_CURRENCIES_MAP = "extra_currencies_map";
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
getUrlData();
return Service.START_NOT_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
public Cubes getUrlData() {
(new AsyncTask<Object, Object, Cubes>() {
Map<CurrencyType, BigDecimal> result = new EnumMap<CurrencyType, BigDecimal>(CurrencyType.class);
#Override
protected Cubes doInBackground(Object... params) {
Cubes cubes = new Cubes();
InputStream is = null;
HttpURLConnection urlConnection = null;
try {
URL url = new URL(ECB_URL);
urlConnection = (HttpURLConnection) url.openConnection();
is = urlConnection.getInputStream();
cubes = EcbEuropeResponseParser.parse(is);
} catch (Exception e) {
e.printStackTrace();
} finally {
if(urlConnection!=null) IOUtils.close(urlConnection);
if(is!=null) IOUtils.closeQuietly(is);
return cubes;
}
}
#Override
protected void onPostExecute(Cubes map) {
super.onPostExecute(map);
sendBroadcastMessage(map);
startTimer();
}
}).execute();
return null;
}
/**
* Restarts timer
* */
public void startTimer() {
cdt.cancel();
cdt.start();
}
CountDownTimer cdt = new CountDownTimer(UPDATE_PERIOD, UPDATE_TICK) {
#Override
public void onTick(long millisUntilFinished) {
}
public void onFinish() {
getUrlData();
}
};
private void sendBroadcastMessage(Cubes currenciesMap) {
Intent intent = new Intent(NOTIFICATION);
intent.putExtra(EXTRA_CURRENCIES_MAP, currenciesMap);
sendBroadcast(intent);
}
I have changed a few things and it works well now.
1.As my phone is 4.4.2(api=19), alarmmanager.setrepeating is inexact. So I turn to use .setExact (new method of .set()) and reschedule the alarm at the end of AsyncTask(network) in Service.
2.Make wakelock instance global, acquiring it in AlarmReceiver and releasing at the end of the AsyncTask. I used to put .release() in onDestroy() which releases the lock before the task is done.
3.There is a setting about protected-background applications in my phone and I didn't turn it on. That can allow system kill the application and disable the alarm manager.
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 am using service for running long background tasks in my application, in the service these functions are running login to XMPP and getting some data from XMPP server. i want to show the progress bar upto login completed. How to get response from service to activity to Update progress bar properly to avoid some exceptions in UI.
I am calling service like this
final Intent gtalk_intent = new Intent(AccountsActivity.this, GtalkService.class);
gtalk_intent.putExtra("user_name", acc.getAcc_Name());
gtalk_intent.putExtra("user_pass", acc.getAcc_Pass());
startService(gtalk_intent);
this is the code from service
public class PmService extends Service {
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public class PmBinder extends Binder {
public PmService getService() {
return PmService.this;
}
}
#Override
public void onCreate() {
super.onCreate();
context = this;
app_preferences = new AppPreferences(this);
chat_source = new ChatsDataSource(this);
chat_source.open();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Bundle extras = intent.getExtras();
if(extras == null) {
full_name = extras.getString("user_name");
if(full_name.contains("#")) {
String[] _na = full_name.split("#");
U_name = _na[0];
}
U_pass = extras.getString("user_pass");
}
new PmAsync().execute();
return START_STICKY;
}
private class PmAsync extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
}
#Override
protected Void doInBackground(Void... params) {
SASLAuthentication.supportSASLMechanism("PLAIN", 0);
ConnectionConfiguration config = new ConnectionConfiguration(server_host, SERVER_PORT, SERVICE_NAME);
configure(ProviderManager.getInstance());
m_connection = new XMPPConnection(config);
try {
m_connection.connect();
Roster.setDefaultSubscriptionMode(Roster.SubscriptionMode.manual);
} catch (XMPPException e) {
e.printStackTrace();
}
try {
m_connection.login(U_name, U_pass);
setPacketFilters();
} catch (XMPPException e) {
}
return null;
}
}
i want to show the progress bar upto login completed, how to response from service after login completed?
Via Binder you can send callbacks to your Activity, which means that you can update UI.
Add according method to your Binder (let's name it onProgress)
From your AsyncTask call method of this Binder
In order to know about progress updates consider using Observer pattern (in other words - your Activity should listen for updates of your Binder, or more specifically - of calling Binder.onProgress method)
You can update the progress bar via overriding the onProgress() method
here is a close to your case that you can refer to.link
This is a common question, and I have read up on the various ways of handling it, but each on seems to fall short for what I am trying to do, which is essentially be a good OO-Citizen.
I have an Activity that invokes a CommunicationManager, which basically polls a TCP socket for data. When the CommunicationManager receives data, it throws a custom event (containing the string it just fetched), which is handled by the Activity. I am doing this, A) because other classes will depend on that data, not just the Activity, and B) because the polling is asynchronous, and should fire an event when it receives results.
My problem lies in that I need to surface those results into a TextView on the UI. I have the polling mechanism all set up, it fires every 1000ms, and invokes the event handler on the Activity. However, the UI never updates.
Assumedly this is a thread issue and the UI thread is not the one getting the change to the TextView, but how do I do this?? I have tried using a Handler, but am not sure where to put it, and when I did get it compiling it never updated the UI.
This seems relatively trivial if everything was done within the Activity, but adding in this other class (CommunicationManager) and the event is making it very confusing for me.
Here is what I have so far:
ACTIVITY (polling is invoked by clicking a button on the UI):
public void onClick(View v) {
if (v.getId() == R.id.testUDPBtn) {
statusText.setText("");
commMgr = new CommunicationManager();
commMgr.addEventListener(this);
MediaPositionPollThread poller = new MediaPositionPollThread(commMgr);
poller.startPolling();
}
}
#Override
public void handleMediaPositionFoundEvent(MediaPositionFoundEvent e) {
statusText.append(e.userData);
}
THREAD:
class MediaPositionPollThread extends Thread {
private CommunicationManager commManager;
private static final String TAG = "MediaPositionPollThread";
private boolean isPolling = false;
public MediaPositionPollThread(CommunicationManager cm) {
commManager = cm;
}
public void startPolling() {
isPolling = true;
this.run();
}
public void stopPolling() {
isPolling = false;
}
#Override
public void run() {
while (isPolling) {
try {
commManager.getCurrentMediaPosition();
Thread.sleep(1000);
}
catch (InterruptedException e) {
Log.d(TAG, "EXCEPTION: " + e.getMessage());
}
}
}
}
COMMUNUCATION MANAGER:
public void getCurrentMediaPosition() {
PrintWriter outStream;
BufferedReader inStream;
String resultString = "";
try {
outStream = new PrintWriter(tcpSocket.getOutputStream(), true);
outStream.println("GET?current_pts");
inStream = new BufferedReader(new InputStreamReader(tcpSocket.getInputStream()));
resultString = inStream.readLine();
fireEventWithData(resultString);
} catch (Exception e) {
e.printStackTrace();
}
}
public synchronized void addEventListener(MediaPositionFoundEventListener listener) {
_listeners.add(listener);
}
public synchronized void removeEventListener(MediaPositionFoundEventListener listener) {
_listeners.remove(listener);
}
private synchronized void fireEventWithData(String outputString) {
MediaPositionFoundEvent evt = new MediaPositionFoundEvent(this);
evt.userData = outputString;
Iterator<MediaPositionFoundEventListener> i = _listeners.iterator();
while(i.hasNext()) {
((MediaPositionFoundEventListener) i.next()).handleMediaPositionFoundEvent(evt);
}
}
So I have the Activity making a thread that gets executed every second, calling CommunicationManager >> getCurrentMediaPosition, which in turn fires the MediaPositionFoundEvent, which is handled by the Activity and updates the TextView (statusText) on the screen.
Everything works except the screen not updating. I have tried runOnUiThread, and a Handler, but am obviously not getting it right.
Thanks in advance for any insight or solutions!
In your Activity class, add a private Handler _handler,
Initialize it in your onCreate Activity method,
and change your handleMediaPositionFoundEvent method to
#Override public void handleMediaPositionFoundEvent(MediaPositionFoundEvent e) {
_handler.post(new Runnable(){
public void run(){
statusText.append(e.userData);
});
}
}
It looks like your blocking the UI thread with your custom Thread. Please update this method to call start() vs run().
public void startPolling() {
isPolling = true;
this.start();
}
I'm trying to download multiple files using IntentService. The IntentService donwloads them okey as expected one at a time, the only problem is that when the Internet is down the intent service will not stop the donwload rather it will get stuck on the current thread. If I manage to stop the current thread it will continue running the other threads stored in its queue even though the internet connection is down.
It was suggested in another post that I use LinkedBlockingQueue and create my own Worker thread that constantly checks this queue for new threads. Now I know there are some increased overheads and thus performance issues when creating and destroying threads but that's not a concern in my case.
At this point, All I want to do is understand how IntentService works which as of yet I don't (and I have looked at the code) and then come up with my own implementation for it using LinkedBlockingQueue controlled by a Worker thread. Has anyone done this before ? Could provide a working example, if you feel uncomfortable providing the source code, pseudo code is fine by me. Thanks!
UPDATE: I eventually implemented my own Intent Service using a thread that has a looper which checks the queue which in turn stores the intents passed from the startService(intent).
public class MyIntentService extends Service {
private BlockingQueue<Download> queue = new LinkedBlockingQueue<Download>();
public MyIntentService(){
super();
}
#Override
public void onCreate() {
super.onCreate();
new Thread(queueController).start();
Log.e("onCreate","onCreate is running again");
}
boolean killed = false;
Runnable queueController = new Runnable() {
public void run() {
while (true) {
try {
Download d =queue.take();
if (killed) {
break;
}
else {
d.downloadFile();
Log.e("QueueInfo","queue size: " + queue.size());
}
}
catch (InterruptedException e) {
break;
}
}
Log.e("queueController", "queueController has finished processing");
Log.e("QueueInfo","queue size: " + queue.toString());
}
};
class Download {
String name;
//Download files process
void downloadFile() {
//Download code here
}
Log.e("Download","Download being processed is: " + name);
}
public void setName(String n){
name = n;
}
public String getName(){
return name;
}
}
public void killService(){
killed = true;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Download d = new Download();
d.setName(intent.getStringExtra("VIDEOS"));
queue.add(d);
return START_NOT_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
Log.e("stopSelf","stopSelf has been just called to stop the Service");
stopSelf();
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
I'm not so sure about the START_NOT_STICKY in the onStartCommand() method. If it's the right flag to return or not. Any clarification on that would be appreciated!
UPDATE: I eventually implemented my own Intent Service using a thread that has a looper which checks the queue which in turn stores the intents passed from the startService(intent).
public class MyIntentService extends Service {
private BlockingQueue<Download> queue = new LinkedBlockingQueue<Download>();
public MyIntentService(){
super();
}
#Override
public void onCreate() {
super.onCreate();
new Thread(queueController).start();
Log.e("onCreate","onCreate is running again");
}
boolean killed = false;
Runnable queueController = new Runnable() {
public void run() {
while (true) {
try {
Download d =queue.take();
if (killed) {
break;
}
else {
d.downloadFile();
Log.e("QueueInfo","queue size: " + queue.size());
}
}
catch (InterruptedException e) {
break;
}
}
Log.e("queueController", "queueController has finished processing");
Log.e("QueueInfo","queue size: " + queue.toString());
}
};
class Download {
String name;
//Download files process
void downloadFile() {
//Download code here
}
Log.e("Download","Download being processed is: " + name);
}
public void setName(String n){
name = n;
}