Google Play Services error dialog potential memory leak? - android

I'm taking a look at this example
https://developer.android.com/training/location/retrieve-current.html#CheckServices
Here is the code in question:
public class MainActivity extends FragmentActivity {
...
private boolean servicesConnected() {
...
if (ConnectionResult.SUCCESS == resultCode) {
...
// Google Play services was not available for some reason.
// resultCode holds the error code.
} else {
// Get the error dialog from Google Play services
Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
resultCode,
this,
CONNECTION_FAILURE_RESOLUTION_REQUEST);
...
}
}
}
If we take a look at GooglePlayServicesUtil.getErrorDialog(..) we are passing a reference to this which happens to be an Activity.
The question is:
Would this cause a memory leak during a configuration change?
I guess the answer depends on how/if GooglePlayServicesUtil.getErrorDialog(..) keeps the reference to Activity internally.

Yep on my app it used to leak
If you have the Google play services error dialog up and then rotate again it will leak
This is the solution I put in place to solve the leak but this assumes your Google play services check is in onResume
public class MainActivity extends Activity
{
private Dialog googlePlayErrorDialog;
#Override
protected void onResume()
{
// TODO Auto-generated method stub
super.onResume();
int isAvaiable = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if(isAvaiable == ConnectionResult.SUCCESS)
{
Log.d("TEST", "GPS IS OK");
}
else if(isAvaiable == ConnectionResult.SERVICE_MISSING ||
isAvaiable == ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED ||
isAvaiable == ConnectionResult.SERVICE_DISABLED)
{
googlePlayErrorDialog = GooglePlayServicesUtil.getErrorDialog(isAvaiable, this, 10);
googlePlayErrorDialog.show();
}
}
#Override
protected void onPause()
{
// TODO Auto-generated method stub
super.onPause();
if(googlePlayErrorDialog != null)
{
googlePlayErrorDialog.dismiss();
}
}
So the deal here is that I set the getErrorDialog equal to one my own dialog variable and then in onPause do a simple null check (to avoid dreaded null pointer exception!) and call dismiss.
I got the idea from reading this if you want more information
http://publicstaticdroidmain.com/2012/01/avoiding-android-memory-leaks-part-1/

Related

PiracyChecker fails to check whether the app is installed from Google Play Store

I'm using 'com.github.javiersantos:PiracyChecker:1.2.3' because my app is not yet integrates AndroidX.
I have numerous reports from user reviews in my app's Google Play page that they have installed the app from the Google Play Store, yet they getting the piracy warning message.
Here are some examples:
Also got a user report via email:
My app have 4k reviews and only 3 of them are like this, but I don't know the exact user count because there could be users who don't comment about this issue.
What is going on?
I use it like this:
public static void showPiracyActivityIfNeeded(final Activity activity) {
if (!BuildConfig.DEBUG) {
//Releaseb build, piracy check.
new PiracyChecker(activity)
.enableInstallerId(InstallerID.GOOGLE_PLAY)
.callback(new PiracyCheckerCallback() {
#Override
public void allow() {
}
#Override
public void dontAllow(#NonNull PiracyCheckerError piracyCheckerError, #Nullable PirateApp pirateApp) {
Intent intent = new Intent(activity, PiracyWarningActivity.class);
activity.startActivity(intent);
activity.finish();
}
})
.start();
}
}
Thanks in advance.
It seems that your Google Play Services are unavailable. Try to check it before start your PiracyActivity:
const val PLAY_SERVICES_RESOLUTION_REQUEST = 9000
fun AppCompatActivity.checkPlayServices(): Boolean {
val apiAvailability = GoogleApiAvailability.getInstance()
val resultCode = apiAvailability.isGooglePlayServicesAvailable(this)
if (resultCode != ConnectionResult.SUCCESS) {
if (apiAvailability.isUserResolvableError(resultCode)) {
apiAvailability.getErrorDialog(this, resultCode, PLAY_SERVICES_RESOLUTION_REQUEST)
.show()
}
return false
}
return true
}
And then:
public static void showPiracyActivityIfNeeded(final Activity activity) {
if (!BuildConfig.DEBUG && activity.checkPlayServices()) { ...
Also note, there are a lot of devices that has Google Play Services turned off because of various reasons, so check availability of Services every time you need it.

Can't use code from manual (GoogleApiClient)

I use code from this page.
First problem - I copy and paste all this code, but then I start this activity I see toast with error message (and screen dimming).
Second problem - I can't use this code:
int errorCode = GooglePlusUtil.checkGooglePlusApp(this);
if (errorCode != GooglePlusUtil.SUCCESS) {
GooglePlusUtil.getErrorDialog(errorCode, this, 0).show();
}
I see this error:
This fragment from build.gradle
dependencies {
...
compile 'com.google.android.gms:play-services:6.5.+'
...
}
P.S. I want use Google Plus for login in my app.
UPDATE1 This screenshot from SDK Manager:
/**
* #param activity activity passed from activity that calls checkPlayServices().
* #param context getting the current context from the activity .
*/
public static boolean checkPlayServices(Activity activity, Context context) {
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(context);
if (resultCode != ConnectionResult.SUCCESS) {
if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) {
GooglePlayServicesUtil.getErrorDialog(resultCode, activity,
Config.PLAY_SERVICES_RESOLUTION_REQUEST).show();
} else {
Log.i(TAG, "This device is not supported.");
activity.finish();
}
return false;
}

Android AdMob addTestDevice not getting deviceID in logcat

I am attempting to implement the AdMob with Google Play Services.
So far I have the default test banner appearing but I want to try some of the test ads.
I read that the emulator (AVD) must have Google APIs 16 or 17 as the target in order to test the AdMob however when I create a device that has this as target the emulator fails to load ( i left it for a good 20 minutes still has not loaded yet :( I just see the flashing android logo
This my AVD device
This is my AdFragment class that contains all the code related to the advertisements
public class AdFragment extends Fragment
{
private AdView mAdView;
#Override
public View onCreateView(LayoutInflater inflater,ViewGroup container, Bundle savedInstanceState) {
// TODO Auto-generated method stub
return inflater.inflate(R.layout.fragment_ad, container, false);
}
#Override
public void onActivityCreated(Bundle savedInstanceState)
{
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
mAdView = (AdView)getView().findViewById(R.id.adView);
AdRequest adRequest = new AdRequest.Builder()
.addTestDevice(AdRequest.DEVICE_ID_EMULATOR)
.build();
// Start loading the ad in the background.
mAdView.loadAd(adRequest);
}
/** Called when leaving the activity */
#Override
public void onPause()
{
if (mAdView != null)
{
mAdView.pause();
}
super.onPause();
}
/** Called when returning to the activity */
#Override
public void onResume()
{
super.onResume();
if (mAdView != null)
{
mAdView.resume();
}
}
/** Called before the activity is destroyed */
#Override
public void onDestroy() {
if (mAdView != null)
{
mAdView.destroy();
}
super.onDestroy();
}
}
Now im unsure whether it is my code that not generating the device ID or a problem with my created AVD device. The tutorials i seen have something like this
.addTestDevice(AdRequest.DEVICE_ID_EMULATOR)
.addTestDevice("2EAB96D84FE62876379A9C030AA6A0AC")
Now i don't know if the last line is the code given by LogCat or is something that i just have to put in. I noticed the developer.google website has a different code so i assume i don't need to include in my code since i have not got it yet.
Please help. thank you.
UPDATE 1
I added this code inside On Resume inside my main activity
#Override
protected void onResume()
{
// TODO Auto-generated method stub
super.onResume();
int isAvaiable = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if(isAvaiable == ConnectionResult.SUCCESS)
{
Log.d("TEST", "GPS IS OK");
}
else if(isAvaiable == ConnectionResult.SERVICE_MISSING || isAvaiable == ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED || isAvaiable == ConnectionResult.SERVICE_DISABLED)
{
Dialog dialog = GooglePlayServicesUtil.getErrorDialog(isAvaiable, this, 1);
dialog.show();
}
}
The API to test banners should be 17 or higher. You have a good answer here that explains it. GPS in emulator.
For the problems related to launch the emulator the only suggestion I can give you is trying VM Acceleration and use an smaller screen.
You can try other emulators like x86Emulator and download and the last ISO versions 4.4. In my case the default android emulator took 10-12minutes to be responsive in a ldpi, with the other only 2 in a hdpi.
About the addTestDevice, I think AdRequest.DEVICE_ID_EMULATOR is enough, but if you see in the logcat the MD5 of your device ID then add this hash.
Last but not least, remember to check if GPS are installed at the beginning, it is explained on the DOCS.
To verify the Google Play services version, call isGooglePlayServicesAvailable(). If the result code is SUCCESS, then the Google Play services APK is up-to-date and you can continue to make a connection. If, however, the result code is SERVICE_MISSING, SERVICE_VERSION_UPDATE_REQUIRED, or SERVICE_DISABLED, then the user needs to install an update.
So you will avoid errors.

Strange dialog with GooglePlayServicesUtil

I'm checking avalibility of google play services on device. I do it with these code:
final int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (resultCode != ConnectionResult.SUCCESS) {
final DialogInterface.OnCancelListener cancelListener = new DialogInterface.OnCancelListener() {
#Override
public void onCancel(final DialogInterface dialog) {
finish();
}
};
final Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
resultCode, this, GOOGLE_PLAY_SERVICES_REQUEST_CODE, cancelListener
);
errorDialog.show();
}
I get resultCode = 2 (it's mean that Google Play Services needs to update). Dialog is shown, but instead of text, I get paths to layout.
It's looks like there are some interference of resource in app and resource in PlaYServices lib. But how it's possible and how to avoid id?
Since the accepted answer is somewhat unclear, I'll leave a signpost with my conclusions (mostly extracted from the comments on the question) which I believe are correct.
Short version: It seems that resource ids were incorrectly generated for this app.
It's obvious that the Google Play Services dialog intended to show strings in those places. The getErrorDialog() method is implemented like this (obfuscated, but the meaning can still be understood):
...
case 1:
return localBuilder.setTitle(R.string.common_google_play_services_install_title).create();
case 3:
return localBuilder.setTitle(R.string.common_google_play_services_enable_title).create();
case 2:
return localBuilder.setTitle(R.string.common_google_play_services_update_title).create();
...
Also, mistakenly doing something like getResources().getString(R.layout.my_layout) will return a string with the name of the original resource file ("res/layout/my_layout.xml").
So, we can conclude that, for some reason, the value of the Play Services Library resource, say, com.google.android.gms.R.string.common_google_play_services_install_title is actually the same as for the resource R.layout.dialog_share in the application project.
This probably stems for an incorrect build process, or an incorrect usage of the Google Play Services library (for example, including its jar directly, without the corresponding library process).
I have tested the code with google play service library version 4452000(use version >= 4452000). The code is as follows:
public class MainActivity extends Activity{
ProgressBar pBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (resultCode != ConnectionResult.SUCCESS) {
final DialogInterface.OnCancelListener cancelListener = new DialogInterface.OnCancelListener() {
#Override
public void onCancel(final DialogInterface dialog) {
finish();
}
};
final Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
resultCode, this, 10, cancelListener
);
errorDialog.show();
}
}
}
Check your version of google play services and update if needed.
Here's the class I've been working on to check google play. It's not in production later this summer it will be so let me know with a comment if you have problems. It works tested on zten9120 and HTC EVO. The flow is like this. If static method isGooglePlay(context) returns false. Initialize the class and call the non static isgoogleplay() which will present the dialog to the user if googleplay services is not installed. The oncofigurationchange method handles when the device is rotated. onstop sets the class to null.
Modify three events in your activity.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (!MyGooglePlay.isGooglePlay(getApplicationContext())) {
myGP = new MyGooglePlay(this);
myGP.isGooglePlay();
}
#Override
public void onStop() {
super.onStop();
myGP = null;
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (myGP != null) {
if (myGP.errorFragment.isVisible()) {
myGP.errorFragment.dismissAllowingStateLoss();
}
}
}
Here's the code which I keep in separate class
package com.gosylvester.bestrides.util;
import android.app.Dialog;
import android.content.Context;
import android.content.IntentSender;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.ActionBarActivity;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
public class MyGooglePlay {
private static final int CONNECTION_FAILURE_RESOLUTION_REQUEST = 31502;
private ActionBarActivity activity;
private FragmentManager fragManager;
public MyGooglePlay(ActionBarActivity activity) {
this.activity = activity;
this.fragManager = activity.getSupportFragmentManager();
}
public static boolean isGooglePlay(Context context) {
return (GooglePlayServicesUtil.isGooglePlayServicesAvailable(context) == ConnectionResult.SUCCESS);
}
public boolean isGooglePlay() {
if (isGooglePlay(activity)) {
return true;
} else {
return checkGooglePlay();
}
}
private static final String DIALOG_ERROR = "dialog_error";
public ErrorDialogFragment errorFragment;
private boolean checkGooglePlay() {
int mIsGooglePlayServicesAvailable = GooglePlayServicesUtil
.isGooglePlayServicesAvailable(activity);
switch (mIsGooglePlayServicesAvailable) {
case ConnectionResult.SUCCESS:
return true;
default:
Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
mIsGooglePlayServicesAvailable, activity,
CONNECTION_FAILURE_RESOLUTION_REQUEST);
// If Google Play services can provide an error dialog
if (errorDialog != null) {
// Create a new DialogFragment for the error dialog
errorFragment = ErrorDialogFragment.newInstance();
// Set the dialog in the DialogFragment
errorFragment.setDialog(errorDialog);
// Show the error dialog in the DialogFragment
errorFragment.show(fragManager, "LocationUpdates");
}
// case ConnectionResult.SERVICE_MISSING:
// case ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED:
// case ConnectionResult.SERVICE_DISABLED:
// case ConnectionResult.SERVICE_INVALID:
// case ConnectionResult.DATE_INVALID:
}
return false;
}
public void dismissMe() {
DialogFragment frag = (DialogFragment) fragManager
.findFragmentByTag("LocationUpdates");
if (frag != null) {
frag.dismissAllowingStateLoss();
}
}
public static class ErrorDialogFragment extends DialogFragment {
// Global field to contain the error dialog
private Dialog mDialog;
static ErrorDialogFragment newInstance() {
ErrorDialogFragment d = new ErrorDialogFragment();
return d;
}
// Default constructor. Sets the dialog field to null
public ErrorDialogFragment() {
super();
mDialog = null;
}
// Set the dialog to display
public void setDialog(Dialog dialog) {
mDialog = dialog;
}
public void onPause(){
super.onPause();
this.dismissAllowingStateLoss();
}
// Return a Dialog to the DialogFragment.
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return mDialog;
}
}
public void onConnectionFailed(ConnectionResult connectionResult) {
/*
* Google Play services can resolve some errors it detects. If the error
* has a resolution, try sending an Intent to start a Google Play
* services activity that can resolve error.
*/
if (connectionResult.hasResolution()) {
try {
// Start an Activity that tries to resolve the error
connectionResult.startResolutionForResult(activity,
CONNECTION_FAILURE_RESOLUTION_REQUEST);
/*
* Thrown if Google Play services canceled the original
* PendingIntent
*/
} catch (IntentSender.SendIntentException e) {
// Log the error
e.printStackTrace();
}
} else {
/*
* If no resolution is available, display a dialog to the user with
* the error.
*/
showErrorDialog(connectionResult.getErrorCode(), activity);
}
}
/* Creates a dialog for an error message */
private void showErrorDialog(int errorCode, ActionBarActivity activity) {
// Create a fragment for the error dialog
ErrorDialogFragment dialogFragment = new ErrorDialogFragment();
// Pass the error that should be displayed
Bundle args = new Bundle();
args.putInt(DIALOG_ERROR, errorCode);
dialogFragment.setArguments(args);
dialogFragment
.show(activity.getSupportFragmentManager(), "errordialog");
}
}
Good Luck with getting google play services installed.
public static boolean isGooglePlayServiceAvailable(Context context) {
boolean isAvailable = false;
int result = GooglePlayServicesUtil
.isGooglePlayServicesAvailable(context);
if (result == ConnectionResult.SUCCESS) {
Log.d(TAG, "Play Service Available");
isAvailable = true;
} else {
Log.d(TAG, "Play Service Not Available");
if (GooglePlayServicesUtil.isUserRecoverableError(result)) {
GooglePlayServicesUtil.getErrorDialog(result,
(Activity) context, PLAY_SERVICES_RESOLUTION_REQUEST)
.show();
} else {
Log.d(TAG, "Play Service Not Available");
GooglePlayServicesUtil.getErrorDialog(result,
(Activity) context, PLAY_SERVICES_RESOLUTION_REQUEST)
.show();
}
}
return isAvailable;
}
Updated answer : (as per GooglePlayServicesUtil.getErrorDialog is null)
If Google Play Services is not installed on the device, you may not be able to use the error dialog.
As per Rahim's comment in the above, you should only use the dialog if you have an "isUserRecoverableError" (his code):
int status = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (status != ConnectionResult.SUCCESS) {
if (GooglePlayServicesUtil.isUserRecoverableError(status)) {
GooglePlayServicesUtil.getErrorDialog(status, this,
REQUEST_CODE_RECOVER_PLAY_SERVICES).show();
} else {
Toast.makeText(this, "This device is not supported.", Toast.LENGTH_LONG).show();
finish();
}
}
UPDATE - (from http://www.riskcompletefailure.com/2013/03/common-problems-with-google-sign-in-on.html)
One additional bug that surfaced quite a lot around the release of the latest version of Google Play Services was the onConnectionFailed method being called with a ConnectionResult which does not have a resolution, and has an error code of ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED.
As you might guess from the name, this indicates that the version of Google Play Services on the device is too low. Normally new versions will be updated automatically, but there is always a time delay in the roll out, so it is quite possible to get this error as updates are released.
You can handle this in the onConnectionFailed by calling getErrorDialog on GooglePlayServicesUtil, but the best way is to actually check whether Google Play Services is installed and up to date before even trying to connect. You can see a snippet of how to do this in the documentation.
So this is suggesting that this error (as you yourself say) should be recoverable, although note the clause that I have made bold. I am not convinced that this dialog would always be usable. Common sense suggests to me that this might depend on the version from which you were upgrading. So I recommend that you explicitly check that the error is marked as recoverable. If it is marked as recoverable then this looks like a bug in Google Play Services.
I found solution. I don't undertand why it's happening, but there is a solution.
I've built my project with maven and include a google play services framework as android library project.
Today, I've migrated to gradle and include dependecy go GPS with gradle and it solved my problem.

GooglePlayServicesUtil.getErrorDialog does nothing

I am trying to handle use cases where the user doesn't have Play set up correctly. It seems extremely straight forward, but I can't get the dialog to do anything. I have the following code in onCreate for a FragmentActivity that uses Maps v2. I have ensured that execution does go inside the if statements.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_map);
this.helper = (DatabaseHelper) OpenHelperManager.getHelper(this, DatabaseHelper.class);
int googlePlayStatus = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if(googlePlayStatus != ConnectionResult.SUCCESS) {
if(GooglePlayServicesUtil.isUserRecoverableError(googlePlayStatus)) {
GooglePlayServicesUtil.getErrorDialog(googlePlayStatus, this, googlePlayStatus).show();
}
}
//... do some stuff, such as use CameraUpdateFactory which throws NPE.
}
I have tried inserting the code into another activity, without the if checks, and the dialog shows just fine.
If a i am getting it right, you are trying to get the dialog error to be shown. Try this:
Edited:
Ok, i was having the same problem as you some time ago, so this is the exact code i use on one of my projects. I call the 'getActivity' on 'isGooglePlayServicesAvailable' instead of passing this.
int checkGooglePlayServices = GooglePlayServicesUtil.isGooglePlayServicesAvailable(getActivity());
if (checkGooglePlayServices != ConnectionResult.SUCCESS) {
Dialog dialog = GooglePlayServicesUtil.getErrorDialog(checkGooglePlayServices, getActivity(), DIALOG_GET_GOOGLE_PLAY_SERVICES);
dialog.show();
}

Categories

Resources