IntentService передает объект Location в пользовательский интерфейс

#java #android #service

#java #Android #Обслуживание

Вопрос:

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

Этот тестовый класс не реализует форму. Я просто хочу заставить службу работать и заставить ее возвращать местоположение как часть этого теста.

Я проверял Интернет и решил использовать обработчик и сообщение для передачи объекта location обратно.

Проблема в том, что я получаю сообщение об ошибке «отправка сообщения обработчику в мертвом потоке». Я знаю, что сообщение должно быть самоочевидным, но я не вижу, что не так с моим кодом.

Мой основной и единственный код действия приведен ниже

 package com.pengilley.locationmanagertest;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;

public class LocationManagerTestService extends Activity{
EditText locationField;
Button askLocation;
Location location;
private final static String TAG = "LocationManagerTestService";

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    locationField = (EditText)findViewById(R.id.locationtext);
    askLocation = (Button)findViewById(R.id.button);

    askLocation.setOnClickListener(new OnClickListener(){

        @Override
        public void onClick(View view) {
            getLocation(getApplicationContext());
        };

    });
    }
private void getLocation(Context context){
   Intent intent = new Intent(context, LocationTest.class);
   startService(intent);
   Log.d(TAG,"getLocation: service request sent");
   }


final Handler handler = new Handler(){
    public void handleMessage(Message msg){
        Log.d(TAG,"handleMessage start");
        //receive the message from our intentservice
        Bundle bundle = msg.getData();
        location = (Location)bundle.getParcelable("location");
        CharSequence charseq = "Latitude: "   location.getLatitude()   "      Longitude: "   location.getLongitude();
        locationField.setText(charseq);
    }
};


}
  

Мой класс IntentService находится здесь

 package com.pengilley.locationmanagertest;

import android.app.IntentService;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;


public class LocationTest extends IntentService{
LocationListener locationListener;
Location lastKnownLocation;
Location goodLocation;
LocationManager locationManager;
boolean gpsEnabled;
boolean networkEnabled;
float distanceBetween;
private final static String TAG = "LocationTest";
private final float MAX_DISTANCE_BETWEEN_LOCATIONS = 20;
public static boolean LOCATION_AVAILABLE = false;
private String fieldVal;
private final Handler handler = new Handler();

public LocationTest(){
    super("Location test"); 
}


public void setLocationManager(Context context){
    Log.d(TAG,"setLocationManager start");
    //setup locationlistener
    locationListener = new LocationListener(){

        @Override
        public void onLocationChanged(Location location) {
            Log.d(TAG,"onLocationChanged start");
            // TODO Auto-generated method stub
            //update check this location against last know location. 
            //If both locations are close to each other we can stop listening and pass the new location to lResult
            lastKnownLocation =     locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
            //if no location from gps try network
            if(lastKnownLocation==null){
                lastKnownLocation = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
            }
            if(lastKnownLocation==null){
                //use current location
                goodLocation = location;
            }else{
                //check accuracy of current location
                distanceBetween = location.distanceTo(lastKnownLocation);

                if(distanceBetween <= MAX_DISTANCE_BETWEEN_LOCATIONS){
                    //good location
                    goodLocation = location;
                }else if(lastKnownLocation.hasAccuracy()){
                    goodLocation = lastKnownLocation;
                }
            }
            Log.d(TAG,"onLocationChanged: "   goodLocation.toString());

            //put result into handler
            Message message = Message.obtain();
            Bundle bundle = new Bundle();
            bundle.putParcelable("location", goodLocation);
            message.setData(bundle);
            handler.sendMessage(message);
            Log.d(TAG,"onLocationChanged: messag sent");

        }

        @Override
        public void onProviderDisabled(String provider) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onProviderEnabled(String provider) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onStatusChanged(String arg0, int arg1, Bundle arg2) {
            // TODO Auto-generated method stub

        }

    };


    //get location manager
    locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);

    //check if network and gps are enabled
    gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
    networkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);

    //request location for providers which are enabled
    if(gpsEnabled){
        locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,0,0,locationListener);
    };
    if(networkEnabled){
        locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);
    }
}

public String getFieldVal(){
    return fieldVal;
}


@Override
protected void onHandleIntent(Intent intent) {
    // TODO Auto-generated method stub
    Log.d(TAG,"onHandleIntent start");
    setLocationManager(this);




}
  

}

Ответ №1:

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

Это не очень хорошая идея. IntentService завершается, когда onHandleIntent() завершается, и больше никакой работы не требуется. Ваша текущая реализация пропускает память как решето — вы никогда не удаляете свой запрос на обновления, и каждый вызов IntentService будет регистрировать новый LocationListener .

Проблема в том, что я получаю сообщение об ошибке «отправка сообщения обработчику в мертвом потоке». Я знаю, что сообщение должно быть самоочевидным, но я не вижу, что не так с моим кодом.

onHandleIntent() выполняется в фоновом потоке, поток, который давно закончился к тому времени, когда вы пытаетесь его использовать. Кроме того, поскольку вы никогда не удосуживались реализовать handleMessage() на Handler , Message никуда не денется, и уж точно не к activity.

Комментарии:

1. Всем привет. Я не понимаю, почему у моего intentservice будет более одного вызова, если он запускается только при нажатии моей кнопки, которую я нажимаю один раз. Разве onHandleIntent () не отключился бы только после того, как местоположение было найдено? или я должен вручную закрыть его? У меня есть событие handMessage в моей основной деятельности, не там ли будет получено мое сообщение?

2. @Stephen: «Я не понимаю, почему мой intentservice должен иметь более одного вызова, если он запускается только при нажатии моей кнопки, которую я нажимаю один раз». — вы нажимаете ее один раз. Ваши пользователи могут, а могут и нет. Ваши пользователи могут нажимать на него пять раз подряд, и вы ничего не делаете, чтобы остановить их. «Разве onHandleIntent() не отключился бы только после того, как местоположение было найдено? » — нет. «У меня есть событие handMessage в моей основной деятельности, не там ли будет получено мое сообщение?» — нет, потому что это не то место, где находится ваше Handler .

3. Спасибо за ваши ответы, CommonsWare. Я должен объяснить, что это тестовое приложение является чисто тестом для запуска intentservice. В моем фактическом опубликованном приложении у меня не будет кнопки для запуска службы определения местоположения. Что я не смог понять с помощью обработчиков, так это то, что если мой обработчик используется и создается в одном потоке, как он взаимодействует с другим потоком? Я думал, что в этом и заключается смысл обработчиков… Должен ли я создать отдельный класс, который расширяет обработчик, и тогда и мой основной поток, и мой intentservice смогут получить к нему доступ?

4. @Stephen: «В моем фактически опубликованном приложении у меня не будет кнопки для запуска службы определения местоположения». — а, ладно. «Должен ли я создать отдельный класс, который расширяет обработчик, и тогда и мой основной поток, и мой intentservice смогут получить к нему доступ?» — Handler должен быть создан экземпляр и доступен любому классу, которому нужны данные. Если вы пытаетесь доставить a Message в Activity , наличие a Handler в a Service бессмысленно. Вам понадобится Handler в Activity и передать Messenger в Service . github.com/commonsguy/cw-android/tree/master/Service/Downloader

5. Привет, общедоступное программное обеспечение. Спасибо за