GPS positioning system - Android terminal

preface

GPS series - Android terminal, github project address tag: gps_mine

Android mobile terminal mainly uses Gaode map positioning, uploads positioning information in the background, and then tries to keep it alive as much as possible.

It includes two small functions: 1. Upload positioning information; 2. Simulate positioning information

They are all hands-on practice to deeply understand its principle. There are many codes throughout, so be careful.

You can check the source code and get what you need.

GPS positioning system series

GPS positioning system (I) -- Introduction

GPS positioning system (II) - Android terminal

GPS positioning system (III) -- Java back end

GPS positioning system (IV) -- Vue front end

GPS positioning system (V) -- Docker

[TOC]

harvest

After learning this article, you will gain:

  • Gaode map, positioning and use
  • Gaode coordinate system conversion (only other coordinate systems are officially transferred to Gaode, and no Gaode is transferred to gps)
  • Analog positioning (clock in)
  • Uninstall and reinstall the same uuid|imei
  • Keep alive strategy and principle

1, Map

The map uses Gaode map. To register and apply for appkey, please go to the official website.

The map interface function is very simple, just follow the official documents

 private void initMap() {
        MyLocationStyle myLocationStyle;
        myLocationStyle = new MyLocationStyle();//Initialize the locating blue dot style class mylocationstyle myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE);// Continuously locate and move the viewing angle to the center point of the map. The positioning point rotates according to the direction of the device and will follow the device. (positioning once every 1 second) if mylocationtype is not set, this mode will also be executed by default.
        myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_FOLLOW);
        myLocationStyle.interval(10000); //Setting the positioning interval in continuous positioning mode is only effective in continuous positioning mode, but not in single positioning mode. The unit is milliseconds.
        AMap map = mMapView.getMap();
        map.setMyLocationStyle(myLocationStyle);//Set the Style to locate the blue dot
        map.setMyLocationEnabled(true);// Set to true to enable the display of positioning blue dots, false to hide the positioning blue dots without positioning, and the default is false.
        map.getUiSettings().setMyLocationButtonEnabled(true); //Displays the default positioning button
        map.setMyLocationEnabled(true);// It can trigger positioning and display the current position
        map.moveCamera(CameraUpdateFactory.zoomTo(16));
        map.setOnMapClickListener(latLng -> {
            Log.d(TAG, "mapCLick:" + latLng.latitude + "\t" + latLng.longitude);
            mMockLat = latLng.latitude;
            mMockLng = latLng.longitude;
            if (mMarker != null) {
                mMarker.remove();
            }
            mMarker = map.addMarker(new MarkerOptions().position(latLng).title("Simulated location").snippet("default"));
        });
        map.setOnMyLocationChangeListener(location -> Log.d(TAG, "onMyLocationChange:" + location.getLatitude() + "\t" + location.getLongitude()));
    }

If you use the map, note: select the point of the map Setonmapcclicklistener to set listening.

gps and Gaode map longitude and latitude rotation

Note: it refers to the mutual transformation of gps and Gaode coordinate system. The simulated gps positioning needs to be transformed into gps positioning for simulation after selecting the simulated points. Here is a tool class.

public class ConvertUtil {
        private final static double a = 6378245.0;
        private final static double pi = 3.14159265358979324;
        private final static double ee = 0.00669342162296594323;

        // WGS-84 to gcj-02 GPS to Gaode
        public static LatLng toGCJ02Point(double latitude, double longitude) {
            LatLng dev = calDev(latitude, longitude);
            double retLat = latitude + dev.latitude;
            double retLon = longitude + dev.longitude;
            return new LatLng(retLat, retLon);
        }

        // GCJ-02 to WGS-84 Gaode to gps
        public static LatLng toWGS84Point(double latitude, double longitude) {
            LatLng dev = calDev(latitude, longitude);
            double retLat = latitude - dev.latitude;
            double retLon = longitude - dev.longitude;
            dev = calDev(retLat, retLon);
            retLat = latitude - dev.latitude;
            retLon = longitude - dev.longitude;
            return new LatLng(retLat, retLon);
        }

        private static LatLng calDev(double wgLat, double wgLon) {
            if (isOutOfChina(wgLat, wgLon)) {
                return new LatLng(0, 0);
            }
            double dLat = calLat(wgLon - 105.0, wgLat - 35.0);
            double dLon = calLon(wgLon - 105.0, wgLat - 35.0);
            double radLat = wgLat / 180.0 * pi;
            double magic = Math.sin(radLat);
            magic = 1 - ee * magic * magic;
            double sqrtMagic = Math.sqrt(magic);
            dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
            dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
            return new LatLng(dLat, dLon);
        }

        private static boolean isOutOfChina(double lat, double lon) {
            if (lon < 72.004 || lon > 137.8347)
                return true;
            if (lat < 0.8293 || lat > 55.8271)
                return true;
            return false;
        }

        private static double calLat(double x, double y) {
            double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2
                    * Math.sqrt(Math.abs(x));
            ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
            ret += (20.0 * Math.sin(y * pi) + 40.0 * Math.sin(y / 3.0 * pi)) * 2.0 / 3.0;
            ret += (160.0 * Math.sin(y / 12.0 * pi) + 320 * Math.sin(y * pi / 30.0)) * 2.0 / 3.0;
            return ret;
        }

        private static double calLon(double x, double y) {
            double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
            ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
            ret += (20.0 * Math.sin(x * pi) + 40.0 * Math.sin(x / 3.0 * pi)) * 2.0 / 3.0;
            ret += (150.0 * Math.sin(x / 12.0 * pi) + 300.0 * Math.sin(x / 30.0 * pi)) * 2.0 / 3.0;
            return ret;
        }
 
}

2, Background keep alive positioning

Keep alive:

It's good to use a framework here, HelloDaemon

Keep alive ideas:

  1. Set the Service as a foreground Service without displaying notifications

  2. Return start in the onStartCommand method of the Service_ STICKY

  3. Overwrite the ondestroy / ontaskeremoved method of the Service, save the data to the disk, and then pull up the Service again

  4. Monitor 8 system broadcasts

  5. Open the guard service: regularly check whether the service is running. If not, pull it up

  6. Guard the enabled state of the Service component so that it is not disabled by tools such as MAT

In addition, there are setting interfaces such as intent jump [power optimization], [auto start setting], [white list] suitable for rom of various mobile phone manufacturers

 IntentWrapper.whiteListMatters(this, "For better real-time location, it's best to add the application to your mobile phone's white list");

Keep alive service inherits AbsWorkService and implements its abstract method

/**
 * Whether the task is completed and the service operation is no longer required?
 * @return The service should be stopped, true; The service should be started, false; Unable to determine, null
 */
Boolean shouldStopService();

/**
 * Is the task running?
 * @return Task is running, true; The task is not currently running, false; Unable to determine, null
 */
Boolean isWorkRunning();

void startWork();

void stopWork();

//Service.onBind(Intent intent)
@Nullable IBinder onBind(Intent intent, Void unused);

//Called when the service is killed, you can save data here
void onServiceKilled();

About keeping alive, safe and privacy

In fact, with the growing maturity of Android, the ecology is healthier, safer, more privacy oriented and user-oriented. Many "black technologies" are no longer available. The current preservation is not as fancy as before. If you are interested in understanding some old versions of preservation strategies, you can learn from these links:

android process resident (2) -- count the means of keeping alive by using the mechanism of android system

D-clock / AndroidDaemonService

Unlike the practices of big manufacturers, all kinds of pull each other and the white list of mobile phone manufacturers; The current "folk" living protection ideas are basically to guide users to add white lists, unlimited power optimization, lock applications, etc.

No one can say that baohuo can survive 100% in the background. Even if baohuo can survive, it is triggered by some broadcast or user behavior. The rom performance of major mobile phone manufacturers is different, and some will still be killed. There is no way. However, my Xiaomi 6 mobile phone can upload gps information every minute after the actual measurement, white list and power optimization settings, but it still consumes a lot of power. To tell the truth. Some other mobile phones don't work, such as Huawei. Huawei's ecology and security are really great.

UploadGpsService implementation:

public class UploadGpsService extends AbsWorkService {
    private static final String TAG = UploadGpsService.class.getSimpleName();
    //Whether the task is completed and the service operation is no longer required?
    public static boolean sShouldStopService;

    int shouldCount;
    int actualCount;


    @Override
    public void onCreate() {
        super.onCreate();
        initGps();
    }

    //Declare AMapLocationClient class object
    public AMapLocationClient mLocationClient = null;
    //Declare location callback listener
    public AMapLocationListener mLocationListener = amapLocation -> {
        if (amapLocation != null) {
            if (amapLocation.getErrorCode() == 0) {
//You can parse amapLocation to get the corresponding content.
                Log.d("mapLocation", amapLocation.toString());
                //Get location time
                SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                Date date = new Date();
                df.format(date);
                Log.d(TAG, String.format("Longitude:%s\t Latitude:%s\t Address:%s\n%s\n Number of times to upload%d\n Actual upload times%d", amapLocation.getLongitude(), amapLocation.getLatitude(), amapLocation.getAddress(), df.format(date), shouldCount, actualCount));
                upload(amapLocation.getLongitude(), amapLocation.getLatitude());
            } else {
                //When positioning fails, the reason for the failure can be determined through ErrCode information. errInfo is the error information. See the error code table for details.
                Log.e("AmapError", "location Error, ErrCode:"
                        + amapLocation.getErrorCode() + ", errInfo:"
                        + amapLocation.getErrorInfo());
            }
        }
    };

    private void upload(double longitude, double latitude) {
        String userId = PrefManager.getInstance(this).userId();
        String token = PrefManager.getInstance(this).getToken();
//        if (TextUtils.isEmpty(userId)) {
//            return;
//        }
        shouldCount++;
        RequestModel requestModel = new RequestModel();
        requestModel.setTime(System.currentTimeMillis() / 1000);
        requestModel.setLat(latitude);
        requestModel.setLng(longitude);
        RetrofitManager.getInstance()
                .mainService()
                .gps(token, requestModel)
                .compose(ReactivexCompat.singleThreadSchedule())
                .subscribe(result -> {
                    if (result.getCode() == 200) {
                        long interval = result.getData();
                        mLocationOption.setInterval(interval);
                        mLocationClient.setLocationOption(mLocationOption);
                        Log.d(TAG, "service upload success:" + new Gson().toJson(result));
                        actualCount++;
                    }
                }, e -> {
                    Log.e(TAG, "service upload err:" + e.getMessage());
                });
    }


    //Declare AMapLocationClientOption object
    public AMapLocationClientOption mLocationOption = null;


    private void initGps() {
//Initialize positioning
        mLocationClient = new AMapLocationClient(getApplicationContext());
//Set location callback listening
        mLocationClient.setLocationListener(mLocationListener);

        //Initialize the AMapLocationClientOption object
        mLocationOption = new AMapLocationClientOption();
        /**
         * Set positioning scenarios. Currently, three scenarios are supported (check-in, travel and sports. There is no scenario by default)
         */
        mLocationOption.setLocationPurpose(AMapLocationClientOption.AMapLocationPurpose.Sport);

        //Set the location mode to amaplocationmode Hight_ Accuracy, high precision mode.
        mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);

        //Set the positioning interval in milliseconds. The default is 2000ms and the minimum is 1000ms.
        mLocationOption.setInterval(5000);

        mLocationClient.setLocationOption(mLocationOption);

        //Start positioning
        mLocationClient.startLocation();
    }


    public static void stopService() {
        //We no longer need the service to run. Set the flag to true
        sShouldStopService = true;
        //Cancel Job / Alarm / Subscription
        cancelJobAlarmSub();
    }

    @Override
    public Boolean shouldStopService(Intent intent, int flags, int startId) {
        return sShouldStopService;
    }

    @Override
    public void startWork(Intent intent, int flags, int startId) {
        Log.i(TAG, "startWork");
        String userId = PrefManager.getInstance(this).userId();
        if (!TextUtils.isEmpty(userId)) {
            if (mLocationClient != null && !mLocationClient.isStarted()) {
                Log.i(TAG, "startLocation");
                mLocationClient.startLocation();
            } else if (mLocationClient == null) {
                initGps();
            }
        } else {
            if (mLocationClient != null) {
                mLocationClient.stopLocation();
            }
        }

    }

    @Override
    public void stopWork(Intent intent, int flags, int startId) {
        Log.i(TAG, "stopWork");
        stopService();
        if (mLocationClient != null) {
            mLocationClient.stopLocation();
            mLocationClient.onDestroy();
        }
    }

    @Override
    public Boolean isWorkRunning(Intent intent, int flags, int startId) {
        //If you haven't unsubscribed, the task is still running
        return null;
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent, Void alwaysNull) {
        return null;
    }

    @Override
    public void onServiceKilled(Intent rootIntent) {
        Log.i(TAG, "onServiceKilled");
    }
}

Upload api

Uploading api design is very simple. Just upload longitude, latitude and time

public interface MainService {

    @POST("/login")
    @FormUrlEncoded
    Single<LoginResult> login(@Field("username") String username, @Field("password") String password);


    @POST("/gps")
    Single<UploadResult> gps(@Header ("token")String token, @Body RequestModel model);
}

public class RequestModel
{
    private Double lat;
    private Double lng;
    private Long time;
}

About imei

The ecosystem of Android is becoming healthier and safer, and many users can't get their privacy information. For example, imei, mobile phone number, sim card number, etc.

However, if you want to ensure uniqueness, ime is the best choice, followed by uuid or other self compiled code s. However, there is a problem involved. If localization is not handled properly, there will be no need to uninstall and reinstall. Therefore, there is a tool class of uuid. The principle is that uuid is stored in sdcard instead of sandbox directory.

However, 29 (Android 10) cannot be accessed directly due to the relationship between file partitions. You need to use an api to access it. android:requestLegacyExternalStorage="true" can also be used for compatibility.

Tools:

public final class DeviceUtil {
    private static final String TAG = DeviceUtil.class.getSimpleName();

    private static final String TEMP_DIR = "system_config";
    private static final String TEMP_FILE_NAME = "system_file";
    private static final String TEMP_FILE_NAME_MIME_TYPE = "application/octet-stream";
    private static final String SP_NAME = "device_info";
    private static final String SP_KEY_DEVICE_ID = "device_id";

    public static String getDeviceId(Context context) {
        SharedPreferences sharedPreferences = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
        String deviceId = sharedPreferences.getString(SP_KEY_DEVICE_ID, null);
        if (!TextUtils.isEmpty(deviceId)) {
            return deviceId;
        }
        deviceId = getIMEI(context);
        if (TextUtils.isEmpty(deviceId)) {
            deviceId = createUUID(context);
        }
        sharedPreferences.edit()
                .putString(SP_KEY_DEVICE_ID, deviceId)
                .apply();
        return deviceId;
    }

    private static String createUUID(Context context) {
        String uuid = UUID.randomUUID().toString().replace("-", "");

        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
            Log.d(TAG,"Q");
            Uri externalContentUri = MediaStore.Downloads.EXTERNAL_CONTENT_URI;
            ContentResolver contentResolver = context.getContentResolver();
            String[] projection = new String[]{
                    MediaStore.Downloads._ID
            };
            String selection = MediaStore.Downloads.TITLE + "=?";
            String[] args = new String[]{
                    TEMP_FILE_NAME
            };
            Cursor query = contentResolver.query(externalContentUri, projection, selection, args, null);
            if (query != null && query.moveToFirst()) {
                Log.d(TAG,"moveToFirst");
                Uri uri = ContentUris.withAppendedId(externalContentUri, query.getLong(0));
                query.close();

                InputStream inputStream = null;
                BufferedReader bufferedReader = null;
                try {
                    inputStream = contentResolver.openInputStream(uri);
                    if (inputStream != null) {
                        bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                        uuid = bufferedReader.readLine();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    if (bufferedReader != null) {
                        try {
                            bufferedReader.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if (inputStream != null) {
                        try {
                            inputStream.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            } else {
                Log.d(TAG,"ContentValues");
                ContentValues contentValues = new ContentValues();
                contentValues.put(MediaStore.Downloads.TITLE, TEMP_FILE_NAME);
                contentValues.put(MediaStore.Downloads.MIME_TYPE, TEMP_FILE_NAME_MIME_TYPE);
                contentValues.put(MediaStore.Downloads.DISPLAY_NAME, TEMP_FILE_NAME);
                contentValues.put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS + File.separator + TEMP_DIR);

                Uri insert = contentResolver.insert(externalContentUri, contentValues);
                if (insert != null) {
                    OutputStream outputStream = null;
                    try {
                        outputStream = contentResolver.openOutputStream(insert);
                        if (outputStream == null) {
                            return uuid;
                        }
                        outputStream.write(uuid.getBytes());
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        if (outputStream != null) {
                            try {
                                outputStream.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        } else {
            Log.d(TAG,"DIRECTORY_DOWNLOADS");
            File externalDownloadsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
            File applicationFileDir = new File(externalDownloadsDir, TEMP_DIR);
            if (!applicationFileDir.exists()) {
                if (!applicationFileDir.mkdirs()) {
                    Log.e(TAG, "Folder creation failed: " + applicationFileDir.getPath());
                }
            }
            File file = new File(applicationFileDir, TEMP_FILE_NAME);
            if (!file.exists()) {
                Log.d(TAG,"mk DIRECTORY_DOWNLOADS");
                FileWriter fileWriter = null;
                try {
                    if (file.createNewFile()) {
                        fileWriter = new FileWriter(file, false);
                        fileWriter.write(uuid);
                    } else {
                        Log.e(TAG, "File creation failed:" + file.getPath());
                    }
                } catch (IOException e) {
                    Log.e(TAG, "File creation failed:" + file.getPath());
                    e.printStackTrace();
                } finally {
                    if (fileWriter != null) {
                        try {
                            fileWriter.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            } else {
                Log.d(TAG,"read DIRECTORY_DOWNLOADS");
                FileReader fileReader = null;
                BufferedReader bufferedReader = null;
                try {
                    fileReader = new FileReader(file);
                    bufferedReader = new BufferedReader(fileReader);
                    uuid = bufferedReader.readLine();

                    bufferedReader.close();
                    fileReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    if (bufferedReader != null) {
                        try {
                            bufferedReader.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }

                    if (fileReader != null) {
                        try {
                            fileReader.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }

        return uuid;
    }

    private static String getIMEI(Context context) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            return null;
        }
        try {
            TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
            if (telephonyManager == null) {
                return null;
            }
            @SuppressLint({"MissingPermission", "HardwareIds"}) String imei = telephonyManager.getDeviceId();
            return imei;
        } catch (Exception e) {
            return null;
        }
    }
}

Three, analog positioning

Now simulate gps positioning, realize simulated positioning and position opening, and most of them also adopt the method of [developer - simulated positioning application]. This method can still be used on some apps that have not done special anti simulation positioning processing, such as Baidu map. After simulation, you can still see where you are simulating. However, for example, nail punch, wechat punch, wechat positioning and other large factory apps have done anti simulation processing and are still unusable.

The realization here is also to practice and understand the principle.

See module: mocklocationlib source code

Implementation steps:

1. Guide users to open the developer mode, select the simulated positioning application, and add their own application

2. Use the system api and LocationManager to add test simulation location information

 public boolean getUseMockPosition(Context context) {
        // Below Android 6.0, through setting Secure. ALLOW_ MOCK_ Location judgment
        // Android 6.0 and above require [Select simulation location information application], and no method is found. Therefore, judge whether addTestProvider is available
        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) {
                    //Obtain all provider s, including network, gps, satellite, etc
                    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)) {//Simulated gps module positioning information
                            locationManager.addTestProvider(
                                    providerStr
                                    , true, true, false, false, true, true, true
                                    , Criteria.POWER_HIGH, Criteria.ACCURACY_FINE);
                        } else if (providerStr.equals(LocationManager.NETWORK_PROVIDER)) { //Analog network location information
                            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;  // Analog location available
                canMockPosition = true;
            } catch (SecurityException e) {
                canMockPosition = false;
            }
        }
        if (canMockPosition == false) {
            stopMockLocation();
        }
        return canMockPosition;
    }
 /**
     * Analog location thread
     */
    private class RunnableMockLocation implements Runnable {

        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(3000);

                    if (hasAddTestProvider == false) {
                        continue;
                    }

                    if (bRun == false) {
                        stopMockLocation();
                        continue;
                    }
                    try {
                        // Simulation location (if addTestProvider succeeds)
                        for (String providerStr : mockProviders) {
                            Log.d(TAG, "providerStr: " + providerStr);
                            locationManager.setTestProviderLocation(providerStr, generateLocation(latitude, longitude));
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                        // Prevent the user from closing the simulation position or selecting other applications during the operation of the software
                        stopMockLocation();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public Location generateLocation(double lat, double lng) {
        Location loc = new Location("gps");
        Log.d(TAG, "mock latitude:" + lat + "\tlongitude:" + lng);

        loc.setAccuracy(2.0F);
        loc.setAltitude(55.0D);
        loc.setBearing(1.0F);
        Bundle bundle = new Bundle();
        bundle.putInt("satellites", 7);
        loc.setExtras(bundle);

        loc.setLatitude(lat);
        loc.setLongitude(lng);
        loc.setTime(System.currentTimeMillis());
        if (Build.VERSION.SDK_INT >= 17) {
            loc.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
        }
        return loc;
    }

Code analysis:

  • First obtain the test provider, including network, gps satellite and other modules
  • Start the thread and add simulated positioning information to the provider regularly for simulation

After the lib is encapsulated, it is easy to use. Start the thread and set the location to be simulated

    @Override
    public void startWork(Intent intent, int flags, int startId) {
        Log.i(TAG, "startWork");
        if (mMockLocationManager == null) {
            mMockLocationManager = new MockLocationManager();
            mMockLocationManager.initService(getApplicationContext());
            mMockLocationManager.startThread();
        }

        if (mMockLocationManager.getUseMockPosition(getApplicationContext())) {
            startMockLocation();
            double lat = intent.getDoubleExtra(INTENT_KEY_LAT, 0);
            double lng = intent.getDoubleExtra(INTENT_KEY_LNG, 0);
            setMangerLocationData(lat, lng);
        }
    }

Use: select points on the map, and then simulate it

summary

Android is just a small start, without the support of background interface, and it is useless to upload data. Therefore, we need to build a java server and write several interfaces to meet our needs.

Please move GPS positioning system (III) -- Java back end

Author about

The author is a programmer who loves learning, open source, sharing, spreading positive energy, playing basketball and having a lot of hair --

Warmly welcome your attention, praise, comments and exchanges!

Brief book: Jafir - short book

github: https://github.com/fly7632785

CSDN: https://blog.csdn.net/fly7632785

Nuggets: https://juejin.im/user/5efd8d205188252e58582dc7/posts



Reproduced in: https://www.jianshu.com/p/e463b1ea6b7d
 

Keywords: Android network websocket gps

Added by zurron on Wed, 23 Feb 2022 08:55:25 +0200