Может кто-нибудь сказать мне, что случилось с моим приложением для Android - PullRequest
0 голосов
/ 06 ноября 2018

Я написал приложение для Android, которое в основном позволяет мне отслеживать время и адрес GPS-координат.

У меня есть 3 списка, каждый из которых соответствует Lyft, Uber и др.

Но я считаю, что мое приложение начинает тормозить мой смартфон (Samsung Galaxy S7 Edge, с Android O)

Может кто-нибудь взглянуть на мой код и сказать, почему он тормозит мой смартфон.

Я предполагаю, что, возможно, проблема с синхронизацией потоков.

Прилагается мой код

1) MainActivity.java

package com.milind.myapp.gpstrackingservice;

import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Build;
import android.os.PowerManager;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.ActionMode;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity implements AddressListener
{
    private static final String TAG = MainActivity.class.getSimpleName();
    private PowerManager.WakeLock wakeLock;
    private TextView labelAddress;
    private TextView multiTextLyft;
    private TextView multiTextUber;
    private TextView multiTextOther;

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

        labelAddress = findViewById(R.id.label_address);
        multiTextLyft = findViewById(R.id.multi_text_lyft);
        multiTextUber = findViewById(R.id.multi_text_uber);
        multiTextOther = findViewById(R.id.multi_text_other);
        if (MyService.isServiceStarted())
        {
            MyService.getInstance().load(getSharedPrefs());
            refreshAllViews();
        }

        PowerManager powerManager = (PowerManager) this.getSystemService(Context.POWER_SERVICE);
        wakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, "myapp:My Lock");
    }

    @Override
    protected void onStart()
    {
        super.onStart();
        ActivityCompat.requestPermissions(this, new String[]
                {Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.WAKE_LOCK}, 1);

        if (!MyService.isServiceStarted())
        {
            Intent intent = new Intent(this, MyService.class);
            intent.setAction(MyService.ACTION_START_SERVICE);

            if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
            {
                startForegroundService(intent);
            }
            else
            {
                startService(intent);
            }
        }
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus)
    {
        super.onWindowFocusChanged(hasFocus);
        if (hasFocus)
        {
            if (MyService.isServiceStarted())
            {
                MyService.getInstance().registerAddressListener(this);
            }
            wakeLock.acquire();
        }
        else
        {
            if (MyService.isServiceStarted())
            {
                MyService.getInstance().unregisterAddressListener(this);
            }
            wakeLock.release();
        }
    }


    public void onRequestPermissionsResult(int requestCode, String permissions[],
                                           int[] grantResults)
    {
        switch (requestCode)
        {
            case 1:
            {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED)
                {

                }
                else
                {
                    // permission denied, boo! Disable the
                    // functionality that depends on this permission.
                }
                return;
            }
            // other 'case' lines to check for other
            // permissions this app might request
        }
    }

    @Override
    public void onLocationChanged(Location loc)
    {
        final String address = MyService.getAddress(this, loc);
        final CharSequence text = labelAddress.getText();
        if (text.toString().equals(address))
        {
            return;
        }

        MyService.getInstance().load(getSharedPrefs());
        labelAddress.setText(address);

        refreshAllViews();

        new Tone().play(880);
    }

    private void refreshAllViews()
    {
        runOnUiThread(new Runnable()
        {
            public void run()
            {
                refereshEditTextLyft();
                refereshEditTextUber();
                refereshEditTextOther();
            }
        });
    }

    private void refereshEditTextLyft()
    {
        multiTextLyft.setText(MyService.getInstance().getLyftAddresses());
    }

    private void refereshEditTextUber()
    {
        multiTextUber.setText(MyService.getInstance().getUberAddresses());
    }

    private void refereshEditTextOther()
    {
        multiTextOther.setText(MyService.getInstance().getOtherAddresses());
    }

    private SharedPreferences getSharedPrefs()
    {
        return getSharedPreferences("name", MODE_PRIVATE);
    }

    public void onLyftButtonClicked(View view)
    {
        MyService.getInstance().addLyftAddress(labelAddress.getText());
        new Tone().play(440);
        refereshEditTextLyft();

        MyService.getInstance().save(getSharedPrefs());
    }

    public void onUberButtonClicked(View view)
    {
        MyService.getInstance().addUberAddress(labelAddress.getText());
        new Tone().play(440);
        refereshEditTextUber();

        MyService.getInstance().save(getSharedPrefs());
    }

    public void onOtherButtonClicked(View view)
    {
        MyService.getInstance().addOtherAddress(labelAddress.getText());
        new Tone().play(440);
        refereshEditTextOther();

        MyService.getInstance().save(getSharedPrefs());
    }

    public void onClearButtonClicked(View view)
    {
        if (MyService.isServiceStarted())
        {
            SharedPreferences sharedPreferences = getSharedPrefs();
            SharedPreferences.Editor editor = sharedPreferences.edit();
            editor.clear();
            editor.commit();
            MyService.getInstance().clear();
            refereshEditTextLyft();
            refereshEditTextUber();
            refereshEditTextOther();
        }
    }

    public void onDelLyftButtonClicked(View view)
    {
        MyService.getInstance().delLyftEntry();
        new Tone().play(440);
        refereshEditTextLyft();

        MyService.getInstance().save(getSharedPrefs());
    }

    public void onDelUberButtonClicked(View view)
    {
        MyService.getInstance().delUberEntry();
        new Tone().play(440);
        refereshEditTextUber();

        MyService.getInstance().save(getSharedPrefs());
    }

    public void onDelOtherButtonClicked(View view)
    {
        MyService.getInstance().delOtherEntry();
        new Tone().play(440);
        refereshEditTextOther();

        MyService.getInstance().save(getSharedPrefs());
    }

    @Override
    protected void onRestart()
    {
        super.onRestart();
    }

    @Override
    public void onActionModeFinished(ActionMode mode)
    {
        super.onActionModeFinished(mode);
    }

    @Override
    public void onBackPressed()
    {
        super.onBackPressed();
    }
}

2) MyService.java

package com.milind.myapp.gpstrackingservice;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.provider.Settings;
import android.support.v4.app.NotificationCompat;
import android.util.Log;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

public class MyService extends Service implements LocationListener
{
    private static final String TAG = MyService.class.getSimpleName();
    public static final String ACTION_START_SERVICE = "ACTION_START_SERVICE";
    public static final String ACTION_STOP_SERVICE = "ACTION_STOP_SERVICE";
    public static final String ACTION_UBER = "ACTION_UBER";
    public static final String ACTION_LYFT = "ACTION_UBER";
    public static final String ACTION_END = "ACTION_END";
    public static final String LYFT_PREFIX = "Lyft";
    public static final String OTHER_PREFIX = "Other";
    public static final String UBER_PREFIX = "Uber";

    private static MyService mInstance = null;

    private List<AddressListener> listeners = new ArrayList<>();
    private List<AddressPoint> lyftAddresses = new ArrayList<>();
    private List<AddressPoint> uberAddresses = new ArrayList<>();
    private List<AddressPoint> otherAddresses = new ArrayList<>();
    private Location mLastLocation;

    public MyService()
    {
        super();
        Log.d(TAG, "MyService(): constructor called");
    }

    public static boolean isServiceStarted()
    {
        Log.d(TAG, "isServiceStarted()");
        return mInstance != null;
    }

    public static final MyService getInstance()
    {
        return mInstance;
    }

    @Override
    public IBinder onBind(Intent intent)
    {
        Log.d(TAG, "onBind(Intent intent)");
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId)
    {
        Log.d(TAG, "onStartCommand(Intent, flags, startId) : " + hashCode());
        Log.d(TAG, "onStartCommand(...) intent=" + intent + "=" + ", flags=" + flags + ", startId=" + startId);
        Log.d(TAG, "onStartCommand(...) isServiceStarted=" + isServiceStarted());
        String action = null;
        if (intent != null)
        {
            Log.d(TAG, intent.toString());
            action = intent.getAction();
        }
        else
        {
            Log.d(TAG, "onStartCommand(...): early return");
            return super.onStartCommand(intent, flags, startId);
        }

        if (isServiceStarted() == false && action == ACTION_START_SERVICE)
        {
            Log.d(TAG, "onStartCommand(...): Service starting=" + startId);
            //startForegroundServivceNotification()
            startRunningInForeground();
            requestLocationUpdates();
            mInstance = this;
            Log.d(TAG, "onStartCommand(...): Service started=" + startId);
        }
        else if (isServiceStarted() == true && action == ACTION_STOP_SERVICE)
        {
            Log.d(TAG, "onStartCommand(...): Service stopping=" + startId);
            stopLocationUpdates();
            stopSelf();
            Log.d(TAG, "onStartCommand(...): Service stop requested" + startId);
            mInstance = null;
        }
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy()
    {
        Log.d(TAG, "onDestroy(): Service destroyed");
        super.onDestroy();
        mInstance = null;
    }

    private void stopLocationUpdates()
    {
        Log.d(TAG, "stopLocationUpdates()");
        LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        locationManager.removeUpdates(this);
    }

    private void requestLocationUpdates()
    {
        Log.d(TAG, "requestLocationUpdates()");
        LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        try
        {
            locationManager.requestLocationUpdates(
                    LocationManager.GPS_PROVIDER, 1500, 0, this);
            Log.w(TAG, "requestLocationUpdates(): ended gracefully");
        }
        catch (SecurityException ex)
        {
            Log.w(TAG, "requestLocationUpdates(): Exception");
            ex.printStackTrace();
        }
    }

    @Override
    public void onLocationChanged(Location location)
    {
        Log.v(TAG, "onLocationChanged(Location location): started, location=" + location.toString());
        dispatchLocationChange(location);
        mLastLocation = location;
        Log.v(TAG, "onLocationChanged: completed");
    }

    private void dispatchLocationChange(Location loc)
    {
        Log.v(TAG, "dispatchLocationChange(Location)");
        for (AddressListener listener : listeners)
        {
            listener.onLocationChanged(loc);
        }
    }

    public static String getAddress(Context context, Location loc)
    {
        Log.v(TAG, "getAddress(Location loc) started");
        List<Address> addresses;
        Geocoder gcd = new Geocoder(context, Locale.getDefault());
        try
        {
            addresses = gcd.getFromLocation(loc.getLatitude(),
                    loc.getLongitude(), 1);
            String strReturnAddress = "";

            if (addresses != null && addresses.size() > 0)
            {
                final Address address = addresses.get(0);
                Log.d(TAG, address.toString());
                String addressLines = "";

                Log.v(TAG, "Locale: " + address.getLocale());

                for (int i = 0; i <= address.getMaxAddressLineIndex(); ++i)
                {
                    Log.v(TAG, "AddressLine " + i + ": " + address.getAddressLine(i));
                    addressLines += address.getAddressLine(i) + ", ";
                    Log.v(TAG, "addressLines:" + addressLines);
                }

                String strAddress =
                        addressLines
                        ;
                Log.v(TAG, "strAddress:" + strAddress);

                strReturnAddress = strAddress.substring(0, strAddress.length() - 2);
                Log.v(TAG, "strReturnAddress:" + strReturnAddress);
            }
            Log.d(TAG, "getAddress(Location loc) completed with return=" + strReturnAddress);
            return strReturnAddress;
        }
        catch (IOException e)
        {
            e.printStackTrace();
            Log.d(TAG, "Exception", e);
        }

        Log.d(TAG, "getAddress(Location loc) completed with return=null");
        return "";
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras)
    {
        Log.d(TAG, "onStatusChanged(provider, status, extras): status=" + status + ", extras=" + extras);

    }

    @Override
    public void onProviderEnabled(String provider)
    {
        Log.d(TAG, "onProviderEnabled(provider) ");
    }

    @Override
    public void onProviderDisabled(String provider)
    {
        Log.d(TAG, "onProviderDisabled(provider) ");
        Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);
    }

    private static void logGetProperties(final String tag, final Object obj)
    {
        Log.v(tag, "logGetProperties(...)");
        Class cls = obj.getClass();

        Method[] methods = cls.getMethods();
        Log.v(tag, "methods.length = " + methods.length);

        for (Method method : methods)
        {
            String methodName = method.getName();
            Log.v(tag, "methodName = " + methodName);

            if (methodName.startsWith("get")
                    && (method.getParameters() == null || method.getParameters().length == 0)
                    && method.getReturnType() != Void.class)
            {
                try
                {
                    Log.v(tag, methodName + " = " + method.invoke(obj, new Object[0]));
                }
                catch (Exception ex)
                {
                    Log.e(tag, methodName + " Failed (exception)");
                }
            }
        }
    }

    public AddressListener registerAddressListener(AddressListener listener)
    {
        Log.d(TAG, "registerAddressListener(AddressListener)");
        if (!listeners.contains(listener))
        {
            listeners.add(listener);
        }
        return listener;
    }

    public AddressListener unregisterAddressListener(AddressListener listener)
    {
        Log.d(TAG, "unregisterAddressListener(AddressListener)");
        if (listeners.contains(listener))
        {
            listeners.remove(listener);
            Log.d(TAG, "unregisterAddressListener(AddressListener): Listener removed");
            return listener;
        }
        Log.d(TAG, "unregisterAddressListener(AddressListener): Listener not found");
        return null;
    }

    public void addLyftAddress(CharSequence text)
    {
        Log.d(TAG, "addLyftAddress(CharSequence text): text: " + text);
        lyftAddresses.add(new AddressPoint(System.currentTimeMillis(), text));
    }

    public void addUberAddress(CharSequence text)
    {
        Log.d(TAG, "addUberAddress(CharSequence text): text: " + text);
        uberAddresses.add(new AddressPoint(System.currentTimeMillis(), text));
    }

    public void addOtherAddress(CharSequence text)
    {
        Log.d(TAG, "addOtherAddress(CharSequence text): text: " + text);
        otherAddresses.add(new AddressPoint(System.currentTimeMillis(), text));
    }

    String getLyftAddresses()
    {
        return getAddresses(lyftAddresses);
    }

    String getUberAddresses()
    {
        return getAddresses(uberAddresses);
    }

    String getOtherAddresses()
    {
        return getAddresses(otherAddresses);
    }

    private String getAddresses(List<AddressPoint> addresses)
    {
        Log.d(TAG, "getAddresses(List<AddressPoint>)");
        String strAddresses = "" + addresses.size() + "\n--------\n";
        for (int i = 0; i < addresses.size(); ++i)
        {
            AddressPoint addresspoint = addresses.get(i);
            strAddresses += addresspoint + "\n--------\n";

            if ((i % 2) != 0)
            {
                strAddresses += "\n\n";
            }
        }
        return strAddresses;
    }

    private void startRunningInForeground()
    {
        //if more than or equal to 26
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
        {
            //if more than 26
            if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O)
            {
                String CHANNEL_ONE_ID = "Package.Service";
                String CHANNEL_ONE_NAME = "Screen service";
                NotificationChannel notificationChannel = null;
                notificationChannel = new NotificationChannel(CHANNEL_ONE_ID,
                        CHANNEL_ONE_NAME, NotificationManager.IMPORTANCE_MIN);
                notificationChannel.enableLights(true);
                notificationChannel.setLightColor(Color.RED);
                notificationChannel.setShowBadge(true);
                notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
                NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
                if (manager != null)
                {
                    manager.createNotificationChannel(notificationChannel);
                }

                Bitmap icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher_foreground);
                Notification notification = new Notification.Builder(getApplicationContext())
                        .setChannelId(CHANNEL_ONE_ID)
                        .setContentTitle("Recording data")
                        .setContentText("App is running background operations")
                        .setSmallIcon(R.drawable.ic_launcher_background)
                        .setLargeIcon(icon)
                        .build();

                Intent notificationIntent = new Intent(getApplicationContext(), MainActivity.class);
                notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
                notification.contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, notificationIntent, 0);

                startForeground(101, notification);
            }
            //if version 26
            else
            {
                startForeground(101, updateNotification());
            }
        }
        //if less than version 26
        else
        {
            Notification notification = new NotificationCompat.Builder(this)
                    .setContentTitle("App")
                    .setContentText("App is running background operations")
                    .setSmallIcon(R.drawable.ic_launcher_foreground)
                    .setOngoing(true).build();

            startForeground(101, notification);
        }
    }

    private Notification updateNotification()
    {

        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
                new Intent(this, MainActivity.class), 0);

        return new NotificationCompat.Builder(this)
                .setContentTitle("Activity log")
                .setTicker("Ticker")
                .setContentText("app is running background operations")
                .setSmallIcon(R.drawable.ic_launcher_foreground)
                .setContentIntent(pendingIntent)
                .setOngoing(true).build();
    }

    public void load(final SharedPreferences lSharedPrefs)
    {
        Log.w(TAG, "load(SharedPreferences): started");
        load(lSharedPrefs, lyftAddresses, LYFT_PREFIX);
        load(lSharedPrefs, uberAddresses, UBER_PREFIX);
        load(lSharedPrefs, otherAddresses, OTHER_PREFIX);
        Log.w(TAG, "load(SharedPreferences): completed");
    }

    private void load(final SharedPreferences lSharedPrefs, final List<AddressPoint> lAddrPoints, final String lPrefix)
    {
        Log.w(TAG, "load(SharedPreferences, lAddrPoints, lPrefix)");
        lAddrPoints.clear();
        final int count = lSharedPrefs.getInt(lPrefix + "Count", lAddrPoints.size());
        for (int i = 0; i < count; ++i)
        {
            String address = lSharedPrefs.getString(lPrefix + "Address" + i, null);
            final long time = lSharedPrefs.getLong(lPrefix + "Time" + i, 0);

            //if address or time is invalid skip to the next entry
            if (address == null || time == 0)
            {
                continue;
            }

            final AddressPoint addressPoint = new AddressPoint(time, address);
            lAddrPoints.add(addressPoint);
        }
    }

    private void save(final SharedPreferences lSharedPrefs, final List<AddressPoint> lAddrPoints, final String lPrefix)
    {
        Log.w(TAG, "save(SharedPreferences, lAddrPoints, lPrefix)");
        SharedPreferences.Editor editor = lSharedPrefs.edit();
        final int count = lAddrPoints.size();

        //Save the count
        editor.putInt(lPrefix + "Count", count);
        for (int i = 0; i < count; ++i)
        {
            //Save the entry
            AddressPoint lAddrPoint = lAddrPoints.get(i);
            editor.putLong(lPrefix + "Time" + i, lAddrPoint.getTime());
            editor.putString(lPrefix + "Address" + i, (String) lAddrPoint.getAddress());
        }
        Log.w(TAG, "save(sharedFrefs, List, String): commit");
        editor.commit();
    }

    public void save(final SharedPreferences sharedPreferences)
    {
        Log.w(TAG, "save(SharedPreferences): started");
        Log.w(TAG, "save: lyftAddresses");
        save(sharedPreferences, lyftAddresses, LYFT_PREFIX);

        Log.w(TAG, "save: uberAddresses");
        save(sharedPreferences, uberAddresses, UBER_PREFIX);

        Log.w(TAG, "save: otherAddresses");
        save(sharedPreferences, otherAddresses, OTHER_PREFIX);
        Log.w(TAG, "save(SharedPreferences) completed");
    }

    public void clear()
    {
        lyftAddresses.clear();
        uberAddresses.clear();
        otherAddresses.clear();
    }

    public void delLyftEntry()
    {
        if (lyftAddresses.size() > 0)
        {
            lyftAddresses.remove(lyftAddresses.size() - 1);
        }
    }

    public void delUberEntry()
    {
        if (uberAddresses.size() > 0)
        {
            uberAddresses.remove(uberAddresses.size() - 1);
        }
    }

    public void delOtherEntry()
    {
        if (otherAddresses.size() > 0)
        {
            otherAddresses.remove(otherAddresses.size() - 1);
        }
    }
}

3) activity_main.xml

4) AndroidManifest.xml

1 Ответ

0 голосов
/ 06 ноября 2018

Это (вероятно) не проблема с многопоточностью в обычном смысле этого слова. Происходит то, что у вас есть вызов GeoCoder.getFromLocation(), выполняющийся в главном потоке. Согласно документации :

Возвращенные значения могут быть получены с помощью поиска в сети. ... Может быть полезно вызывать этот метод из потока, отдельного от вашего основного потока пользовательского интерфейса.

Это означает, что метод может блокироваться на несколько секунд при каждом вызове. Это более вероятно, если вы проезжаете через зону с пятнистой сотовой связью. Поскольку метод вызывается при каждом обновлении местоположения (примерно каждые 2 секунды), понятно, что пользовательский интерфейс зависает.

ПРЕДЛАГАЕМЫЙ ИСПРАВЛЕНИЕ

Замените вашу функцию getAddress() на AsyncTask, которая перемещает вызов getFromLocation() в фоновый поток ( сейчас ваше приложение действительно будет многопоточным). Примерно так должно работать:

private class GetFromLocationTask extends AsyncTask<Location, Void, List<Address>> {
    protected List<Address> doInBackground(Location... locs) {
        return gcd.getFromLocation(locs[ 0 ].getLatitude(), locs[ 0 ].getLongitude(), 1);
    }

    protected void onProgressUpdate(Void... progress) {}

    protected void onPostExecute(List<Address> result) {
        //execute the remainder of your getAddress() logic here 
    }
}

Затем выполните его, используя new GetFromLocationTask().execute(location). Назовите это вместо getAddress(). Вам не нужно передавать Context в getAddress(), так как Service.this будет работать так же хорошо (это Context).

Дополнительная подсказка: обратите внимание, что onLocationChanged() работает в потоке пользовательского интерфейса, как и refreshAllViews(). Это означает, что ваш вызов runOnUiThread() является излишним, и он просто будет выполнять заданный Runnable синхронно.

...