This question already has answers here:
What is the meaning of "this" in Java?
(22 answers)
Closed 4 years ago.
I'm trying to write a function where I have to pass an Activity object to a method that requires such an argument. Usually in such case I'm supposed to just type "this" and it automatically recognizes which type of object it's supposed to create. But sometimes this doesn't work and it for whatever reason reassings a different type of object than the one that is required. For example, I actually use the exact same method in both of these cases:
if (checkLocationPermission(this)){
In this first one, the program automatically recognizes "this" as an Activity object. Here's the second one:
#Override
public void onSuccess(Location location) {
if (location == null || !checkLocationPermission(this)){
In this case the exact same method recognizes "this" as an OnSuccessListener instead of an Activity.
Another example I have in the same program is one where "this" object is supposed to be a Looper but instead it again gets recognized as an OnSuccessListener:
fusedLocationClient.requestLocationUpdates(locationRequest,new LocationCallback(),this);
I don't know how to actually select the proper type of object for "this" argument since I can only type the same damn word.
EDIT:
Here's the full code. I used Looper.this just so you can find it easier. I also tried with MapsActivity.this and it doesn't work:
public class MapsActivity extends FragmentActivity implements OnMapReadyCallback, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {
private GoogleMap mMap;
private GoogleApiClient googleApiClient;
public static final String TAG = MapsActivity.class.getSimpleName();
private FusedLocationProviderClient fusedLocationClient;
private final static int CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000; //Request code to send to Google Play Services
private LocationRequest locationRequest;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
setUpMapIfNeeded();
googleApiClient = new GoogleApiClient.Builder(this).addConnectionCallbacks(this).addOnConnectionFailedListener(this).addApi(LocationServices.API).build();
locationRequest = LocationRequest.create().setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY).setInterval(10*1000).setFastestInterval(1*1000);
}
private void setUpMapIfNeeded(){
if (mMap==null){
SupportMapFragment mapFragment = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map));
mapFragment.getMapAsync(this);
}
}
#Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
//setUpMap();
// Add a marker in Sydney and move the camera
LatLng sydney = new LatLng(-34, 151);
mMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney"));
mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney));
}
#Override
public void onConnected(Bundle bundle) {
Log.i(TAG,"Location Services Connected");
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
if (checkLocationPermission(this)){
fusedLocationClient.getLastLocation().addOnSuccessListener(this, new OnSuccessListener<Location>() {
#Override
public void onSuccess(Location location) {
if (location == null || !checkLocationPermission(MapsActivity.this)){
fusedLocationClient.requestLocationUpdates(locationRequest,new LocationCallback(),Looper.this);
}
else{
handleNewLocation(location);
}
}
});
}
}
public static boolean checkLocationPermission(Activity activity){
if(ActivityCompat.checkSelfPermission(activity, android.Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED
|| ActivityCompat.checkSelfPermission(activity, android.Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(activity, new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION,
android.Manifest.permission.ACCESS_COARSE_LOCATION},0);
return false;
}
return true;
}
private void handleNewLocation(Location location){
Log.d(TAG,location.toString());
}
#Override
public void onConnectionSuspended(int i) {
Log.i(TAG,"Location Services suspended. Please reconnect.");
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
if (connectionResult.hasResolution()){
//Starts an Activity that tries to resolve the error
try {
connectionResult.startResolutionForResult(this,CONNECTION_FAILURE_RESOLUTION_REQUEST);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
}
else{
Log.i(TAG,"Location services connection failed code: " + connectionResult.getErrorCode());
}
}
#Override
protected void onResume(){
super.onResume();
setUpMapIfNeeded();
googleApiClient.connect();
}
#Override
protected void onPause(){
super.onPause();
if (googleApiClient.isConnected()){
googleApiClient.disconnect();
}
}
#Override
public void onLocationChanged(Location location) {
handleNewLocation(location);
}
}
this corresponds to the object in which it is used. onSuccess is a method of OnSuccessListener class and hence this refers to OnSuccessListener. You need to use ActivityName.this. For example if you activity name is MainActivity, then
#Override
public void onSuccess(Location location) {
if (location == null || !checkLocationPermission(MainActivity.this)){
When you use an anonymous inner class, such as a listener, and use this, it refers to the anonymous inner class, because that's your current location.
For instance, with an OnClickListener:
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//"this" here will refer to the OnClickListener instance you create
}
}
Nothing is being "created" with a this argument. It's a direct reference to the current enclosing class. If you need to reference the Activity, you can use:
ActivityClassName.this
as long as you're in an inner class and not a static class.
If you use a lambda (only available for API 24+):
view.setOnClickListener((v) -> {
//"this" will reference your Activity because there's no inner class anymore
}
the this refers to the object of the immediate enclosing class. So, if you have some interface or class as an argument to a function, we usually do like this :
functionThatTakesInterfaceOrClassAsArgument( new TheInterfaceOrClass {
#Override
public void someMethod () {
// if you use `this` here, it refers to the object of `TheInterfaceOrClass`
}
});
If you want to use the object corresponding to an enclosing class (yet not the immediate enclosing class) using <className>.this
So, if the name of the enclosing Activity is MyActivity, one would need to use MyActivity.this.
Related
I have an activity that extends a base class called LocationAwareActivity all this LocationAwareActivity activity does is creates a location service client
LocationServices.getFusedLocationProviderClient and listens to
location updates.
Source for this activity is here
https://github.com/snijsure/MultiActivity/blob/master/app/src/main/java/com/example/subodhnijsure/multiactivity/LocationAwareActivity.java
And when activity is destroyed it calls removeLocationUpdates . What I am finding is
removeLocationUpdate returns a task that always returns not-successful
More concerning is because location activities is not removed, the activity is not getting being garbage collected.
- So if I start the any activity that inherits from LocationAwareActivity that activity always stays on heap.
So the question is what is the correct way to stop receiving location updates thus allowing activity to be garbage collected.
Entire source for this project can be accessed here - https://github.com/snijsure/MultiActivity
In removeLocationUpdates you should pass locationCallback, current implementation is wrong.
Still, there is chance of memory leak somewhere else. You should try integrating Leakcanary in your app and it can give you reference tree and will tell you which field or listener is causing this memory leak.
You can refer one of my only blog post here
public void stopLocationUpdates() {
if (locationProviderClient != null) {
try {
final Task<Void> voidTask = locationProviderClient.removeLocationUpdates(locationCallback);
if (voidTask.isSuccessful()) {
Log.d(TAG,"StopLocation updates successful! ");
} else {
Log.d(TAG,"StopLocation updates unsuccessful! " + voidTask.toString());
}
}
catch (SecurityException exp) {
Log.d(TAG, " Security exception while removeLocationUpdates");
}
}
}
Hi #Subodh Nijsure Please check below code and paste into your code and after checked it:
final Task<Void> voidTask = locationProviderClient.removeLocationUpdates(locationCallback);
voidTask.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
Log.e(TAG, "addOnCompleteListener: "+task.isComplete());
}
});
voidTask.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Log.e(TAG, "addOnSuccessListener: " );
}
});
voidTask.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.e(TAG, "addOnFailureListener: ");
}
});
I think voidTask.isSuccessful() this method is not working when you put this listener at that time it working fine and i also see into memory it's release all memory when come to previous Activity.
And when you are redirecting to any activity then please stopLocationUpdates() called once into onPause() and remove from other method like onDestroy(),onStop() because it stop once so why should we call multiple time.
Hope this helps you.
By looking at the code in the repository I discovered some issues in your design that maybe cause the leaking of your Activity.
1) You are using two different LocationCallbacks. One in the start and one in the stop method, but you should actually use the same. So one time instantiating it would be sufficient and would lead probably also to a successful result of your Task when removing the LocationCallback.
2) Since your instantiating the LocationCallback twice with an Anonymous Class you are keeping a non-static reference of an inner class even if you finish the containing class and this causes your Memory Leak. You can read more about this here.
3) IMHO it is better to use a separate manager class for handling your location requests than abstracting an Activity.
That said here is my...
Solution
GpsManager.java
public class GpsManager extends LocationCallback {
private FusedLocationProviderClient client;
private Callback callback;
public interface Callback {
void onLocationResult(LocationResult locationResult);
}
public boolean start(Context context, Callback callback) {
this.callback = callback;
client = LocationServices.getFusedLocationProviderClient(context);
if (!checkLocationPermission(context)) return false;
client.requestLocationUpdates(getLocationRequest(), this, null);
return true;
}
public void stop() {
client.removeLocationUpdates(this);
}
#Override
public void onLocationResult(LocationResult locationResult) {
callback.onLocationResult(locationResult);
}
private boolean checkLocationPermission(Context context) {
int permissionCheck = ContextCompat.checkSelfPermission(
context, android.Manifest.permission.ACCESS_FINE_LOCATION);
return permissionCheck == PackageManager.PERMISSION_GRANTED;
}
private LocationRequest getLocationRequest() {
return LocationRequest.create()
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.setInterval(30_000L)
.setFastestInterval(20_000L);
}
}
and calling this from your Activity like this
YourActivity.java
public class MapsActivity extends AppCompatActivity implements GpsManager.Callback {
private static final int PERMISSION_REQUEST_FINE_LOCATION = 1;
private GpsManager mGpsManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
...
mGpsManager = new GpsManager(getApplicationContext(), this);
// check if user gave permissions, otherwise ask via dialog
if (!checkPermission()) {
getLocationPermissions();
return;
}
mGpsManager.start();
...
}
#Override
protected void onStop() {
super.onStop();
mGpsManager.stop();
}
#Override
public void onLocationResult(LocationResult locationResult) {
// do something with the locationResult
}
// CHECK PERMISSIONS PART
private boolean checkPermission() {
return isGranted(ActivityCompat.checkSelfPermission(this, ACCESS_FINE_LOCATION)) &&
isGranted(ActivityCompat.checkSelfPermission(this, ACCESS_COARSE_LOCATION));
}
#TargetApi(Build.VERSION_CODES.M)
private void getLocationPermissions() {
requestPermissions(new String[] {Manifest.permission.ACCESS_FINE_LOCATION},
PERMISSION_REQUEST_FINE_LOCATION);
}
#Override
public void onRequestPermissionsResult(int code, #Nullable String permissions[], #Nullable int[] results) {
switch (code) {
case PERMISSION_REQUEST_FINE_LOCATION:
if (isPermissionGranted(results)) {
getLocationRequest();
}
}
}
private boolean isPermissionGranted(int[] results) {
return results != null && results.length > 0 && isGranted(results[0]);
}
private boolean isGranted(int permission) {
return permission == PackageManager.PERMISSION_GRANTED;
}
}
This is just a guess because I didn't try your code but the solution should help you anyways. Please correct me if I'm wrong ;)
The reason why the Task object returns false is in your stopLocationUpdates method, you are again creating a local **LocationCallback** reference and then using this reference to as an argument in locationProviderClient.removeLocationUpdates(cL);
where your local LocationCallBack is never present in the locationProviderClient
So what you have to do is , instead of creating another LocationCallBack object ,you have to pass the same global object which you are instantiating in your startLocationUpdates method
your code should be like this
final Task<Void> voidTask = locationProviderClient.removeLocationUpdates(locationCallback);
I have GPS listener class AppLocationListener that has a parent activity GPSSensorActivity (not shown below).
I'm trying to access AppLocationListener from another class QuestionSearch by using AppLocationListener searchLL = new AppLocationListener(); searchLL.parentActivity = this; but using this gives a type mismatch between QuestionSearch and GPSSensorActivity. .
Is this because it's not possible to access classes that have parent activities? How can I access AppLocationListener. QuestionSearch is something I've added on to existing code that isn't my original code, I'm trying to I am new to java and don't fully understand passing data and linking classes/methods yet.
// AppLocationListener class
import java.util.ArrayList;
...
public class AppLocationListener implements LocationListener {
public GPSSensorActivity parentActivity;
public ArrayList<GeoPoint> pointList;
public void onLocationChanged(Location location) {
}
public void onProviderDisabled(String s) {
}
public void onProviderEnabled(String s) {
}
public void onStatusChanged(String arg0, int arg1, Bundle arg2) {
}
}
// QuestionSearch class
import java.util.ArrayList;
...
public class QuestionSearch extends Activity {
private static final long MINIMUM_DISTANCECHANGE_FOR_UPDATE = 1; // in Meters
private static final long MINIMUM_TIME_BETWEEN_UPDATE = 5000; // in Milliseconds
private LocationManager locationManager;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_quizmap);
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
AppLocationListener searchLL = new AppLocationListener();
searchLL.parentActivity = this;
ArrayList<GeoPoint> pointList = new ArrayList<GeoPoint>();
...
// add these points to the list
pointList.add(gMapPoint);
pointList.add(gMapPoint2);
pointList.add(gMapPoint3);
// now set up the location manager and listener
searchLL.pointList = pointList;
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
MINIMUM_TIME_BETWEEN_UPDATE,
MINIMUM_DISTANCECHANGE_FOR_UPDATE,
searchLL
);
}
}
To be able to pass this in searchLL.parentActivity = this; your QuestionSearch should be GPSSensorActivity or should extend it.
Is this because it's not possible to access classes that have parent activities?
No, it has nothing to do with it, it's a pure Java matter - just pass right classes where they are expected.
I am developing an app related to Google map. I have done following steps successfully.
Created API key to access Google Map
Added Google Play Services Library in my app
Added required permissions
Added map in my activity with SupportMapFragment
Added a separate class MyMap.java to manipulate the map
Passed tow parameters to this class - Context of main activity and object of GoogleMap
Turned Wi-Fi and GPS on and ran the app
After this I am getting map with nice look and controls.
MyMap.java
public class MyMap implements ConnectionCallbacks, OnConnectionFailedListener {
private Context context;
private GoogleMap map;
private GoogleApiClient client = null;
public MyMap(Context context, GoogleMap map) {
this.context = context;
this.map = map;
client = new GoogleApiClient.Builder(context)
.addApi(LocationServices.API).addConnectionCallbacks(this)
.addOnConnectionFailedListener(this).build();
}
#Override
public void onConnectionFailed(ConnectionResult arg0) {
// TODO Auto-generated method stub
}
#Override
public void onConnected(Bundle arg0) {
Toast.makeText(context, "Connected", 1).show();
Location mLastLocation = LocationServices.FusedLocationApi
.getLastLocation(client);
if (mLastLocation != null) {
Toast.makeText(
context,
String.valueOf(mLastLocation.getLatitude()) + ","
+ String.valueOf(mLastLocation.getLongitude()), 1)
.show();
}
}
#Override
public void onConnectionSuspended(int arg0) {
// TODO Auto-generated method stub
}
}
Problem
In the above class I want to toast the current location. But it is not toasting anything. At least I need to see a toast saying "connected" on onConnected
event. Is there something wrong in my implementation?
Thanks in advance.
You seemingly never connect your client so it would be a real suprise if onConnected was called :)
You create your client with
client = new GoogleApiClient.Builder(context)
.addApi(LocationServices.API).addConnectionCallbacks(this)
.addOnConnectionFailedListener(this).build();
but for the client to do something you have to add:
client.connect();
getLastLocation() is going to give location only once. To get periodic location updates, you need to override onLocationChanged() method. You can get this Link
Best way that I found is simple implement you activity like so:
public class MapActivity extends Activity implements GoogleMap.OnMyLocationChangeListener
and override method
#Override
public void onMyLocationChange(Location location) {
mMap.addMarker(new MarkerOptions().position(location).icon(
BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN)));
}
And don't forget about mMap.setMyLocationEnabled(true); and mMap.setOnMyLocationChangeListener(this); in map init method
That's all!
Also, you can check is map available like here:
public boolean checkMapsAvailable() {
int isAvailable = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (isAvailable == ConnectionResult.SUCCESS) {
return true;
} else if (GooglePlayServicesUtil.isUserRecoverableError(isAvailable)) {
Dialog dialog = GooglePlayServicesUtil.getErrorDialog(isAvailable, this, 9001);
dialog.show();
} else {
Constants.showToast(Constants.ALERT_GOOGLEPLAY_CONNECTION);
}
return false;
}
Hope this helps.
I have started building an android application that focuses on a map from google-play-services-libs. When I start the app, it takes a while for the map to load from the gray grid to showing the map image.
I'd like to move/play around a bit with the map when it has fully loaded the part that is currently on screen. I have however been unable to find a way to programmatically check whether the map has loaded.
How can I achieve this?
In short:
From a instance of GoogleMap, how do I determine if it is actually showing something?
You can try using a OnCameraChangeListener on your map. The onCameraChange call will be called when the map's tiles are initially loaded.
this.map.setOnCameraChangeListener(new OnCameraChangeListener() {
public void onCameraChange(CameraPosition arg0) {
isMapReady = true;
map.setOnCameraChangeListener(null);
}
});
chckReady() function checks whether the map is ready or not
public class MapView extends android.support.v4.app.FragmentActivity {
private GoogleMap mMap;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.ui_settings_demo);
setUpMapIfNeeded();
}
#Override
protected void onResume() {
super.onResume();
setUpMapIfNeeded();
}
private void setUpMapIfNeeded() {
if (mMap == null) {
mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map))
.getMap();
}
}
private boolean checkReady() {
if (mMap == null) {
return false;
}
}
}
Currently i have the coding as below for use on my activity on android. Right now i use this as an object and start scanning location and get back it's location using returnBestLocation method 3seconds later using a handler.
However i would like to ask is there any possibility that MyLocationListener object will automatically return call the activity on location change instead of calling the object to retrtive location 3s later?
public class MyLocationListener implements LocationListener {
LocationManager locationManager;
Date currentBestLocationDate;
Intent notificationIntent;
Context mContext;
Location currentBestLocation = null, lastKnownLocation=null;
public MyLocationListener(Context mContext)
{this.mContext = mContext;
}
public void startLocationScan()
{
Log.d(Config.log_id, "Custom Location Listener started");
if (locationManager == null) {
locationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
Location locationNETWORK = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
if (locationNETWORK != null) {
lastKnownLocation=locationNETWORK;
}
Location locationGPS = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if (locationGPS != null) {
lastKnownLocation=locationGPS;
}
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, MyLocationListener.this);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0,MyLocationListener.this);
}
}
public void stopLocationScan()
{
if(locationManager!=null)
{
locationManager.removeUpdates(MyLocationListener.this);
Log.d(Config.log_id, "Custom Location Listener Stopped");
}
}
public Location returnBestLocation()
{
return currentBestLocation;
}
#Override
public void onProviderDisabled(String provider) {
// TODO Auto-generated method stub
}
#Override
public void onProviderEnabled(String provider) {
// TODO Auto-generated method stub
}
#Override
public void onLocationChanged(Location location) {
// TODO Auto-generated method stub
if (currentBestLocation == null) {
currentBestLocation = location;
}
long timeDelta = location.getTime() - currentBestLocation.getTime();
Log.d(Config.log_id, "locationpostingservice's changed with accuracy " + location.getAccuracy() + " s different " + (float) timeDelta / 1000);
if (timeDelta >= 120000) {
currentBestLocation = location;
Log.d(Config.log_id,"posting service Location changed due to over 2min "+ location.getAccuracy() + " s different "+ (float) timeDelta / 1000);
}
if (currentBestLocation.getAccuracy() >= location.getAccuracy()) {
currentBestLocation = location;
}
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
// TODO Auto-generated method stub
}
You could pass the Activity you want to be notified to MyLocationListener as a Listener. Create your own Listener interface, let the Activity implement it and add a method to MyLocationListener like addListener(). Every time you want to notify those activities iterate through the list of Listeners and call their locationChanged method (or whatever you called it in your interface definition). Just be sure to add error handling for null listener Activities, etc.
So basically you have your own custom Listeners listening to a LocationListener.
Another way would be to use Broadcast Receivers and broadcast the location change.
The proper way to do this is, as Fraggle says, to create your own listener interface, implement it in your class, then call your listener from your Activity.
But a quick-n-dirty alternative could be to just implement the LocationListener interface in your Activity, rather than spinning it off into a new class (MyLocationListener). Then just put all the code you want to run into onLocationChanged, and don't worry about communicating with other objects.