Android inquiry service

Posted by kessels1234 on Wed, 26 Jan 2022 11:57:24 +0100

Android inquiry service

1. What is the service

Service is a solution to realize the background running of programs in Android. It is suitable for performing tasks that do not need to interact with users and require long-term running The operation of the service does not depend on any user interface. If the program is switched to the background or the user opens another application, the service can still run normally
The service does not run in a separate process, but depends on the application in which the service is created When a program is erased, the service can still run normally
The actual service does not automatically start the thread, and all code runs in the main thread by default We need to manually create sub threads inside the service and perform specific tasks here, otherwise the main thread may be blocked

2.Android multithreaded programming

1. Basic usage of thread

	class MyThread extends Thread{
	@Override
	public void run(){
	//Handling specific logic
	}
	}
	new MyThread().start();//call
class MyThread implements Runnable{
@Override
public void run(){
//Handling specific logic
}
}
MyTread myThread=new MyThread();
new Thread(myThread).start();
new Thread(new Runnable(){
@Override
public void run(){
//Handling specific logic
}
}).start();

2. Update UI in child thread

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <Button
        android:id="@+id/change_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Change Text"/>
    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="Hello world"
        android:textSize="20sp"/>

</RelativeLayout>

Android does not allow UI operations in child threads Through a set of asynchronous message processing mechanism provided by Android, the problem of UI operation in sub threads can be solved

public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    public static final int UPDATE_TEXT=1;
    private TextView text;
    private Handler handler=new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case UPDATE_TEXT:
                    ;
                    //UI operations can be performed here
                    text.setText("Nice to meet you");
                    break;
                default:
                    break;
            }
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        text=(TextView) findViewById(R.id.text);
        Button changeText=(Button) findViewById(R.id.change_text);
        changeText.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.change_text:
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Message message=new Message();
                        message.what=UPDATE_TEXT;
                        handler.sendMessage(message);//Send Message object
                    }
                }).start();
                break;
            default:
                break;
        }
    }
}

3. Parsing asynchronous message processing mechanism

Android asynchronous message processing consists of four parts
1.Message
Message is a message passed between threads. It can carry a small amount of information internally, which is used to exchange data between different threads
2.Handler
As the name suggests, Handler means Handler. It is mainly used to send and process messages. Sending messages usually uses Handler's sendMessage() method, post() method, etc. after a series of tossing and turning, the sent messages will eventually be delivered to Handler's handleMessage() method
3.MessageQueue
MessageQueue means message queue, which is mainly used to store all messages sent through the Handler This part of the message will always exist in the message queue and wait to be processed There will only be one MessageQueue object per thread
4.Loper
Lord is the steward of MessageQueue in each thread. After calling Looper's loop() method, it will enter an infinite loop. Then whenever a message is found in MessageQueue, it will be taken out and passed to Handler's handleMessage() method There will only be one looper object per thread
Basic process
1. Create a Handler object in the main thread and override the handleMessage() method
2. When UI operation is required in the child thread, create a Message object and send the Message through the Handler
3. This message will be added to the queue of MessageQueue for processing, while Looper will always try to get the message to be processed from the MessageQueue, and finally distribute it back to the Handler's handleMessage() method
Because we passed looper. In the Handler constructor Getmainlooper (), so the code in the handleMessage() method will also run in the main thread

4. Use AsyncTask

AsyncTask is a tool provided by Android to switch the child thread to the main thread The implementation principle is also based on asynchronous message processing mechanism
Because AsyncTask is an abstract class, it needs subclasses to inherit During inheritance, we can specify three generic parameters for the AsyncTask class
Params parameters that need to be passed in when executing AsyncTask can be used in background tasks
When the Progress background task is executed, if the current Progress needs to be displayed on the interface, the generic specified here is used as the Progress unit
Result if the result needs to be returned after the task is executed, the generic type specified here is used as the return type

class DownloadTask extends AsyncTask<Void,Integer,Boolean>{
...
}

Parameter 1: Void indicates that it is not necessary to pass in parameters to the background task when executing AsyncTask;
Parameter 2: Integer indicates that Integer data is used as the progress display unit;
Parameter 3: Boolean indicates that Boolean data is used to feed back the execution result;
override method
1.onPreExecute()
This method is called before the background task starts execution, and is used for initialization operations on some interfaces (showing a progress bar dialog box).
2.doInBackground(Params...)
All the code in this method will run in the sub thread, and we should deal with all the time-consuming tasks here Once the task is completed, you can return the task execution result through the return statement. If the third generic parameter of AsyncTask specifies Void, you can not return the task execution result UI operations are not allowed in this method. If you need to update UI elements, you can call the publishProgress(Progress...) method to complete it
3.onProgressUpdate(Progress...)
Called publishProgress(Progress...) in background tasks. Method, onProgressUpdate(Progress...) Method will be called soon, and the parameters carried in the method are passed in the background task In this method, the UI can be operated, and the interface elements can be updated accordingly by using the values in the parameters
4.onPostExecute(Result)
When the background task is completed and returned through the return statement, this method will be called soon The returned data will be passed to this method as parameters. You can use the returned data to perform some UI operations, such as reminding the task execution results, closing the progress bar dialog box, etc

public class DownloadTask extends AsyncTask<Void,Integer,Boolean> {
    @Override
    protected void onPreExecute() {
        progressDialog.show();//Show progress dialog box
    }

    @Override
    protected Boolean doInBackground(Void... voids) {
        try {
            while (true){
                int downloadPercent=doDownload();//This is a fictional method
                publishProgress(downloadPercent);
                if(downloadPercent>=100){
                    break;
                }
            }
        } catch (Exception e) {
           return false;
        }
        return true;
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        //Update download progress here
        progressDialog.setMessage("Downloaded"+values[0]+"%");
    }

    @Override
    protected void onPostExecute(Boolean aBoolean) {
        progressDialog.dismiss();//Close the progress dialog box
        //Here you will be prompted to download the results
        if(result){
            Toast.makeText(context, "Download succeeded", Toast.LENGTH_SHORT).show();
        }else{
            Toast.makeText(content, "Download failed", Toast.LENGTH_SHORT).show();
        }
    }
}

Start the task new downloadtask() execute();

3. Basic usage of services

1. Define a service

public class MyService extends Service {
    public MyService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }
//Called when the service is created
    @Override
    public void onCreate() {
        super.onCreate();
    }
//Called when the service starts
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }
//Service destruction
    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}

Need to be in androidmanifest XML to register

<service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true"></service>

2. Start and stop services

The download function is provided in MyService, and then you can decide when to start downloading and check the download progress at any time The idea of realizing this function is to create a special Binder object to manage the download function

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

   <Button
       android:id="@+id/start_service"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:text="Start Service"/>
    <Button
        android:id="@+id/stop_Service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Stop Service"/>
    <Button
        android:id="@+id/bind_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Bind Service"/>
    <Button
        android:id="@+id/unbind_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Unbind Service"/>

</LinearLayout>

First, create a ServiceConnection anonymous class and override onServiceConnected() and onServiceDisconnected(). These two methods will be called when the activity and service are successfully bound and disconnected respectively In the onServiceConnected() method, we get the DownloadBinder instance through downward transformation. Through this instance, we can make activities and services more connected We can call any public method in the DownloadBinder according to the specific scenario in the activity, that is, the function of commanding the service to do whatever it does

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button startService=(Button) findViewById(R.id.start_service);
        Button stopService=(Button) findViewById(R.id.stop_Service);
        startService.setOnClickListener(this);
        stopService.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch(v.getId()){
            case R.id.start_service:
             Intent startIntent=new Intent();
             startService(startIntent);//Start service
                break;
            case R.id.stop_Service:
                Intent stopIntent=new Intent(this,MyService.class);
                stopService(stopIntent);//Out of Service
                break;
            default:
                break;
        }
    }
}

3. Communication of activities and services

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private MyService.DownloadBinder downloadBinder;
    private ServiceConnection connection=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder service) {
            downloadBinder=(MyService.DownloadBinder) service;
            downloadBinder.startDownload();
            downloadBinder.getProgress();
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button startService=(Button) findViewById(R.id.start_service);
        Button stopService=(Button) findViewById(R.id.stop_Service);
        startService.setOnClickListener(this);
        stopService.setOnClickListener(this);
        Button bindService=(Button) findViewById(R.id.bind_service);
        Button unbindService=(Button) findViewById(R.id.unbind_service);
        bindService.setOnClickListener(this);
        unbindService.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch(v.getId()){
            case R.id.start_service:
             Intent startIntent=new Intent();
             startService(startIntent);//Start service
                break;
            case R.id.stop_Service:
                Intent stopIntent=new Intent(this,MyService.class);
                stopService(stopIntent);//Out of Service
                break;
            case R.id.bind_service:
                Intent bindIntent=new Intent(this,MyService.class);
                bindService(bindIntent,connection,BIND_ABOVE_CLIENT);//Binding service
                break;
            case R.id.unbind_service:
                unbindService(connection);//Decommission
                break;
            default:
                break;
        }
    }
}

Bind MainActivity and MyService through the bindService() method
bindService(): parameter 1: constructed Intent object; Parameter 2: the created instance of ServiceCommand(); Parameter 3: is a flag bit, BIND_AUTO_CREATE indicates that an activity automatically creates a service after binding with the service
This enables the onCreate() method in MyService to be executed, and the onStartCommand() method will not be executed
Unbindservice() unbinds the activity from the service

4. Service life cycle

Once the startservice () method of Context is called anywhere in the project, the corresponding service will start and call back the onstartcommand () method If this service has not been created yet, the onCreate() method will execute before the onStratCommand() method After the service is started, it will remain running until the stopService() or stopSelf() method is called Note that although onStartCommand() executes every time the startService() method is called, there is only one instance of each service So no matter how many times you call the startservice () method, you only need to call the stopService() or stopSelf() method once, and the service will stop
You can also call bindService() of Context to get the persistent connection of a service, and then the onBind() method in the service will be called back Similarly, if this service has not been created, the onCread() method will execute before the onBind() method After that, the caller can get an instance of the IBidner object returned in the onBind() method, so that it can communicate with the service freely. As long as the link between the caller and the service is not broken, the service will remain running
When the startService() method is called, the onDestroy() method in the service will be executed, indicating that the service has been destroyed. Similarly, when the bindService() method is called, the unbindService() method will be called, and the onDestroy() method will also be executed. Both cases are well understood However, it should be noted that it is entirely possible for us to call the startService() method and the bindService() method on a server. In this case, how can we destroy the service? According to the mechanism of Android system, a service will always be running as long as it is started or bound. The service can only be destroyed if the above two conditions are not met at the same time Therefore, in this case, the onDestroy() method will execute only when stopService() and unbindService() methods are called at the same time

5. More skills of service

1. Use and front desk service

Service opportunities are running in the background. It has been doing hard work silently all the time However, the system priority of services is still relatively low. When the system runs out of memory, it may recycle the services running in the background If you want to keep the service running all the time without being recycled due to insufficient system memory, you can consider using the foreground service The biggest difference between the front desk service and the ordinary service is that it will always have a running icon displayed in the status bar of the system. After pulling down the status bar, you can see more detailed information, which is very similar to the effect of notification

public class MyService extends Service {
...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        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,"my_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);
    }
    ...
    }

startForeground(1,notification); Parameter 1: the id of the Notification. Parameter 2: the constructed Notification object. This method will turn MyService into a foreground service and display it in the system status bar

2. Use IntentService

The code in the service runs in the main thread by default. In order to avoid processing time-consuming logic in the service, ANR Therefore, we need to use Android multithreading programming to open a sub thread in each specific method of the service to deal with time-consuming logic

public class MyService extends Service{
...
 @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
     new Thread(new Runnable(){
		@Override
		public void run(){
		//Handling specific logic
		stopSelf();//This method is to stop the service automatically after execution
		}
	}).start();
        return super.onStartCommand(intent, flags, startId);
    }
}

public class MyIntentService extends IntentService {

    public MyIntentService(String name) {
        super("MyIntentService");//Call the parameterized constructor of the parent class
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        //Print the id of the current thread
        Log.d("MyIntentService","Thread id is"+Thread.currentThread().getId());
    }

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

Example:

<Button
        android:id="@+id/start_intent_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Start IntentService"/>
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
...

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ...
        Button startIntentService=(Button) findViewById(R.id.start_intent_service);
        startIntentService.setOnClickListener(this);
    }
     @Override
    public void onClick(View v) {
        switch(v.getId()){
          ...
            case R.id.start_intent_service:
                //Print the id of the main thread
                Log.d("MainActivity","Thread id is"+Thread.currentThread().getId());
                Intent intentService=new Intent(this,MyIntentService.class);
                startService(intentService);
                break;
            default:
                break;
        }
    }
}

AndroidManifest.xml to register

<service android:name=".MyIntentService"/>

Topics: Java Android