Подтвердить что ты не робот

Связь между деятельностью и службой

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

Моя проблема в том, что я не могу организовать связь между Activity и Service, поскольку между ними происходит много общения, включая движущиеся объекты в обоих направлениях. Я пробовал много методов, которые я искал здесь, в Stack Overflow, но каждый раз, когда у меня были проблемы. Мне нужно, чтобы Служба могла отправлять объекты в Activity и наоборот. Когда я добавляю виджет, я также хочу, чтобы он мог общаться с Сервисом.

Любые советы приветствуются, если вам нужен исходный код, укажите комментарий ниже, но теперь на этом переходе он стал хаотичным.

Есть ли более продвинутое руководство по этому поводу, чем вызов одного метода, который возвращает случайное число из службы?: P

EDIT: Возможное решение - использовать библиотеку RoboGuice и перемещать объекты с инъекцией

4b9b3361

Ответ 1

Я реализовал связь между Activity и Service, используя интерфейс Bind and Callbacks.

Для отправки данных в службу я использовал Binder, который извлекает служебную информацию для Activity, а затем Activity может обращаться к общедоступным методам в Сервисе.

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

Вот несколько примеров кода для каждого: В следующем примере показаны двухстороннее отношение активности и обслуживания: У Activity есть 2 кнопки: Первая кнопка начнет и остановит службу. Вторая кнопка запускает таймер, который запускается в службе.

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

Активность:

    //Activity implements the Callbacks interface which defined in the Service  
    public class MainActivity extends ActionBarActivity implements MyService.Callbacks{

    ToggleButton toggleButton;
    ToggleButton tbStartTask;
    TextView tvServiceState;
    TextView tvServiceOutput;
    Intent serviceIntent;
    MyService myService;
    int seconds;
    int minutes;
    int hours;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
         serviceIntent = new Intent(MainActivity.this, MyService.class);
        setViewsWidgets();
    }

    private void setViewsWidgets() {
        toggleButton = (ToggleButton)findViewById(R.id.toggleButton);
        toggleButton.setOnClickListener(btListener);
        tbStartTask = (ToggleButton)findViewById(R.id.tbStartServiceTask);
        tbStartTask.setOnClickListener(btListener);
        tvServiceState = (TextView)findViewById(R.id.tvServiceState);
        tvServiceOutput = (TextView)findViewById(R.id.tvServiceOutput);

    }

    private ServiceConnection mConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName className,
                                       IBinder service) {
            Toast.makeText(MainActivity.this, "onServiceConnected called", Toast.LENGTH_SHORT).show();
            // We've binded to LocalService, cast the IBinder and get LocalService instance
            MyService.LocalBinder binder = (MyService.LocalBinder) service; 
            myService = binder.getServiceInstance(); //Get instance of your service! 
            myService.registerClient(MainActivity.this); //Activity register in the service as client for callabcks! 
            tvServiceState.setText("Connected to service...");
            tbStartTask.setEnabled(true);
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            Toast.makeText(MainActivity.this, "onServiceDisconnected called", Toast.LENGTH_SHORT).show();
            tvServiceState.setText("Service disconnected");
            tbStartTask.setEnabled(false);
        }
    };

    View.OnClickListener btListener =  new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if(v == toggleButton){
                if(toggleButton.isChecked()){
                    startService(serviceIntent); //Starting the service 
                    bindService(serviceIntent, mConnection,            Context.BIND_AUTO_CREATE); //Binding to the service! 
                    Toast.makeText(MainActivity.this, "Button checked", Toast.LENGTH_SHORT).show();
                }else{
                    unbindService(mConnection);
                    stopService(serviceIntent);
                    Toast.makeText(MainActivity.this, "Button unchecked", Toast.LENGTH_SHORT).show();
                    tvServiceState.setText("Service disconnected");
                    tbStartTask.setEnabled(false);
                }
            }

            if(v == tbStartTask){
                if(tbStartTask.isChecked()){
                      myService.startCounter();
                }else{
                    myService.stopCounter();
                }
            }
        }
    };

    @Override
    public void updateClient(long millis) {
        seconds = (int) (millis / 1000) % 60 ;
        minutes = (int) ((millis / (1000*60)) % 60);
        hours   = (int) ((millis / (1000*60*60)) % 24);

        tvServiceOutput.setText((hours>0 ? String.format("%d:", hours) : "") + ((this.minutes<10 && this.hours > 0)? "0" + String.format("%d:", minutes) :  String.format("%d:", minutes)) + (this.seconds<10 ? "0" + this.seconds: this.seconds));
    }
}

И вот сервис:

 public class MyService extends Service {
    NotificationManager notificationManager;
    NotificationCompat.Builder mBuilder;
    Callbacks activity;
    private long startTime = 0;
    private long millis = 0;
    private final IBinder mBinder = new LocalBinder();
    Handler handler = new Handler();
    Runnable serviceRunnable = new Runnable() {
        @Override
        public void run() {
            millis = System.currentTimeMillis() - startTime;
            activity.updateClient(millis); //Update Activity (client) by the implementd callback
            handler.postDelayed(this, 1000);
        }
    };


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        //Do what you need in onStartCommand when service has been started
        return START_NOT_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
    //returns the instance of the service
    public class LocalBinder extends Binder{
        public MyService getServiceInstance(){
            return MyService.this;
        }
    }

    //Here Activity register to the service as Callbacks client
    public void registerClient(Activity activity){
        this.activity = (Callbacks)activity;
    }

    public void startCounter(){
        startTime = System.currentTimeMillis();
        handler.postDelayed(serviceRunnable, 0);
        Toast.makeText(getApplicationContext(), "Counter started", Toast.LENGTH_SHORT).show();
    }

    public void stopCounter(){
        handler.removeCallbacks(serviceRunnable);
    }


    //callbacks interface for communication with service clients! 
    public interface Callbacks{
        public void updateClient(long data);
    }
}

Ответ 2

Обновление: 10 июля 2016 года

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

Вы можете создать пользовательский приемник BroadCast для событий в активности. Тогда вы также можете использовать Messengers.

  • В Activity

    создать класс MessageHandler как

    public static class MessageHandler extends Handler {
        @Override
        public void handleMessage(Message message) {
            int state = message.arg1;
            switch (state) {
            case HIDE:
                progressBar.setVisibility(View.GONE);
                break;
            case SHOW:
                progressBar.setVisibility(View.VISIBLE);
                break;
            }
        }
    }
    

    Теперь вы можете иметь экземпляр как

    public static Handler messageHandler = new MessageHandler();
    

    Начните свой Service с этого объекта Handler в качестве дополнительных данных как

    Intent startService = new Intent(context, SERVICE.class)
    startService.putExtra("MESSENGER", new Messenger(messageHandler));
    context.startService(startService);
    
  • В вашем Service вы получаете этот объект из намерения и инициализируете переменную Messenger в службе как

    private Messenger messageHandler;
    Bundle extras = intent.getExtras();
    messageHandler = (Messenger) extras.get("MESSENGER");
    sendMessage(ProgressBarState.SHOW);
    

    И затем напишите метод sendMessage для отправки сообщений в действие.

    public void sendMessage(ProgressBarState state) {
    Message message = Message.obtain();
    switch (state) {
        case SHOW :
            message.arg1 = Home.SHOW;
            break;
        case HIDE :
            message.arg1 = Home.HIDE;
            break;
    }
    try {
        messageHandler.send(message);
    } catch (RemoteException e) {
        e.printStackTrace();
    }
    }
    

Пример кода выше показывает и скрывает ProgressBar в Activity, поскольку сообщения принимаются из Сервиса.

Ответ 3

Умения - хорошее решение для общения между Activitiy и Service.

Быстрое решение для целей получения в вашей службе - это подкласс класса IntentService. Он обрабатывает асинхронные запросы, выраженные как Intents, используя очередь и рабочий поток.

Для связи из службы в Activity вы можете транслировать намерение, но вместо использования обычного метода sendBroadcast() из контекста более эффективным способом является использование LocalBroadcastManager из поддержка библиотеки.

Пример сервиса.

public class MyIntentService extends IntentService {
    private static final String ACTION_FOO = "com.myapp.action.FOO";
    private static final String EXTRA_PARAM_A = "com.myapp.extra.PARAM_A";

    public static final String BROADCAST_ACTION_BAZ = "com.myapp.broadcast_action.FOO";
    public static final String EXTRA_PARAM_B = "com.myapp.extra.PARAM_B";

    // called by activity to communicate to service
    public static void startActionFoo(Context context, String param1) {
        Intent intent = new Intent(context, MyIntentService.class);
        intent.setAction(ACTION_FOO);
        intent.putExtra(EXTRA_PARAM1, param1);
        context.startService(intent);
    }

    public MyIntentService() {
        super("MyIntentService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        if (intent != null) {
            final String action = intent.getAction();
            if (ACTION_FOO.equals(action)) {
                final String param1 = intent.getStringExtra(EXTRA_PARAM_A);
                // do something
            }
        }
    }

    // called to send data to Activity
    public static void broadcastActionBaz(String param) {
        Intent intent = new Intent(BROADCAST_ACTION_BAZ);
        intent.putExtra(EXTRA_PARAM_B, param);
        LocalBroadcastManager bm = LocalBroadcastManager.getInstance(this);
        bm.sendBroadcast(intent);
    }
}

Пример Activity

public class MainActivity extends ActionBarActivity {

    // handler for received data from service
    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(MyIntentService.BROADCAST_ACTION_BAZ)) {
                final String param = intent.getStringExtra(EXTRA_PARAM_B);
                // do something
            }
        }
    };

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

        IntentFilter filter = new IntentFilter();
        filter.addAction(MyIntentService.BROADCAST_ACTION_BAZ);
        LocalBroadcastManager bm = LocalBroadcastManager.getInstance(this);
        bm.registerReceiver(mBroadcastReceiver, filter);
    }

    @Override
    protected void onDestroy() {
        LocalBroadcastManager bm = LocalBroadcastManager.getInstance(this);
        bm.unregisterReceiver(mBroadcastReceiver);
        super.onDestroy();
    }

    // send data to MyService
    protected void communicateToService(String parameter) {
        MyIntentService.startActionFoo(this, parameter);
    }
}

Ответ 4

Я думаю, что есть проблема с правильным ответом. У меня недостаточно репутации, чтобы прокомментировать это.

Правильно в ответ: Activity call bindService(), чтобы получить указатель на Service, в порядке. Поскольку контекст службы поддерживается, когда соединение поддерживается.

неверно в ответе: служебный указатель на класс Activity для обратного вызова - это плохой способ. Экземпляр действия, возможно, не является нулевым во время контекста действия, является Release = > exception здесь.

решение для неправильного ответа: сервис отправляет намерение в действие. и назначение приемника активности через BroadcastReceiver.

Примечание: в этом случае "Служба и активность в том же процессе", вы должны использовать LocalBroadcastManager для отправки намерения. Это улучшает производительность и безопасность.

Ответ 5

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

Ответ 6

Это простой пример связи между активностью и сервисом

Деятельность

MyReceiver myReceiver; //my global var receiver
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.layourAwesomexD);
    registerReceiver();
}

//When the activity resume, the receiver is going to register...
@Override
protected void onResume() {
    super.onResume();
    checkStatusService(); // verficarStatusServicio(); <- name change
    registerReceiver();
}
//when the activity stop, the receiver is going to unregister...
@Override
protected void onStop() {
    unregisterReceiver(myReceiver); //unregister my receiver...
    super.onStop();
}
//function to register receiver :3
private void registerReceiver(){
    //Register BroadcastReceiver
    //to receive event from our service
    myReceiver = new MyReceiver();
    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction(MyService.SENDMESAGGE);
    registerReceiver(myReceiver, intentFilter);
}

// class of receiver, the magic is here...
private class MyReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context arg0, Intent arg1) {
        //verify if the extra var exist
        System.out.println(arg1.hasExtra("message")); // true or false
        //another example...
        System.out.println(arg1.getExtras().containsKey("message")); // true or false
        //if var exist only print or do some stuff
        if (arg1.hasExtra("message")) {
            //do what you want to
            System.out.println(arg1.getStringExtra("message"));
        }    
    }
}

public void checkStatusService(){
    if(MyService.serviceStatus!=null){
        if(MyService.serviceStatus == true){
            //do something
            //textview.text("Service is running");
        }else{
            //do something
            //textview.text("Service is not running");
        }
    }
}

Сервис

public class MyService extends Service {

final static String SENDMESAGGE = "passMessage";

public static Boolean serviceStatus = false;

@Override
public void onCreate() {
    super.onCreate();
    serviceStatus=true;
}

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

@Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //you service etc...             
        passMessageToActivity("hello my friend this an example of send a string...");
        return START_STICKY;
    }

@Override
    public void onDestroy() {
        super.onDestroy();
        passMessageToActivity("The service is finished, This is going to be more cooler than the heart of your ex...");
        System.out.println("onDestroy");
        serviceStatus=false;
   }

private void passMessageToActivity(String message){
        Intent intent = new Intent();
        intent.setAction(SENDMESAGGE);
        intent.putExtra("message",message);
        sendBroadcast(intent);
    }
}
  • Если мы не отменим регистрацию BroadcastReceiver, у нас будет ошибка, вам нужно отменить регистрацию, когда действие будет продолжатьсяPause, onStop, onDestroy...
  • Если вы не зарегистрируете BroadcastReceiver при возврате к активности, он ничего не услышит от службы... служба отправит информацию BroadcastReceiver, но ничего не получит, потому что она не зарегистрирована.
  • Когда вы создаете несколько сервисов, в onStartCommand начинаются следующие службы.
  • Вы можете передать информацию в службу с намерением, и вы получите ее в onStartCommand
  • Разница между return в onStartCommand: Разница между START_STICKY и START_REDELIVER_INTENT? и проверьте официальный сайт google: Услуги

Ответ 8

Очень простой, но мощный способ - использовать EventBus, вы можете добавить его в свой gradle создавать и наслаждаться простым шаблоном издателя/подписчика.