Chapter 10 - Service s

It is suggested to think based on the downloaded cases, rely on the following knowledge points to the downloaded cases, and think about which knowledge points and principles are applied in the downloaded cases, which will be very well understood.

catalogue

1, Service profile

2, Multithreaded programming in Android

2.1} basic usage of threads

2.2} update ui in child thread

2.3} principle of updating ui in sub thread (principle of asynchronous message processing mechanism)

2.4  AsyncTask

2.4. 1. Introduction to AsyncTask

2.4. 2. Use of AsyncTask

3, Basic usage of services

3.1} define a service

doubt?

3.2 starting and stopping a service

3.3 communication between activities and services

3.4 service life cycle

4, More tips for service

4.1 front desk service

4.2  IntentService

5, Best practices for services - download examples of the full version

1, Service profile

Service is one of the four components in Android. The service runs in the background, that is, even if the user is using other app s, the code in the service can continue to execute and run at the same time (Guo Shen: the service is suitable for performing tasks that do not need to interact with the user but need to be executed for a long time, and the service does not depend on any user interface)

According to the above understanding, the service should run in the sub thread. Yes, some code in the service should be allowed in the sub thread, but it is not automatically allowed in the sub thread. We need to manually open a thread and put the relevant code!

At the same time, it should be noted that the service depends on the process of creating the service. If the process is closed, the service will also be closed!

Remember that the definition is very important. A service also belongs to one of the four components and has a very high status. It is similar to the status of an activity, but it depends on an activity (created in the activity), and its life cycle is similar to that of an activity. However, after the activity that starts the service is destroyed, the service will also be destroyed.

2, Multithreaded programming in Android

Why learn this multi-threaded programming in the services section?

Because the service usually runs in the background, and the current app program has a main thread, Then, because the service often needs to perform time-consuming operations (such as networking operations, downloading...), if these time-consuming operations are placed in the main thread, the main thread will be blocked and the program will crash. Therefore, if the service needs to perform these actions, it must be placed in a new thread. We need to manually create threads in the service.

2.1} basic usage of threads

There are three basic uses of threads

(1) Define a class, inherit the Thread, override the run method, and then new out the instance of this class.

public class MyThread extends Thread {
    @Override
    public void run() {
        //Related code executed by thread
    }
}

//............
MyThread myThread = new MyThread();
myThread.start();

(2) Define a class to implement the runnable interface

public class MyThread implements Runnable {
    @Override
    public void run() {
        //Thread code
    }
}
//Create this thread and start it
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread);
thread.start();

(3) The form of anonymous inner classes - the most commonly used

new Thread(new Runnable() {
            @Override
            public void run() {
                //Code executed by thread
            }
        }).start();

2.2} update ui in child thread

There are questions in the process of copying the code here? Why is it possible to update the ui in a branch thread using a LinearLayout layout, but not in a RelativeLayout?

Android stipulates that ui can only be updated in the main thread and not in the sub thread. Therefore, Android provides a set of "asynchronous message processing mechanism" to perform this operation.

Fixed process:

(1) new the Handler instance object in the main thread, rewrite the HandleMessage method in it, write the handlerMessage method, and perform relevant operations according to the data carried in the parameter Message. Generally, the main thread operation of updating ui is performed here, because this object is created in the main thread and the method is also executed in the main thread, Therefore, the operation of updating the ui must also be performed in the main thread!

(2) Create a message Object in the branch thread. This message Object can carry the results of some time-consuming operations (int, Object...) and then send this message through the Handler Object, and then receive it with the handlerMessage(Message msg) method, Then make corresponding judgment and processing according to the relevant data (results) carried by this msg.

The code is as follows:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    public static final int UPDATE_TEXT = 1;
    TextView text;

    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case UPDATE_TEXT:
                    text.setText("Nice to meet you");
                    break;
                default:
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button changeText = findViewById(R.id.changeText);
        changeText.setOnClickListener(this);
        text = findViewById(R.id.text_view);
        
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.changeText:
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                      //text.setText("nice to meet you");
                        Message message = new Message();
                        message.what = UPDATE_TEXT;
                        handler.sendMessage(message);
                    }
                }).start();
                break;
            default:
                break;
        }
    }
}

2.3} principle of updating ui in sub thread (principle of asynchronous message processing mechanism)

This asynchronous message processing mechanism has four components:

(1) Message: information transmitted between threads. It is used to transmit data between different threads and can carry data. message.what carries data, message.arg1 and message.arg2 carry integer data, and message.obj carries an object object.

(2) Handler: for sending and processing messages, sendMessage() is used to send messages, and handlerMessage() is used to process messages;

(3) MessageQueue: stores all messages sent by the Handler, and the messages in it will always be waiting for processing; each thread will only have one MessageQueue object.

(4) Looper: looper is the steward of MessageQueue. The looper object calls the loop () method and then performs an infinite loop. If a message is found in MessageQueue, it will take out the message and pass it to the parameters in the HandlerMessage (Message msg) method.

To sum up: there is an msg in the branch thread, which carries a data, sends it through the handler's sendMessage method, stores it in the messageQueue, and then the looper calls the loop() method. Then take out the msg in the message queue and pass it to the handlerMessage () in the main thread, and finally complete the switching from the branch to the main thread.

 

2.4  AsyncTask

2.4. 1. Introduction to AsyncTask

This AsyncTask does not need to know the asynchronous message processing mechanism directly, which makes it convenient for us to use branch threads to send data... This AsyncTask is also based on this asynchronous message processing mechanism.

2.4. 2. Use of AsyncTask

(1) AsyncTask is an abstract class, so we need to write a class to inherit it. Then, there are generic types and specify three types. These three data types are the parameter data types in the methods of this class. See the code for parameter descriptions:

/*
Params, the type of the parameters sent to the task upon execution.
-------In the parameters passed into onInBackground
Progress, the type of the progress units published during the background computation.
------onProgressUpdate Parameter types in
Result, the type of the result of the background computation.
--------onPostExecute Parameter types in
*/
public class DownloadTask extends AsyncTask<String,Integer,Boolean> {}

(2) rewriting the related methods in this class is mainly four methods. One is to call the task before it is executed formally; the other is to execute the code in the branch thread; the other is that the execution process of the branch thread needs to update the ui and execute it; after the last task is executed, the related processing is carried out according to the result of the return.

public class DownloadTask extends AsyncTask<String,Integer,Boolean> {

    @Override
    //It is executed before the task is called, that is, it will be called when the execute method is executed
    protected void onPreExecute() {
        //ProgressDialog.show();// Show progress bar dialog box
    }

    @Override
    //The code in the time-consuming operation will operate in the sub thread
    //This is also the most important method of this class, which executes thread code
    protected Boolean doInBackground(String... voids) {
        try{
//            while (true){
//                int downloadPercent = doDownload();// Perform download operation
//                publishProgress(downloadPercent);
//                if (downloadPercent>=100){
//                    break;
//                }
//           }
        }catch (Exception e){
            return false;
        }
        return true;
    }

    @Override
    //Methods that will be executed during doInBackground execution
    //Executes when the publishProgress() method is called in the doInBackground method.
    //Update the interface elements accordingly and update the ui
    //That is, the current task progress is displayed on the interface
    protected void onProgressUpdate(Integer... values) {
        //progressDialog.setMessage("Downloaded"+values[0]+"%");
    }

    @Override
    //This method will be executed after doInBackground execution
    //Perform some ui operations based on the returned data, such as closing the message box
    protected void onPostExecute(Boolean result) {
        //progressDialog.dismiss();
        if (result){
            //Toast.makeText(, "", Toast.LENGTH_SHORT).show();
        }else{
            //Toast.makeText(, "", Toast.LENGTH_SHORT).show();
        }
    }

}

(3) Call this class and let the methods in the class execute. Execute the execute method. When the execute method is executed, the methods in the class will be executed by the system call!

//Update ui operations in a encapsulated way
DownloadTask downloadTask = new DownloadTask();
downloadTask.execute();

The above can complete the operation of switching from the main thread to the branch thread and from the branch thread to the main thread. We also call it a task, because this is the execution process of a task;

For example, the download task needs to be networked, so it needs to be placed in the branch thread, and then the ui is generally updated according to the download situation while downloading, so the main thread needs to perform operations. Then this is a task, and then a result is returned after the final task is executed. And service? General services need to have tasks, such as downloading. Downloading is that the user can still perform the download operation when using other app s, and the service can do this, and the download is multi-threaded. Therefore, it is necessary to start a task in the service. General services are bound to tasks. Tasks depend on services!

3, Basic usage of services

The basic usage of service is how to define a service, then how to start and stop a service, and then how to control the service in the activity.

3.1} define a service

Define a class to inherit the service, then override the methods inside (understand the role of each method), and then register in the manifest file. See the code:

//Define a class to inherit Service
public class MyService extends Service {
    

//Construction method
    public MyService() {
        Log.d(TAG, "MyService is been initilized");
    }

//The service executes when it is first created
    @Override
    public void onCreate() {}

//The service is created at startup
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "service has been started");
        return super.onStartCommand(intent, flags, startId);
    }

//The service is created when it is destroyed
    @Override
    public void onDestroy() {
        Log.d(TAG, "service has been destroy");
        super.onDestroy();
    }

//This method executes when the binding method for services and activities executes
    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return mBinder;
    }
}

doubt?

onStartCommand method is a method that will be executed every time the service is started, but isn't it one stroke to start the service again after the service has been started------- Remember this rule first. Maybe this rule may be useful?

3.2 starting and stopping a service

It is to construct an intention, then call startService and invoke the stopService method to turn on or off the service.

 public void onClick(View v) {
        switch (v.getId()){
            //Manually start the service
            case R.id.start_service:
                Intent startIntent = new Intent(this,MyService.class);
                startService(startIntent);
                break;
//At this time, if the service has not been created, the onCreate() method will execute,
//The onStartCommand() method is then executed, and the onCreate() method is executed only once
//The onStartCommand() method executes multiple times

            //Stop service manually
            case R.id.stop_service:
                Intent stopIntent = new Intent(this,MyService.class);
                stopService(stopIntent);
                break;
//The onDestroy() method executes
            default:
                break;
        }
    }

3.3 communication between activities and services

(think about it based on the download case) in fact, the activity control service is also inaccurate. It should be said that the activity controls the behavior of the activity, because it actually returns a bound object, and then we execute the method of the bound object to change or kill the task, but what we control is the method of the bound object returned by the service, Instead of a real service, of course, we can also write down as many operations we want to perform on the service in the binding object, so that we can call the methods of the binding object in the activity.

This is also a very good design concept. This method can also be considered in the process of writing -------- > call a method and then return an object. There are many methods in the object. We call the methods in the return object.

The main communication between services and activities is through the Binder object returned by the onBind() method. In the activity, we can call the relevant methods in the Binder object to control the tasks in the service

    public void onClick(View v) {
        switch (v.getId()){
            //The binding service starts the service at the same time as the current activity
            //Calling this method will cause the onCreate() method to execute if the service has not been created
            //The onStartCommand method is then executed as well
            //Most importantly, the onBind method executes and then returns a binder object
            //The onServiceConnected method then executes
            case R.id.bind_service:
                Intent bindIntent = new Intent(this,MyService.class);
                bindService(bindIntent,connection,BIND_AUTO_CREATE);
                break;
            //Contact the binding between the current activity and the service and stop the service at the same time
            //The onDestroy method executes
            //The onServiceDisconnected method executes
            case R.id.unbind_service:
                Intent unbindIntent = new Intent(this,MyService.class);
                unbindService(connection);
                break;
        }

    private MyService.DownloadBinder downloadBinder;

        private ServiceConnection connection = new ServiceConnection() {
            @Override
            //When a service is bound to an activity, it is executed
            //The IBinder here is the binder returned from the activity and service
            public void onServiceConnected(ComponentName name, IBinder binder) {
                downloadBinder = (MyService.DownloadBinder)binder;
                //Call the method in the binder
                downloadBinder.startDownload();
                downloadBinder.getProgress();
            }

            @Override
            //When a service contacts an activity, it is unbound
            public void onServiceDisconnected(ComponentName name) { }
        };
    }
}

Binders and onBind methods defined in the service:

public class MyService extends Service {

    private DownloadBinder mBinder = new DownloadBinder();

    class DownloadBinder extends Binder{
        public void startDownload(){
            Log.d(TAG, "startDownload: executed");
        }
        public void getProgress(){
            Log.d(TAG, "getProgress: executed");
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return mBinder;
    }
}

If you return the binder object above, you can invoke all the binder objects' public methods in the activity. Then note that a service can bind to any activity in the app and return the same binder instance, that is, the same object.

3.4 service life cycle

In fact, the service life cycle is the execution process of those methods.

Call the startService() method: onCreate()------onStartCommand() ----------------; Call stopService () method: the onDestroy() method is executed, and the service is destroyed

Call bindService() method: oncreate() ------ onstartcommand() ------ onbind() ------; Call unbindService() method: onDestory(), and the service is destroyed

Note that although the onStartCommand method is executed every time it is started, in fact, only one service instance is created. Executing the stopService method or unbindService once will destroy the service.

When both startService() and bindService methods are executed, you need to call both stopService () and unbindService methods to destroy the service.

4, More tips for service

The main points are the front desk service and intentService

4.1 front desk service

As the name suggests, the foreground service is running in the foreground rather than in the background, that is, we can see this service, especially some changes and progress of the service.

Because when the memory is insufficient, the service may be recycled by the system, but the foreground service will not. For example, we need to show the download progress to the user or let the download continue. At this time, we should set the download service as the foreground service. The biggest difference between the foreground service and the background service is that the foreground service will have an icon above, and more detailed information can be seen in the drop-down status bar.

The usage steps of front service: in fact, it is to define a notification and then call the startForeground () method to turn the service into the foreground service.

@Override
    public void onCreate() {
        super.onCreate();
        if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
            NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
            String channelId = "fore_service";
            String channelName = "Front desk service";
            int channelImportance = NotificationManager.IMPORTANCE_HIGH;
            NotificationChannel notificationChannel = new NotificationChannel(channelId,channelName,channelImportance);
            manager.createNotificationChannel(notificationChannel);
        }

        Log.d("MyService", "onCreate executed");
        Intent intent = new Intent(this, MainActivity.class);
        PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0);
        Notification notification = new NotificationCompat.Builder(this,"fore_service")
                .setContentTitle("This is content title")
                .setContentText("This is content text")
                .setWhen(System.currentTimeMillis())
                .setSmallIcon(R.mipmap.ic_launcher)
                .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
                .setContentIntent(pi)
                .build();
        startForeground(1, notification);
    }

Note that there is no progress change displayed here. Add a setProgress concatenation to display the progress with some dynamic changes. The most important thing is to call the startforegroup method.

4.2  IntentService

Why does this IntentSevice appear? Because we know that a lot of code in the service should run in the sub thread, and it needs to be closed after allowing, otherwise it will be running all the time. Therefore, the usual code should be as follows:

However, many people forget to start the thread and end the service. Therefore, with this class, some methods run in the sub thread and automatically close the service.

//Define a class to inherit the IntentService
public class myIntentService extends IntentService {

    private static final String TAG = "myIntentService";

    //You must have this constructor and call the parameterized constructor of the parent class
    public myIntentService() {
        super("myIntentService");
    }

    @Override
    //The operation in the sub thread will end automatically after running
    protected void onHandleIntent(@Nullable Intent intent) {
        Log.d(TAG, "onHandleIntent: Thread id is :"+Thread.currentThread().getName());
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy: executed");
    }
}

Open service

public void onClick(View v) {
        switch (v.getId()){
            case R.id.start_service_withIntentService:
                Intent intent_2 = new Intent(this,myIntentService.class);
                startService(intent_2);
                Log.d(TAG, "Thread is :"+Thread.currentThread().getName());
            default:
                break;
        }
    }

The above is the use of intentService

5, Best practices for services - download examples of the full version

We have to look at the code. There are many codes and it is very difficult, but it is not so difficult after sorting out the logic. Here is to sort out the ideas and explain some details. At the same time, we have gained an experience from this case, that is, when looking at complex code, we should start from the user's point of view and look at it step by step.

Idea:

First, the user needs to click download and then start downloading, click stop to stop, and then click Cancel to cancel. Then download should be a service, because it can also run when users are using other app s, so it should be defined as a service.

Then there is a download task DownloadTask (involving networking time-consuming operation) in this service, so we need to define a task to inherit AsyncTask. The time-consuming operation is performed in doInBackground in the AsyncTask subclass, that is, send a request to the server, and then download the file byte stream and store it in the mobile phone file.

Then, a Binder in the Service is needed to connect activities and services. The Binder in the Service defines some methods to start or stop the task or cancel the task.

Then the binder is returned to the activity, where the method in the binder is called to control the download task.

There is also a Listener for feedback. In short, several important classes, Service, Binder in Service, DownLoadTask, and Listener. There are many design details, you need to look at the code!

The above is the summary of Service in the four components. I hope you can correct my mistakes!

Keywords: Android

Added by Impact on Sat, 25 Dec 2021 14:08:11 +0200