更踪设备的地理位置(LocationManager)

本文深入探讨了Android系统中地理位置服务的实现方式,包括LocationListener接口和PendingIntent的使用方法,展示了如何通过RunManager类实现地理位置的持续跟踪及更新。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

地理位置与LocationManager

Android系统中的地理位置数据是由LocationManager系统服务提供的。该系统服务向所有需要地理数据的应用提供数据更新。更新数据的传送通常采用两种方式。

其中,使用LocationListener接口可能是最直接的一种方式。通过onLocationChanged(Location)方法,该接口提供的信息有:地理位置数据更新、状态更新以及定位服务提供者启动状态的通知消息。

如只需将地理位置数据发送给应用中的单个组件,使用LocationListener接口会很方便。提供LocationListener接口实现给LocationManager的requestLocationUpdates(…)或requestSingleUpdate(…)方法即可。

然而,若不管用户界面是否存在(如应用在后台运行),应用都需要持续定位用户地理位置。当然,我们也可以使用stickyService,但stickyService本身复杂难用,而且这种方式也不够轻量级。因此,我们使用自Android 2.3开始引入的PendingIntent。

使用PendingIntent获取地理位置信息更新,我们实际是要求LocationManager在将来某个时点帮忙发送某种类型的Intent。这样,即使应用组件。甚至是整个应用进程都销毁了,LocationManager仍会一直发送Intent,直到要求停止并按需启动新组件响应它们。利用这种优势,即使持续进行设备定位,也可以避免应用消耗过多的资源。

为管理与LocationManager的通讯,创建一个名为RunManager的单例类:

package com.huangfei.runtracker;

import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.location.LocationManager;

public class RunManager {
    public static final String TAG = "RunManager";

    public static final String ACTION_LOCATION = "com.huangfei.runtracker.ACTION_LOCATION";

    private static RunManager sRunManager;
    private Context mAppContext;
    private LocationManager mLocationManager;

    private RunManager(Context appContext) {
        mAppContext = appContext;
        mLocationManager = (LocationManager) mAppContext
                .getSystemService(Context.LOCATION_SERVICE);
    }

    public static RunManager get(Context context) {
        if (sRunManager == null) {
            sRunManager = new RunManager(context.getApplicationContext());
        }

        return sRunManager;
    }

    private PendingIntent getLocationPeindingIntent(boolean shouldcCreate) {
        Intent broadcast = new Intent(ACTION_LOCATION);
        int flags = shouldcCreate ? 0 : PendingIntent.FLAG_NO_CREATE;
        //flags参数告诉 PendingIntent.getBroadcast方法是否应该在系统中创建新的PendingIntent
        return PendingIntent.getBroadcast(mAppContext, 0, broadcast, flags);
    }

    /**
     * 启动地理位置更新
     */
    public void startLocationupdates() {
        // 要求LocationManager通过GPS定位装置提供实时的定位数据更新
        String provider = LocationManager.GPS_PROVIDER;

        /**
         * 利用LocationManager的最近一次地理位置(使用于各种定位方式,如GPS、WIFE网络、手机基站等),
         * 来消除免用户开启定位时的长时间等待。
         * 
         * 也可向LocationManager请求来自不同定位服务提供者的最近一次地理位置信息,或使用getAllProviders()方法
         * 获知协同工作的定位服务提供者。如遍历查看所有最近一次地理位置信息,应查看其准确性,并确定是否为比较新的时间戳。如较为久远,
         * 可不采用这些数据信息。
         */
        Location lastKnownLocation = mLocationManager.getLastKnownLocation(provider);
        if(lastKnownLocation != null){
            lastKnownLocation.setTime(System.currentTimeMillis());
            broadcastLocation(lastKnownLocation);
        }

        PendingIntent pi = getLocationPeindingIntent(true);
        /**
         * requestLocationUpdates(String provider, long minTime, float minDistance, PendingIntent intent)
         * 四个参数中,最小等待时间(以毫秒为单位)以及最短移动距离(以米为单位)可用与决定发送下一次定位数据更新要移动的距离和要等待的时间
         */
        mLocationManager.requestLocationUpdates(provider, 0, 0, pi);
    }

    /**
     * 发送最近一次地理位置信息的广播
     */
    private void broadcastLocation(Location lastKnownLocation) {
        Intent intent = new Intent(ACTION_LOCATION);
        intent.putExtra(LocationManager.KEY_LOCATION_CHANGED, lastKnownLocation);
        mAppContext.sendBroadcast(intent);
    }

    /**
     * 停止地理位置更新
     * 
     */
    public void stopLocationUpdates() {
        PendingIntent pi = getLocationPeindingIntent(false);
        if (pi != null) {
            mLocationManager.removeUpdates(pi);
            pi.cancel();
        }
    }

    /**
     * 判断地理位置是否更新
     */
    public boolean isTrackRun() {
        return getLocationPeindingIntent(false) != null;
    }
}

接收定位数据更新broadcast

package com.huangfei.runtracker;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.location.LocationManager;
import android.util.Log;

public class LocationReceiver extends BroadcastReceiver {
    public static final String TAG = "LocationReceiver";

    @Override
    public void onReceive(Context context, Intent intent) {
        /**
         * LocationManager打包了附加额外信息的intent
         * LocationManager.KEY_LOCATION_CHANGED键值可指定一个表示最新更新的Location实例
         */
        Location location = (Location) intent
                .getParcelableExtra(LocationManager.KEY_LOCATION_CHANGED);

        if (location != null) {
            onLocationReceived(context, location);
            return;
        }

        if (intent.hasExtra(LocationManager.KEY_PROVIDER_ENABLED)) {
            boolean enabled = intent.getBooleanExtra(
                    LocationManager.KEY_PROVIDER_ENABLED, false);
            onProviderEnabledChanged(enabled);
        }
    }

    protected void onProviderEnabledChanged(boolean enabled) {
        Log.d(TAG, "Provider " + (enabled ? "enabled" : "disabled"));
    }

    protected void onLocationReceived(Context context, Location location) {
        Log.d(TAG, this + " Got location from " + location.getProvider() + ": "
                + location.getLatitude() + ", " + location.getLongitude());
    }
}
<!-- 添加定位的权限以及GPS硬件uses-feature节点的添加 -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-feature
        android:name="android.hardware.location.gps"
        android:required="true" />

<receiver
            android:name="com.huangfei.runtracker.LocationReceiver"
            android:exported="false" >
            <intent-filter>
                <action android:name="com.huangfei.runtracker.ACTION_LOCATION" />
            </intent-filter>
        </receiver>

使用定位数据刷新UI显示

package com.huangfei.runtracker;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.IntentFilter;
import android.location.Location;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class RunFragment extends Fragment {
    private BroadcastReceiver mLocationReceiver = new LocationReceiver(){

        protected void onLocationReceived(Context context, Location location) {
            mLastLocation = location;
            if(isVisible())
                updateUI();
        }

        protected void onProviderEnabledChanged(boolean enabled) {
            //GPS服务提供者启动或停止时,消息提示
            int toastText = enabled ? R.string.gps_enabled : R.string.gps_disabled;
            Toast.makeText(getActivity(), toastText, Toast.LENGTH_LONG).show();
        }
    };

    private RunManager mRunManager;
    private Run mRun;
    private Location mLastLocation;
    private Button mStartButton, mStopButton;
    private TextView mStartedTextView, mLatitudeTextView, mLongitudeTextView,
            mAltitudeTextView, mDurationTextView;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
        mRunManager = RunManager.get(getActivity());
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_run, container, false);

        mStartedTextView = (TextView) view
                .findViewById(R.id.run_startedTextView);
        mLatitudeTextView = (TextView) view
                .findViewById(R.id.run_latitudeTextView);
        mLongitudeTextView = (TextView) view
                .findViewById(R.id.run_longitudeTextView);
        mAltitudeTextView = (TextView) view
                .findViewById(R.id.run_altitudeTextView);
        mDurationTextView = (TextView) view
                .findViewById(R.id.run_durationTextView);

        mStartButton = (Button) view.findViewById(R.id.run_startButton);
        mStartButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mRunManager.startLocationupdates();
                mRun = new Run();
                updateUI();
            }
        });

        mStopButton = (Button) view.findViewById(R.id.run_stopButton);
        mStopButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mRunManager.stopLocationUpdates();
                updateUI();
            }
        });

        return view;
    }

    @Override
    public void onStart() {
        super.onStart();
        getActivity().registerReceiver(mLocationReceiver, new IntentFilter(RunManager.ACTION_LOCATION));
    }

    @Override
    public void onStop() {
        super.onStop();
        getActivity().unregisterReceiver(mLocationReceiver);
    }

    private void updateUI(){
        boolean started = mRunManager.isTrackRun();

        if(mRun != null){
            mStartedTextView.setText(mRun.getSartDate().toString());
        }

        int durationSenconds = 0;

        if(mRun != null && mLastLocation != null){
            durationSenconds = mRun.getDurationSeconds(mLastLocation.getTime());
            mLatitudeTextView.setText(Double.toString(mLastLocation.getLatitude()));
            mLongitudeTextView.setText(Double.toString(mLastLocation.getLongitude()));
            mAltitudeTextView.setText(Double.toString(mLastLocation.getAltitude()));
        }

        mDurationTextView.setText(Run.formatDuration(durationSenconds));

        mStartButton.setEnabled(!started);
        mStopButton.setEnabled(started);
    }
}

代码地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值