Scheduling a database update and shows a dialog - android

Ok, I think this is a recurring question, sorry for that. I need to update my DB collecting some data from web and display a dialog while the data are being downloaded.
I have my dialog, data comes fine and writes in database.
I'm doing some improvements. Today this works starting from one Activity. My will is this occurs in all application and start after a interval. And this is the moment when problems arise.
I'm using AlertManager to schedule my action. I have a BroadcastReceiver that, checks my database and starts to get data from web. At this point I'm facing problems. My progress dialog aren't shown and data doesn't come totally from web.
Code from BroadcastReceiver:
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.os.Handler;
import android.util.Log;
import android.widget.Toast;
public class SchedulerUpdate extends BroadcastReceiver {
private Handler mHandler = null;
#Override
public void onReceive(Context context, Intent intent) {
try {
mHandler = new Handler();
Log.i("INFO", "SCHEDULED");
if(checkDataBase(context)){
updateData(context);
}
}
catch (Exception e) {
e.printStackTrace();
}
}
private boolean checkDataBase(Context context){
String path = "/data/data/"+context.getPackageName()+"/databases/mdsdacpdatabase.db";
SQLiteDatabase database = null;
boolean result = false;
try{
database = SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.CREATE_IF_NECESSARY);
database.close();
result = true;
}
catch(SQLiteException e){
}
return result;
}
private void updateData(final Context context) {
((Activity)context.getApplicationContext()).showDialog(ID_DIALOG_PROGRESS_DOWNLOAD);
Runnable runnable = new Runnable(){
public void run(){
Log.i("INFO", "RUNNING");
...
mHandler.post(new Runnable() {
public void run() {
((Activity)context).dismissDialog(ID_DIALOG_PROGRESS_DOWNLOAD);
Log.i("INFO", "DONE");
}
});
};
};
new Thread(runnable).start();
}
}
Reading these posts on StackOverflow and on Kam:
BroadCast receiver dialog issue in Android
AlertDialog in BroadcastReceiver
show an alert dialog in broadcast receiver after a system reboot
How to send data from BroadcastReceiver to an Activity in android?
Android: Prefer Alarms and Intent Receivers to Services <--This is Kam
My conclusions are:
BroadcastReceivers can't display dialogs because they aren't
Activities(like Commonsware point out);
BroadcastReceivers can't
handle orientation issues that crashes app(I fix this in my activity
overriding onCreateDialog method);
Handlers aren't indicated to use
in BroadcastReceivers;
Services aren't indicated to do this kind of
task;
I thought a way to solve this. Creating a class that inherits from activity and inside that I handle orientation issues, schedule my broadcast in it to download the content at some time and displays dialog. And all of other classes inherits from it.
This solution works I think, but it's big workaround to solve the problem.
Sorry if my question is too long. I would like to discuss some other solutions(if what I proposed is a valid solution, I didn't tested).

A BroadcastReceiver object is only valid for the duration of the call
to onReceive(Context, Intent). Once your code returns from this
function, the system considers the object to be finished and no longer
active.
This has important repercussions to what you can do in an
onReceive(Context, Intent) implementation: anything that requires
asynchronous operation is not available, because you will need to
return from the function to handle the asynchronous operation, but at
that point the BroadcastReceiver is no longer active and thus the
system is free to kill its process before the asynchronous operation
completes.
In particular, you may not show a dialog or bind to a service from
within a BroadcastReceiver. For the former, you should instead use the
NotificationManager API. For the latter, you can use
Context.startService() to send a command to the service.
Source: http://developer.android.com/reference/android/content/BroadcastReceiver.html

Related

Checking for new data in background

What is the recommended approach for checking for new data regardless if the app is in the foreground or background? I am wondering which Android API people are typically using to do this. There seems to be a few ways to achieve my goal, and I want to make sure I'm on the right path.
I have something put together which uses AlarmManager.SetInexactRepeating() to call an IntentService which does the sync and inserts/updates data in the database. This works while the app is in the foreground and background, but if I force stop the app then I keep seeing "Unfortunately, has stopped working" messages when the AlarmManager alarm would've triggered. In this case, I only care about checking for new data only when the app is running in the foreground or background.
My first thought is to detect when the app is force closed, and stop the alarm, but that does not seem possible. So I am asking here, is my approach wrong? If so, which approach is used to perform some periodic task regardless if the phone is in the foreground or background? The problem with the AlarmManager solution I am using is the alarms continue to fire even when the app is closed.
If your idea is to check if your API has new data and perform a background sync to your local database or other data storage, I think you would like to take a look at this:
Creating a Sync Adapter
Running a Sync Adapter
The Sync adapter is the recommended way of achieving this in Android. The pros of using it are multiple:
Optimisations out of the box - the OS bundles calls, uses the most appropriate windows to run the sync adapter at a minimal bandwidth and battery cost
The lifecycle of your background sync component is managed internally by the OS
Observers can be notified when data has been changed so the UI can be updated easily
Multiple ways of running the sync - at intervals, automatically with the OS message to keep TCP/IP connections open or on demand
However, implementing this requires some things, that can cause a bit of a pain at first:
It is mandatory that the adapter works with a ContentProvider
Sync Adapters use Account for authentication. If this is not needed, a Stub has to be provided
For backgrounding on Android usually you use even a Service that can run alone and independently from the App or a Bounded service that takes and returns data from the App. A complete reference on backgrounding can be found here
Using a Service is the right way to go. Have your app start the Service and it will continue running while the app is in the foreground or the background. Then, if you want to kill the Service when your app closes, you could just call stopService(yourServiceIntent); from the onDestroy() override in your app's activity. That should effectively shut down the service when the app closes.
So some sample code of how this works (taken from the Services docs)...
The Service (just Logs a message every 1 second for 60 seconds):
public class MyService extends Service {
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
// Handler that receives messages from the thread
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message msg) {
long endTime = System.currentTimeMillis() + 60*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(1000);
Log.d("SERVICE", "The service is still running.");
} catch (Exception e) {
e.printStackTrace();
}
}
}
stopSelf(msg.arg1);
}
}
#Override
public void onCreate() {
HandlerThread thread = new HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
return START_NOT_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
}
}
And in your activity you would do something like:
public class MainActivity extends AppCompatActivity {
Intent serviceIntent;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
serviceIntent = new Intent(this, MyService.class);
startService(serviceIntent);
}
#Override
protected void onDestroy() {
stopService(serviceIntent);
super.onDestroy();
}

Keep Internet Connection during sleep - Softphone application

I am trying to create a softphone application that can receive and make calls with a users credentials. I have everything working but the issue I am having now is keeping the user logged in, or registered, to the SIP Client. I have read many articles to keep the Wifi connection alive while I am trying to connect. I added some code I got from another program and even that doesn't work.
What I really need is a way for the phone to stay registered all the time. I know it can be done because of apps like Linphone and Bria. Does anyone have any suggestions.
Here is my onNetworkChangeReceiver which listens for network changes. Please let me know if this code is garbage and if you have any suggestions please let me know
import com.zxd.activity.util.PhoneState;
import android.content.Context;
import android.content.Intent;
import android.support.v4.content.WakefulBroadcastReceiver;
public class OnNetworkChangeReceiver extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
PhoneState phoneState = PhoneState.getInstance();
// This is the Intent to deliver to our service.
Intent service = new Intent(context, OnNetworkChangeListener.class);
if(phoneState.threadIntent != null){
WakefulBroadcastReceiver.completeWakefulIntent(phoneState.threadIntent);
}
phoneState.setThreadIntent(intent);
// Start the service, keeping the device awake while it is launching.
startWakefulService(context, service);
}
}
PS: This receiver gets android.net.conn.CONNECTIVITY_CHANGE
This is the OnNetworkChangeListener
import android.app.IntentService;
import android.content.Intent;
import android.support.v4.content.WakefulBroadcastReceiver;
import com.zxd.activity.SipManager;
public class OnNetworkChangeListener extends IntentService{
private static String TAG = "OnNetworkChangeListener";
private static Intent i;
public OnNetworkChangeListener() {
super(TAG);
}
#Override
protected void onHandleIntent(Intent intent) {
i = intent;
SipManager sipManager = SipManager.getInstance();
sipManager.onNetworkChange();
}
#Override
public void onDestroy(){
super.onDestroy();
if(i != null){
WakefulBroadcastReceiver.completeWakefulIntent(i);
}
}
}
I used this for my app. But in the end, that damage my battery.
Android Developer Docs:
Settings.System.putInt(getContentResolver(), Settings.System.WIFI_SLEEP_POLICY, Settings.System.WIFI_SLEEP_POLICY_NEVER);
Have you tried to use WifiLock?
For my solution I looked at linphone. They are an open-source softphone application and they have a great way to implement a KeepAlive Service and KeepAlive Handler. This has worked for me
Did you try using a WakeLock? Also have a look at the PowerManager class.
Hope this helps.

How to make sure the Broadcast Receiver is disconnected

I wrote a simple Broadcast Receiver which catches incoming calls and starts activity with the caller's number:
package com.example.nrsearch;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;
public class CallReceiver extends BroadcastReceiver {
public CallReceiver() {
}
public Context context;
#Override
public void onReceive(Context context, Intent intent) {
Log.i("CallReceiverBroadcast", "onReceive() is called. ");
this.context = context;
TelephonyManager teleMgr = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
PhoneStateListener psl = new PhoneStateListener() {
#Override
public void onCallStateChanged(int state, String incomingNumber) {
Log.i("CallReceiverBroadcast", "onCallStateChanged() is called. ");
switch (state) {
case TelephonyManager.CALL_STATE_RINGING:
Log.i("CallReceiverBroadcast", "Incoming call caught. Caller's number is " + incomingNumber + ".");
startNumberDisplayActivity(incomingNumber);
}
}
};
teleMgr.listen(psl, PhoneStateListener.LISTEN_CALL_STATE);
teleMgr.listen(psl, PhoneStateListener.LISTEN_NONE);
}
public void startNumberDisplayActivity(String incomingNumber) {
Intent i = new Intent(context, MainActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.putExtra("incomingNumber", incomingNumber);
context.startActivity(i);
}
}
However, after the first incoming call I feel like my device's battery starts to drain pretty quickly. I'm afraid that some process still prevents my Broadcast Receiver from disconnecting (I mean my Broadcast Receiver may be running forever). So, is there something in my code that could cause such a behavior and does this line really stops the TelephonyManager from listening for call state changes: teleMgr.listen(psl, PhoneStateListener.LISTEN_NONE); or should I do it some other way?
EDIT: I'm almost sure that this class causes battery drain, because now I'm testing battery life with my app uninstalled and it's much lower than previous when this app was installed and broadcast receiver was called. I can't swear that this class is the cause of the drain but the battery consumption difference is clearly visible with and without this app. Could somebody look at this code and say what could cause the battety drain? Thanks in advance!
in the method
#Override
public void onPause() {
super.onPause();
mActivity.unregisterReceiver(myReceiver);
}
You can put this other places, but that's a good one. DO your registration in onResume.
Also please read the broadcastreceiver docs, they don't work the way you seem to believe they do:
http://developer.android.com/reference/android/content/BroadcastReceiver.html
Basically the receiver lifecycle is:
Receiver Lifecycle
A BroadcastReceiver object is only valid for the duration of the call
to onReceive(Context, Intent). Once your code returns from this
function, the system considers the object to be finished and no longer
active.
This has important repercussions to what you can do in an
onReceive(Context, Intent) implementation: anything that requires
asynchronous operation is not available, because you will need to
return from the function to handle the asynchronous operation, but at
that point the BroadcastReceiver is no longer active and thus the
system is free to kill its process before the asynchronous operation
completes.
In particular, you may not show a dialog or bind to a service from
within a BroadcastReceiver. For the former, you should instead use the
NotificationManager API. For the latter, you can use
Context.startService() to send a command to the service.
Edit:
As per your comments - the concern about the BroadcastReceiver eating battery life isn't real. The receiver only lasts as long as it takes to run the code in it's on receiver method. At that point Android will clean it up as it deems nescesary. If anything in your code is breaking this it would be:
this.context = context;
/// these three lines
TelephonyManager teleMgr = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
.....
teleMgr.listen(psl, PhoneStateListener.LISTEN_CALL_STATE);
teleMgr.listen(psl, PhoneStateListener.LISTEN_NONE);
since you create a object and then try to listen on it.
However you should really read the docs:
anything that requires
asynchronous operation is not available,
Which is EXACTLY what you are doing in your code - attaching to a service and and waiting for its asyncronous response. This isn't acceptable in a BroadcastReceiver, and is clearly indicated in the docs at this point:
In particular, you may not show a dialog or bind to a service from
within a BroadcastReceiver.

Service does not run from BroadcastReceiver? - Android

I am trying to get my service to run when there is an outgoing call on my phone. But for some reason my service does not run when this happens. I know the code for the "CallReceiver" executes since I used a toast message to display if that runs. I am able to run the service through my main activity, but this means it will run regardless of whether an outgoing call is made....
Below is my code:
The Receiver:
package com.example.hiworld;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.Toast;
public class CallReceiver extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
context.startService(new Intent(context, CallService.class));
Toast.makeText(context, "Call Receiver started",
Toast.LENGTH_LONG).show();
Log.d("Calling Someone", "onReceived");
}
}
The Service:
package com.example.hiworld;
import android.app.IntentService;
import android.content.Context;
import android.content.Intent;
import android.telephony.TelephonyManager;
import android.widget.Toast;
public class CallService extends IntentService {
public long StartTime=0;
public long EndTime =0;
public long TotalTime = 0;
public long NumFreeMins = 0;
public CallService() {
super("CallService");
}
#Override
protected void onHandleIntent(Intent intent) {
StartTime = (System.currentTimeMillis())/60;
TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
if(tm.getCallState()==0) //getting the time whenever the phone is off
{
EndTime = (System.currentTimeMillis())/60;
TotalTime = EndTime-StartTime;
NumFreeMins = 300-TotalTime;
//notify user
this.displaymsg();
}
}
public void displaymsg()
{
Toast toast = Toast.makeText(getApplicationContext(), ""+NumFreeMins, Toast.LENGTH_SHORT);
toast.show();
}
}
I have seen some people use the line:
context.startService(new Intent(this, CallService.class));
instead of:
context.startService(new Intent(context, CallService.class));
but the latter does not work for me...
Try specifying an <intent-filter> for your IntentService in the manifest for a specific 'action'. Example...
<service
android:name=".CallService" >
<intent-filter>
<action android:name="com.example.hiworld.intent.DO_SOMETHING" />
</intent-filter>
</service>
Then in the onReceive(...) method of your BroadcastReceiver do something like the following...
Intent callReceiverIntent = new Intent("com.example.hiworld.intent.DO_SOMETHING");
// Put the Intent received by the BroadcastReceiver as extras so the
// IntentService can process it...
callReceiverIntent.putExtras(intent);
context.startService(callReceiverIntent);
EDIT:
I built a simple test app based on the code you posted to pasrebin. I kept the code identical for the manifest, receiver and service and simply had to add a default Activity in order to get it to run.
I can see from monitoring logcat in the DDMS perspective of eclipse that the CallReceiver successfully receives the NEW_OUTGOING_CALL Intent and does, in fact, start the CallService.
The problem is, however, with attempting to show a Toast from an IntentService which causes an exception due to a 'leaked handler' and silently crashes.
The reason behind this is that an IntentService uses a background thread to carry out its work and trying to show the Toast (i.e., a UI element) from a non-UI thread won't work as the app has no UI components running. So, regardless of whether you use the local context variable or getApplicationContext(), there simply is no UI context with which to associate the Toast.
The reason why it works when starting the CallService from an Activity is obviously because the Activity provides a UI context that can be used by the Toast. In short, it generally seems that attempting to use Toast from an IntentService isn't a good idea unless the IntentService is always started by a UI component and even then, having a background thread create UI elements (such as a Toast) may cause problems.
The only way that this can be made to work with the current model is to change the IntentService to a Service. By default, code execution of a Service is done on the main (UI) thread and it is quite legal for a Service to show a Toast regardless of whether there are any of the app's Activities showing or not. In fact, I modified the code to use a Service and placed the code from onHandleIntent in onStartCommand() and I can see the Toasts when making an outgoing call.
Try
context.startService(new Intent(context.getApplicationContext(), CallService.class));

How do I cancel all pending intents that are qued for intent Service

I have an intentservice that gets qued by the user and by my app automatically. I need to be able to kill all pending intents that are qued when the user logs out of my application, but I cannot seem to get that to work. I have tried stopService() and stopself(), but the intents continue to fire off the intentservice after the user has logged out. I would try to get the id of the intent but that is difficult as everytime the intentservice starts, the variable holding the intent id's is empty. Here is my intentservice code:
public class MainUploadIntentService extends IntentService {
private final String TAG = "MAINUPLOADINTSER";
private GMLHandsetApplication app = null;
private SimpleDateFormat sdf = null;
public boolean recStops = true;
public MainUploadIntentService() {
super("Main Upload Intent Service");
GMLHandsetApplication.writeToLogs(TAG,
"GMLMainUploadIntentService Constructor");
}
#Override
protected void onHandleIntent(Intent intent) {
GMLHandsetApplication.writeToLogs(TAG, "onHandleIntent Started");
if (app == null) {
app = (GMLHandsetApplication) getApplication();
}
uploadData(app);
GMLHandsetApplication.writeToLogs(TAG, "onHandleIntent Finished");
}
#Override
public void onDestroy() {
GMLHandsetApplication.writeToLogs(TAG, "onDestroy Started");
app = null;
stopSelf();
GMLHandsetApplication.writeToLogs(TAG, "onDestroy completed");
}
public void uploadData(GMLHandsetApplication appl) {
//All of my code that needs to be ran
}
Unfortunately, I don't think it's possible to accomplish that with the standard IntentService methods since it doesn't offer a way to interrupt it while it's already going.
There are a few options I can think of that you can try to see if they fit your need.
Copy the IntentService code to make your own modifications to it that would allow you to remove pending messages. Looks like someone had some success with that here: Android: intentservice, how abort or skip a task in the handleintent queue
Instead of copying all the IntentService code, you might also be able to Bind to it like a normal Service (since IntentService extends Service) so you can write your own function to remove pending messages. This one is also mentioned in that link.
Rewrite the IntentService as a regular Service instead. With this option, you'd have more control over adding and removing messages.
I had what sounds like a similar situation where I was using an IntentService, and I eventually just converted it to a Service instead. That let me run the tasks concurrently and also cancel them when I needed to clear them.
Here
When should I free the native (Android NDK) handles? is the HangAroundIntentService class that has the method cancelQueue().
The class also has the method
public static Intent markedAsCancelIntent(Intent intent)
that converts an intent into a cancel intent, and
public static boolean isCancelIntent(Intent intent).
The class is based on the open-sourced Google's code.
Just a thought but inside of your onhandleintent can you have an argument that checks to see if app is running if not then don't run the code? example. In the start of your app you could have a static var
boolean appRunning;
Next in your onhandle of the intent, when you set the appRunning to false, after an onPause or onDestroy of activity, you could wrap the onhandleintent code in a boolean:
protected void onHandleIntent(final Intent intent) {
if(MainActivity.appRunning){
...
}
}
Just a thought

Categories

Resources