Android 實(shí)現(xiàn)模擬地圖定位功能

一栖秕、項(xiàng)目git地址:

??https://github.com/XieXiePro/MockLocation

二傍菇、實(shí)現(xiàn)原理:

??手機(jī)定位方式目前有4種:基站定位惨恭,WIFI定位,GPS定位丹鸿,AGPS定位缸血。

??本工程利用手機(jī)自帶的"模擬位置"功能實(shí)現(xiàn)運(yùn)行時(shí)修改LocationManager結(jié)果澜汤。
??原理:使用android自帶的調(diào)試api,模擬gps provider的結(jié)果敢艰。

??Android 6.0系統(tǒng)以下诬乞,可以通過Setting.Secure.ALLOW_MOCK_LOCATION獲取是否【允許模擬位置】,當(dāng)【允許模擬位置】開啟時(shí),可addTestProvider震嫉;

??Android 6.0系統(tǒng)及以上森瘪,棄用Setting.Secure.ALLOW_MOCK_LOCATION變量,沒有【允許模擬位置】選項(xiàng)票堵,
增加【選擇模擬位置信息應(yīng)用】扼睬,此時(shí)需要選擇當(dāng)前應(yīng)用,才可以addTestProvider换衬,
但未找到獲取當(dāng)前選擇應(yīng)用的方法痰驱,因此通過addTestProvider是否成功來(lái)判斷是否可用模擬位置。

三瞳浦、代碼分析:

MockLocationManager:模擬地址管理類
??首先通過Android系統(tǒng)模擬位置管理器LocationManager獲取系統(tǒng)模擬位置服務(wù)担映,Android 6.0以下,通過Setting.Secure.ALLOW_MOCK_LOCATION判斷是否可模擬位置叫潦,Android 6.0及以上蝇完,需要【選擇模擬位置信息應(yīng)用】,未找到方法矗蕊,因此通過addTestProvider是否可用判斷短蜕。

    /**
     * 模擬位置是否啟用
     * 若啟用,則addTestProvider
     */
    public boolean getUseMockPosition(Context context) {
        // Android 6.0以下傻咖,通過Setting.Secure.ALLOW_MOCK_LOCATION判斷
        // Android 6.0及以上朋魔,需要【選擇模擬位置信息應(yīng)用】,未找到方法卿操,因此通過addTestProvider是否可用判斷
        boolean canMockPosition = (Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION, 0) != 0)
                || Build.VERSION.SDK_INT > 22;
        if (canMockPosition && hasAddTestProvider == false) {
            try {
                for (String providerStr : mockProviders) {
                    LocationProvider provider = locationManager.getProvider(providerStr);
                    if (provider != null) {
                        locationManager.addTestProvider(
                                provider.getName()
                                , provider.requiresNetwork()
                                , provider.requiresSatellite()
                                , provider.requiresCell()
                                , provider.hasMonetaryCost()
                                , provider.supportsAltitude()
                                , provider.supportsSpeed()
                                , provider.supportsBearing()
                                , provider.getPowerRequirement()
                                , provider.getAccuracy());
                    } else {
                        if (providerStr.equals(LocationManager.GPS_PROVIDER)) {
                            locationManager.addTestProvider(
                                    providerStr
                                    , true, true, false, false, true, true, true
                                    , Criteria.POWER_HIGH, Criteria.ACCURACY_FINE);
                        } else if (providerStr.equals(LocationManager.NETWORK_PROVIDER)) {
                            locationManager.addTestProvider(
                                    providerStr
                                    , true, false, true, false, false, false, false
                                    , Criteria.POWER_LOW, Criteria.ACCURACY_FINE);
                        } else {
                            locationManager.addTestProvider(
                                    providerStr
                                    , false, false, false, false, true, true, true
                                    , Criteria.POWER_LOW, Criteria.ACCURACY_FINE);
                        }
                    }
                    locationManager.setTestProviderEnabled(providerStr, true);
                    locationManager.setTestProviderStatus(providerStr, LocationProvider.AVAILABLE, null, System.currentTimeMillis());
                }
                hasAddTestProvider = true;  // 模擬位置可用
                canMockPosition = true;
            } catch (SecurityException e) {
                canMockPosition = false;
            }
        }
        if (canMockPosition == false) {
            stopMockLocation();
        }
        return canMockPosition;
    }

??接下來(lái)設(shè)置模擬經(jīng)緯度數(shù)據(jù):

  // 模擬位置(addTestProvider成功的前提下)
  for (String providerStr : mockProviders) {
  Location mockLocation = new Location(providerStr);
  mockLocation.setLatitude(latitude);   // 維度(度)
  mockLocation.setLongitude(longitude);  // 經(jīng)度(度)
  mockLocation.setAccuracy(0.1f);   // 精度(米)
  mockLocation.setTime(new Date().getTime());   // 本地時(shí)間
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
    mockLocation.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
  }
    locationManager.setTestProviderLocation(providerStr, mockLocation);
  }

??取消模擬定位方法:

    /**
     * 取消位置模擬警检,以免啟用模擬數(shù)據(jù)后無(wú)法還原使用系統(tǒng)位置
     * 若模擬位置未開啟,則removeTestProvider將會(huì)拋出異常害淤;
     * 若已a(bǔ)ddTestProvider后扇雕,關(guān)閉模擬位置,未removeTestProvider將導(dǎo)致系統(tǒng)GPS無(wú)數(shù)據(jù)更新窥摄;
     */
    public void stopMockLocation() {
        if (hasAddTestProvider) {
            for (String provider : mockProviders) {
                try {
                    locationManager.removeTestProvider(provider);
                } catch (Exception ex) {
                    // 此處不需要輸出日志镶奉,若未成功addTestProvider,則必然會(huì)出錯(cuò)
                    // 這里是對(duì)于非正常情況的預(yù)防措施
                }
            }
            hasAddTestProvider = false;
        }
    }

??注冊(cè)位置服務(wù)崭放,獲取系統(tǒng)位置

        // 注冊(cè)位置服務(wù)哨苛,獲取系統(tǒng)位置
        if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return;
        }
        mockLocationManager.locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener);
    

??最后通過LocationListener.onLocationChanged()回調(diào)方法獲取GPS定位數(shù)據(jù):

 private LocationListener locationListener = new LocationListener() {
        @Override
        public void onLocationChanged(final Location location) {
            setLocationData(location);
        }

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {

        }

        @Override
        public void onProviderEnabled(String provider) {

        }

        @Override
        public void onProviderDisabled(String provider) {

        }
    };

    /**
     * 獲取到模擬定位信息,并顯示
     *
     * @param location 定位信息
     */
    private void setLocationData(Location location) {
        tvProvider.setText(location.getProvider());
        tvTime.setText(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(location.getTime())));
        tvLatitude.setText(location.getLatitude() + " °");
        tvLongitude.setText(location.getLongitude() + " °");
    }

四莹菱、使用模擬定位需先開啟系統(tǒng)設(shè)置中的模擬位置:

  • Android 6.0 以下:【開發(fā)者選項(xiàng) -> 允許模擬位置】


    Android 6.0 以下:【開發(fā)者選項(xiàng) -> 允許模擬位置】
  • Android 6.0 及以上:【開發(fā)者選項(xiàng) -> 選擇模擬位置信息應(yīng)用】

Android 6.0 及以上:【開發(fā)者選項(xiàng) -> 選擇模擬位置信息應(yīng)用】

參考鏈接:
1移国、【科普】GPS、Wifi等各種手機(jī)定位方式的含義及原理詳解
2道伟、Android 使用模擬位置(支持Android 6.0)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末迹缀,一起剝皮案震驚了整個(gè)濱河市使碾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌祝懂,老刑警劉巖票摇,帶你破解...
    沈念sama閱讀 211,639評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異砚蓬,居然都是意外死亡矢门,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門灰蛙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)祟剔,“玉大人,你說(shuō)我怎么就攤上這事摩梧∥镅樱” “怎么了?”我有些...
    開封第一講書人閱讀 157,221評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵仅父,是天一觀的道長(zhǎng)叛薯。 經(jīng)常有香客問我,道長(zhǎng)笙纤,這世上最難降的妖魔是什么耗溜? 我笑而不...
    開封第一講書人閱讀 56,474評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮省容,結(jié)果婚禮上抖拴,老公的妹妹穿的比我還像新娘。我一直安慰自己腥椒,他們只是感情好城舞,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著寞酿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪脱柱。 梳的紋絲不亂的頭發(fā)上伐弹,一...
    開封第一講書人閱讀 49,816評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音榨为,去河邊找鬼惨好。 笑死,一個(gè)胖子當(dāng)著我的面吹牛随闺,可吹牛的內(nèi)容都是我干的日川。 我是一名探鬼主播,決...
    沈念sama閱讀 38,957評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼矩乐,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼龄句!你這毒婦竟也來(lái)了回论?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,718評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤分歇,失蹤者是張志新(化名)和其女友劉穎傀蓉,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體职抡,經(jīng)...
    沈念sama閱讀 44,176評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡葬燎,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了缚甩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谱净。...
    茶點(diǎn)故事閱讀 38,646評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖擅威,靈堂內(nèi)的尸體忽然破棺而出壕探,到底是詐尸還是另有隱情,我是刑警寧澤裕寨,帶...
    沈念sama閱讀 34,322評(píng)論 4 330
  • 正文 年R本政府宣布浩蓉,位于F島的核電站,受9級(jí)特大地震影響宾袜,放射性物質(zhì)發(fā)生泄漏捻艳。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評(píng)論 3 313
  • 文/蒙蒙 一庆猫、第九天 我趴在偏房一處隱蔽的房頂上張望认轨。 院中可真熱鬧,春花似錦月培、人聲如沸嘁字。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)纪蜒。三九已至,卻和暖如春此叠,著一層夾襖步出監(jiān)牢的瞬間纯续,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工灭袁, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留猬错,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,358評(píng)論 2 360
  • 正文 我出身青樓茸歧,卻偏偏與公主長(zhǎng)得像倦炒,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子软瞎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容