Lesson 95. Service. Feedback with PendingIntent

Lesson 95. Service. Feedback with PendingIntent


In this lesson:

– get the result from the service using PendingIntent

In the past lessons, we started services, passed on data to them, but did not get anything back into action. But it is possible, and there are several ways. In this tutorial, we’ll look at PendingIntent.

I will not particularly go into explaining what PendingIntent is and what it is capable of – a lesson not about it. I’ll show you how to use it to get the results of the service in Activity.

The scheme is as follows:

– In Activity, we create PendingIntent using the createPendingResult method

– we put this PendingIntent in the usual Intent which we use for start of service and we call startService

– in the service, extract PendingIntent from the Intent object received in the onStartCommand method

– when we need to pass the results of the service to Activity, we call the send method for the PendingIntent object

– we catch these results from the service in Activity in the onActivityResult method

That is, the PendingIntent widget is here that it contains some connection to the Activity (in which it was created) and when the send method is called, it goes into that Activity and carries data if necessary. All in all, a kind of mail pigeon who knows exactly how to get home.

The scheme is generally simple. Let’s try to draw an example on it. We will have a program that will send to the service to perform three tasks. And the service will inform when it started each task, when it finished and with what result. All this will be displayed on the Activity screen.

By the way, to make it easier to perceive it all, I note that the algorithm is very similar to the work startActivityForResult. Only there we interact not with service, but with Activity. Forget it, see Lessons 29 and 30. It will be much easier to grasp the current material.

Let’s create a project:

Project name: P0951_ServiceBackPendingIntent
Build Target: Android 2.3.3
Application name: ServiceBackPendingIntent
Package name: en.startandroid.develop.p0951servicebackpendingintent
Create Activity: MainActivity

Add to strings.xml rows:

Start

screen main.xml:



	
	
	
	
	
	
	

Three TextView, in which we will output the information coming from the service. And the service start button.

Create a service class – MyService.java. And write it in the manifesto. There is nothing Kodyma in it.

MainActivity.java:

package ru.startandroid.develop.p0951servicebackpendingintent;

import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends Activity {

  final String LOG_TAG = "myLogs";

  final int TASK1_CODE = 1;
  final int TASK2_CODE = 2;
  final int TASK3_CODE = 3;

  public final static int STATUS_START = 100;
  public final static int STATUS_FINISH = 200;

  public final static String PARAM_TIME = "time";
  public final static String PARAM_PINTENT = "pendingIntent";
  public final static String PARAM_RESULT = "result";

  TextView tvTask1;
  TextView tvTask2;
  TextView tvTask3;

  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    tvTask1 = (TextView) findViewById(R.id.tvTask1);
    tvTask1.setText("Task1");
    tvTask2 = (TextView) findViewById(R.id.tvTask2);
    tvTask2.setText("Task2");
    tvTask3 = (TextView) findViewById(R.id.tvTask3);
    tvTask3.setText("Task3");
  }

  public void onClickStart(View v) {
    PendingIntent pi;
    Intent intent;

    // Создаем PendingIntent для Task1
    pi = createPendingResult(TASK1_CODE, null, 0);
    // Создаем Intent для вызова сервиса, кладем туда параметр времени
    // и созданный PendingIntent
    intent = new Intent(this, MyService.class).putExtra(PARAM_TIME, 7)
        .putExtra(PARAM_PINTENT, pi);
    // стартуем сервис
    startService(intent);

    pi = createPendingResult(TASK2_CODE, null, 0);
    intent = new Intent(this, MyService.class).putExtra(PARAM_TIME, 4)
        .putExtra(PARAM_PINTENT, pi);
    startService(intent);

    pi = createPendingResult(TASK3_CODE, null, 0);
    intent = new Intent(this, MyService.class).putExtra(PARAM_TIME, 6)
        .putExtra(PARAM_PINTENT, pi);
    startService(intent);
  }

  @Override
  protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    Log.d(LOG_TAG, "requestCode = " + requestCode + ", resultCode = "
        + resultCode);

    // Ловим сообщения о старте задач
    if (resultCode == STATUS_START) {
      switch (requestCode) {
      case TASK1_CODE:
        tvTask1.setText("Task1 start");
        break;
      case TASK2_CODE:
        tvTask2.setText("Task2 start");
        break;
      case TASK3_CODE:
        tvTask3.setText("Task3 start");
        break;
      }
    }

    // Ловим сообщения об окончании задач
    if (resultCode == STATUS_FINISH) {
      int result = data.getIntExtra(PARAM_RESULT, 0);
      switch (requestCode) {
      case TASK1_CODE:
        tvTask1.setText("Task1 finish, result = " + result);
        break;
      case TASK2_CODE:
        tvTask2.setText("Task2 finish, result = " + result);
        break;
      case TASK3_CODE:
        tvTask3.setText("Task3 finish, result = " + result);
        break;
      }
    }
  }
}

IN onCreate we find TextView and assign them the original text. For each task your own TextView.

IN onClickStart we create PendingIntent by createPendingResult. Only the request code is passed to the method input – it can be considered an identifier. Using this code, we will then determine which task returned the response from the service. The other two options are Intent and Flags. We don’t need them now, pass null and 0. respectively (It turned out that in version 4, Android does NOT roll using null instead of Intent. So you can create an empty Intent and use it.)

Next, we create an Intent to call the MyService service, put a time parameter (which we will use for a pause in the service) and PendingIntent there. After that, we send it all to service.

We produce similar actions for Task2 and Task3.

IN onActivityResult check what type of message came from the service.

If the job started (STATUS_START), then we determine what the job is and write it to the corresponding TextView.

And if the task is completed (STATUS_FINISH), then we read from Intent the result of execution, determine what the task is, and write the info about it in the corresponding TextView.

We write the whole info about the incoming messages in a log.

As you understand, STATUS_START and STATUS_FINISH codes, as well as the result we will now generate in the service.

Kodyma service MyService.java:

package ru.startandroid.develop.p0951servicebackpendingintent;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

public class MyService extends Service {

  final String LOG_TAG = "myLogs";
  ExecutorService es;

  public void onCreate() {
    super.onCreate();
    Log.d(LOG_TAG, "MyService onCreate");
    es = Executors.newFixedThreadPool(2);
  }

  public void onDestroy() {
    super.onDestroy();
    Log.d(LOG_TAG, "MyService onDestroy");
  }

  public int onStartCommand(Intent intent, int flags, int startId) {
    Log.d(LOG_TAG, "MyService onStartCommand");

    int time = intent.getIntExtra(MainActivity.PARAM_TIME, 1);
    PendingIntent pi = intent.getParcelableExtra(MainActivity.PARAM_PINTENT);

    MyRun mr = new MyRun(time, startId, pi);
    es.execute(mr);

    return super.onStartCommand(intent, flags, startId);
  }

  public IBinder onBind(Intent arg0) {
    return null;
  }

  class MyRun implements Runnable {

    int time;
    int startId;
    PendingIntent pi;

    public MyRun(int time, int startId, PendingIntent pi) {
      this.time = time;
      this.startId = startId;
      this.pi = pi;
      Log.d(LOG_TAG, "MyRun#" + startId + " create");
    }

    public void run() {
      Log.d(LOG_TAG, "MyRun#" + startId + " start, time = " + time);
      try {
        // сообщаем об старте задачи
        pi.send(MainActivity.STATUS_START);

        // начинаем выполнение задачи
        TimeUnit.SECONDS.sleep(time);

        // сообщаем об окончании задачи
        Intent intent = new Intent().putExtra(MainActivity.PARAM_RESULT, time * 100);
        pi.send(MyService.this, MainActivity.STATUS_FINISH, intent);

      } catch (InterruptedException e) {
        e.printStackTrace();
      } catch (CanceledException e) {
        e.printStackTrace();
      }
      stop();
    }

    void stop() {
      Log.d(LOG_TAG, "MyRun#" + startId + " end, stopSelfResult("
          + startId + ") = " + stopSelfResult(startId));
    }
  }
}

We again use the scheme of the Executor and Runnable that is familiar in the past.

IN onCreate we create an executive with two threads. That is, when the service receives three tasks, it will immediately start performing two of them, and the third will wait for free flow.

IN onStartCommand we extract from the Intent a time parameter for pause and PendingIntent. We create MyRun and pass it this data. We transfer MyRun to the executor for execution.

MyRun on startup invokes the send method for PendingIntent and sends the message type STATUS_START there. This comes to the onActivityResult method in Activity and we will see on the screen how one of the TextView text will appear that the task started to work.

Next we emulate the work as usual just by pausing. And after that we create Intent with result of work (simply time * 100), and cause slightly different implementation of method send. In addition to the message type (STATUS_FINISH), we pass an Intent with the result there and specify the context. This goes to the onActivityResult method in Activity and we will see on the screen how in one of the TextView text will appear that the task has finished working with some result.

Next we call the stop method, which calls the stopSelfResult method.

We all save and launch the application.

press Start.

We see that two tasks have started, because the executor is configured for two threads.

One task was completed and showed the result, the flow was released, the remaining task starts.

Another task is over.

The latter is over.

We look at the logs (you may have a slightly different sequence of log entries):

MyService onCreate
MyService onStartCommand
MyRun # 1 create
MyService onStartCommand
MyRun # 2 create
MyRun # 1 start, time = 7
MyService onStartCommand
MyRun # 3 create

The service was created and received all three calls.

requestCode = 1, resultCode = 100

MyRun1 started working and sent a message about it: requestCode = TASK1_CODE, resultCode = STATUS_START.

MyRun # 2 start, time = 4
requestCode = 2, resultCode = 100

MyRun2 started working and sent a message about it: requestCode = TASK2_CODE, resultCode = STATUS_START.

MyRun # 2 end, stopSelfResult (2) = false
MyRun # 3 start, time = 6
requestCode = 2, resultCode = 200
requestCode = 3, resultCode = 100

MyRun2 quit and sent a response message: requestCode = TASK2_CODE, resultCode = STATUS_FINISH. The thread in the executor is released and MyRun3 starts. MyRun3 started working and sent a message about it: requestCode = TASK3_CODE, resultCode = STATUS_START.

MyRun # 1 end, stopSelfResult (1) = false
requestCode = 1, resultCode = 200

MyRun1 quit and sent a response message: requestCode = TASK1_CODE, resultCode = STATUS_FINISH.

requestCode = 3, resultCode = 200
MyRun # 3 end, stopSelfResult (3) = true

MyRun3 quit and sent a corresponding message: requestCode = TASK3_CODE, resultCode = STATUS_FINISH.

MyService onDestroy

The last received call executed the stopSelfResult method, the service stops.

At first I wanted to make an example easier, but the chat got carried away, and it turned out to be more difficult, but also more interesting.

Once again I will say that we used PendingIntent superficially here and did not dig it in detail, because the lesson is not about it. We will soon meet with this object again as we study Notifications.

In the next lesson:

– Get the result from the service using BroadcastReceiver




Discuss in the forum [32 replies]

Leave a Comment