【问题标题】:Updating location using FusedLocationProviderClient, JobScheduler and JobService使用 FusedLocationProviderClient、JobScheduler 和 JobService 更新位置
【发布时间】:2018-01-09 14:36:35
【问题描述】:

我正在尝试使用带有 JobScheduler 和 JobService 的新 FusedLocationProviderClient 创建后台位置跟踪服务。我读过here

我们应该为此使用requestLocationUpdates (LocationRequest request, PendingIntent callbackIntent)。我正在我的 MainActivity 中安排工作,如下所示:

final ComponentName serviceComponent = new ComponentName(this, LocationJobService.class);
final JobScheduler jobScheduler;
final JobInfo.Builder builder = new JobInfo.Builder(LocationJobService.LOCATION_JOB_SERVICE_ID,
                        serviceComponent);
builder.setPeriodic(JobInfo.getMinPeriodMillis(), JobInfo.getMinFlexMillis());
builder.setPersisted(true);
final JobInfo jobInfo = builder.build();
jobScheduler = (JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE);
if (jobScheduler != null)
{
    if(jobScheduler.schedule(jobInfo) == JobScheduler.RESULT_SUCCESS)
       Log.d(TAG, String.format("Job %s successfully scheduled", LocationJobService.LOCATION_JOB_SERVICE_ID));
    else
       Log.e(TAG, "Job schedule failed!");
    }

LocationJobService 类:

private static final String TAG = "Class " + LocationJobService.class.getSimpleName();
public static final int LOCATION_JOB_SERVICE_ID = 1001;
private FusedLocationProviderClient client;

@Override
public boolean onStartJob(final JobParameters params)
{
    //some logic before initializing location update
    initializeLocationUpdate(getApplicationContext());
}
private void initializeLocationUpdate(Context context)
{
    client = LocationServices.getFusedLocationProviderClient(context);
    LocationRequest locationRequest = LocationRequest.create();
    locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    locationRequest.setInterval(3000);
    locationRequest.setFastestInterval(1000);

    client.requestLocationUpdates(locationRequest, pendingIntent)
            .addOnCompleteListener(new OnCompleteListener<Void>()
            {
                @Override
                public void onComplete(@NonNull Task<Void> task)
                {
                    Log.d(TAG, "onComplete " + task.getResult());
                }
            }).addOnFailureListener(new OnFailureListener()
            {
                @Override
                public void onFailure(@NonNull Exception e)
                {
                    Log.d(TAG, "onFailure");
                }
            });
    }

任何想法如何为requestLocationUpdates 提供 PendingIntent 以及如何从onComplete(@NonNull Task&lt;Void&gt; task) 获取Location?

【问题讨论】:

    标签: android background-service android-jobscheduler fusedlocationproviderclient


    【解决方案1】:

    我想出的解决方案是我创建了扩展BroadcastReceiver 的接收器,然后使用它来创建PendingIntent。然后从我在onReceive 方法中得到的意图中提取位置。 在JobService:

    client = LocationServices.getFusedLocationProviderClient(getApplicationContext());
    Intent locationReceiverIntent = new Intent(getApplicationContext(), LocationJobServiceReceiver.class);
    locationReceiverIntent.setAction(LocationJobServiceReceiver.PROCESS_UPDATES);
    PendingIntent locationReceiverPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), LocationJobServiceReceiver.LOCATION_JOB_SERVICE_RECEIVER_CODE, locationReceiverIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    LocationRequest locationRequest = new LocationRequest();
    locationRequest.setInterval(60000);
    locationRequest.setFastestInterval(30000);
    locationRequest.setExpirationDuration(60000);
    locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    if (checkPermission(getApplicationContext()))
    {
        client.requestLocationUpdates(locationRequest,locationReceiverPendingIntent);
        jobFinished(jobParameters, false);
    }
    

    在LocationJobServiceReceiver的onReceive(final Context context, Intent intent):

    client = LocationServices.getFusedLocationProviderClient(context);
    final LocationResult locationResult = LocationResult.extractResult(intent);
    final String action = intent.getAction();
    pendingIntent = PendingIntent.getBroadcast(context, LOCATION_JOB_SERVICE_RECEIVER_CODE, intent, PendingIntent.FLAG_NO_CREATE);
    if (PROCESS_UPDATES.equals(action))
    {
        if (locationResult != null)
        {
            Location location;
            List<Location> locations = locationResult.getLocations();
            if (locations.size() > 0)
            {
                location = locations.get(0);
            }
            else
            {
                location = locationResult.getLastLocation();
            }
        }
    }
    client.removeLocationUpdates(pendingIntent);
    

    【讨论】:

    • 您可以访问完整的示例吗? Android noob 试图在此处设置后台地理位置(应用已关闭)。
    • 很遗憾没有。这是我公司项目的一部分,所以我不能分享整个项目。但是你已经从我的问题和我的回答中得到了你需要的所有代码。这是我使用的 Google Developers 的示例github.com/googlesamples/android-play-location/tree/master/…
    【解决方案2】:

    使用 Kotlin 而不是 java here 推出了 this google android/java 示例项目的 kotlin fork(未经测试,可能有错误)

    字符串和维度值文件不包括在下面。您自己的硬编码或查看fork

    MainActivity.kt

        /**
     * Copyright 2017 Google Inc. All Rights Reserved.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     * http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package com.google.android.gms.location.sample.locationupdatespendingintent
    
    import android.Manifest
    import android.app.PendingIntent
    import android.content.Intent
    import android.content.SharedPreferences
    import android.content.pm.PackageManager
    import android.net.Uri
    import android.os.Bundle
    import android.preference.PreferenceManager
    import android.provider.Settings
    import android.support.design.widget.Snackbar
    import android.support.v4.app.ActivityCompat
    import android.support.v4.app.FragmentActivity
    import android.util.Log
    import android.view.View
    import android.widget.Button
    import android.widget.TextView
    
    import com.google.android.gms.location.FusedLocationProviderClient
    import com.google.android.gms.location.LocationRequest
    import com.google.android.gms.location.LocationServices
    
    
    /**
     * The only activity in this sample. Displays UI widgets for requesting and removing location
     * updates, and for the batched location updates that are reported.
     *
     * Location updates requested through this activity continue even when the activity is not in the
     * foreground. Note: apps running on "O" devices (regardless of targetSdkVersion) may receive
     * updates less frequently than the interval specified in the [LocationRequest] when the app
     * is no longer in the foreground.
     */
    class MainActivity : FragmentActivity(), SharedPreferences.OnSharedPreferenceChangeListener {
    
        /**
         * Stores parameters for requests to the FusedLocationProviderApi.
         */
        val mLocationRequest = LocationRequest()
    
        /**
         * Provides access to the Fused Location Provider API.
         */
        private var mFusedLocationClient: FusedLocationProviderClient? = null
    
        // UI Widgets.
        private var mRequestUpdatesButton: Button? = null
        private var mRemoveUpdatesButton: Button? = null
        private var mLocationUpdatesResultView: TextView? = null
    
    
        // Note: for apps targeting API level 25 ("Nougat") or lower, either
        // PendingIntent.getService() or PendingIntent.getBroadcast() may be used when requesting
        // location updates. For apps targeting API level O, only
        // PendingIntent.getBroadcast() should be used. This is due to the limits placed on services
        // started in the background in "O".
        // TODO(developer): uncomment to use PendingIntent.getService().
        //        Intent intent = new Intent(this, LocationUpdatesIntentService.class);
        //        intent.setAction(LocationUpdatesIntentService.ACTION_PROCESS_UPDATES);
        //        return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        private val pendingIntent: PendingIntent
            get() {
                val intent = Intent(this, LocationUpdatesBroadcastReceiver::class.java)
                intent.action = LocationUpdatesBroadcastReceiver.ACTION_PROCESS_UPDATES
                return PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
            }
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
    
            mRequestUpdatesButton = findViewById(R.id.remove_updates_button)
            mRemoveUpdatesButton = findViewById(R.id.request_updates_button)
            mLocationUpdatesResultView = findViewById(R.id.location_updates_result)
    
            // Check if the user revoked runtime permissions.
            if (!checkPermissions()) {
                requestPermissions()
            }
    
    
            createLocationRequest()
        }
    
        override fun onStart() {
            super.onStart()
            PreferenceManager.getDefaultSharedPreferences(this)
                    .registerOnSharedPreferenceChangeListener(this)
        }
    
    
        override fun onResume() {
            super.onResume()
            updateButtonsState(Utils.getRequestingLocationUpdates(this))
            mLocationUpdatesResultView?.text = Utils.getLocationUpdatesResult(this)
        }
    
        override fun onStop() {
            PreferenceManager.getDefaultSharedPreferences(this)
                    .unregisterOnSharedPreferenceChangeListener(this)
            super.onStop()
        }
    
        /**
         * Sets up the location request. Android has two location request settings:
         * `ACCESS_COARSE_LOCATION` and `ACCESS_FINE_LOCATION`. These settings control
         * the accuracy of the current location. This sample uses ACCESS_FINE_LOCATION, as defined in
         * the AndroidManifest.xml.
         *
         *
         * When the ACCESS_FINE_LOCATION setting is specified, combined with a fast update
         * interval (5 seconds), the Fused Location Provider API returns location updates that are
         * accurate to within a few feet.
         *
         *
         * These settings are appropriate for mapping applications that show real-time location
         * updates.
         */
        private fun createLocationRequest() {
    
            // Sets the desired interval for active location updates. This interval is
            // inexact. You may not receive updates at all if no location sources are available, or
            // you may receive them slower than requested. You may also receive updates faster than
            // requested if other applications are requesting location at a faster interval.
            // Note: apps running on "O" devices (regardless of targetSdkVersion) may receive updates
            // less frequently than this interval when the app is no longer in the foreground.
            mLocationRequest.interval = UPDATE_INTERVAL
    
            // Sets the fastest rate for active location updates. This interval is exact, and your
            // application will never receive updates faster than this value.
            mLocationRequest.fastestInterval = FASTEST_UPDATE_INTERVAL
    
            mLocationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
    
            // Sets the maximum time when batched location updates are delivered. Updates may be
            // delivered sooner than this interval.
            mLocationRequest.maxWaitTime = MAX_WAIT_TIME
        }
    
        /**
         * Return the current state of the permissions needed.
         */
        private fun checkPermissions(): Boolean {
            val permissionState = ActivityCompat.checkSelfPermission(this,
                    Manifest.permission.ACCESS_FINE_LOCATION)
            return permissionState == PackageManager.PERMISSION_GRANTED
        }
    
        private fun requestPermissions() {
            val shouldProvideRationale = ActivityCompat.shouldShowRequestPermissionRationale(this,
                    Manifest.permission.ACCESS_FINE_LOCATION)
    
            // Provide an additional rationale to the user. This would happen if the user denied the
            // request previously, but didn't check the "Don't ask again" checkbox.
            if (shouldProvideRationale) {
                Log.i(TAG, "Displaying permission rationale to provide additional context.")
                Snackbar.make(
                        findViewById(R.id.activity_main),
                        R.string.permission_rationale,
                        Snackbar.LENGTH_INDEFINITE)
                        .setAction(R.string.ok) {
                            // Request permission
                            ActivityCompat.requestPermissions(this@MainActivity,
                                    arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                                    REQUEST_PERMISSIONS_REQUEST_CODE)
                        }
                        .show()
            } else {
                Log.i(TAG, "Requesting permission")
                // Request permission. It's possible this can be auto answered if device policy
                // sets the permission in a given state or the user denied the permission
                // previously and checked "Never ask again".
                ActivityCompat.requestPermissions(this@MainActivity,
                        arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                        REQUEST_PERMISSIONS_REQUEST_CODE)
            }
        }
    
        /**
         * Callback received when a permissions request has been completed.
         */
        override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>,
                                                grantResults: IntArray) {
            Log.i(TAG, "onRequestPermissionResult")
            if (requestCode == REQUEST_PERMISSIONS_REQUEST_CODE) {
                if (grantResults.size <= 0) {
                    // If user interaction was interrupted, the permission request is cancelled and you
                    // receive empty arrays.
                    Log.i(TAG, "User interaction was cancelled.")
                } else if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    // Permission was granted.
                    requestLocationUpdates(null)
                } else {
                    // Permission denied.
    
                    // Notify the user via a SnackBar that they have rejected a core permission for the
                    // app, which makes the Activity useless. In a real app, core permissions would
                    // typically be best requested during a welcome-screen flow.
    
                    // Additionally, it is important to remember that a permission might have been
                    // rejected without asking the user for permission (device policy or "Never ask
                    // again" prompts). Therefore, a user interface affordance is typically implemented
                    // when permissions are denied. Otherwise, your app could appear unresponsive to
                    // touches or interactions which have required permissions.
                    Snackbar.make(
                            findViewById(R.id.activity_main),
                            R.string.permission_denied_explanation,
                            Snackbar.LENGTH_INDEFINITE)
                            .setAction(R.string.settings) {
                                // Build intent that displays the App settings screen.
                                val intent = Intent()
                                intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
                                val uri = Uri.fromParts("package",
                                        BuildConfig.APPLICATION_ID, null)
                                intent.data = uri
                                intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
                                startActivity(intent)
                            }
                            .show()
                }
            }
        }
    
        override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, s: String) {
            if (s == Utils.KEY_LOCATION_UPDATES_RESULT) {
                mLocationUpdatesResultView?.text = Utils.getLocationUpdatesResult(this)
            } else if (s == Utils.KEY_LOCATION_UPDATES_REQUESTED) {
                updateButtonsState(Utils.getRequestingLocationUpdates(this))
            }
        }
    
        /**
         * Handles the Request Updates button and requests start of location updates.
         */
        fun requestLocationUpdates(view: View?) {
            try {
                Log.i(TAG, "Starting location updates")
                Utils.setRequestingLocationUpdates(this, true)
                mFusedLocationClient?.requestLocationUpdates(mLocationRequest, pendingIntent)
            } catch (e: SecurityException) {
                Utils.setRequestingLocationUpdates(this, false)
                e.printStackTrace()
            }
    
        }
    
        /**
         * Handles the Remove Updates button, and requests removal of location updates.
         */
        fun removeLocationUpdates(view: View) {
            Log.i(TAG, "Removing location updates")
            Utils.setRequestingLocationUpdates(this, false)
            mFusedLocationClient?.removeLocationUpdates(pendingIntent)
        }
    
        /**
         * Ensures that only one button is enabled at any time. The Start Updates button is enabled
         * if the user is not requesting location updates. The Stop Updates button is enabled if the
         * user is requesting location updates.
         */
        private fun updateButtonsState(requestingLocationUpdates: Boolean) {
            if (requestingLocationUpdates) {
                mRequestUpdatesButton?.isEnabled = false
                mRemoveUpdatesButton?.isEnabled = true
            } else {
                mRequestUpdatesButton?.isEnabled = true
                mRemoveUpdatesButton?.isEnabled = false
            }
        }
    
        companion object {
    
            private val TAG = MainActivity::class.java.getSimpleName()
            private val REQUEST_PERMISSIONS_REQUEST_CODE = 34
            /**
             * The desired interval for location updates. Inexact. Updates may be more or less frequent.
             */
            private val UPDATE_INTERVAL: Long = 60000 // Every 60 seconds.
    
            /**
             * The fastest rate for active location updates. Updates will never be more frequent
             * than this value, but they may be less frequent.
             */
            private val FASTEST_UPDATE_INTERVAL: Long = 30000 // Every 30 seconds
    
            /**
             * The max time before batched results are delivered by location services. Results may be
             * delivered sooner than this interval.
             */
            private val MAX_WAIT_TIME = UPDATE_INTERVAL * 5 // Every 5 minutes.
        }
    }
    

    activity_main.xml 这里的监听器触发 MainActivity.kt 中的方法

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/activity_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        tools:context="com.google.android.gms.location.sample.locationupdatespendingintent.MainActivity">
    
        <TextView
            android:id="@+id/using_batched_location_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/batched_location_updates"
            android:layout_marginBottom="@dimen/default_margin"
            android:textSize="@dimen/text_large" />
    
        <Button
            android:id="@+id/request_updates_button"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="requestLocationUpdates"
            android:text="@string/request_updates" />
    
        <Button
            android:id="@+id/remove_updates_button"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="removeLocationUpdates"
            android:layout_marginBottom="@dimen/default_margin"
            android:text="@string/remove_updates" />
    
        <TextView
            android:id="@+id/location_updates_result"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>
    

    LocationUpdatesIntentService.kt

       /**
     * Copyright 2017 Google Inc. All Rights Reserved.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     * http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    package com.google.android.gms.location.sample.locationupdatespendingintent
    
    import android.app.IntentService
    import android.content.Context
    import android.content.Intent
    import android.location.Location
    import android.util.Log
    
    import com.google.android.gms.location.LocationResult
    
    
    /**
     * Handles incoming location updates and displays a notification with the location data.
     *
     * For apps targeting API level 25 ("Nougat") or lower, location updates may be requested
     * using [android.app.PendingIntent.getService] or
     * [android.app.PendingIntent.getBroadcast]. For apps targeting
     * API level O, only `getBroadcast` should be used.
     *
     * Note: Apps running on "O" devices (regardless of targetSdkVersion) may receive updates
     * less frequently than the interval specified in the
     * [com.google.android.gms.location.LocationRequest] when the app is no longer in the
     * foreground.
     */
    class LocationUpdatesIntentService : IntentService(TAG) {
    
        override fun onHandleIntent(intent: Intent?) {
            if (intent != null) {
                val action = intent.action
                if (ACTION_PROCESS_UPDATES == action) {
                    val result = LocationResult.extractResult(intent)
                    if (result != null) {
                        val locations = result.locations
                        Utils.setLocationUpdatesResult(this, locations)
                        Utils.sendNotification(this, Utils.getLocationResultTitle(this, locations))
                        Log.i(TAG, Utils.getLocationUpdatesResult(this))
                    }
                }
            }
        }
    
        companion object {
    
            private val ACTION_PROCESS_UPDATES = "com.google.android.gms.location.sample.locationupdatespendingintent.action" + ".PROCESS_UPDATES"
            private val TAG = LocationUpdatesIntentService::class.java.simpleName
        }
    }
    

    LocationUpdatesBroadcastReceiver.kt

        /**
     * Copyright 2017 Google Inc. All Rights Reserved.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     * http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package com.google.android.gms.location.sample.locationupdatespendingintent
    
    import android.content.BroadcastReceiver
    import android.content.Context
    import android.content.Intent
    import android.location.Location
    import android.util.Log
    
    import com.google.android.gms.location.LocationResult
    
    /**
     * Receiver for handling location updates.
     *
     * For apps targeting API level O
     * [android.app.PendingIntent.getBroadcast] should be used when
     * requesting location updates. Due to limits on background services,
     * [android.app.PendingIntent.getService] should not be used.
     *
     * Note: Apps running on "O" devices (regardless of targetSdkVersion) may receive updates
     * less frequently than the interval specified in the
     * [com.google.android.gms.location.LocationRequest] when the app is no longer in the
     * foreground.
     */
    class LocationUpdatesBroadcastReceiver : BroadcastReceiver() {
    
        override fun onReceive(context: Context, intent: Intent?) {
            if (intent != null) {
                val action = intent.action
                if (ACTION_PROCESS_UPDATES == action) {
                    val result = LocationResult.extractResult(intent)
                    if (result != null) {
                        val locations = result.locations
                        Utils.setLocationUpdatesResult(context, locations)
                        Utils.sendNotification(context, Utils.getLocationResultTitle(context, locations))
                        Log.i(TAG, Utils.getLocationUpdatesResult(context))
                    }
                }
            }
        }
    
        companion object {
            private val TAG = "LUBroadcastReceiver"
    
            internal val ACTION_PROCESS_UPDATES = "com.google.android.gms.location.sample.locationupdatespendingintent.action" + ".PROCESS_UPDATES"
        }
    }
    

    AndroidManifest.xml 在这里定义服务和接收者。

       <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.google.android.gms.location.sample.locationupdatespendingintent">
    
        <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">
            <activity android:name=".MainActivity">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
    
            <service
                android:name=".LocationUpdatesIntentService"
                android:exported="false" />
    
            <receiver android:name=".LocationUpdatesBroadcastReceiver"
                android:exported="true">
                <intent-filter>
                    <action android:name="com.google.android.gms.location.sample.locationupdatespendingintent.LocationUpdatesBroadcastReceiver.ACTION_PROCESS_UPDATES" />
                </intent-filter>
            </receiver>
        </application>
    
    </manifest>
    

    【讨论】:

    猜你喜欢
    • 2017-09-30
    • 2018-02-08
    • 2017-12-10
    • 2018-02-09
    • 1970-01-01
    • 1970-01-01
    • 2018-06-08
    • 2023-04-09
    • 2019-08-19
    相关资源
    最近更新 更多