java - 未触发地理围栏(广播接收器)

标签 java android android-geofence

没有使用广播接收器触发地理围栏。它与 IntentService 一起工作,但是当应用程序在后台时它不起作用。我已更改为广播接收器以使其工作,无论应用程序是在前台还是后台运行。在我通过查看下面的示例将其更改为广播接收器后,它甚至停止工作进入和驻留......

https://github.com/googlesamples/android-play-location/tree/master/Geofencing/app/src/main/java/com/google/android/gms/location/sample/geofencing

主要 Activity

public class MainActivity extends AppCompatActivity implements
        GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, ResultCallback<Status> {

    private static final String TAG = "Geofence example";
    private static final int MULTIPLE_PERMISSIONS = 1;
    private static final int REQUEST_PERMISSIONS_REQUEST_CODE = 34;
    private static final String ADDRESS_REQUESTED_KEY = "address-request-pending";
    private static final String LOCATION_ADDRESS_KEY = "location-address";
    String[] permissions = new String[]{
            Manifest.permission.ACCESS_FINE_LOCATION,
            Manifest.permission.READ_SMS,
            Manifest.permission.RECEIVE_SMS,

            Manifest.permission.SEND_SMS};
    private GeofencingClient mGeofencingClient;
    private PendingIntent mGeofencePendingIntent;
    private Context mContext;
    /**
     * Provides access to the Fused Location Provider API.
     */
    private FusedLocationProviderClient mFusedLocationClient;

    /**
     * Represents a geographical location.
     */
    private Location mLastLocation;

    /**
     * Tracks whether the user has requested an address. Becomes true when the user requests an
     * address and false when the address (or an error message) is delivered.
     */
    private boolean mAddressRequested;

    /**
     * The formatted location address.
     */
    private String mAddressOutput;

    /**
     * Receiver registered with this activity to get the response from FetchAddressIntentService.
     */
    private AddressResultReceiver mResultReceiver;

    /**
     * Displays the location address.
     */
    private TextView mLocationAddressTextView;

    /**
     * Visible while the address is being fetched.
     */
    private ProgressBar mProgressBar;

    /**
     * Kicks off the request to fetch an address when pressed.
     */
    private Button mFetchAddressButton;
    private List<Geofence> mGeofenceList;
    private GoogleApiClient mGoogleApiClient;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mResultReceiver = new AddressResultReceiver(new Handler());

        mLocationAddressTextView = (TextView) findViewById(R.id.location_address_view);
        mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
        mFetchAddressButton = (Button) findViewById(R.id.fetch_address_button);

        // Set defaults, then update using values stored in the Bundle.
        mAddressRequested = false;
        mAddressOutput = "";
        mContext = this;
        updateValuesFromBundle(savedInstanceState);
        mGeofencingClient = LocationServices.getGeofencingClient(this);
        //LatLng latLng = getLocationFromAddress(mContext, "517 Carrville Rd, Richmond Hill, ON L4C 6E5");

        mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
        SmsReceiver.bindListener(new SMSListener() {
            @Override
            public void messageReceived(String messageText) {

                //From the received text string you may do string operations to get the required OTP
                //It depends on your SMS format
                Log.d("Message", messageText);
                Toast.makeText(MainActivity.this, "Message: " + messageText, Toast.LENGTH_LONG).show();
                //startIntentService();


            }
        });
        Log.d(GeofenceTransitionsIntentService.TAG, "OnCreate - Building google API client");
        buildGoogleApiClient();
        Log.d(GeofenceTransitionsIntentService.TAG, "OnCreate - Before permission check");
        if (checkPermissions()) {
            Log.d(GeofenceTransitionsIntentService.TAG, "OnCreate - inside Permission ");
            getAddress();

        }

        Log.d(GeofenceTransitionsIntentService.TAG, "mGoogleApiClient = " + mGoogleApiClient);

        //test_sendNotification("testing");
        updateUIWidgets();
    }

    @Override
    public void onStart() {
        super.onStart();
        Log.d(GeofenceTransitionsIntentService.TAG, "onStart - before Permission ");
        if (!mGoogleApiClient.isConnecting() || !mGoogleApiClient.isConnected()) {
            mGoogleApiClient.connect();
        }

    }

    @Override
    protected void onStop() {
        super.onStop();
        if (mGoogleApiClient.isConnecting() || mGoogleApiClient.isConnected()) {
            mGoogleApiClient.disconnect();
        }
    }


    private PendingIntent getGeofencePendingIntent() {
        // Reuse the PendingIntent if we already have it.
        if (mGeofencePendingIntent != null) {
            return mGeofencePendingIntent;
        }

        Intent intent = new Intent(this, GeofenceBroadcastReceiver.class);
        // We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when
        // calling addGeofences() and removeGeofences().
        mGeofencePendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.
                FLAG_UPDATE_CURRENT);
        Log.d(GeofenceTransitionsIntentService.TAG, "getGeofencePendingIntent " + mGeofencePendingIntent);
        return mGeofencePendingIntent;
    }

    /**
     * Updates fields based on data stored in the bundle.
     */
    private void updateValuesFromBundle(Bundle savedInstanceState) {
        if (savedInstanceState != null) {
            // Check savedInstanceState to see if the address was previously requested.
            if (savedInstanceState.keySet().contains(ADDRESS_REQUESTED_KEY)) {
                mAddressRequested = savedInstanceState.getBoolean(ADDRESS_REQUESTED_KEY);
            }
            // Check savedInstanceState to see if the location address string was previously found
            // and stored in the Bundle. If it was found, display the address string in the UI.
            if (savedInstanceState.keySet().contains(LOCATION_ADDRESS_KEY)) {
                mAddressOutput = savedInstanceState.getString(LOCATION_ADDRESS_KEY);
                displayAddressOutput();
            }
        }
    }

    /**
     * Runs when user clicks the Fetch Address button.
     */
    @SuppressWarnings("unused")
    public void fetchAddressButtonHandler(View view) {
        if (mLastLocation != null) {
            Log.d(GeofenceTransitionsIntentService.TAG, "fetchAddressButtonHandler - mLastLocation " + mLastLocation);
            startIntentService();
            return;
        }

        // If we have not yet retrieved the user location, we process the user's request by setting
        // mAddressRequested to true. As far as the user is concerned, pressing the Fetch Address button
        // immediately kicks off the process of getting the address.
        mAddressRequested = true;
        updateUIWidgets();
    }

    /**
     * Creates an intent, adds location data to it as an extra, and starts the intent service for
     * fetching an address.
     */
    private void startIntentService() {
        // Create an intent for passing to the intent service responsible for fetching the address.
        Intent intent = new Intent(this, FetchAddressIntentService.class);

        // Pass the result receiver as an extra to the service.
        intent.putExtra(Constants.RECEIVER, mResultReceiver);


        // Pass the location data as an extra to the service.
        intent.putExtra(Constants.LOCATION_DATA_EXTRA, mLastLocation);

        // Start the service. If the service isn't already running, it is instantiated and started
        // (creating a process for it if needed); if it is running then it remains running. The
        // service kills itself automatically once all intents are processed.
        startService(intent);
        Log.d(GeofenceTransitionsIntentService.TAG, "startIntentService - addGeoFenceList " + (mGeofenceList != null));
        addGeoFence();

    }

    /**
     * Gets the address for the last known location.
     */
    @SuppressWarnings("MissingPermission")
    private void getAddress() {

        Log.d(GeofenceTransitionsIntentService.TAG, "MainActivity - getAddress () ");
        mFusedLocationClient.getLastLocation()
                .addOnSuccessListener(this, new OnSuccessListener<Location>() {
                    @Override
                    public void onSuccess(Location location) {
                        if (location == null) {
                            Log.w(GeofenceTransitionsIntentService.TAG, "onSuccess:null");
                            return;
                        }

                        mLastLocation = location;


                        // Determine whether a Geocoder is available.
                        if (!Geocoder.isPresent()) {
                            showSnackbar(getString(R.string.no_geocoder_available));
                            return;
                        }

                        // If the user pressed the fetch address button before we had the location,
                        // this will be set to true indicating that we should kick off the intent
                        // service after fetching the location.
                        if (mAddressRequested) {
                            startIntentService();


                        }
                    }
                })
                .addOnFailureListener(this, new OnFailureListener() {
                    @Override
                    public void onFailure(@NonNull Exception e) {
                        Log.w(TAG, "getLastLocation:onFailure", e);
                    }
                });
    }


    private GeofencingRequest getGeofencingRequest() {
        Log.d(GeofenceTransitionsIntentService.TAG, "MainActivity - getGeofencingRequest ");
        GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
        builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER | GeofencingRequest.INITIAL_TRIGGER_DWELL | GeofencingRequest.INITIAL_TRIGGER_EXIT);
        builder.addGeofences(mGeofenceList);
        return builder.build();
    }

    private void addGeoFence() {
        Log.d(GeofenceTransitionsIntentService.TAG, "MainActivity - mGeofenceList () ");
        mGeofenceList = new ArrayList<Geofence>();
        for (Map.Entry<String, LatLng> entry : Constants.GEO_FENCE_LIST.entrySet()) {

            mGeofenceList.add(new Geofence.Builder()
                    // Set the request ID of the geofence. This is a string to identify this
                    // geofence.
                    .setRequestId(entry.getKey())

                    // Set the circular region of this geofence.
                    .setCircularRegion(
                            entry.getValue().latitude,
                            entry.getValue().longitude,
                            Constants.GEOFENCE_RADIUS_IN_METERS
                    )

                    .setExpirationDuration(Geofence.NEVER_EXPIRE)
                    .setLoiteringDelay(6000)
                    .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER |
                            Geofence.GEOFENCE_TRANSITION_EXIT | Geofence.GEOFENCE_TRANSITION_DWELL)

                    // Create the geofence.
                    .build());
        }

        Log.d(GeofenceTransitionsIntentService.TAG, "addGeoFenceList - mGeofenceList " + mGeofenceList);
        //getGeofencingRequest();


        if (!mGoogleApiClient.isConnected()) {
            mGoogleApiClient.connect();
            Toast.makeText(this, "not connected", Toast.LENGTH_SHORT).show();
            return;
        }
        Log.d(GeofenceTransitionsIntentService.TAG, "addGeoFenceList - mGoogleApiClient " + mGoogleApiClient.isConnected());
        try {
            LocationServices.GeofencingApi.addGeofences(
                    mGoogleApiClient,
                    // The GeofenceRequest object.
                    getGeofencingRequest(),
                    // A pending intent that that is reused when calling removeGeofences(). This
                    // pending intent is used to generate an intent when a matched geofence
                    // transition is observed.
                    getGeofencePendingIntent()
            ).setResultCallback(this); // Result processed in onResult().
        } catch (SecurityException securityException) {
            // Catch exception generated if the app does not use ACCESS_FINE_LOCATION permission.
            Log.d(GeofenceTransitionsIntentService.TAG, "addGeoFenceList - error " + securityException.getLocalizedMessage());
        }

    }

    /**
     * Updates the address in the UI.
     */
    private void displayAddressOutput() {
        mLocationAddressTextView.setText(mAddressOutput);
    }

    /**
     * Toggles the visibility of the progress bar. Enables or disables the Fetch Address button.
     */
    private void updateUIWidgets() {
        if (mAddressRequested) {
            mProgressBar.setVisibility(ProgressBar.VISIBLE);
            mFetchAddressButton.setEnabled(false);
        } else {
            mProgressBar.setVisibility(ProgressBar.GONE);
            mFetchAddressButton.setEnabled(true);
        }
    }

    /**
     * Shows a toast with the given text.
     */
    private void showToast(String text) {
        Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onSaveInstanceState(Bundle savedInstanceState) {
        // Save whether the address has been requested.
        savedInstanceState.putBoolean(ADDRESS_REQUESTED_KEY, mAddressRequested);

        // Save the address string.
        savedInstanceState.putString(LOCATION_ADDRESS_KEY, mAddressOutput);
        super.onSaveInstanceState(savedInstanceState);
    }

    @Override
    public void onConnected(@Nullable Bundle bundle) {

    }

    @Override
    public void onConnectionSuspended(int i) {

    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {

    }

    @Override
    public void onResult(Status status) {
        if (status.isSuccess()) {
            Toast.makeText(
                    this,
                    "Geofences Added",
                    Toast.LENGTH_SHORT
            ).show();
        } else {
            // Get the status code for the error and log it using a user-friendly message.
            Toast.makeText(
                    this,
                    "Geofences error - onResult",
                    Toast.LENGTH_SHORT
            ).show();
        }
    }

    /**
     * Shows a {@link Snackbar} using {@code text}.
     *
     * @param text The Snackbar text.
     */
    private void showSnackbar(final String text) {
        View container = findViewById(android.R.id.content);
        if (container != null) {
            Snackbar.make(container, text, Snackbar.LENGTH_LONG).show();
        }
    }


    private void showSnackbar(final int mainTextStringId, final int actionStringId,
                              View.OnClickListener listener) {
        Snackbar.make(findViewById(android.R.id.content),
                getString(mainTextStringId),
                Snackbar.LENGTH_INDEFINITE)
                .setAction(getString(actionStringId), listener).show();
    }

    /**
     * Return the current state of the permissions needed.
     */
    private boolean checkPermissions() {
        int result;
        List<String> listPermissionsNeeded = new ArrayList<>();
        for (String p : permissions) {
            result = ContextCompat.checkSelfPermission(getApplicationContext(), p);
            if (result != PackageManager.PERMISSION_GRANTED) {
                listPermissionsNeeded.add(p);
            }
        }
        if (!listPermissionsNeeded.isEmpty()) {
            ActivityCompat.requestPermissions(this, listPermissionsNeeded.toArray(new String[listPermissionsNeeded.size()]), MULTIPLE_PERMISSIONS);
            return false;
        }
        return true;
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
            case MULTIPLE_PERMISSIONS: {
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    // permissions granted.
                    getAddress();
                } else {
                    String perStr = "";
                    for (String per : permissions) {
                        perStr += "\n" + per;
                    }
                    // permissions list of don't granted permission
                }
                return;
            }
        }
    }


    protected synchronized void buildGoogleApiClient() {
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
        if (!mGoogleApiClient.isConnecting() || !mGoogleApiClient.isConnected()) {
            mGoogleApiClient.connect();
        }
        Log.d(GeofenceTransitionsIntentService.TAG, "buildGoogleApiClient - mGoogleApiClient " + mGoogleApiClient);
    }



private class AddressResultReceiver extends ResultReceiver {
        AddressResultReceiver(Handler handler) {
            super(handler);
        }


        @Override
        protected void onReceiveResult(int resultCode, Bundle resultData) {

            // Display the address string or an error message sent from the intent service.
            mAddressOutput = resultData.getString(Constants.RESULT_DATA_KEY);
            displayAddressOutput();

            // Show a toast message if an address was found.
            if (resultCode == Constants.SUCCESS_RESULT) {
                showToast(getString(R.string.address_found));
            }
            Log.d(GeofenceTransitionsIntentService.TAG, "onReceiveResult - onReceiveResult ");
            // Reset. Enable the Fetch Address button and stop showing the progress bar.
            mAddressRequested = false;
            updateUIWidgets();
        }
    }




}

工作意向服务:
public class GeofenceTransitionsIntentService extends JobIntentService {
    protected static final String TAG = "geofence-transitions";
    private static final int JOB_ID = 573;


    @Override
    public void onCreate() {
        super.onCreate();
        Log.e(TAG, "GeofenceTransitionsIntentService onCreate ");
    }

    @Override
    protected void onHandleWork(@NonNull Intent intent) {

            Log.e(TAG, "GeofenceTransitionsIntentService intent " + intent);
            GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
            if (geofencingEvent.hasError()) {
                String errorMessage = "error";
                Log.e(TAG, errorMessage);
                return;
            }

            // Get the transition type.
            int geofenceTransition = geofencingEvent.getGeofenceTransition();
            String geofenceTransitionString = getTransitionString(geofenceTransition);
            Log.e(TAG, "geofence_transition_geofenceTransition " + geofenceTransition);
            // Test that the reported transition was of interest.
            if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER ||
                    geofenceTransition == Geofence.GEOFENCE_TRANSITION_DWELL || geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) {

                // Get the geofences that were triggered. A single event can trigger multiple geofences.
                List<Geofence> triggeringGeofences = geofencingEvent.getTriggeringGeofences();

                // Get the transition details as a String.
                String geofenceTransitionDetails = getGeofenceTransitionDetails(
                        this,
                        geofenceTransition,
                        triggeringGeofences
                );

                // Send notification and log the transition details.
                sendNotification(geofenceTransitionDetails, geofenceTransitionString);
                Log.i(TAG, geofenceTransitionDetails);
            } else {
                // Log the error.
                Log.e(TAG, "geofence_transition_invalid_type" + geofenceTransition);
            }


    }

    /**
     * Convenience method for enqueuing work in to this service.
     */
    public static void enqueueWork(Context context, Intent intent) {
        enqueueWork(context, GeofenceTransitionsIntentService.class, JOB_ID, intent);
    }



    private String getGeofenceTransitionDetails(
            Context context,
            int geofenceTransition,
            List<Geofence> triggeringGeofences) {

        String geofenceTransitionString = getTransitionString(geofenceTransition);

        // Get the Ids of each geofence that was triggered.
        ArrayList triggeringGeofencesIdsList = new ArrayList();
        for (Geofence geofence : triggeringGeofences) {
            triggeringGeofencesIdsList.add(geofence.getRequestId());
        }
        String triggeringGeofencesIdsString = TextUtils.join(", ", triggeringGeofencesIdsList);

        return geofenceTransitionString + ": " + triggeringGeofencesIdsString;
    }

    private String getTransitionString(int transitionType) {
        switch (transitionType) {
            case Geofence.GEOFENCE_TRANSITION_ENTER:
                return "Entered";
            case Geofence.GEOFENCE_TRANSITION_EXIT:
                return "Exited";
            case Geofence.GEOFENCE_TRANSITION_DWELL:
                return "Dwell";
            default:
                return "unkwn";
        }
    }

    private void sendNotification(String notificationDetails, String geofenceTransitionString) {

        NotificationCompat.Builder mBuilder =
                new NotificationCompat.Builder(getApplicationContext(), "notify_geofence");
        Intent ii = new Intent(getApplicationContext(), MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, ii, 0);

        //Assign BigText style notification
        NotificationCompat.BigTextStyle bigText = new NotificationCompat.BigTextStyle();
        bigText.bigText(notificationDetails);
        bigText.setSummaryText(geofenceTransitionString);

        mBuilder.setContentIntent(pendingIntent);
        mBuilder.setSmallIcon(R.mipmap.ic_launcher_round);
        mBuilder.setContentTitle("Geofence notification");
        mBuilder.setContentText(notificationDetails);
        mBuilder.setPriority(Notification.PRIORITY_MAX);
        mBuilder.setStyle(bigText);


        NotificationManager mNotificationManager =
                (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);


        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel("notify_001",
                    "Channel human readable title",
                    NotificationManager.IMPORTANCE_DEFAULT);
            mNotificationManager.createNotificationChannel(channel);
        }

        mNotificationManager.notify(new Random().nextInt(61) + 20, mBuilder.build());
    }

}

广播接收器:
public class GeofenceBroadcastReceiver extends BroadcastReceiver {


    @Override
    public void onReceive(Context context, Intent intent) {
        // Enqueues a JobIntentService passing the context and intent as parameters
        GeofenceTransitionsIntentService.enqueueWork(context, intent);
    }
}

日志:
2018-10-30 12:40:02.850 24811-24811/com.lakshmiapps.findme D/geofence-transitions: fetchAddressButtonHandler - mLastLocation Location[fused 43.849304,-79.449305 hAcc=14 et=+2d21h3m43s23ms alt=161.3000030517578 vAcc=2 sAcc=??? bAcc=??? {Bundle[mParcelledData.dataSize=52]}]
2018-10-30 12:40:02.857 24811-24811/com.lakshmiapps.findme D/geofence-transitions: startIntentService - addGeoFenceList false
2018-10-30 12:40:02.857 24811-24811/com.lakshmiapps.findme D/geofence-transitions: MainActivity - mGeofenceList () 
2018-10-30 12:40:07.819 24811-24811/com.lakshmiapps.findme D/geofence-transitions: addGeoFenceList - mGeofenceList [Geofence[CIRCLE id:Ross Doan transitions:7 43.857670, -79.443370 1000m, resp=0s, dwell=6000ms, @-1], Geofence[CIRCLE id:Niagara falls transitions:7 43.078350, -79.081910 1000m, resp=0s, dwell=6000ms, @-1], Geofence[CIRCLE id:Taste of Malayalees  transitions:7 43.635190, -79.622830 1000m, resp=0s, dwell=6000ms, @-1], Geofence[CIRCLE id:Home transitions:7 43.849301, -79.449306 1000m, resp=0s, dwell=6000ms, @-1], Geofence[CIRCLE id:Luv2Play transitions:7 43.855660, -79.430200 1000m, resp=0s, dwell=6000ms, @-1]]
2018-10-30 12:40:09.534 24811-24811/com.lakshmiapps.findme D/geofence-transitions: addGeoFenceList - mGoogleApiClient true
2018-10-30 12:40:09.534 24811-24811/com.lakshmiapps.findme D/geofence-transitions: MainActivity - getGeofencingRequest 
2018-10-30 12:40:13.598 24811-24811/com.lakshmiapps.findme D/geofence-transitions: getGeofencePendingIntent PendingIntent{5fddab6: android.os.BinderProxy@47dc3b7}
2018-10-30 12:40:13.631 4938-5579/? I/GeofencerStateMachine: addGeofences called by com.lakshmiapps.findme
2018-10-30 12:40:13.631 4726-4773/? E/GmsLocationManagerService_FLP: [GeofencingApi] AddGeofence from com.lakshmiapps.findme and size = 5
2018-10-30 12:40:13.632 4726-4773/? D/GeofenceRequestInfo_FLP: removeExpiredGeofences - com.lakshmiapps.findme: 0 fences
2018-10-30 12:40:13.632 4726-4773/? E/GeofenceRequestManager_FLP: [GeofencingApi] AddGeofence 5 fences from com.lakshmiapps.findme
2018-10-30 12:40:13.632 4726-4773/? D/GeofenceRequestManager_FLP: 1540668973552  gpslastknowntime: 243443714 / nlplastknowntime: 248290794
2018-10-30 12:40:16.290 24811-24811/com.lakshmiapps.findme D/geofence-transitions: onReceiveResult - onReceiveResult 
2018-10-30 12:44:08.692 4938-24505/? I/GeofencerStateMachine: removeGeofences: removeRequest=RemoveGeofencingRequest[REMOVE_BY_PENDING_INTENT pendingIntent=PendingIntent[creatorPackage=com.marriott.mrt], packageName=null]

谢谢,
拉梅什

最佳答案

添加

<uses-permission android:name="android.permission.WAKE_LOCK" />

在我的情况下,AndroidManifest.xml 似乎解决了这个问题

关于java - 未触发地理围栏(广播接收器),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53069414/

相关文章:

Java:在实现各种树结构时我应该支持泛型吗?

android - 更新 gradle 依赖项后呈现问题

android - 无法从 PendingIntent 触发地理围栏转换 IntentService

android - 为什么 Android 上的 Google Geofencing API 不适用于所有设备

java - 使用 Java 代码导入 Eclipse 项目

java - Eclipse 内存不足且运行缓慢

java - Mockito 验证匹配 数组的可选

android - 带有 Proguard 的 Ant - java.lang.NoClassDefFoundError

Android模拟器sdcard权限

Android,当应用程序打开时显示 alertDialog 而不是通知