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.
Related
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.
I have a intent service that receives explicit intents from other activities on what to do, and currently returns an implicit intent with the data as extras, like below:
Intent someImplicitIntent= new Intent(APP_PACKAGE_NAME + ACTION_SOME_DATA);
someImplicitIntent.putExtra(APP_PACKAGE_NAME + EXTRA_SOME_DATA, "HELLO");
sendBroadcast(someImplicitIntent);
Software design wise, I have multiple activities within the app, that when running, would use the identical data for doing different things. So, currently I have setup broadcast receivers in each activity to listen for "APP_PACKAGE_NAME + EXTRA_SOME_DATA" and then act accordingly, like interrupting something. The broadcast receivers are tied to the their respective activities' life-cycles
This setup is fine for testing functionality. But the problem is, I understand that implicit intents in Android broadcasts the intents system wide, so any app which a registered broadcast receiver listening to the action would received the intent and its extra. Implicit intents are great for its intended purposes, but for me the convenience of minimal code and sending just one and multiple recipients are carrying obvious security risks.
I'm thinking of making explicit intents, but my current & limited understanding is I would have to create an explicit intent for each activity in order to keep the data private within the scope of the application, like below:
Intent someExplicitIntent= new Intent(this, activityOne.class);
someExplicitIntent.setAction(APP_PACKAGE_NAME + ACTION_SOME_DATA)
someExplicitIntent.putExtra(APP_PACKAGE_NAME + EXTRA_SOME_DATA, "Hi");
sendBroadcast(someExplicitIntent);
Intent someExplicitIntent2= new Intent(this, activityTwo.class);
someExplicitIntent2.setAction(APP_PACKAGE_NAME + ACTION_SOME_DATA)
someExplicitIntent2.putExtra(APP_PACKAGE_NAME + EXTRA_SOME_DATA, "Hi");
sendBroadcast(someExplicitIntent2); ..... so on, for the same data to N activities
The snippet above look needlessly redundant, and will be wasting resources sending intents that won't be picked up. How can I securely send one intent extra to multiple receivers within an application?
You can make use of LocalBroadcastManager to send broadcasts which can be recieved only by the components of your application, thereby making it secure.
So, you can do as follows :
Intent someImplicitIntent= new Intent(APP_PACKAGE_NAME + ACTION_SOME_DATA);
someImplicitIntent.putExtra(APP_PACKAGE_NAME + EXTRA_SOME_DATA, "HELLO");
LocalBroadcastManager.getInstance(this).sendBroadcast(someImplicitIntent);
Thereafter, just register your broadcast receiver in those components in which you want the intent extra/s.
In this way, you will be able to send intent extra/s through local broadcasts.
For more information about LocalBroadcastManager, you can check out the following link :
https://developer.android.com/reference/android/support/v4/content/LocalBroadcastManager.html
I need to detect when my app is being uninstalled. For that, I've seen logcat sends out UNINSTALL_PACKAGE intent, and I simply added it to existing Broadcast Receiver I have. But it just doesn't catch it, while other intent I'm listening to, TIME_TICK, works perfectly. Why?
Code currently used:
private IntentFilter intentFilter;
static {
intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_TIME_TICK);
intentFilter.addAction(Intent.ACTION_UNINSTALL_PACKAGE);
}
private final BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (action.equals(Intent.ACTION_UNINSTALL_PACKAGE) {
Log.e("App", "Uninstalling");
} else if (action.equals(Intent.ACTION_TIME_TICK){
// this works
}
}
};
I need to detect when my app is being uninstalled
There is no supported way to do this, for obvious security reasons.
But it just doesn't catch it
ACTION_UNINSTALL_PACKAGE is an activity action. Nothing on an Android device should be sending it as a broadcast. Hence, you cannot listen to it via a BroadcastReceiver.
I 've seen logcat sends out UNINSTALL_PACKAGE intent, and I simply added it to existing Broadcast Reciever I have. But it just doesn't catch it, while other intent I'm listening to, TIME_TICK, works perfectly. Why?
Whenever your application is being uninstalled, Android OS kills all the components associated with that application as well free memory & resources. Uninstall broadcast receiver intent was to listen uninstall event of others app not for your app.
If you want to capture uninstall event of your app then there is no concept google provides, however you can do by observing data change in your file system where you need to keep monitoring changes in file system.
This solution is not going to work for all phones & different versions of Android.
Can't you just listen for android.intent.action.PACKAGE_REMOVED with your IntentFilter when you uninstall using ACTION_DELETE or ACTION_UNINSTALL_PACKAGE?
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.
I need some suggestions for approaches to take...
Here's some background info:
Right now I have an Android app and a separate java program running on my server.
The java program continuously go out and gets information from different sites and stores them in 14 different entries in an SQL database on the server.
The Android app then queries the databases to retrieve the info to be displayed.
My goal:
I need suggestions on how to have the app handle checking for updates from the database, and then letting the user know that there is new information.
My first thought is that maybe I need to start a separate thread that queries the database for a time modified. Then if it finds updates, it would pop up on the screen that there is new information.
I'm not too well educated with the way threads or services work, so I guess I'm looking for how to implement this, or whether there is a completely different way to go about update checking that would be better.
Thanks in advance, I appreciate any feedback, input, or suggestions.
Hi Ryan I have also implemented a similar thing in my android app and surprisingly I also had 14 tables in my PostgreSQL Server. First of all, you would want to poll the server periodically even when the app is not in the foreground. For that you need to run a background Service - here you will have to manually create a thread in the service, because Service by default runs on the UI thread OR use an IntentService - you don't have to create a separate thread. Whatever code you write in the intent service will be handled in a different thread automatically
Now you have to make this service execute periodically. For that use an AlarmManager and use the setRepeating()function. In the arguments you have to give a PendingIntent to your Service or IntentService. But don't use an alarm manager if you are going to poll the server for every less than 1 minute. Because the battery will be wasted a lot.
Here is some code that might give you an idea :
function setalarm()
{
Intent intent = new Intent(getBaseContext(), Intent_Service.class);
PendingIntent sender = PendingIntent.getBroadcast(getBaseContext(), 192837, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Random randomGenerator = new Random();
long interval=60000; //1 minute in milliseconds
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
am.setRepeating(AlarmManager.RTC, cal.getTimeInMillis(),interval,sender);
}
This is Intent_Service of type IntentService :
public class BackService extends IntentService
{
Context context=this;
//public Timer t=null;
public BackService()
{
super("myintentservice");
}
#Override
protected void onHandleIntent(Intent intent)
{
try
{
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "My Tag");
wl.acquire();
//..CPU will remain on during this section..
//make our network connections, poll the server and retrive updates
//Provide a notification if you want
wl.release();//Release the powerlock
}
}
}
But if you want instantaneous updates, then use Google Cloud Messaging Services. To know more about how it works see this
Hope this helps you.