【问题标题】:LocationSettingsRequest dialog to enable GPS - onActivityResult() skipped启用 GPS 的 LocationSettingsRequest 对话框 - 跳过了 onActivityResult()
【发布时间】:2015-09-22 23:49:44
【问题描述】:

我的部分应用需要定位服务,所以如果定位当前处于关闭状态,应用会提示用户启用它。 Here 是我的做法:(也见于this Stack Overflow 答案)

LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
            .addLocationRequest(mLocationRequest);
builder.setAlwaysShow(true);

PendingResult<LocationSettingsResult> result = LocationServices.SettingsApi.checkLocationSettings(mGoogleApiClient, builder.build());

result.setResultCallback(new ResultCallback<LocationSettingsResult>() 
{
     @Override
     public void onResult(LocationSettingsResult result) 
     {
         final Status status = result.getStatus();
         final LocationSettingsStates = result.getLocationSettingsStates();
         switch (status.getStatusCode()) 
         {
             case LocationSettingsStatusCodes.SUCCESS:
                 // All location settings are satisfied. The client can initialize location
                 // requests here.
                 ...
                 Log.d("onResult", "SUCCESS");
                 break;
             case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
                 // Location settings are not satisfied. But could be fixed by showing the user
                 // a dialog.
                 Log.d("onResult", "RESOLUTION_REQUIRED");
                 try 
                 {
                     // Show the dialog by calling startResolutionForResult(),
                     // and check the result in onActivityResult().
                     status.startResolutionForResult(OuterClass.this, REQUEST_LOCATION);
                 } 
                 catch (SendIntentException e) 
                 {
                     // Ignore the error.
                 }
                 break;
             case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
                 // Location settings are not satisfied. However, we have no way to fix the
                 // settings so we won't show the dialog.
                 ...
                 Log.d("onResult", "UNAVAILABLE");
                 break;
         }
     }
 });

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
    // This log is never called
    Log.d("onActivityResult()", Integer.toString(resultCode));

    final LocationSettingsStates states = LocationSettingsStates.fromIntent(data);
    switch (requestCode)
    {
        case REQUEST_LOCATION:
            switch (resultCode)
            {
                case Activity.RESULT_OK:
                {
                    // All required changes were successfully made
                    break;
                }
                case Activity.RESULT_CANCELED:
                {
                    // The user was asked to change settings, but chose not to
                    break;
                }
                default:
                {      
                    break;
                }
            }
            break;
    }
}

这段代码运行良好,但是,onActivityResult() 总是被跳过。无论用户是否从Dialog 按下YesNobackonActivityResult() 都不会运行。

我需要Android调用onActivityResult()所以如果用户选择不开启定位服务,我可以适当的处理。

Google 的开发者页面(以及上面的代码)明确指出应该调用 onActivityResult()。有谁知道它为什么被跳过?

我也不知道这行的目的是什么:

final LocationSettingsStates states = LocationSettingsStates.fromIntent(data);

谢谢!

编辑:关于我的应用程序结构的基本信息:

  • 此代码包含在FragmentonResume() 方法中,该方法实现GoogleApiClient.ConnectionCallbacksGoogleApiClient.OnConnectionFailedListenerLocationListener 以接收位置更新。示例见here
  • onLocationChanged() 中,Fragment 将有一个自定义的 View 调用 invalidate() 并使用更新的信息重新绘制自身。

【问题讨论】:

    标签: android dialog


    【解决方案1】:

    更新

    下面的原始答案是使用 Java 和现已弃用的 SettingsApi

    这是使用 Kotlin 和 SettingsClient 的更现代的方法:

    fun showEnableLocationSetting() {
        activity?.let {
            val locationRequest = LocationRequest.create()
            locationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
    
            val builder = LocationSettingsRequest.Builder()
                    .addLocationRequest(locationRequest)
    
            val task = LocationServices.getSettingsClient(it)
                    .checkLocationSettings(builder.build())
    
            task.addOnSuccessListener { response ->
                val states = response.locationSettingsStates
                if (states.isLocationPresent) {
                    //Do something
                }
            }
            task.addOnFailureListener { e ->
                if (e is ResolvableApiException) {
                    try {
                        // Handle result in onActivityResult()
                        e.startResolutionForResult(it,
                                MainActivity.LOCATION_SETTING_REQUEST)
                    } catch (sendEx: IntentSender.SendIntentException) { }
                }
            }
        }
    }
    

    在MainActivity中,定义常量:

    companion object {
        const val LOCATION_SETTING_REQUEST = 999
    }
    

    原始答案:

    看起来主要问题是您将所有代码都放在了 Fragment 中,并且由于 startResolutionForResult() 需要将 Activity 传递给它,因此 Activity 是获得 onActivityResult() 回调的原因。

    解决这个问题的一种方法是使用here 描述的技术,当结果进来时,从 Activity 手动调用 Fragment 的 onActivityResult() 方法。

    我刚刚完成了这个简单的示例。

    首先是Activity,它添加了Fragment,并且还具有将onActivityResult()的结果传递给Fragment的功能:

    public class MainActivity extends AppCompatActivity{
    
        LocationFragment lFrag;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            lFrag = LocationFragment.newInstance();
            getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, lFrag).commit();
    
        }
        
        @Override
        public void onActivityResult(int requestCode, int resultCode, Intent data) {
            if (requestCode == LocationFragment.REQUEST_LOCATION){
                lFrag.onActivityResult(requestCode, resultCode, data);
            }
            else {
                super.onActivityResult(requestCode, resultCode, data);
            }
        }
    }
    

    这是片段,它包含显示对话框和处理结果的所有功能。在这个简单的示例中,我只是使用 Toast 消息来验证它是否按预期工作。请注意,我在这里对您问题中的代码所做的主要更改是使用getActivity() 来获取调用startResolutionForResult() 所需的活动参考。

    public class LocationFragment extends Fragment
            implements GoogleApiClient.ConnectionCallbacks,
            GoogleApiClient.OnConnectionFailedListener {
    
    
        LocationRequest mLocationRequest;
        GoogleApiClient mGoogleApiClient;
        PendingResult<LocationSettingsResult> result;
        final static int REQUEST_LOCATION = 199;
    
        public static LocationFragment newInstance() {
            LocationFragment fragment = new LocationFragment();
            return fragment;
        }
    
        public LocationFragment() {
            // Required empty public constructor
        }
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
        }
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
    
            mGoogleApiClient = new GoogleApiClient.Builder(getActivity())
                    .addApi(LocationServices.API)
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this).build();
            mGoogleApiClient.connect();
    
            // Inflate the layout for this fragment
            return inflater.inflate(R.layout.fragment_location, container, false);
        }
    
    
        @Override
        public void onResume() {
            super.onResume();
        }
    
        @Override
        public void onConnected(Bundle bundle) {
    
            mLocationRequest = LocationRequest.create();
            mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
            mLocationRequest.setInterval(30 * 1000);
            mLocationRequest.setFastestInterval(5 * 1000);
    
            LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
                    .addLocationRequest(mLocationRequest);
            builder.setAlwaysShow(true);
    
            result = LocationServices.SettingsApi.checkLocationSettings(mGoogleApiClient, builder.build());
    
            result.setResultCallback(new ResultCallback<LocationSettingsResult>() {
                @Override
                public void onResult(LocationSettingsResult result) {
                    final Status status = result.getStatus();
                    //final LocationSettingsStates state = result.getLocationSettingsStates();
                    switch (status.getStatusCode()) {
                        case LocationSettingsStatusCodes.SUCCESS:
                            // All location settings are satisfied. The client can initialize location
                            // requests here.
                            //...
                            break;
                        case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
                            // Location settings are not satisfied. But could be fixed by showing the user
                            // a dialog.
                            try {
                                // Show the dialog by calling startResolutionForResult(),
                                // and check the result in onActivityResult().
                                status.startResolutionForResult(
                                        getActivity(),
                                        REQUEST_LOCATION);
                            } catch (IntentSender.SendIntentException e) {
                                // Ignore the error.
                            }
                            break;
                        case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
                            // Location settings are not satisfied. However, we have no way to fix the
                            // settings so we won't show the dialog.
                            //...
                            break;
                    }
                }
            });
    
        }
    
        @Override
        public void onActivityResult(int requestCode, int resultCode, Intent data)
        {
            Log.d("onActivityResult()", Integer.toString(resultCode));
    
            //final LocationSettingsStates states = LocationSettingsStates.fromIntent(data);
            switch (requestCode)
            {
                case REQUEST_LOCATION:
                    switch (resultCode)
                    {
                        case Activity.RESULT_OK:
                        {
                            // All required changes were successfully made
                            Toast.makeText(getActivity(), "Location enabled by user!", Toast.LENGTH_LONG).show();
                            break;
                        }
                        case Activity.RESULT_CANCELED:
                        {
                            // The user was asked to change settings, but chose not to
                            Toast.makeText(getActivity(), "Location not enabled, user cancelled.", Toast.LENGTH_LONG).show();
                            break;
                        }
                        default:
                        {
                            break;
                        }
                    }
                    break;
            }
        }
    
        @Override
        public void onConnectionSuspended(int i) {
    
        }
    
        @Override
        public void onConnectionFailed(ConnectionResult connectionResult) {
    
        }
    
    }
    

    以下是视觉结果,如果位置模式被禁用,则首先显示对话框:

    然后,如果用户点击否,结果会从 Activity 传递到 Fragment,其中显示一个 Toast:

    当用户点击是,但结果是成功,并且启用了定位模式时,情况相同:

    请注意,最好将所有这些功能保留在 Activity 中,然后在结果到来时调用 Fragment 中的公共方法。

    这是用于在 Activity 中保留功能的完整工作代码。 当然在这个解决方案中,你需要在调用onActivityResult()之后,在Fragment中添加一个调用来更新Location Mode的状态。

    public class MainActivity extends AppCompatActivity
            implements GoogleApiClient.ConnectionCallbacks,
            GoogleApiClient.OnConnectionFailedListener {
    
    
        LocationRequest mLocationRequest;
        GoogleApiClient mGoogleApiClient;
        PendingResult<LocationSettingsResult> result;
        final static int REQUEST_LOCATION = 199;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mGoogleApiClient = new GoogleApiClient.Builder(this)
                    .addApi(LocationServices.API)
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this).build();
            mGoogleApiClient.connect();
    
        }
    
        @Override
        public void onConnected(Bundle bundle) {
    
            mLocationRequest = LocationRequest.create();
            mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
            mLocationRequest.setInterval(30 * 1000);
            mLocationRequest.setFastestInterval(5 * 1000);
    
            LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
                    .addLocationRequest(mLocationRequest);
            builder.setAlwaysShow(true);
    
            result = LocationServices.SettingsApi.checkLocationSettings(mGoogleApiClient, builder.build());
    
            result.setResultCallback(new ResultCallback<LocationSettingsResult>() {
                @Override
                public void onResult(LocationSettingsResult result) {
                    final Status status = result.getStatus();
                    //final LocationSettingsStates state = result.getLocationSettingsStates();
                    switch (status.getStatusCode()) {
                        case LocationSettingsStatusCodes.SUCCESS:
                            // All location settings are satisfied. The client can initialize location
                            // requests here.
                            //...
                            break;
                        case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
                            // Location settings are not satisfied. But could be fixed by showing the user
                            // a dialog.
                            try {
                                // Show the dialog by calling startResolutionForResult(),
                                // and check the result in onActivityResult().
                                status.startResolutionForResult(
                                        MainActivity.this,
                                        REQUEST_LOCATION);
                            } catch (SendIntentException e) {
                                // Ignore the error.
                            }
                            break;
                        case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
                            // Location settings are not satisfied. However, we have no way to fix the
                            // settings so we won't show the dialog.
                            //...
                            break;
                    }
                }
            });
    
        }
    
        @Override
        public void onActivityResult(int requestCode, int resultCode, Intent data)
        {
            Log.d("onActivityResult()", Integer.toString(resultCode));
    
            //final LocationSettingsStates states = LocationSettingsStates.fromIntent(data);
            switch (requestCode)
            {
                case REQUEST_LOCATION:
                    switch (resultCode)
                    {
                        case Activity.RESULT_OK:
                        {
                            // All required changes were successfully made
                            Toast.makeText(MainActivity.this, "Location enabled by user!", Toast.LENGTH_LONG).show();
                            break;
                        }
                        case Activity.RESULT_CANCELED:
                        {
                            // The user was asked to change settings, but chose not to
                            Toast.makeText(MainActivity.this, "Location not enabled, user cancelled.", Toast.LENGTH_LONG).show();
                            break;
                        }
                        default:
                        {
                            break;
                        }
                    }
                    break;
            }
        }
    
        @Override
        public void onConnectionSuspended(int i) {
    
        }
    
        @Override
        public void onConnectionFailed(ConnectionResult connectionResult) {
    
        }
    }
    

    【讨论】:

    • 如何在ActivityResult上刷新位置
    • 一旦用户启用了位置服务,您将需要请求位置更新。见这里:stackoverflow.com/questions/34582370/…
    • 嗨@DanielNugent 感谢您的帖子。我有一种情况,每次单击按钮时,我都会检查用户的设置是否符合预期。这只是意味着,我每次都调用 result.setResultCallback 来检查用户是否更改了设置。奇怪的是,这只适用于第一次。 onResult 永远不会被调用。我对同一个 stackoverflow.com/questions/38151650/… 有一个 SO 问题。你能帮忙吗
    • 6.0 设备的最佳答案之一。但是,我在另一个片段中调用 LocationFragment 而根本没有得到 Toast。你能告诉我问题吗?
    • 每次都返回0
    【解决方案2】:

    您需要将此添加到您的结果回调中:

    case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
        try {
            fragment.startIntentSenderForResult(status.getResolution().getIntentSender(), REQUEST_CHECK_SETTINGS, null, 0, 0, 0, null);
        } catch (IntentSender.SendIntentException e) {
            // Ignore the error.
        }
        break;
    

    onActivityResult 将在您的片段上调用,您无需在活动中手动调用它。这基本上就是startResolutionForResult 的工作原理。

    【讨论】:

    • 请在您的内联代码位周围添加反引号包装器
    • 我评论了这一行:status.startResolutionForResult(hostActivity, REQUEST_CHECK_SETTINGS);并使用您的代码及其工作,而无需在主要活动中覆盖 onActiivtyResult。
    • 这应该是公认的答案。完美运行。
    • 您也可以跳转到 Status.startResolutionForResult 的源代码并验证它是否正在执行此操作。不过,对 Status.hasResolution 也进行同样的检查可能是个好主意。
    • 我们可以从前台服务调用 LocationSettingRequest 吗?因为我正在从前台服务请求位置更新。
    【解决方案3】:

    当您需要解析StatusResolvableApiException 时,我建议您利用activity.registerForActivityResult API 代替startResolutionForResult

    ActivityResultLauncher<IntentSenderRequest> launcher = activity.registerForActivityResult(
            new ActivityResultContracts.StartIntentSenderForResult(),
            new ActivityResultCallback<ActivityResult>() {
                @Override
                public void onActivityResult(ActivityResult result) {
                    if (result.getResultCode() == Activity.RESULT_OK) {
                        // All required changes were successfully made
                    } else {
                        // The user was asked to change settings, but chose not to
                    }
                }
            });
    
    IntentSenderRequest intentSenderRequest = new IntentSenderRequest.Builder(exception.getResolution()).build();
    launcher.launch(intentSenderRequest);
    

    您正在使用 Java,但如果需要 Kotlin:

    val launcher = activity.registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { result ->
            if (result.resultCode == Activity.RESULT_OK) {
                // User accepted
            } else {
                // User didn't accepted
            }
        }
    
    val intentSenderRequest = IntentSenderRequest.Builder(exception.resolution).build()
    launcher.launch(intentSenderRequest)
    

    【讨论】:

    • 正是我正在搜索的内容,如何为此利用活动结果 API,因此整个位置获取、权限检查、设置都可以放入一个扩展函数中,我只需从任何需要的地方调用它。 .不用大惊小怪
    • 谢谢兄弟,你节省了我的时间
    • 现在应该标记为答案,旧标记的答案太过时了。这是目前最新的方法。
    【解决方案4】:

    如果您希望结果返回到您的片段而不是使用

    startIntentSenderForResult(status.getResolution().getIntentSender(), REQUEST_CODE_LOCATION_SETTING, null, 0, 0, 0, null);
    

    而不是status.startResolutionForResult(YourActivity, LOCATION_REQUEST);

    使用上述方法只会将结果返回到您的片段。

    【讨论】:

    • 谢谢,但这是 Saša Tarbuk 回答 (stackoverflow.com/a/39579124/2914140) 的副本。
    • 我们可以从前台服务调用 LocationSettingRequest 吗?因为我正在从前台服务请求位置更新。
    • @user3410835 不,你不能。
    • @Ghodasara Bhaumik。谢谢。原因??
    • @user3410835 因为要启用位置,android 必须显示对话框并显示对话框,它需要您的应用程序的上下文。如果应用程序有前台服务正在运行,则意味着应用程序未启动并且 android 无法找到您的应用程序的上下文以显示位置启用对话框。
    【解决方案5】:

    用于处理以下片段中的启用位置是可以使用的最新代码。设置 API 现已弃用。以下是 SettingsClient API 的使用方法。

    我还注意到,在 Android 10 设备中,即使用户启用了位置; onActivityResult 中的状态结果以 RESULT_CANCELED 出现,我无法在 Android 10 设备中找到解决该问题的方法,而在 Android PIE 中,结果代码为 RESULT_OK。因此,检测用户是否启用它的唯一方法是使用 Android 10 设备的 LocationManagerCompat API 显式检查是否启用了位置

    private fun enableLocationIfRequired() {
        val builder = LocationSettingsRequest.Builder()
            .addLocationRequest(LocationRequest().setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY))
            .setAlwaysShow(true)
    
        val settingsClient = LocationServices.getSettingsClient(context!!)
    
        val task = settingsClient!!.checkLocationSettings(builder.build())
        task.addOnCompleteListener {
            try {
                val response = it.getResult(ApiException::class.java)
    
                //Success
                Log.d(javaClass.simpleName, "Location is enabled")
    
            } catch (exception: ApiException) {
                Log.d(javaClass.simpleName, "exception thrown: ${exception.statusCode}")
                when (exception.statusCode) {
                    LocationSettingsStatusCodes.RESOLUTION_REQUIRED -> {
                        // Location settings are not satisfied. But could be fixed by showing the
                        // user a dialog.
                        try {
                            // Cast to a resolvable exception.
                            val resolvable = exception as ResolvableApiException
                            // Show the dialog by calling startResolutionForResult(),
                            // and check the result in onActivityResult().
                            Log.d(javaClass.simpleName, "startResolutionForResult called")
    
                            this.startIntentSenderForResult(
                                resolvable.resolution.intentSender,
                                RC_LOCATION_ENABLE,
                                null, 0, 0, 0, null
                            )
    
                        } catch (e: IntentSender.SendIntentException) {
                            // Ignore the error.
                            Log.d(javaClass.simpleName, "IntentSender.SendIntentException")
                        } catch (e: ClassCastException) {
                            // Ignore, should be an impossible error.
                            Log.d(javaClass.simpleName, "ClassCastException")
                        }
                    }
                    LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE -> {
                        // Location settings are not satisfied. However, we have no way to fix the
                        // settings so we won't show the dialog.
                    }
                }
            }
        }
    }
    
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        when (requestCode) {
            RC_LOCATION_ENABLE -> {
                if (resultCode == Activity.RESULT_OK) {
                    Log.d(javaClass.simpleName, "Location is enabled by user")
                } else {
                    Log.d(javaClass.simpleName, "Location enable request is cancelled by user")
                }
                val lm = context!!.getSystemService(LOCATION_SERVICE) as LocationManager
                if (LocationManagerCompat.isLocationEnabled(lm)) {
                    Log.d(javaClass.simpleName, "Location is enabled by user")
                } else {
                    Log.d(javaClass.simpleName, "Location enable request is cancelled by user")
                }
            }
        }
    
    }
    

    【讨论】:

      【解决方案6】:

      感谢@gianlucaparadise solution,您应该为新 API 编写代码:

      片段(或者可能是活动):

      private lateinit var checkLocationSettings: ActivityResultLauncher<IntentSenderRequest>
      
      override fun onCreate(savedInstanceState: Bundle?) {
          checkLocationSettings = 
              registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { result ->
                  if (result.resultCode == RESULT_OK) {
                      // GPS is turned on in system settings.
                  }
          }
      }
      

      要启用 GPS 的片段或实用程序类(请参阅 12):

      .addOnFailureListener(context) { e ->
          when ((e as? ApiException)?.statusCode) {
              LocationSettingsStatusCodes.RESOLUTION_REQUIRED ->
                  try {
                      // Cast to a resolvable exception.
                      val resolvable = e as ResolvableApiException
                      // Old API: show the dialog by calling startResolutionForResult(),
                      // and check the result in onActivityResult().
                      // New API: call registerForActivityResult::launch
                      // and check the result in callback.
                      val intentSenderRequest =
                          IntentSenderRequest.Builder(resolvable.resolution).build()
                      checkLocationSettings.launch(intentSenderRequest)
                  } catch (sie: IntentSender.SendIntentException) {
                      Timber.e("GPS: Unable to execute request.")
                  } catch (cce: java.lang.ClassCastException) {
                      // Ignore, should be an impossible error.
                      Timber.e("GPS: Unable to execute request, ClassCastException.")
                  }
      

      已弃用 Fragment 和 onActivityResult 的 API 变体:LocationSettingsRequest dialog to enable GPS - onActivityResult() skipped

      【讨论】:

      【解决方案7】:

      我看到您对请求代码使用了不同的常量 REQUEST_CHECK_SETTINGSREQUEST_LOCATION。它们具有相同的价值吗?

      代码:final LocationSettingsStates states = LocationSettingsStates.fromIntent(intent);
      上述代码的目的是在更改设置后获取位置设置的当前状态(如使用网络、GPS、...)。
      另外,在您的代码中,我认为它应该是 LocationSettingsStates.fromIntent(data);,因为 intent 在这里不存在,也许这只是一个错字。

      【讨论】:

      • 谢谢。对于这个问题,我直接从谷歌的开发者页面复制了代码。我的应用程序中不存在这些错误,但无论如何它们都不是问题。如果您现在查看onActivityResult(),您会看到一个从未调用过的Log 函数。
      【解决方案8】:

      这是因为片段中存在所有 google api 代码。尝试以下操作将有助于克服...

      1.为您的片段创建一个空的构造函数。

      2.在onCreateView()之前需要oncreate()方法...

      3. 将 Google api 代码粘贴到 oncreate()....

          public mainFragment(){
      
      }
      
      @Override
      public void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          try {
              buildGoogleApiClient();
              buildLocationSettingsRequest();
      
              checkLocationSettings();
              mGoogleApiClient.connect();
          } catch (Exception e) {
              e.printStackTrace();
          }
      }
      

      供您参考...

      Click here...

      【讨论】:

        【解决方案9】:

        在活动中保存片段字段(如丹尼尔建议的那样)通常不是一个好的决定,因为假设您有多个片段并且每个片段都包含位置代码。我以不同的方式做到了:

        public class MainActivity extends Activity implements PlaceFragment.SettingsModifyHandler {
        
            private static final int LOCATION_SETTINGS_RESULT = 1;
            private OnResultCallback placeCallback;
        
            ...
        
            @Override
            protected void onActivityResult(int requestCode, int resultCode, Intent data) {
                super.onActivityResult(requestCode, resultCode, data);
                if (requestCode == LOCATION_SETTINGS_RESULT) {
                    if (resultCode == Activity.RESULT_OK) {
                        placeCallback.resultOk();
                    } else {
                        placeCallback.resultFail();
                    }
                placeCallback = null;
                }
            }
        
            @Override
            public void handle(IntentSender intentSender, OnResultCallback callback) {
                placeCallback = callback;
                try {
                    startIntentSenderForResult(intentSender, LOCATION_SETTINGS_RESULT, null, 0, 0, 0);
                } catch (IntentSender.SendIntentException e) {
                    callback.resultFail();
                }
            }
        }
        
        public class PlaceFragment extends Fragment {
        
            private SettingsModifyHandler settingsModifyHandler;
        
            ...
        
            @Override
            public void onAttach(Activity activity) {
                super.onAttach(activity);
                if (context instanceof SettingsModifyHandler) {
                    settingsModifyHandler = (SettingsModifyHandler) context;
                } else {
                    throw new RuntimeException("Parent activity must implement PlaceFragment.SettingsModifyHandler interface");
                }
            }
        
            /* Callback from module, where you implemented status.getStatusCode().LocationSettingsStatusCodes.RESOLUTION_REQUIRED case
            (status is instance of com.google.android.gms.common.api.Status)
            You provide intentSender here through status.getResolution().getIntentSender() */
            @Override
            public void placeLoadError(IntentSender sender) {
                TextView view_text = (TextView) root.findViewById(R.id.text_error);
                TextView view_btn = (TextView) root.findViewById(R.id.btn_reply);
        
                view_text.setText("Need to change location settings");
                view_btn.setText("Change");
                view_btn.setOnClickListener(v -> {
                    settingsModifyHandler.handle(sender, new SettingsModifyHandler.OnResultCallback() {
                        @Override
                        public void resultOk() {
                            presenter.loadPlace(placeId);
                        }
        
                        @Override
                        public void resultFail() {
                            ToastUtils.show("You should change location settings!");
                        }
                    });
                });
            }
        
            public interface SettingsModifyHandler {
                void handle(IntentSender intentSender, OnResultCallback callback);
        
                interface OnResultCallback {
                    void resultOk();
                    void resultFail();
                }
            }
        }
        

        【讨论】:

          【解决方案10】:

          源代码

          https://drive.google.com/open?id=0BzBKpZ4nzNzUOXM2eEhHM3hOZk0

          build.gradle 文件中的依赖关系

          编译'com.google.android.gms:play-services-location:7.8.0'

          清单文件中的权限

          <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
          <uses-permission android:name="android.permission.INTERNET"/>
          <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
          <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
          
          
          package com.keshav.enablelocationwithokcancelbuttoncontrol;
          
          import android.content.Context;
          import android.location.Address;
          import android.location.Geocoder;
          import android.os.Bundle;
          import android.os.Handler;
          import android.os.Message;
          import android.util.Log;
          
          import java.io.IOException;
          import java.util.List;
          import java.util.Locale;
          
          public class LocationAddress
          {
              private static final String TAG = "LocationAddress";
          
              public static void getAddressFromLocation(final double latitude, final double longitude,
                                                        final Context context, final Handler handler) {
                  Thread thread = new Thread() {
                      @Override
                      public void run() {
                          Geocoder geocoder = new Geocoder(context, Locale.getDefault());
                          String result = null;
                          try {
                              List<Address> addressList = geocoder.getFromLocation(
                                      latitude, longitude, 1);
                              if (addressList != null && addressList.size() > 0) {
                                  Address address = addressList.get(0);
                                  StringBuilder sb = new StringBuilder();
                                  for (int i = 0; i < address.getMaxAddressLineIndex(); i++) {
                                      sb.append(address.getAddressLine(i)).append("\n");
                                  }
                                  sb.append(address.getLocality()).append("\n");
                                  sb.append(address.getPostalCode()).append("\n");
                                  sb.append(address.getCountryName());
                                  result = sb.toString();
                              }
                          } catch (IOException e) {
                              Log.e(TAG, "Unable connect to Geocoder", e);
                          } finally {
                              Message message = Message.obtain();
                              message.setTarget(handler);
                              if (result != null) {
                                  message.what = 1;
                                  Bundle bundle = new Bundle();
                                  result = "Latitude: " + latitude + " Longitude: " + longitude +
                                          "\n\nAddress:\n" + result;
                                  bundle.putString("address", result);
                                  message.setData(bundle);
                              } else {
                                  message.what = 1;
                                  Bundle bundle = new Bundle();
                                  result = "Latitude: " + latitude + " Longitude: " + longitude +
                                          "\n Unable to get address for this lat-long.";
                                  bundle.putString("address", result);
                                  message.setData(bundle);
                              }
                              message.sendToTarget();
                          }
                      }
                  };
                  thread.start();
              }
          }
          
          package com.keshav.enablelocationwithokcancelbuttoncontrol;
          
          import android.app.AlertDialog;
          import android.app.Service;
          import android.content.Context;
          import android.content.DialogInterface;
          import android.content.Intent;
          import android.location.Location;
          import android.location.LocationListener;
          import android.location.LocationManager;
          import android.os.Bundle;
          import android.os.IBinder;
          import android.provider.Settings;
          import android.util.Log;
          
          public class GPSTracker extends Service implements LocationListener
          {
          
              private final Context mContext;
          
              // flag for GPS status
              boolean isGPSEnabled = false;
          
              // flag for network status
              boolean isNetworkEnabled = false;
          
              // flag for GPS status
              boolean canGetLocation = false;
          
              Location location; // location
              double latitude; // latitude
              double longitude; // longitude
          
              // The minimum distance to change Updates in meters
              private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 10; // 10 meters
          
              // The minimum time between updates in milliseconds
              private static final long MIN_TIME_BW_UPDATES = 1000 * 60 * 1; // 1 minute
          
              // Declaring a Location Manager
              protected LocationManager locationManager;
          
              public GPSTracker(Context context) {
                  this.mContext = context;
                  getLocation();
              }
          
              public Location getLocation() {
                  try {
                      locationManager = (LocationManager) mContext.getSystemService(LOCATION_SERVICE);
          
                      // getting GPS status
                      isGPSEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
          
                      // getting network status
                      isNetworkEnabled = locationManager
                              .isProviderEnabled(LocationManager.NETWORK_PROVIDER);
          
                      if (!isGPSEnabled && !isNetworkEnabled) {
                          // no network provider is enabled
                      } else {
                          this.canGetLocation = true;
                          // First get location from Network Provider
                          if (isNetworkEnabled) {
                              locationManager.requestLocationUpdates(
                                      LocationManager.NETWORK_PROVIDER,
                                      MIN_TIME_BW_UPDATES,
                                      MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
          
                              Log.d("Network", "Network");
                              if (locationManager != null) {
                                  location = locationManager
                                          .getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
          
                                  if (location != null) {
                                      latitude = location.getLatitude();
                                      longitude = location.getLongitude();
                                  }
                              }
                          }
          
                          // if GPS Enabled get lat/long using GPS Services
                          if (isGPSEnabled) {
                              if (location == null) {
                                  locationManager.requestLocationUpdates(
                                          LocationManager.GPS_PROVIDER,
                                          MIN_TIME_BW_UPDATES,
                                          MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
          
                                  Log.d("GPS Enabled", "GPS Enabled");
                                  if (locationManager != null) {
                                      location = locationManager
                                              .getLastKnownLocation(LocationManager.GPS_PROVIDER);
          
                                      if (location != null) {
                                          latitude = location.getLatitude();
                                          longitude = location.getLongitude();
                                      }
                                  }
                              }
                          }
                      }
          
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
          
                  return location;
              }
          
              /**
               * Stop using GPS listener
               * Calling this function will stop using GPS in your app
               * */
          
              public void stopUsingGPS(){
                  if(locationManager != null){
                      locationManager.removeUpdates(GPSTracker.this);
                  }
              }
          
              /**
               * Function to get latitude
               * */
          
              public double getLatitude(){
                  if(location != null){
                      latitude = location.getLatitude();
                  }
          
                  // return latitude
                  return latitude;
              }
          
              /**
               * Function to get longitude
               * */
          
              public double getLongitude(){
                  if(location != null){
                      longitude = location.getLongitude();
                  }
          
                  // return longitude
                  return longitude;
              }
          
              /**
               * Function to check GPS/wifi enabled
               * @return boolean
               * */
          
              public boolean canGetLocation() {
                  return this.canGetLocation;
              }
          
              /**
               * Function to show settings alert dialog
               * On pressing Settings button will lauch Settings Options
               * */
          
              public void showSettingsAlert(){
                  AlertDialog.Builder alertDialog = new AlertDialog.Builder(mContext);
          
                  // Setting Dialog Title
                  alertDialog.setTitle("GPS is settings");
          
                  // Setting Dialog Message
                  alertDialog.setMessage("GPS is not enabled. Do you want to go to settings menu?");
          
                  // On pressing Settings button
                  alertDialog.setPositiveButton("Settings", new DialogInterface.OnClickListener() {
                      public void onClick(DialogInterface dialog, int which) {
                          Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                          mContext.startActivity(intent);
                      }
                  });
          
                  // on pressing cancel button
                  alertDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                      public void onClick(DialogInterface dialog, int which) {
                          dialog.cancel();
                      }
                  });
          
                  // Showing Alert Message
                  alertDialog.show();
              }
          
              @Override
              public void onLocationChanged(Location location) {
              }
          
              @Override
              public void onProviderDisabled(String provider) {
              }
          
              @Override
              public void onProviderEnabled(String provider) {
              }
          
              @Override
              public void onStatusChanged(String provider, int status, Bundle extras) {
              }
          
              @Override
              public IBinder onBind(Intent arg0) {
                  return null;
              }
          }
          
          
          package com.keshav.enablelocationwithokcancelbuttoncontrol;
          
          import android.content.Context;
          import android.location.Address;
          import android.location.Geocoder;
          import android.os.Bundle;
          import android.os.Handler;
          import android.os.Message;
          import android.util.Log;
          
          import java.io.IOException;
          import java.util.List;
          import java.util.Locale;
          
          public class LocationAddress
          {
              private static final String TAG = "LocationAddress";
          
              public static void getAddressFromLocation(final double latitude, final double longitude,
                                                        final Context context, final Handler handler) {
                  Thread thread = new Thread() {
                      @Override
                      public void run() {
                          Geocoder geocoder = new Geocoder(context, Locale.getDefault());
                          String result = null;
                          try {
                              List<Address> addressList = geocoder.getFromLocation(
                                      latitude, longitude, 1);
                              if (addressList != null && addressList.size() > 0) {
                                  Address address = addressList.get(0);
                                  StringBuilder sb = new StringBuilder();
                                  for (int i = 0; i < address.getMaxAddressLineIndex(); i++) {
                                      sb.append(address.getAddressLine(i)).append("\n");
                                  }
                                  sb.append(address.getLocality()).append("\n");
                                  sb.append(address.getPostalCode()).append("\n");
                                  sb.append(address.getCountryName());
                                  result = sb.toString();
                              }
                          } catch (IOException e) {
                              Log.e(TAG, "Unable connect to Geocoder", e);
                          } finally {
                              Message message = Message.obtain();
                              message.setTarget(handler);
                              if (result != null) {
                                  message.what = 1;
                                  Bundle bundle = new Bundle();
                                  result = "Latitude: " + latitude + " Longitude: " + longitude +
                                          "\n\nAddress:\n" + result;
                                  bundle.putString("address", result);
                                  message.setData(bundle);
                              } else {
                                  message.what = 1;
                                  Bundle bundle = new Bundle();
                                  result = "Latitude: " + latitude + " Longitude: " + longitude +
                                          "\n Unable to get address for this lat-long.";
                                  bundle.putString("address", result);
                                  message.setData(bundle);
                              }
                              message.sendToTarget();
                          }
                      }
                  };
                  thread.start();
              }
          }
          

          【讨论】:

            【解决方案11】:

            对于 Kotlin 用户

            此解决方案适用于ActivityFragment,只需对checkLocationSetting() 进行以下更改:

            关于活动 resolvableApiException.startResolutionForResult(this@MainActivity, REQUEST_CHECK_SETTING)

            片段 startIntentSenderForResult(resolvableApiException.resolution.intentSender, REQUEST_CHECK_SETTING, null, 0, 0,0,null)

            使用LocationSettingsResponse可以完成这个任务。

            MainActivity.kt

            
                private fun checkLocationSetting()
                    {
                        locationRequest = LocationRequest.create()
                        locationRequest.apply {
                            priority=LocationRequest.PRIORITY_HIGH_ACCURACY
                            interval = 5000
                            fastestInterval = 2000
                        }
                
                        val builder = LocationSettingsRequest.Builder()
                            .addLocationRequest(locationRequest)
                        builder.setAlwaysShow(true)
                
                        val result: Task<LocationSettingsResponse> = LocationServices.getSettingsClient(applicationContext)
                            .checkLocationSettings(builder.build())
                
                        result.addOnCompleteListener {
                            try{
                                val response: LocationSettingsResponse = it.getResult(ApiException::class.java)
                                Toast.makeText(this@MainActivity, "GPS is On", Toast.LENGTH_SHORT).show()
                                Log.d(TAG, "checkSetting: GPS On")
                            }catch(e:ApiException){
                
                                when(e.statusCode){
                                    LocationSettingsStatusCodes.RESOLUTION_REQUIRED ->{
                                        val resolvableApiException = e as ResolvableApiException
                                        resolvableApiException.startResolutionForResult(this@MainActivity, REQUEST_CHECK_SETTING)
                                        Log.d(TAG, "checkSetting: RESOLUTION_REQUIRED")
                                    }
                
                                    LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE -> {
                                        // USER DEVICE DOES NOT HAVE LOCATION OPTION
                                    }
                                }
                            }
                        }
                    }
            
            

            onActivityResult

             override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
                    super.onActivityResult(requestCode, resultCode, data)
                    when(requestCode)
                    {
                        REQUEST_CHECK_SETTING ->{
                            when(resultCode){
                                Activity.RESULT_OK->{
                                    Toast.makeText(this@MainActivity, "GPS is Turned on", Toast.LENGTH_SHORT).show()
                                }
                                Activity.RESULT_CANCELED ->{
                                    Toast.makeText(this@MainActivity, "GPS is Required to use this app", Toast.LENGTH_SHORT).show()
                                }
                            }
                        }
                    }
                }
            
            

            完整代码链接MainActivity.kt

            输出:

            链接到完整代码MainActivity.kt

            【讨论】:

            猜你喜欢
            • 2021-05-08
            • 2016-01-22
            • 1970-01-01
            • 1970-01-01
            • 2016-09-03
            • 2017-09-09
            • 2014-04-28
            相关资源
            最近更新 更多