I am used to developing standalone applications, ones that you click on, it runs, and when you are done, you exit.
I am now interested in tackling a new type (not sure if that's the right word) of app, and was wondering how I should go about it. I am not sure what to research, and would appreciate your advice to help me get the ball rolling. I'll give you an idea about what I have in mind.
My app would need to perform a special action in the dialer. When the user dials a number and is in the middle of a call, I would like the user to be able to press the Menu key, and find an option to scroll through all their contacts (either the stock app, or my own list which I grab from the contacts stored in the phone), and select one. Upon selection, that contact's number is pasted into the dialer (keep in mind, in the middle of a call).
I certainly don't expect an answer telling me how to do this exactly, I just need some guidance as I have never written an app of this nature before. On top of that, is it even possible to do what I want to do?
Thank you.
You need to go through Android Service or IntentService. A Service is an application component that can perform long-running operations in the background and does not provide a user interface(UI).
The following example is taken from android blog which is an implementation of the Service class
public class HelloService 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) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
long endTime = System.currentTimeMillis() + 5*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
// Stop the service using the startId, so that we don't stop
// the service in the middle of handling another job
stopSelf(msg.arg1);
}
}
#Override
public void onCreate() {
// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block. We also make it
// background priority so CPU-intensive work will not disrupt our UI.
HandlerThread thread = new HandlerThread("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
// For each start request, send a message to start a job and deliver the
// start ID so we know which request we're stopping when we finish the job
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
// If we get killed, after returning from here, restart
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
// We don't provide binding, so return null
return null;
}
#Override
public void onDestroy() {
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
}
}
On the other hand, The same thing can be achieved using IntentService, which is a base class for Services that handle asynchronous requests on demand.
public class HelloIntentService extends IntentService {
/**
* A constructor is required, and must call the super IntentService(String)
* constructor with a name for the worker thread.
*/
public HelloIntentService() {
super("HelloIntentService");
}
/**
* The IntentService calls this method from the default worker thread with
* the intent that started the service. When this method returns, IntentService
* stops the service, as appropriate.
*/
#Override
protected void onHandleIntent(Intent intent) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
long endTime = System.currentTimeMillis() + 5*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
}
}
You can also go through SO post https://stackoverflow.com/a/4353653/432903
If your app isn't mainly written in javascript/webview/phonegap, then all you have to do is look at the Service class. That class and the linked documents tell you everything you need to know.
maybe you can use an IntentFilter so you can get a system notify when the user uses a dialer.
and you should learn the Service component which can work in background in android.
Related
I was reading about services in Android and especially i came down to this
While an app is in the foreground, it can create and run both
foreground and background services freely. When an app goes into the
background, it has a window of several minutes in which it is still
allowed to create and use services. At the end of that window, the app
is considered to be idle. At this time, the system stops the app's
background services, just as if the app had called the services'
Service.stopSelf() methods.
In the code below, when the app goes to background after one minute or so the services gets destroy but the thread still executes.
So what is the point of killing a service? The process/thread is still being execute.
Why killing a service in the first place? What if i want to execute a download process and NOT wanted to be a foreground process?
public class HelloService 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) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
Log.d("sssssssss",msg.toString()+"sssssssssss");
while(true){
Log.d("sssssssss","sssssssssss");
}
// Stop the service using the startId, so that we don't stop
// the service in the middle of handling another job
//stopSelf(msg.arg1);
}
}
#Override
public void onCreate() {
// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block. We also make it
// background priority so CPU-intensive work doesn't disrupt our UI.
HandlerThread thread = new HandlerThread("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
// For each start request, send a message to start a job and deliver the
// start ID so we know which request we're stopping when we finish the job
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
// If we get killed, after returning from here, restart
return START_STICKY;
}
#Override
public void onDestroy() {
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
System is letting You perform a clean finish of the Service by triggering its destroy method. Your thread won't run forever, I think 30 min is hard limit before app process is killed.
This is new policy since android O to preserve battery life and improve performance. A lot of developers performed heavy operations in background (sockets open non-stop, periodic sensor readings etc.) and without foreground notification users were unaware of why their devices were sluggish and had poor battery uptime.
Read more on Background Execution Limits doc.
Im trying to achive, that my service downloads information and fills a database with that information in the background.
SOLUTION (Using foreground service, code is now the edited-version)
Thats the service:
public class UnitPullService extends Service {
private final static String name = UnitPullService.class.getSimpleName();
public static Boolean isRunning = false;
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message msg) {
try {
UnitDataSource unitdataSource = new UnitDataSource(getApplicationContext());
unitdataSource.fillTables();
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
SharedPreferences.Editor spEitor = sharedPreferences.edit();
// disables the button which starts this service
spEitor.putBoolean("isFilled", true);
spEitor.commit();
} catch (JSONException 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) {
// PART OF THE SOLUTION
startForeground(1000, new Notification());
Toast.makeText(this, "service starting",
Toast.LENGTH_SHORT).show();
// to check if the service is currently running
isRunning = true;
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
return START_REDELIVER_INTENT;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
}
}
And thats the intent in the activity (actually from a fragment):
Intent i= new Intent(getContext(), UnitPullService.class);
getContext().startService(i);
In the manifest, the service is declared like:
<service
android:name=".UnitPullService"
android:exported="false"/>
The point is, that i want to do the whole "download and database filling"-stuff completly independent from the status of the app which is starting the service (so that the user can use other apps while the service is doing its job).
EDIT (Made false assumptions)
The following behaves weird:
Start application
Start service
Close application (swipe away from running-apps-overview)
Start application before service has finished
"service starting"-toast shows up, handleMessage() gets called and DatabaseErros happening (because of unique constrains, but thats not the point).
the other scenario:
Start application
Start service
Close application (swipe away from running-apps-overview)
fillTables() has finished.
It seems that directly after fillTables() has finished and the service gets restarted (Toast shows up).
Q1: So where is my fault, that the running service is going to restart when I start the application while a running service has not finished yet?
Q1.1: How do i call/create a completly independent service, which does not get restarted in such an anoying way, while doing its job?
Swiping away the app puts it in Force-close state. That kills any services attached to it. It won't allow the service or any other activity in the app to run again until the user has launched it.
Now if the user doesn't swipe away, your service will continue to run no matter what app is in the foreground. And if the service is killed by the OS for resources, you can have it automatically restart when they're available. But nothing will make it run services after being swiped away.
I'm trying to create a periodical service where I can validate certain values stored on my mobile with the data I have on my API Server. If a user's password gets changed or if the user gets deleted the API Server should send a response back so the mobile app knows the user should be signed out. The request/response is not the problem. Just having issues getting the service periodically.
MyService.class:
public class MyService extends IntentService {
private static final String TAG = "MyService";
public MyService() {
super("MyService");
}
#Override
protected void onHandleIntent(Intent intent) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
try {
Thread.sleep(5000);
Log.d(TAG,"loop service");
} catch (InterruptedException e) {
// Restore interrupt status.
Thread.currentThread().interrupt();
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
return super.onStartCommand(intent, flags, startId);
}
}
Inside the onCreate of another random class:
Context mContext = getApplicationContext();
Intent i= new Intent(mContext, MyService.class);
i.putExtra("KEY1", "Value to be used by the service");
mContext.startService(i);
The service does start (it shows me the Log.d), how do I continue from here to get it restarted or to make it start after certain time?
First things first: IntentService is designed only to execute a queue of tasks off the main thread. So you must create a new one every time you want to check for updates: for that see Alarm Manager
But this is not necessarily the best way to do it. Instead of you polling the server for changes, try the push notifications approach.
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();
}
A simple question, how to make a Service repeat at log cat console msg "hello", as long as the Service is living/is active? I've tried:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.v("LocalService", "Received start id " + startId + ": " + intent);
Mano lopas = new Mano(this);
lopas.Lopas();
while(true) {
Log.v("HAHA", "hello");
}
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}
But it never returns START_STICKY, actually it doesnt even build the project. I think im missing something about services? I start my service, in MainActivity in my application, like:
startService(new Intent(getApplicationContext(),Myclass.class));
And it runs only once without my while loop in my service. It says me something in Logcat and this is it. Morever, my service starts so many times as many times I re-open my application. How to make service start run once and "forever", I mean untill it gets killed by the system or user kills it.
Services don't have their own thread, they run on the UI thread. If you want it to run in parallel, you need to create a Thread. So here you should create a Thread in your onStartCommand, and that thread should do whatever it is you want.
Same way you'd log something at intervals in your activity: use a Handler. Post a task that logs a message and re-posts itself to the same Handler using postDelayed(...). Cancel any pending execution in your activity's onPause() or your service's onDestroy().
Note that there is almost never a good reason to explicitly create a Thread or use J2SE constructs like TimerTask in Android. For heavyweight tasks, use AsyncTask. For lightweight tasks, use Handlers. In your case, creating a new Thread would be a ridiculously heavyweight solution for what you want to do.
First of all, do not forget to register the Service in your Manifest.
This can be done by using
<service android:enabled="true" android:name=".services.Paycan" />
Next is, that your Service (if its not an intentservice) run in the MainUI. If you create an endless loop make sure it's in a background thread, otherwise it will freeze the app.
You can do it using an Asynctask, a Thread which get created with onStartCommand and stopped in onDestroy or use an IntentService instead of a Service.
example:
public class yourTestClass extends Service {
Thread testThread;
boolean threadRunning = false;
#Override
public void onCreate() {
super.onCreate();
testThread = new Thread() {
#Override
public void run() {
try {
while(threadRunning) {
Thread.sleep(1000);
Log.i("test", "....");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
threadRunning = true;
testThread.start();
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
threadRunning = false;
}