Flow of execution - java

Suppose I have one activity , MainActivity and one service MyService in my android studio project.
MainActivity.class
Toast.makeText(this,"Point 1",Toast.LENGTH_SHORT).show();
startService( new Intent(this,MyService.class) ) ;
Toast.makeText(this,"Point 3",Toast.LENGTH_SHORT).show();
MyService.class
Toast.makeText(this,"Point 2",Toast.LENGTH_SHORT).show();
The flow of execution is Point 1 -> Point 3 -> Point 2 & not 1->2->3
Since service runs in main ui thread only , so what actually happens in background ? since all 3 points execute on same thread .
EDIT:
What can I do to make execution like 1->2->3 ?
Using a static variable in MyService(and setting a value there) and importing that variable in MainActivity to check if service has been started successfully ,doesnt work( causes ANR- reason: Starting .MyService ).
In MainActivity
while(! staticvar.hasBeenSetInMyService){ }
It gets stuck in endless while loop . So is the static variable never updated in MainActivity or MyService's onStartCommand() doesnt get executed ??

There are several ways to let components exchange information. In your case, the Activity could register a BroadcastReceiver with the LocalBroadcastManager before starting the Service.
private BroadcastReceiver myReceiver = new BroadcastReceiver(){
public void onReceive(Context context, Intent intent) {
Toast.makeText(context,"Received broadcast from Service",Toast.LENGTH_SHORT).show();
MainActivity.this.doPointThree();
}
}
The Activity has to register and (never forget!) unregister the BroadcastReceiver, but I won't go into details here and link instead to this post on Stack Overflow.Let's just assume the BroadcastReceiver will be registered with an
IntentFilter if = new IntentFilter();
if.addAction("your.package.com.POINT_2");
The Service on the other hand can send a local broadcast once it has reached "Point 2".
Intent intent = new Intent();
intent.setAction("your.package.com.POINT_2");
LocalBroadcastManager.getInstance().sendBroadcast(intent);
This local broadcast will trigger the BroadcastReceiver's onReceive()
Please note that I'm talking about local broadcasts as opposed to system wide, inter-app broadcasts. Local broadcasts are easy on the system and totally private to your app.

Related

startActivity() in BroadcastReceiver

I am trying to create an application that calls the sender of an SMS as soon as the smartphone receives an SMS.
This is my code:
public class SmsReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("android.provider.Telephony.SMS_RECEIVED")){
// ... (Managing SMS)
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:" + sender));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
}
But it dials and calls the sender only when the application is in foreground, while I'd like it to work always. Using the debugger, the execution flows, but it is unable to start the ACTION_CALL activity somehow. Am I missing anything? Thank you very much in advance
Since Android 10, there are limitations on when/how background processes (Service, BroadcastReceiver) can launch activities. This is your problem.
See this guide for more details.
while I'd like it to work always
That is not an option on modern versions of Android. You cannot start an activity from the background, because you do not know what is going on in the foreground at the time. For example, if the user is relying on a navigation app for driving, taking over the foreground could cause the user to crash.
You could raise a high-priority Notification instead.

How to communicate between android and another process using broadcast receiver

I have a problem with registering a broadcastReceiver between my Activity and my Service which is defined in another process in my manifest.
I've tried some other tips like using handlers or ContentProvider
for communicating but it did't work as I expected, In fact I want to get the data continuously.
Here is my code in my Service:
Intent locationIntent = new Intent("LocationIntent");
locationIntent.setAction("updatedLocations");
locationIntent.setClass(getApplicationContext(), MapBoxActivity.class);
locationIntent.putExtra("list",updatedList);
sendBroadcast(locationIntent);
and I register it in OnCreate of my Activity:
updatedLocationBroadcast = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Timber.tag("localB").d("registered!");
if (intent != null && intent.getAction() != null && intent.getAction().equals("updatedLocations")) {
sawLocationsList = (HashMap<Integer, MarkerItem>)intent.getSerializableExtra("list");
Timber.tag("sawL").d("updated" + sawLocationsList.toString());
}
}
};
registerReceiver(updatedLocationBroadcast , new IntentFilter("updatedLocations"));
As I expected, I wanted to see my broadcastReceiver registers and my Timber logs localB registered! which is defined in my receiver but it doesn't work.
So, What's the best way to communicate between Activity and Service defined in another process and getting data continuously???
Note : my service gets data from server but the server is not realTime so I check the data by requesting periodically to server using handlers.
but it doesn't work
That is because you over-specified your Intent. Replace:
Intent locationIntent = new Intent("LocationIntent");
locationIntent.setAction("updatedLocations");
locationIntent.setClass(getApplicationContext(), MapBoxActivity.class);
locationIntent.putExtra("list",updatedList);
sendBroadcast(locationIntent);
with:
Intent locationIntent = new Intent("updatedLocations");
locationIntent.putExtra("list",updatedList);
sendBroadcast(locationIntent);
Note, though, that any app will be able to listen to this broadcast. Consider using setPackage() on the Intent to restrict the delivery to your own app.
What's the best way to communicate between Activity and Service defined in another process and getting data continuously?
If I were forced into this process separation, I would consider a Messenger.
my service gets data from server but the server is not realTime so I check the data by requesting periodically to server using handlers.
That hasn't been a recommended pattern in years. Please use WorkManager. Or, use JobScheduler, if you are not in position to adopt WorkManager (since it is part of AndroidX). In either of those approaches, you could get rid of the second process and greatly simplify your communications.

Prevent app from running on two instances

I just recently added the capability of my app to check for updates on our local server (this app is not published in the Google Play store. It's going to be used internally and there is no internet connection where it will be used. Don't ask. That's how it is :) ). I keep track of updates by checking a certain table in SQL Server and if my app's version is lower than what is indicated in this table, I download the new APK from an internal website (LAN only) then install the APK. I also have another application in the device that listens for PACKAGE_ADDED broadcasts. I can capture the broadcast successfully.
The problem is, after installation, the broadcast receiver starts the app by calling the following.
public class PackageInstalledBroadcastReceiver extends BroadcastReceiver {
private final String MY_PACKAGE_NAME = "com.company.packagename";
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_PACKAGE_ADDED)) {
String packageName = intent.getData().getSchemeSpecificPart();
if (packageName.equalsIgnoreCase(MY_PACKAGE_NAME)) {
Intent i = new Intent();
i.setClassName(MY_PACKAGE_NAME, MY_PACKAGE_NAME + ".LoginActivity");
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
}
}
}
But it takes a long time for the app to start. The user might think that nothing is happening, so he/she can start the app manually by clicking the app's icon. If the user clicks the app's icon, the app starts immediately. After a while, the activity that the broadcast receiver started is also opened. So now, I end up with two instances of my app in the same activity (LoginActivity). I can say that this is the case, because if I press the Back key on the device from the LoginActivity, I still end up in another LoginActivity and if I press the Back key again, I end up on the device's desktop.
You have two problem in your question:
The fist, why your BroadcastReceiver take a long time to start your activity.
It have not enough information for stoving this.
The second your want to make your activity have a single instance.
Android provide a way to do that:
Step one: Visit your application androidmanifest file
Step two: Find your activity declaration.
Step there: Add the following property android:launchMode = "singleInstance"
The reference here:
P/s: If you could provide my some more information of your fist problem. Please create a new question. Hope you resolve it.

Local broadcast in android?

I have two activity activityA and activityB.
activityA is starting a background process and mean while it will also start activityB
background process started by activityA will finish after sometime. Is it possible to notify activityB when process is finish by activityA if activiyB in foreground and activotyA is background.
Yes it is definitely possible and LocalbroadcastMaanager is a good choice here is how you do it.
In the long running process, write this at the end of the process
LocalBroadcastManager.getInstance(context).sendBroadcast(
new Intent("backgroundProcessCallBack"));
And in your activity you should have this (inside oncreate):
LocalBroadcastManager.getInstance(context)
.registerReceiver(mTaskListener, new IntentFilter("backgroundProcessCallBack"));
where mTaskListener is a class level variable (BroadcastReceiver):
private BroadcastReceiver mTaskListener = new BroadcastReceiver() {
#Override
public void onReceive(Context cxt, Intent intent) {
// Do you stuff here. You got the callBack in your activity
}
};
then unregister this receiver in activity's onDestroy() method.
You cannot have two activities running at the same time.
If your background process runs in a Service, you can subsribe to broadcasts in Activity A and in Activity B.
When the work in Service is finished, it sends broadcast - and whatever Activity (A or B) is in foreground - it will receive a Broadcast.
Sample code here: Programmatically register a broadcast receiver

Service crashing and restarting

There are several questions about it but I always read the same thing: "the service will be killed if the system need resources" or "you can't build an service that runs forever because the more it runs in background, more susceptible it is to the system kills it" and etc.
The problem I'm facing is: My service runs fine and as it is expected, if I run my app then exit it my service is still running, but when I kill my app (by going to the "recent apps" and swype it away) the service stops. In this moment, if I go to the Settings >> aplications >> running I'll see that the service is restarting. After a while, it goes back and my Service run with no problem.
I google it and I find some things I could do but lets see my code first:
I start my service by this way (after a button click):
Intent intent = new Intent (MainActivity.this, MyService.class);
startService(intent);
I also have 3 Integers I put in extra, so I have something like this:
final Integer i, i2, i3;
i = 5; //for example
i2 = 10; //for example
i3 = 15; //for example
final Intent intent = new Intent (MainActivity.this, MyService.class);
intent.putExtra("INTEGER1", i);
intent.putExtra("INTEGER2", i2);
intent.putExtra("INTEGER3", i3);
startService(intent);
In MyService I have the folloywing:
public class MyService extends Service
{
AlarmManager am;
BroadcastReceiver br;
PendingIntent pi;
Integer i, i2, i3;
#Override
public void onCreate()
{
super.onCreate();
am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
pi = PendingIntent.getBroadcast(this, 0, new Intent("anyany"); 0) //Why those zeros?
br = new BroadcastReceiver ()
{
public void onReceive (Context context, Intent i) {
new thread(new Runnable()
{
public void run()
{
//do something
}
}).start();
}
};
}
#Override
public void onStartCommand(Intent intent, int flags, int startId)
{
super.onStartCommand(intent, flags, startId);
try
{
i = intent.getIntExtra("INTENT1", 0) // I don't understant yet why this zero are here
i2 = intent.getIntExtra("INTENT2", 0)
i3 = intent.getIntExtra("INTENT3", 0);
}
catch(NullPointerException e) {}
this.registerReceiver(br, new IntentFilter("anyany"));
new thread(new Runnable()
{
public void run()
{
am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock. elapsedRealtime() + i*1000, i2*1000, pi);
}
}).start();
return START_REDELIVER_INTENT; //so I can get my Extra even with my Activity closed
}
My onDestroy:
#Override
public void onDestroy()
{
unregisterReceiver(br);
super.onDestroy();
}
I also have onBind() method (without #Override), but it returns null.
I google a lot and I tried to run the service in foreground, so I did this (inside de onStartCommand):
Notification n = new Notification(R.drawable.ic_laucher), getText(R.string.app_name), System.currentTimeMillis());
PendingIntent npi = PendingIntent.getActivity(this, MainActivity.class);
n.setLatestEventInfo(this, getText(R.string.notification_title), getText(R.string.notification_message), npi);
startForeground(3563, n);
My notification appears and when I click on it my app runs, but the problem with my service wasn't fixed (I believe it still not run on foreground). The notification is restarted too.
I also deleted the Try catch and I define a value for the integers (so I didn't use the getIntExtra() method), but nothing changed
After several tests I tried to see the logs, when I kill my App I have the following message: Scheduling restart of crashed service.
So, for some reason my service crash when my MainActivity dies, why? The intention here is not to transform the service in a god that can not be killed (I don't think it is impossible at all, the WhatsApp are running for 105 hours !) but prevent my Service to not being crashed after my App dies.
I don't know if this'll help but this is what I add on my Manifest.xml
<Activity android:name = ".MyService"/>
<service android:name ="Myservice" android:enabled="true" android: exported="false"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
Min API = 9, target API = 17.
Size of the Service when running: about 3MB.
Hope I was clear and sorry for my English.
PS: the entire code are running as expected, so if you see any sintax error fell free to edit it.
EDIT
If I add android:isolatedProcess="true" in the <service> in AndroidManifest.xml I receive this error in logCat: java.lang.RuntimeException: Unable to create a service in com.mycompany.myapp.myservice: java.lang.SecurityException: Isolated process not allow ed to call getIntentSender
When I start my service using this, the MainActivity does not show any erros, only the service crashes.
I finally found the solution ! I removed the AlarmManager from the Service and the service does not cashed anymore, but I have to use it
The problem is the service crash after the user swype away the app from Recent App, so what I did was prevent the app to appear in that window. Add the following to your AndroidManifest.xml as a child of <activity>
android:excludeFromRecents="true"
Now when the user exit from your app it wil not appear in the recent apps window, what means the system kills the Activity right after you exit it, so it'll not waste any resources.
PS: don't forget to set the service to run in a separate process, add the following to your AndroidManifest.xml, as a child of <service>
android:process=":remote"
EDIT - REAL SOLUTION FOUND
After a lot of research and study (months of study) I took a deep look at android APIs and here is what a found, this is na expected behaviour that occours only at API 16+, a change at android arquiteture changed the way that PendingIntents are broadcasted by the system, so Google added the flag FLAG_RECEIVER_FOREGROUND, you must pass this flag to the intent you are using as a parameter on the PendingIntent.getBroadcast(), here is na example:
if(Build.VERSION.SDK_INT >= 16) //The flag we used here was only added at API 16
myIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
//use myIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); if you want to add more than one flag to this intent;
PendingIntent pi = PendingIntent.getBroadcast(context, 1, myIntent, 0); // the requestCode must be different from 0, in this case I used 1;
Android versions older than API 16 will work as expected, the service won't crash if you swype away the app from Recent Apps page.
As the documentation says, a Service runs in the main thread of its callee, that usually is the UI Thread. So what is happening is that when you kill your application, you kill your application process and thus the service is killed too.
You can workaround this behavior by creating your Service in a different process by using android:process in your <service> tag in the Manifest.xml file.
Usually, though, you start a Service in its own process if the Service needs to be independent from the callee and if it may be used by different application. If your Service is for your own application use only, then stick with the default behavior and simply don't kill you application.
EDIT 1:
The documentation for android:isolatedProcess says:
If set to true, this service will run under a special process that is
isolated from the rest of the system and has no permissions of its
own. The only communication with it is through the Service API
(binding and starting).
From another SO answer (Link), this is the expected behavior. But surely, someone here will have a workaround or a solution.
Your questions from code:
pi = PendingIntent.getBroadcast(this, 0, new Intent("anyany"); 0)
//Why those zeros?
The first zero you see is mentioned as a requesCode and decribed as not being used presently:
requestCode: Private request code for the sender (currently not used).
The second zero should actually be one of the flags given (here).
i = intent.getIntExtra("INTENT1", 0) // I don't understant yet why
this zero are here
The getIntExtra(String, int) method of Intent doesn't need to have 0 as its second argument: it can be any integer. getIntExtra(String, int) returns an integer corresponding to the String key you provide. In the event that this key no long exists(or never did), getIntExtra(String, int) returns the integer we pass as the second argument. It is the default value when the key fails.

Categories

Resources