I have an app in which I'm trying to detect WHEN the Internet connection appears and when it disappears.
At the moment, when it appears, I'm starting a new thread (different from the UI) which connects my app to a remote server.
For that I'm hardly trying to implement a broadcast receiver which LISTENS for connectivity, but I'm having problems in understanding the concept.
In my onCreate() I have somethig like:
onCreate()
{
cThread = new Thread(new ClientThread(syncToken));
cThread.start();
}
When there is connection to the Internet I'm sending data through the socket, when there is not I'm storing the data in a database. And when the Internet appears I'm restarting my thread to reconnect and send the old data (which hasn't been sent because of network crashing) and the new one.
Let's say I would implement something like this:
DoRefreshBroadcastReceiver refreshBroadcastReceiver;
...
onResume() {
// register the refreshing complete broadcast receiver
IntentFilter intentFilter = new IntentFilter(DO_REFRESH);
refreshBroadcastReceiver = new doRefreshBroadcastReceiver();
registerReceiver(refreshBroadcastReceiver, intentFilter);
}
public class DoRefreshBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// call method to run fetch code...
}
}
Does this mean that when the Internet connection is detected my onReceive() gets called? And I could start my thread there?
What is the concept of using an intent? Because I really don't get it. How to use it, and what its purpose?
THE IDEA: I don't really know how to use this intent in this case or how to use it in my app!
Would this thing detect the connection to the Internet even when I'm not in this activity?
EDIT:
Here is how my onReceive looks like:
onCreate()
{
cThread = new Thread(new ClientThread(syncToken));
// cThread.start();
connIntentFilter = new IntentFilter(
"android.net.conn.CONNECTIVITY_CHANGE");
connListener = new MyConnectivityListener();
}
public void onReceive(Context context, Intent intent)
{
mNetworkInfo = (NetworkInfo) intent
.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
if (mNetworkInfo != null && mNetworkInfo.isConnected())
{
/*
* if(mNetworkInfo.getType()==ConnectivityManager.TYPE_WIFI);
*
*
* else
*/
cThread.start();
}
else {
System.out.println("There is no internet connection!");
try {
cThread.stop();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
mNetworkInfo != null && mNetworkInfo.isConnected()
Does this mean it's connected or should I verify for a certain type of connection on the emulator?
*I think that I should start my thread directly in onReceive(). As soon as my app starts it detects the Internet connection and BroadcastReceiver gets fired, doesn't it?
Try something like this...
public class MyActivity extends Activity {
private MyConnectivityListener connListener = null;
private IntentFiler connIntentFilter = null;
private Boolean connIntentFilterIsRegistered = false;
#Override
protected void onCreate(...) {
...
connIntentFilter = new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE");
connListener = new MyConnectivityListener();
}
#Override
protected void onResume() {
...
if (!connIntentFilterIsRegistered) {
registerReceiver(connListener, connIntentFilter);
connIntentFilterIsRegistered = true;
}
}
#Override
protected void onPause() {
...
if (connIntentFilterIsRegistered) {
unregisterReceiver(connListener);
connIntentFilterIsRegistered = false;
}
}
protected class MyConnectivityListener extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// The NetworkInfo for the affected network is sent
// as an extra; it should be consulted to see what
// kind of connectivity event occurred.
}
}
}
A BroadcastReceiver is effectively a 'listener' which listens for events either sent by the system or, in some cases, by your own application components.
In this case, the system broadcasts android.net.conn.CONNECTIVITY_CHANGE whenever there is a connection change (connected/disconnected). By registering your BroadcastReceiver to 'listen' for that event, you can get the extra included in the Intent from your BroadcastReceiver's onReceive(...) method and do whatever you need to do accordingly. The extra is a `NetworkInfo object which will contain information about the particular network and whether it is connected or not.
Related
I have this BroadcastReceiver implementation in my Fragment :
private BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
NetworkInfo networkInfo =
intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
if(networkInfo.isConnected()) {
//do stuff
}
//Other actions implementation
}
}
};
With standart register/unregister methods:
#Override
public void onStart() {
super.onStart();
getActivity().registerReceiver(receiver, getIntentFilter());
}
#Override
public void onStop() {
super.onStop();
getActivity().unregisterReceiver(receiver);
}
And receiver with same implementation for WifiManager.NETWORK_STATE_CHANGED_ACTION in other Fragment
The issue: this action triggers everytime one of the fragments started, but i need it to trigger only if Wifi was really just connected, and as the name of action says WifiManager.NETWORK_STATE_CHANGED_ACTION, so it should work only if Wifi state changes
Edit: as was replied this action will trigger every time by the default, so my question is: There is no action for Wifi connecting ?
That's how the WifiManager works when you register for NETWORK_STATE_CHANGED_ACTION. When you register it, you will get 1 update immediately, to show the current state.
To circumvent this, you could have a simple boolean in your fragment set as default to 'true'. In your broadcast receiver, check if this is true, and if so, it's the first time you get an update, set it to false and then simply ignore the update. The next time it's called, the boolean will be true and you can use the data.
private boolean firstTime = true;
private BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if(firstTime) {
firstTime = false;
return;
}
if(intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
NetworkInfo networkInfo =
intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
if(networkInfo.isConnected()) {
//do stuff
}
//Other actions implementation
}
}
};
The NETWORK_STATE_CHANGED_ACTION broadcast is likely "sticky", use the isInitialStickyBroadcast method check if the broadcast is the first "sticky" one:
#Override
public void onReceive (Context context, Intent intent) {
if (!isInitialStickyBroadcast()) {
// Only check updates in state
}
}
See "ConnectivityManager.NetworkCallback". This is only available in API 21 onward. This may not have the exact condition you are after, but is close.
So, I have an app that starts a service. This service starts to scan for bluetooth devices with BTAdapter.startDiscovery(). Further I have a broadcastreceiver which listens for the DISCOVERY_FINISHED action. If that occurs I want to call a method from onReceive() in my service that starts the scanning process again. How am I gonna do this?
Here my receiver:
public class PollingReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
ScanBTService sBTs = new ScanBTService();
sBTs.startScan();
}
}
and here the service:
public class ScanBTService extends IntentService {
private BluetoothAdapter mBTAdapter;
private PollingReceiver mPollingReceiver;
public ScanBTService() {
super("ScanBTService");
}
#Override
protected void onHandleIntent(Intent intent) {
final BluetoothManager btManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBTAdapter = btManager.getAdapter();
mBTAdapter.startDiscovery();
}
public void startScan() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mBTAdapter.startDiscovery();
}
}
In your onReceive()-method, restart your service using the following two lines. I did not tested it out but it should work like that.
#Override
public void onReceive(Context context, Intent intent) {
//ScanBTService sBTs = new ScanBTService();
//sBTs.startScan();
Intent i = new Intent(getApplicationContext(), ScanBTService.class);
startService(i);
}
You can then remove the startScan()-method, too.
Try this to resolve the method:
context.startService(new Intent(context, SimpleWakefulService.class));
Since you are using an IntentService, you will need to create an intent for the started service to handle.
This can be achieved by the following :
Intent intent = new Intent(context, ScanBTService.class);
startService(intent);
As described here : https://developer.android.com/training/run-background-service/send-request.html
Now, if you are looking to have a service that maintains bluetooth connections, discover devices, send & receive data... If this is the case, then in my experience, i would argue the following points :
Perhaps the best way to go about this (depending on what you're doing of course), would be to have a service running in it's own separate process which would be responsible for all of that. Check : http://developer.android.com/guide/topics/manifest/service-element.html and the tag
android:process
Take advantage of Android's IPC communication feature to pass & receive messages between you're main app thread and your service. Tutorial : http://www.survivingwithandroid.com/2014/01/android-bound-service-ipc-with-messenger.html.
Create & Maintain connection quick guide : http://developer.android.com/guide/topics/connectivity/bluetooth.html#ConnectingAsAClient
Hope it helps
Cheers
I have written the following code for detecting the network status from within the BroadcastReceiver. I start a service when the network is available and stop the service when the network is not available.
I have the following class level variable.
private boolean IsNetworkAlreadyConnected = false;
Within onCreate method of the main class I start the service if the internet is available.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (InternetConnectivity.isConnected(MainActivity.this)) {
IsNetworkAlreadyConnected = true;
Intent timerIntent = new Intent(getBaseContext(), InActivityTimer.class);
startService(timerIntent);
}
}
and below is the code for my BroadcastReceiver in the same class,
public class mConnectivityCheckReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals("android.net.conn.CONNECTIVITY_CHANGE")) {
try {
boolean networkAvailable = InternetConnectivity.isConnected(context);
if (networkAvailable) {
if (!IsNetworkAlreadyConnected) {
Intent timerIntent = new Intent(getBaseContext(), InActivityTimer.class);
startService(timerIntent);
IsNetworkAlreadyConnected = true;
}
else {
Log.d("KC_HomeActivity", "Network was already connected. No need to start service again.");
}
}
else {
Log.d("KC_HomeActivity", "Network Disconnected. Service Stopped.");
IsNetworkAlreadyConnected = false;
Intent timerIntent = new Intent(getBaseContext(), InActivityTimer.class);
stopService(timerIntent);
}
}
catch (Exception e) {
}
}
}
};
When both Mobile data and Wifi are turned on then the service is started from onCreate method and it is not started again in the BroadcastReceiver but when I turn off the Wifi the Android changes the network mode to Mobile Data but for few seconds there is no internet connectivity and the service is stopped and then started again. I don't want to do this. If there is no connectivity only then the service should be stopped. If the network is shifting from Wifi to Mobile Data then the service should not be stopped.
Note: To check the internet connectivity I am using,
NetworkInfo info = InternetConnectivity.getNetworkInfo(context);
return (info != null && info.isConnectedOrConnecting());
Network connections aren't that precise. You should make it relax a bit, or you'll pull your hair out.
I would implement a smoothing function from the broadcasts. When you get a connectivity change notification, set a timeout for like 15 seconds. At that time, check your status and either start, stop, or do nothing. If another broadcast comes in, clear the first and reset for another 15 seconds. That will give the device time to reconnect.
I have an intent service in my app that is called from the main thread. The intent service is started upon clicking on a button. Once started, the service connects to the server and retrieves information.
I want to send broadcast to the activity once the data is retrieved. If I send it from the onHandleIntent(), the data might not be retrieved yet.
Can't I send the broadcast from the method that retrieves the data? If not, any alternatives?
code sample:
onHandleIntent()
{
myMethod();
//Here where it is expected to send the broadcast
Intent broadcastIntent = new Intent();
broadcastIntent.setAction("com.example.intent.action.MESSAGE_PROCESSED");
broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT);
broadcastIntent.putExtra("TAG",Message);
getApplicationContext().sendBroadcast(broadcastIntent);
}
MyMethod()
{
//Retrieving data from server, which returns Message.
//Here Where I want to send broadcast (Message is ready)
}
Thank you for your help.
You could also use a handler/runnable combo to act as a timer, so that you check whether the value is null or not before sending the broadcast. See this for how to do that.
edit:
It would look like this:
Handler handler = new Handler();
Runnable runnable = new Runnable() {
public void run() {
sendBroadcast();
}
};
onHandleIntent()
{
myMethod();
runnable.run();
}
MyMethod()
{
//Retrieving data from server, which returns Message.
//Here Where I want to send broadcast (Message is ready)
}
sendBroadcast(){
// If your value is still null, run the runnable again
if (Message == null){
handler.postDelayed(runnable, 1000);
}
else{
//Here where it is expected to send the broadcast
Intent broadcastIntent = new Intent();
broadcastIntent.setAction("com.example.intent.action.MESSAGE_PROCESSED");
broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT);
broadcastIntent.putExtra("TAG",Message);
getApplicationContext().sendBroadcast(broadcastIntent);
}
}
You could do the following in your activity class:
1- Create a BroadcastReceiver
private class MyBroadcastReceiver extends BroadcastReceiver
#Override
public void onReceive(Context context, Intent intent) {
//Get your server response
String server_response = intent.getExtras().getString("TAG");
//Do your work
}
}
2- Create an object in your activity (as a member of the activity)
MyBroadcastReceiver mReceiver= new MyBroadcastReceiver ();
3- Register it in your onResume() method and deregister it in your onPause() method.
#Override
public void onResume() {
super.onResume();
IntentFilter filter = new IntentFilter();
filter.addAction("com.example.intent.action.MESSAGE_PROCESSED");
registerReceiver(mChatReceiver, filter);
}
#Override
public void onPause() {
super.onPause();
unregisterReceiver(mReceiver);
}
That should be enough!, Hope it helps!
I have an IntentService which updates a preference like this:
SharedPreferences.Editor editor = userPrefs.edit();
editor.putInt("COUNT", intCount);
editor.commit();
In my main activity I am listening for preference changes and update a TextView
userPrefsListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if(key.equals("COUNT")) {
final TextView txvCounter = (TextView) findViewById(R.id.TXV_COUNTER);
if(txvCounter != null) {
SharedPreferences userPrefs = getSharedPreferences("USER_SCORE", 0);
int intCount = userPrefs.getInt("COUNT", 0);
txvFishcounter.setText(String.format("%03d",intCount));
}
}
}
};
userPrefs.registerOnSharedPreferenceChangeListener(userPrefsListener);
For Android 2.3 everything works fine, but for 2.2 I'm getting CalledFromWrongThreadException everytime the OnSharedListener is triggered.
Thanks for your Help!
The thread running the onSharedPreferenceChanged() callback is not the main UI thread in the 2.2 device which give you CalledFromWrongThreadException (therefore violating the second of the Android UI thread access rules of only calling UI toolkit from the UI thread). A straightforward way to get code to run on the UI thread is to use Activity.runOnUiThread(). This could be done simply by wrapping the body of your code in a new Runnable:
activity.runOnUiThread(new Runnable() {
public void run() {
// Code which updates UI controls goes here.
}
});
A short discussion of the change between 2.2 and 2.3 and code snippets of why it occurred can be found here.
The reason why the CalledFromWrongThreadException is raised, is because the OnChangeListener is not supposed to be called from the IntentService.
What you could do though, is sending a Broadcast (where you can actually include the value as well).
In case you are only using the SharedPreference for communication, you can replace it entirely (and I would recommend this, as SharedPreferences are wasting Write Cycles).
You could use a code like this for sending a Broadcast:
/**
* Send an Intent with the Broadcast, a permission and a Bundle
*
* #param context
* A context to use
* #param broadcast
* String to use, eg. "de.bulling.smstalk.ENABLE"
* #param permission
* Permission needed for receiving
* #param bundle
* Extras to attach
*/
public static void send_broadcast(Context context, String broadcast, String permission, Bundle bundle) {
//SettingsClass.log_me(tag, "Sending broadcast " + broadcast);
Intent i = new Intent();
i.setAction(broadcast);
if (bundle != null) {
i.putExtras(bundle);
}
if (permission != null) {
context.sendBroadcast(i, permission);
} else {
context.sendBroadcast(i);
}
}
You should include a custom permission as well, so other apps don't get the Broadcast, but it is not necessarily needed.
To receive the Broadcast, register a Receiver in your Activity, eg
private final BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Bundle b = intent.getExtras();
doSomething();
}
};
#Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
IntentFilter filter = new IntentFilter();
filter.addAction(YOURBROADCAST);
registerReceiver(receiver, filter);
[...]
}
#Override
public void onDestroy() {
unregisterReceiver(receiver);
super.onDestroy();
}
Also you should consider using Handlers, so your methods in the OnChangeListener call are run by the MainUI thread.
Example:
Handler mHandler = new Handler();
Runnable myCode = new Runnable(){
#Override
protected void onRun() {
yourCode();
}
};
mHandler.run(myCode);
This has also the advantage of running the code delayed with runDelayed(), so you don't have to use sleep, and you UI will be still responding.