Android BroadcastReceiver: context issue - java

I create an application that implements simple geolocation problem: once in, say, 20 minutes, it takes a LatLng coordinate.
For this purpose, from MainActivity, I initiate BroadcastReceiver to work. It instantiates LocationManager to find coordinates, which needs application context.
The problem is: due to memory reasons, Android OS can kill my MainActivity, so, BroadcastReceiver, firing next time, catches null pointer exception, referring to application's context.
Ideas:
I. I could restart the activity inside BroadcastReceiver like this:
#Override
public void onReceive(Context context, Intent intent) {
//start activity
Intent i = new Intent();
i.setClassName("com.test", "com.test.MainActivity");
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
but context is null due to killed Activity.
II. Maybe, the paradigm, presented in my solution, too cumbersome?
Maybe here is graceful solution, I even didn't think of?
Well, my code snippet:
public class MainActivity extends AppCompatActivity {
// ...
public void onStartSessionButtonClicked (View view) {
Intent alarmRecIntent = new Intent(this, AlarmReceiver.class);
PendingIntent mAlarmIntent = PendingIntent.getBroadcast(this, 0, alarmRecIntent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager)this.getSystemService(ALARM_SERVICE);
alarmManager.cancel(mAlarmIntent);
alarmManager.setInexactRepeating(
AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + 1000,
RConstants.locUpdateInterval,
mAlarmIntent
);
}
}
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive (Context context, Intent intent) {
Context mContext = context;
try {
LocationManager locationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
if (!locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
throw new Exception("network provider is not enabled");
}
locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER,
MIN_TIME_BW_UPDTS,
MIN_DIST_CHANGE_FOR_UPDTS,
locationListener
);
}
catch (Exception e) { /* catch codeblock */ }
}

I really confused myself supposing that context comes NULL to BroadcastReceiver's onReceive() method, when MainActivity is killed by Android OS. The other one pointer came NULL in my code, so it caused exception.
If you interested, please, check out the code snippet below for details:
public class AlarmReceiver extends BroadcastReceiver {
LocationManager mlocationManager = null;
private CountDownTimer cdTimer = null;
private Context mContext;
final private static long MIN_TIME_BW_UPDTS = 1000 * 1; // ms
final private static float MIN_DIST_CHANGE_FOR_UPDTS = 5.0f; // meters
#Override
public void onReceive (Context context, Intent intent) {
mContext = context; // < -- 1 -- >
try {
mlocationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
if (!mlocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
throw new Exception("network provider is not enabled");
}
// < -- 2 -- >
cdTimer = new CountDownTimer(RConstants.locUpdateTimeoutUsed, RConstants.locUpdateTimeoutUsed) {
/// other required methods overridden here ...
#Override
public void onFinish() {
mlocationManager.removeUpdates(locationListener);
mlocationManager = null; // < -- 4 -- >
Log.d("ALARM", getCurrentDateTime() + ", timeout");
}
}.start();
mlocationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER,
MIN_TIME_BW_UPDTS,
MIN_DIST_CHANGE_FOR_UPDTS,
locationListener
);
}
catch (Exception e) {
Log.d("ALARM", getCurrentDateTime() + ", exc: " + e.getMessage()); < -- 5 -- >
}
}
private LocationListener locationListener = new LocationListener() {
/// other required methods overridden here ...
#Override
public void onLocationChanged(Location location) {
cdTimer.cancel();
cdTimer = null;
mlocationManager.removeUpdates(locationListener); // < -- 6 -- >
mlocationManager = null;
String latLng = " {" + location.getLatitude() + ":" + location.getLongitude() + "}";
Log.d("ALARM", getCurrentDateTime() + latLng);
}
}
}
So, context (mark "< -- 1 -- >"), coming to onReceive is not null.
In note "< -- 2 -- >" I instantiate CountDownTimer with constant RConstants.locUpdateTimeoutUsed. The problem is, it defined and assigned outside of AlarmReceiver, so, when my Activity is killed, RConstants.locUpdateTimeoutUsed comes declared, but not defined. So, CountDownTimer constructs with (0, 0) values. That's why it's instance - cdTimer, fires onFinish() hardly being started. Here it cancels update for mLocationManager and sets it to NULL. But before mLocationManager completely stopped, method onLocationChanged manages to work, so, in line < -- 6 -- > caughts exception.

Related

Android player won't stop

I have call receiver , after first incoming call , ringtone playing as usual , but after second or more incoming calls sound does not stop playing.
here call receiver :-
public class IncomingCallReceiver extends BroadcastReceiver {
/**
* Processes the incoming call, answers it, and hands it over to the
* WalkieTalkieActivity.
*
* #param context The context under which the receiver is running.
* #param intent The intent being received.
*/
WalkieTalkieActivity wtActivity;
public SipAudioCall.Listener listener;
public SipAudioCall incomingCall = null;
public static Ringtone r;
public int state = 0;
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
#Override
public void onReceive(Context context, Intent intent) {
wtActivity = (WalkieTalkieActivity) context;
try {
incomingCall = wtActivity.manager.takeAudioCall(intent, listener);
if (incomingCall.getState() == 3) {
state = 1;
Log.e("BUTTON", "Incoming Call here" + wtActivity.manager);
wtActivity.updateStatus("INCOMING CALL " + incomingCall.getState());
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_RING);
Uri uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
r = RingtoneManager.getRingtone(context, uri);
audioManager.setStreamVolume(AudioManager.STREAM_RING, maxVolume / 2, AudioManager.FLAG_PLAY_SOUND);
r.play();
wtActivity.setContentView(R.layout.incomig_call);
Log.e("peer profile", incomingCall.getPeerProfile().toString());
}
} catch (Exception e) {
if (incomingCall != null) {
incomingCall.close();
}
}
}
}
I have static r , to stop r.play , on when call is end , but after calling again , I can hear two ringtones , and when I do end call , one ringtone stop play, but second continuous playing
I do not understand why please help me
If anyone will have same issue . you need just unregister receiver after end call or on reject . this.unregisterReceiver(yourReceiver);

How to start a method from MainActivity in a foreground service

I have got a MainActivity which gets the current location on click of a button. In the activity, and the location is stored with three different methods in
SharedPreferences
An online SQL Database
In a text file on the device
I have another class to start a foreground service (ForegroundService.java) which starts with the click of another button in the MainAcivity and stops with a third button.
My plan is to have regular (1 hour interval) location updates using the ForegroundService and a JobService. So if we click on the StartService-button in the MainActivity the foreground service should start and regularly call the methods from the MainActivity: getCurrentGPSPosition (to get the location), and the three methods to store the information.
The MainActivity works fine. The ForegroundService can be started and stopped bud I do not know how to make it call the methods from the MainActivity.
Here is the code -
ForegroundService.java:
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import static com.example.currentlocation.App.CHANNEL_ID;
public class ForegroundService extends Service {
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//String input = intent.getStringExtra("inputExtra");
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("GPS position tracker")
//.setContentText(input)
.setSmallIcon(R.drawable.ic_baseline_gps_fixed_24)
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);
return START_NOT_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
App.java
import android.app.Application;
import android.app.NotificationChannel;
import android.app.NotificationManager;
public class App extends Application {
public static final String CHANNEL_ID = "CurrentLocationServiceChannel";
#Override
public void onCreate() {
super.onCreate();
createNotificationChannel();
}
private void createNotificationChannel(){
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationChannel serviceChannel = new NotificationChannel(
CHANNEL_ID,
"Current Location Service Channel",
NotificationManager.IMPORTANCE_DEFAULT
);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(serviceChannel);
}
}
}
In the MainActivity the following parts:
public class MainActivity extends AppCompatActivity {
........
//Backgroundservice
public void startService(View v){
// String input = editTextInput.getText().toString();
Intent serviceIntent = new Intent(this, ForegroundService.class);
// serviceIntent.putExtra("inputExtra", input);
startService(serviceIntent);
}
public void stopService(View v){
Intent serviceIntent = new Intent(this, ForegroundService.class);
stopService(serviceIntent);
}
//Method to store in SQL online
public void OnReg() {.......code....}
//Method to save data in SharedPreferences
public void saveData() {.......code....}
//Method to save data in Internal File
public void saveToFile() {.......code....}
//method for GPS request
getCurrentGPSLocation() {.......code....}
}
Has anybody got an idea how to work that out? Or do you need more details? Thanks for your help!
Don't put those functions in MainActivity. If you need them to be called from both the Activity and a Service, put them in a separate class that can be instantiated as needed. If you can't do that because there's data in MainActivity that both need, rethink your architecture- that data won't be available when the job service fires anyway.
OK, first solution is to make a new class (Functions.java) and using an intent to call this class form the MainActivity and then do the location request there and then give the data back to the MainActivity.
Second step would then be to do the same from the foreground service.
The Problem is, the functions class does not get the data from the method. If I put hard-coded strings into the intent, they are transmitted to the MainActivity, so the intent works. So this is only part of the full solution.
Here's the code:
public class Functions extends AppCompatActivity {
//initialize variable
FusedLocationProviderClient fusedLocationProviderClient;
private double ser_Latitude;
private double ser_Longitude;
private String ser_Accuracy;
private String ser_Altitude;
private String currentDateandTime;
private String ser_Location;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Boolean precision = getIntent().getBooleanExtra("precision", true);
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(
Functions.this);
getCurrentGPSLocation();
Intent intent = new Intent();
intent.putExtra("latitude", ser_Latitude);
intent.putExtra("longitude", ser_Longitude);
intent.putExtra("accuracy", ser_Accuracy);
intent.putExtra("altitude", ser_Altitude);
intent.putExtra("location", ser_Location);
intent.putExtra("currentDateandTime", currentDateandTime);
setResult(RESULT_OK, intent);
Functions.this.finish();
}
//Force new GPS Location Request
private void getCurrentGPSLocation() {
// get the new location from the fused client
// update the UI - i.e. set all properties in their associated text view items
//Initialize new location request
LocationRequest locationRequest = new LocationRequest()
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.setInterval(3000)
.setFastestInterval(2000)
.setNumUpdates(1)
;
//Initialize location call back
LocationCallback locationCallback = new LocationCallback() {
#Override
public void onLocationResult(LocationResult locationResult) {
//Initialize location1
Location location1 = locationResult.getLastLocation();
//Set latitude
ser_Latitude = location1.getLatitude();
//Set longitude
ser_Longitude = location1.getLongitude();
//Set Accuracy
double ser_accura1 = location1.getAccuracy();
ser_Accuracy = new DecimalFormat("##.##").format(ser_accura1) + " m";
//Set Altitude
double ser_altit1 = location1.getAltitude();
ser_Altitude = new DecimalFormat("##.##").format(ser_altit1) + " m";
//Get Adress
/* Geocoder geocoder = new Geocoder(getApplicationContext(), Locale.getDefault());
try {
List<Address> listAddresses = geocoder.getFromLocation(ser_Latitude, ser_Longitude, 1);
if (null != listAddresses && listAddresses.size() > 0) {
String _Location1 = listAddresses.get(0).getAddressLine(0);
//Set Location
//ser_Location = String.valueOf(_Location1);
}
} catch (IOException e) {
e.printStackTrace();
}*/
//Set location as hard-coded string for testing
ser_Location = "London";
//Set Update Time
SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy 'um ' HH:mm:ss z");
currentDateandTime = sdf.format(new Date());
}
};
//Request location updates
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return;
}
fusedLocationProviderClient.requestLocationUpdates(locationRequest
, locationCallback, Looper.myLooper());
}
}
The new GPS request is triggered as I can see running the app, but no information is delivered from the method getCurrentGPSLocation() to the intent, which I can see because even the hard-coded location string is not delivered.
This needs revision please.

onSensorChanged stops being called after 3 minutes of Locked screen

I am trying to develop a simple app which will record the user's activity (accelerometer values) on a txt or csv file.
My app consists of 2 java classes MainActivity and MyService. The MainActivity includes two buttons to start and stop the service and the required permissions. However, the onSensorChanged normally logs for the first 3 minutes after locking the phone (turning off the screen) and then stops logging. As soon as I open the screen the logd starts working again. Same behavior for the records in txt file. I found out that the app seems to be working excellent if I override the battery optimizations. However, I need the phone to also be working in doze mode to save some battery drain. Has anyone else had a similar issue?
Here is my Foreground Service:
public class MyService extends Service implements SensorEventListener {
public static final String CHANNEL_ID = "ForegroundServiceChannel";
private static final String TAG = "MyService";
private Messenger messageHandler;
private SensorManager mSensorManager;
private Sensor mAccelerometer;
private Context mContext;
private PowerManager.WakeLock wakeLock = null;
//private HandlerThread mSensorThread;
//private Handler mHandler;
#SuppressLint("MissingPermission")
#Override
public void onCreate() {
super.onCreate();
Log.v("shake service startup", "registering for shake");
mContext = getApplicationContext();
//mHandler = new Handler(mSensorThread.getLooper());
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mAccelerometer = mSensorManager
.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mSensorManager.registerListener(this, mAccelerometer,
SensorManager.SENSOR_DELAY_NORMAL);
PowerManager manager = (PowerManager) getSystemService(Context.POWER_SERVICE);
wakeLock = manager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Wakelock :: TAG");
// Register our receiver for the ACTION_SCREEN_OFF action. This will make our receiver
// code be called whenever the phone enters standby mode.
//IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
//registerReceiver(mReceiver, filter);
}
/*
// BroadcastReceiver for handling ACTION_SCREEN_OFF.
public BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Check action just to be on the safe side.
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
Log.v("shake mediator screen off","trying re-registration");
// Unregisters the listener and registers it again.
mSensorManager.unregisterListener(MyService.this);
mSensorManager.registerListener(MyService.this, mAccelerometer,
SensorManager.SENSOR_DELAY_NORMAL, mHandler);
}
}
};
*/
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
String input = intent.getStringExtra("inputExtra");
createNotificationChannel();
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Foreground Service")
.setContentText(input)
.setSmallIcon(R.drawable.ic_launcher)
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);
return START_STICKY;
//stopSelf();
//return START_NOT_STICKY;
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel serviceChannel = new NotificationChannel(
CHANNEL_ID,
"Foreground Service Channel",
NotificationManager.IMPORTANCE_DEFAULT
);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(serviceChannel);
}
}
#Override
public void onDestroy() {
super.onDestroy();
if(mSensorManager != null){
//noinspection MissingPermission
mSensorManager.unregisterListener(MyService.this);
}
//unregisterReceiver(mReceiver);
try{
wakeLock.release();//always release before acquiring for safety just in case
}
catch(Exception e){
//probably already released
Log.e(TAG, e.getMessage());
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onSensorChanged(SensorEvent event) {
Log.d(TAG, "onSensorChanged: " + event.timestamp + " " + event.values[0] + " " + event.values[1] + " " + event.values[2]);
recordAccelValues(String.valueOf(event.timestamp), event.values[0] + " " + event.values[1] + " " + event.values[2]);
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
private void recordAccelValues(String time, String accel_values) {
String record = time + " " + accel_values + "\n";
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
if (Build.VERSION.SDK_INT >= 23) {
File sdcard = Environment.getExternalStorageDirectory();
File dir = new File(sdcard.getAbsolutePath() + "/text/");
if(!dir.exists()) {
dir.mkdir();
}
File file = new File(dir, "dailyRecordsAccel.dat");
FileOutputStream os = null;
try {
os = new FileOutputStream(file, true);
os.write(record.getBytes());
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
As you can see in the code I tried several recommendations from other questions I found, like wakelock and Intent.ACTION_SCREEN_OFF but they didn't seem to work.
Accelerometer stops delivering samples when the screen is off on Droid/Nexus One even with a WakeLock
The only one way to keep alive your service it's to avoid battery optimization for your application. Which is possible within two ways below. Please note! In both cases you will keep device alive, which means that device will never sleep (enter doze states obviously). It's whole point of device sleep, to avoid pending work of background services like yours.
Using Android WakeLocks, For ex. below.
val wakeLock: PowerManager.WakeLock =
(getSystemService(Context.POWER_SERVICE) as PowerManager).run {
newWakeLock(PowerManager. FULL_WAKE_LOCK, "MyApp::MyWakelockTag").apply {
acquire()
}
}
Changing setting to avoid battery optimization for specific app. As you mentioned in your question.
It is normal behavior. Android delete all proceses to save power. If you want do a job then ask user to keep screen on, else you can use AlarmManager only to call a Service (Intent, Reciver) do "small job" and go to sleep again.

Receive unplugging of headphones takes too long

I want to pause the MediaPlayer when the user unplugs his headphones. I found out that I can use the "ACTION_AUDIO_BECOMING_NOISY" broadcast , so I tried it out !
Theoretically it works , BUT the time of receiving takes too long. The music is still playing for 3-5 seconds before it really pauses.This wouldnt be acceptable for an user.
How are other devolopers able to pause it in milliseconds ? Are there better Soloutions ?
My BroadcastReceiver which is actually for Notifications :
public class NotificationBroadcast extends BroadcastReceiver {
...
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(android.media.AudioManager.ACTION_AUDIO_BECOMING_NOISY)) {
Intent iPause = new Intent(context , SongService.class);
iPause.putExtra("com.Hohos.mplay.Services.SongService.MEDIA_ACTION", NOTIFY_EXTRA_PAUSE);
context.startService(iPause);
}
...
}
I also added <uses-permission android:name="android.permission.ACTION_HEADSET_PLUG"/>
, which really didnt any difference
Thanks for your help guys !
To know when the user unplugs his headphones you need to listen the action ACTION_HEADSET_PLUG and check the state extra:
Broadcast Action: Wired Headset plugged in or unplugged.
The intent will have the following extra values:
state - 0 for unplugged, 1 for plugged.
name - Headset type, human
readable string
microphone - 1 if headset has a microphone, 0
otherwise
This is an example:
public class MainActivity extends Activity {
private HeadsetBroadcastReceiver mHeadsetBroadcastReceiver;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myReceiver = new HeadsetBroadcastReceiver();
}
#Override
protected void onResume() {
IntentFilter filter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
registerReceiver(mHeadsetBroadcastReceiver, filter);
super.onResume();
}
#Override
protected void onPause() {
unregisterReceiver(mHeadsetBroadcastReceiver);
super.onPause();
}
private class HeadsetBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_HEADSET_PLUG)) {
int state = intent.getIntExtra("state", -1);
if (state == 0) {
//Headset is unplugged
} else if(state == 1) {
//Headset is plugged
}
}
}
}
}

Error in Intent Service in Android

I am trying to fetch some for Location Address using IntentService but ended up with error leading to app crash. Please help me.
Here is the Stacktrace:
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.os.ResultReceiver.send(int, android.os.Bundle)' on a null object reference
at com.example.ajender.sample2.FetchAddressIntentService.deliverResultToReceiver(FetchAddressIntentService.java:91)
at com.example.ajender.sample2.FetchAddressIntentService.onHandleIntent(FetchAddressIntentService.java:81)
at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:65)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.os.HandlerThread.run(HandlerThread.java:61)
Here is FetchAddressIntentService:
public class FetchAddressIntentService extends IntentService {
private static String TAG="Fetch-address-Service";
protected ResultReceiver mReceiver;
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* #param name Used to name the worker thread, important only for debugging.
*/
public FetchAddressIntentService(String name) {
super(name);
}
public FetchAddressIntentService(){
super("FetchAddressIntentService");
}
#Override
protected void onHandleIntent(Intent intent) {
String errorMessage = "";
Geocoder geocoder = new Geocoder(this, Locale.getDefault());
Location location = intent.getParcelableExtra(
Constants.LOCATION_DATA_EXTRA);
mReceiver=intent.getParcelableExtra(Constants.RECEIVER);
Log.e(TAG,"1-----");
List<Address> addresses = null;
try {
addresses = geocoder.getFromLocation(
location.getLatitude(),
location.getLongitude(),
// In this sample, get just a single address.
1);
} catch (IOException ioException) {
// Catch network or other I/O problems.
errorMessage = "service_not_available";
Log.e(TAG, errorMessage, ioException);
} catch (IllegalArgumentException illegalArgumentException) {
// Catch invalid latitude or longitude values.
errorMessage = "invalid_lat_long_used";
Log.e(TAG, errorMessage + ". " +
"Latitude = " + location.getLatitude() +
", Longitude = " +
location.getLongitude(), illegalArgumentException);
}
// Handle case where no address was found.
if (addresses == null || addresses.size() == 0) {
if (errorMessage.isEmpty()) {
errorMessage = "no_address_found";
Log.e(TAG, errorMessage);
}
deliverResultToReceiver(Constants.FAILURE_RESULT, errorMessage);
} else {
Address address = addresses.get(0);
ArrayList<String> addressFragments = new ArrayList<String>();
// Fetch the address lines using getAddressLine,
// join them, and send them to the thread.
for(int i = 0; i < address.getMaxAddressLineIndex(); i++) {
addressFragments.add(address.getAddressLine(i));
}
Log.i(TAG, "address_found");
deliverResultToReceiver(Constants.SUCCESS_RESULT,
TextUtils.join(System.getProperty("line.separator"),
addressFragments));
}
}
private void deliverResultToReceiver(int resultCode, String message) {
Bundle bundle = new Bundle();
bundle.putString(Constants.RESULT_DATA_KEY, message);
Log.e(TAG, "2-----");
mReceiver.send(resultCode, bundle);
Log.e(TAG, "3-----");
}
This service should have to send back bundle with Result Receiver and result code but not happening....
The error can be resolved following the steps below
In the MainActivity
Add public AddressResultReceiver mResultReceiver;
mResultReceiver = new AddressResultReceiver(null)- This will automatically assign a id for the main activity class.
In the FetchAddressIntentService
Add mReceiver = intent.getParcelableExtra(Constants.RECEIVER);
Check whether mReceiver is null by logging it.
Send the data using your current code.
It should work. Thats how I got Around it.If you have any problem comment.
Probably you haven't initialize the mResultReceiver from your activity correctly, which you are supposed to pass to the FetchAddressIntentService intent:
mResultReceiver = new AddressResultReceiver(new android.os.Handler());
..
Intent intent = new Intent(this, FetchAddressIntentService.class);
intent.putExtra(Constants.RECEIVER, mResultReceiver);
..
startService(intent);
What happens in case of IntentService is that you have three components that are playing role: MainActivity (that will call the intent service), IntentService (which is responsible for handling the intent) and the last ResultReceiver which receives the result after the intent has been handled (or operated).
As evident from the Log you have not initialized or assigned any value to ResultReceiver mReceiver
You should initialize mResultReceiver by declaring a class let us call it AddressResultReceiver which extends ResultReceiver and has a parameterized constructor that accepts a single parameter as Handler object and overrides the onReceiveResult() method like the following:
AddressResultReceiver(Handler handler) {
super(handler);
}
//Result from intent service
#Override
public void onReceiveResult(int resultCode, Bundle bundle) {
...
}
Now you have successfully obtained two of three components: MainActivity for starting an intent request and ResultReceiver for receiving the result. Let us now make our IntentService by defining a class in the project hierarchy and extending it with IntentService and overriding its method onHandleIntent(Intent intent)():
public class FetchAddressIntentService extends IntentService {
public FetchAddressIntentService() {
super("FetchAddressIntentService");
}
#Override
protected void onHandleIntent(Intent intent) {...}
}
So we are now good to go to get the things up and working. Now write the following code in your MainActivity:
//Initializing the reference with AddressResultReceiver object
mResultReceiver = new AddressResultReceiver(new Handler());
...
//Setting the IntentService to FetchAddressIntentService
Intent intent = new Intent(this, FetchAddressIntentService.class);
/*passing the receiver object to the service so as to let it know where to
publish results*/
intent.putExtra(Constants.RECEIVER, mResultReceiver);
...
//starting the service
startService(intent);
Now your deliverResult(int, String) would no longer throw NullPointerException. For more information visit IntentService and ResultReceiver. Hope it helps! :)
Add the below code in your protected void onHandleIntent (#Nullable Intent intent){} before Geocoder
if (intent != null){
String errorMessage ="";
resultReceiver = intent.getParcelableExtra(Constants.RECEIVER);
Location location = intent.getParcelableExtra(Constants.LOCATION_DATA_EXTRA);
if (location == null) {
return;
}

Categories

Resources