Backstage silent workers
01 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 operation
the operation of the service does not depend on any other user interface. Even if the program is switched to the background or the user opens another application, the service can still run normally
02 there will be an error when the child thread updates the UI
Android UI is also thread unsafe If you want to update UI elements, you must do it in the main thread, otherwise there will be exceptions
button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new Thread(new Runnable() { @Override public void run() { textView.setText("hello"); } }).start(); } }); //The thread was started to modify the UI of the main thread, resulting in an error
03 asynchronous message processing case
private Handler handler = new Handler(Looper.myLooper()){ @Override public void handleMessage(@NonNull Message msg) { switch (msg.what){ case UPDATE_TEXT: //Perform UI operations textView.setText("hello"); break; default: break; } } };
new Thread(new Runnable() { @Override public void run() { //Sub thread update UI Message message= new Message(); message.what = UPDATE_TEXT; handler.sendMessage(message); } }).start();
04 asynchronous message processing mechanism
1. Message
Message is a Message passed between threads Can carry a small amount of information The what field, arg1 and arg2 fields of Message can carry integer data The obj field carries an obj object
2. Handler
processor, used to send and process messages The sendMessage() method of Handler is generally used for sending A series of processed messages will arrive in the Handler's handleMessage() method
3. MessageQueue
message queue It is used to store all messages sent through the Handler Messages will always exist in the message queue waiting to be processed There will only be one MessageQueue object per thread
4. Looper
the steward of MessageQueue in each thread will enter an infinite loop after calling Looper's loop() method Whenever a message is found in the MessageQueue, it will be taken out and passed to the Handler's hanleMessage() method There is also only one looper object per thread
technological process:
- Create a Handler object in the main thread and override the handleMessage() method
- If the child thread wants to perform UI operation, it creates a Message object and sends the Message through the Handler object of the main thread
- The message is added to the MessageQueue queue in the Handler
- Looper always fetches messages from MessageQueue and returns them to Handler's handleMessage() method
05 using AsyncTask
Abstract class, which needs to be inherited when used There are three generic parameters that can be specified
class DownloadTask extends AsyncTask<Void, Integer, Boolean>{ }
1. Params
parameters that need to be passed in when executing AsyncTask can be used in background tasks
2. Progress
during background task execution, if the current progress needs to be displayed on the interface, the generic specified here is used as the progress unit
3. Result
after the task is executed, if the result needs to be returned, use the generic specified here as the return value type
Here you can specify that the first generic parameter is void It means that no parameters need to be passed to the background task during execution The second generic parameter is specified as Integer, indicating that Integer data is used as the progress display unit The third parameter is Boolean, which means that the execution result is fed back with Boolean data
The methods that often need to be rewritten are as follows:
1. onPreExecute()
This method will be called before the background task is executed, for initialization operations on some interfaces, such as display.
A progress bar dialog box, etc.
02. 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. Note that UI operations are not allowed in this method. If you need to update UI elements, such as feedback on the execution progress of the current task, you can call the onpublishProgress (Progress...) method to complete it.
03. onProgressUpdate(Progress...)
When the publishProgress(Progress...) method is invoked in the background task, the onProgressUpdate (Progress...) method will be called quickly, 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.
04. onPostExecute(Result)
this method will be called soon after the background task is executed and returned through the return statement. Number of returned
Data is passed to this method as a parameter. You can use the returned data to perform some UI operations, such as reminding task execution
Line, and close the progress bar dialog box.
The following codes are incomplete
class DownloadTask extends AsyncTask<Void, Integer, Boolean> { @Override protected void onPreExecute() { //1 display progress dialog box progressDialog.show(); } @Override protected Boolean doInBackground(Void... voids) { try{ while (true){ //2 time consuming operation int downloadPercent = doDownload();//This is a fictional download method; publishProgress(downloadPercent); //Because UI operation cannot be performed, download progress is transferred to onProgressUpdate() method if (downloadPercent >=100){ break; } } }catch (Exception e){ return false; } return true; } @Override protected void onProgressUpdate(Integer... values) { //4. Perform UI operation to display the download progress progressDialog.setMessage("DownLoaded" + values[0] + "%"); } @Override protected void onPostExecute(Boolean aBoolean) { //5 display download results progressDialog.dismiss();// close dialog boxes if (aBoolean){ Toast.makeText(context,"Download succeed",Toast.LENGTH_SHORT).show(); }else{ Toast.makeText(context,"Download failed",Toast.LENGTH_SHORT).show(); } } }
To perform this task, simply
//The code runs in child threads new DownloadTask().execute();
here is a fictional method called download () to calculate the current download progress and return it. We assume that this method already exists. After getting the current download progress, we should consider how to display it on the interface. Because the doInBackground() method runs in the sub thread, UI operation cannot be performed here, so we can call the publishProgress() method and pass in the current download progress, so the onProgressUpdate() method will be called soon, and UI operation can be performed here. When the download is completed, the doInBackground() method will return a boolean variable, so the onPostExecute() method will be called soon, and this method is also run in the main thread. Then, we will pop up the corresponding Toast prompt according to the download results, so as to complete the whole DownloadTask task.
in short, the trick of using AsyncTask is to execute specific time-consuming tasks in the doInBackground() method, perform UI operations in the onProgressUpdate() method, and perform the finishing work of some tasks in the onPostExecute() method.
06 service usage
1 define a service
new->service->service . Check the exported and enabled attributes
Override onCreate(), onStartCommand(), and onDestroy()
among them, onCreate() method will be called when the Service is created, onStartCommand() method will be called every time the Service is started, and onDestroy() method will be called when the Service is destroyed.
2 start and restart services
Code case
Main activity
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button startService = (Button) findViewById(R.id.startService); startService.setOnClickListener(this); Button stopService = (Button) findViewById(R.id.stopService); stopService.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()){ case R.id.startService: //01 open service Intent startIntent = new Intent(this,MyService.class); startService(startIntent); break; case R.id.stopService: //02 shut down service Intent stopIntent = new Intent(this,MyService.class); stopService(stopIntent); break; default: break; } }
Custom service
public class MyService extends Service { private static final String TAG = "Service"; public MyService() { } @Override public void onCreate() { super.onCreate(); Log.d(TAG, "onCreate: executed"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.d(TAG, "onStartCommand: executed"); return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { super.onDestroy(); Log.d(TAG, "onDestroy: executed"); } @Override public IBinder onBind(Intent intent) { // TODO: Return the communication channel to the service. throw new UnsupportedOperationException("Not yet implemented"); } }
Result: the oncreate method and onstartcommand method execute
3 communicate with activities and services
Principle: callback
public class MainActivity extends AppCompatActivity implements View.OnClickListener{ private MyService.DownloadBinder downloadBinder; //01 to bind services private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { //Calling methods in a service downloadBinder = (MyService.DownloadBinder) service; downloadBinder.startDownload(); downloadBinder.getProgress(); } @Override public void onServiceDisconnected(ComponentName name) { } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button bindService = (Button) findViewById(R.id.bindService); bindService.setOnClickListener(this); Button unbindService = (Button) findViewById(R.id.unbindService); unbindService.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()){ case R.id.bindService: //02 generate service and bind Intent bindIntent = new Intent(this,MyService.class); bindService(bindIntent,connection,BIND_AUTO_CREATE); break; case R.id.unbindService: //03 unbinding service Intent unbindIntent = new Intent(this,MyService.class); unbindService(connection); break; default: break; } } }
When binding a service, only the oncreate method of the service will be called, not the onstartcommand method
07 service life cycle
onCreate(), onStartCommand(), onBind() and onDestroy() we used earlier
Methods are methods that may be called back within the 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 the Service has not been created before, onCreate() method will be executed before onStartCommand() method. After the Service is started, it will remain running until the stopService() or stopSelf() method is called or recycled by the system. Note that although onStartCommand() is executed once every time the startService() method is called, in fact, there is only one instance of each Service. So no matter how many times you call the startservice () method, just call the stopService() or stopSelf() method once, and the Service will stop.
Front desk service
Add the following code in onCreate of service
//Create notification management NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); //Create channel if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){ NotificationChannel channel = new NotificationChannel( "vashon","Test notification",NotificationManager.IMPORTANCE_HIGH); manager.createNotificationChannel(channel); } //Details of my message Intent intent = new Intent(this,MainActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity( this,0,intent,0); //Create notification Notification notification = new NotificationCompat.Builder(this,"vashon") .setContentTitle("My notice") .setContentText("This is the content of the notice") .setSmallIcon(R.drawable.ic_launcher_background)//Cannot be RGB .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher_background))//Large icon .setColor(Color.parseColor("#ff0000 ") / / small icon color .setAutoCancel(true) //Click to cancel .setContentIntent(pendingIntent) //Notification details .build(); startForeground(1,notification);
At manifest Add permissions to XML
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
You can see the front desk service in the notice bar Can't remove
08 using intentservice (processing tasks in child threads)
because the service runs in the main thread by default, sometimes the service needs to process time-consuming logic, which is prone to ANR Intentservice solves this problem
Create IntentService class
public class MyIntentService extends IntentService { private static final String TAG = "MyIntentService"; public MyIntentService() { super("MyIntentService"); } @Override protected void onHandleIntent(Intent intent) { //Print current process id Log.d(TAG, "onHandleIntent: Thread id is" + Thread.currentThread().getId()); } @Override public void onDestroy() { super.onDestroy(); Log.d(TAG, "onDestroy: executed"); } }
Add code to main activity
case R.id.startIntentService: //Print current process id Log.d(TAG, "Thread id is " + Thread.currentThread().getId()); Intent intentService = new Intent(this,MyIntentService.class); startService(intentService); break;
The results are as follows:
D/MainActivity: Thread id is 2 D/MyIntentService: onHandleIntent: Thread id is2508 D/MyIntentService: onDestroy: executed