我写了一个android应用程序,基本上可以跟踪GPS坐标的时间和地址。
我有3个列表,分别对应于Lyft,Uber和其他。
但是我认为我的应用开始放慢我的智能手机(三星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);
}
}
}
答案 0 :(得分:1)
按照通常的说法,这(可能)不是线程问题。发生的事情是您在主线程上执行了一个GeoCoder.getFromLocation()
调用。根据{{3}}:
可以通过网络查找获得返回的值。 ...从与主UI线程分开的线程中调用此方法可能很有用。
这意味着该方法每次被调用可能会阻塞几秒钟。如果您要开车穿越杂乱无章的小区,则更有可能。由于该方法是在每次位置更新时(大约每2秒一次)调用一次,因此UI处于挂起状态是可以理解的。
建议的修补程序
用getAddress()
替换AsyncTask
函数,该函数将getFromLocation()
调用移至后台线程( now 您的应用程序将真正成为多线程)。这样的事情应该起作用:
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()
在UI线程上运行,refreshAllViews()
也是如此。这意味着您对runOnUiThread()
的调用是多余的,它将仅同步执行给定的Runnable
。