Explain several modes of communication between Android Service and Activity in detail

  • 2021-08-31 09:21:09
  • OfStack

In Android, Activity is mainly responsible for the display of foreground pages, and Service is mainly responsible for the tasks that need long-term operation. Therefore, in our actual development, we often encounter the communication between Activity and Service. We generally start the background Service in Activity and start it through Intent. In Intent, we can pass data to Service. When we want to update UI threads after Service performs certain operations, what should we do? Next, I will introduce two ways to realize the communication between Service and Activity

Through the Binder object

When Activity calls bindService (Intent service, ServiceConnection conn, int flags), we can get an object instance of Service, and then we can access the methods in Service. Let's understand 1 through an example, and a small example of simulating download, so that we can understand the way of communication through Binder

First, we create a new project Communication, and then create a new Service class


package com.example.communication; 
import android.app.Service; 
import android.content.Intent; 
import android.os.Binder; 
import android.os.IBinder; 
public class MsgService extends Service { 
  /** 
   *  Maximum value of progress bar  
   */ 
  public static final int MAX_PROGRESS = 100; 
  /** 
   *  Progress value of progress bar  
   */ 
  private int progress = 0; 
 
  /** 
   *  Increase get() Methods, for Activity Call  
   * @return  Download progress  
   */ 
  public int getProgress() { 
    return progress; 
  } 
 
  /** 
   *  Simulated download task, updated every second 1 Times  
   */ 
  public void startDownLoad(){ 
    new Thread(new Runnable() { 
       
      @Override 
      public void run() { 
        while(progress < MAX_PROGRESS){ 
          progress += 5; 
          try { 
            Thread.sleep(1000); 
          } catch (InterruptedException e) { 
            e.printStackTrace(); 
          } 
           
        } 
      } 
    }).start(); 
  } 
  
  /** 
   *  Return 1 A Binder Object  
   */ 
  @Override 
  public IBinder onBind(Intent intent) { 
    return new MsgBinder(); 
  }    
  public class MsgBinder extends Binder{ 
    /** 
     *  Gets the current Service Example of  
     * @return 
     */ 
    public MsgService getService(){ 
      return MsgService.this; 
    } 
  }  
} 

The above code is relatively simple, Note is also more detailed, the most basic Service application, believe you can understand, we call startDownLoad () method to simulate the download task, and then update the progress of 1 time per second, but this is in the background, we can't see, so sometimes we need him to show the progress of the download in the foreground, so we use Activity next


Intent intent = new Intent("com.example.communication.MSG_ACTION");  
bindService(intent, conn, Context.BIND_AUTO_CREATE); 

Through the above code, we bind an Service in Activity, which requires an ServiceConnection object, which is an interface, and we use anonymous internal classes here


ServiceConnection conn = new ServiceConnection() { 
     
    @Override 
    public void onServiceDisconnected(ComponentName name) { 
       
    } 
     
    @Override 
    public void onServiceConnected(ComponentName name, IBinder service) { 
      // Return 1 A MsgService Object  
      msgService = ((MsgService.MsgBinder)service).getService(); 
       
    } 
  };

In the callback method of onServiceConnected (ComponentName name, IBinder service), an Binder object in MsgService is returned. We can get an MsgService object through getService () method, and then we can call some methods in MsgService. The code of Activity is as follows


package com.example.communication;  
import android.app.Activity; 
import android.content.ComponentName; 
import android.content.Context; 
import android.content.Intent; 
import android.content.ServiceConnection; 
import android.os.Bundle; 
import android.os.IBinder; 
import android.view.View; 
import android.view.View.OnClickListener; 
import android.widget.Button; 
import android.widget.ProgressBar;  
public class MainActivity extends Activity { 
  private MsgService msgService; 
  private int progress = 0; 
  private ProgressBar mProgressBar; 
   
  @Override 
  protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main);      
    // Binding Service 
    Intent intent = new Intent("com.example.communication.MSG_ACTION"); 
    bindService(intent, conn, Context.BIND_AUTO_CREATE);      
    mProgressBar = (ProgressBar) findViewById(R.id.progressBar1); 
    Button mButton = (Button) findViewById(R.id.button1); 
    mButton.setOnClickListener(new OnClickListener() {        
      @Override 
      public void onClick(View v) { 
        // Start downloading  
        msgService.startDownLoad(); 
        // Monitor progress  
        listenProgress(); 
      } 
    }); 
     
  } 
   
 
  /** 
   *  Listen for progress and get calls every second MsgService Adj. getProgress() Method to get progress, update the UI 
   */ 
  public void listenProgress(){ 
    new Thread(new Runnable() {        
      @Override 
      public void run() { 
        while(progress < MsgService.MAX_PROGRESS){ 
          progress = msgService.getProgress(); 
          mProgressBar.setProgress(progress); 
          try { 
            Thread.sleep(1000); 
          } catch (InterruptedException e) { 
            e.printStackTrace(); 
          } 
        }          
      } 
    }).start(); 
  }    
  ServiceConnection conn = new ServiceConnection() { 
    @Override 
    public void onServiceDisconnected(ComponentName name) { 
       
    } 
     
    @Override 
    public void onServiceConnected(ComponentName name, IBinder service) { 
      // Return 1 A MsgService Object  
      msgService = ((MsgService.MsgBinder)service).getService();        
    } 
  }; 
 
  @Override 
  protected void onDestroy() { 
    unbindService(conn); 
    super.onDestroy(); 
  }  
}

In fact, I still have some questions about the above code, that is, the method of monitoring progress changes. I updated UI directly in the thread, not to say that I can't update UI in other threads. It may be that ProgressBar is special. I didn't study its source code. Friends who know it can tell me 1, thank you!

The above code completes the operation of updating UI in Service, but have you found that we have to actively call getProgress () to get the progress value every time, and then call getProgress () method once every 1 second, will you feel very passive? Can there be a method to actively notify Activity when the progress changes in Service? The answer is yes. We can use the callback interface to realize the active notification of Service. If you don't understand the callback method, you can see https://www.ofstack.com/article/112637. htm

Create a new callback interface


public interface OnProgressListener { 
  void onProgress(int progress); 
} 

MsgService code has a little change, in order to facilitate everyone to understand, I still post all the code


package com.example.communication;  
import android.app.Service; 
import android.content.Intent; 
import android.os.Binder; 
import android.os.IBinder;  
public class MsgService extends Service { 
  /** 
   *  Maximum value of progress bar  
   */ 
  public static final int MAX_PROGRESS = 100; 
  /** 
   *  Progress value of progress bar  
   */ 
  private int progress = 0; 
   
  /** 
   *  Callback interface for updating progress  
   */ 
  private OnProgressListener onProgressListener; 
      
  /** 
   *  Registers the method of the callback interface for external invocation  
   * @param onProgressListener 
   */ 
  public void setOnProgressListener(OnProgressListener onProgressListener) { 
    this.onProgressListener = onProgressListener; 
  } 
 
  /** 
   *  Increase get() Methods, for Activity Call  
   * @return  Download progress  
   */ 
  public int getProgress() { 
    return progress; 
  } 
 
  /** 
   *  Simulated download task, updated every second 1 Times  
   */ 
  public void startDownLoad(){ 
    new Thread(new Runnable() {        
      @Override 
      public void run() { 
        while(progress < MAX_PROGRESS){ 
          progress += 5; 
           
          // Notify the caller of a change in progress  
          if(onProgressListener != null){ 
            onProgressListener.onProgress(progress); 
          } 
           
          try { 
            Thread.sleep(1000); 
          } catch (InterruptedException e) { 
            e.printStackTrace(); 
          }            
        } 
      } 
    }).start(); 
  } 
  
  /** 
   *  Return 1 A Binder Object  
   */ 
  @Override 
  public IBinder onBind(Intent intent) { 
    return new MsgBinder(); 
  }    
  public class MsgBinder extends Binder{ 
    /** 
     *  Gets the current Service Example of  
     * @return 
     */ 
    public MsgService getService(){ 
      return MsgService.this; 
    } 
  }  
}

The code in Activity is as follows


package com.example.communication;  
import android.app.Activity; 
import android.content.ComponentName; 
import android.content.Context; 
import android.content.Intent; 
import android.content.ServiceConnection; 
import android.os.Bundle; 
import android.os.IBinder; 
import android.view.View; 
import android.view.View.OnClickListener; 
import android.widget.Button; 
import android.widget.ProgressBar;  
public class MainActivity extends Activity { 
  private MsgService msgService; 
  private ProgressBar mProgressBar; 
 
  @Override 
  protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main);       
    // Binding Service 
    Intent intent = new Intent("com.example.communication.MSG_ACTION"); 
    bindService(intent, conn, Context.BIND_AUTO_CREATE);      
    mProgressBar = (ProgressBar) findViewById(R.id.progressBar1); 
    Button mButton = (Button) findViewById(R.id.button1); 
    mButton.setOnClickListener(new OnClickListener() { 
       
      @Override 
      public void onClick(View v) { 
        // Start downloading  
        msgService.startDownLoad(); 
      } 
    }); 
     
  } 
   
 
  ServiceConnection conn = new ServiceConnection() { 
    @Override 
    public void onServiceDisconnected(ComponentName name) { 
       
    } 
     
    @Override 
    public void onServiceConnected(ComponentName name, IBinder service) { 
      // Return 1 A MsgService Object  
      msgService = ((MsgService.MsgBinder)service).getService();        
      // Register the callback interface to receive changes in download progress  
      msgService.setOnProgressListener(new OnProgressListener() {          
        @Override 
        public void onProgress(int progress) { 
          mProgressBar.setProgress(progress);            
        } 
      });        
    } 
  }; 
 
  @Override 
  protected void onDestroy() { 
    unbindService(conn); 
    super.onDestroy(); 
  }  
} 

Is it more convenient to use the callback interface? When the progress changes, Service actively notifies Activity, and Activity can update UI operation

In the form of broadcast (broadcast)

When our schedule changes, we send a broadcast, and then register the broadcast receiver of Activity, and update ProgressBar after receiving the broadcast. The code is as follows


package com.example.communication; 
import android.app.Activity; 
import android.content.BroadcastReceiver; 
import android.content.Context; 
import android.content.Intent; 
import android.content.IntentFilter; 
import android.os.Bundle; 
import android.view.View; 
import android.view.View.OnClickListener; 
import android.widget.Button; 
import android.widget.ProgressBar;  
public class MainActivity extends Activity { 
  private ProgressBar mProgressBar; 
  private Intent mIntent; 
  private MsgReceiver msgReceiver; 
  @Override 
  protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 
     
    // Dynamic Register Broadcast Receiver  
    msgReceiver = new MsgReceiver(); 
    IntentFilter intentFilter = new IntentFilter(); 
    intentFilter.addAction("com.example.communication.RECEIVER"); 
    registerReceiver(msgReceiver, intentFilter); 
    mProgressBar = (ProgressBar) findViewById(R.id.progressBar1); 
    Button mButton = (Button) findViewById(R.id.button1); 
    mButton.setOnClickListener(new OnClickListener() { 
      @Override 
      public void onClick(View v) { 
        // Startup service  
        mIntent = new Intent("com.example.communication.MSG_ACTION"); 
        startService(mIntent); 
      } 
    }); 
     
  } 

  @Override 
  protected void onDestroy() { 
    // Stop service  
    stopService(mIntent); 
    // Cancel a broadcast  
    unregisterReceiver(msgReceiver); 
    super.onDestroy(); 
  } 
 
  /** 
   *  Broadcast receiver  
   * @author len 
   * 
   */ 
  public class MsgReceiver extends BroadcastReceiver{  
    @Override 
    public void onReceive(Context context, Intent intent) { 
      // Get the progress and update UI 
      int progress = intent.getIntExtra("progress", 0); 
      mProgressBar.setProgress(progress); 
    } 
   }  
} 

package com.example.communication;  
import android.app.Service; 
import android.content.Intent; 
import android.os.IBinder;  
public class MsgService extends Service { 
  /** 
   *  Maximum value of progress bar  
   */ 
  public static final int MAX_PROGRESS = 100; 
  /** 
   *  Progress value of progress bar  
   */ 
  private int progress = 0;    
  private Intent intent = new Intent("com.example.communication.RECEIVER"); 
   
 
  /** 
   *  Simulated download task, updated every second 1 Times  
   */ 
  public void startDownLoad(){ 
    new Thread(new Runnable() { 
      @Override 
      public void run() { 
        while(progress < MAX_PROGRESS){ 
          progress += 5;            
          // Send Action For com.example.communication.RECEIVER The broadcast of  
          intent.putExtra("progress", progress); 
          sendBroadcast(intent);            
          try { 
            Thread.sleep(1000); 
          } catch (InterruptedException e) { 
            e.printStackTrace(); 
          }            
        } 
      } 
    }).start(); 
  } 

  @Override 
  public int onStartCommand(Intent intent, int flags, int startId) { 
    startDownLoad(); 
    return super.onStartCommand(intent, flags, startId); 
  } 
 
  @Override 
  public IBinder onBind(Intent intent) { 
    return null; 
  }  
}

Summary:

Activity calls bindService (Intent service, ServiceConnection conn, int flags) methods to get a reference to Service object, so that Activity can directly call the methods in Service. If we want to actively notify Activity, we can use callback methods

Service sends messages to Activity, using broadcast, although Activity registers the appropriate receiver. For example, if Service sends the same message to multiple Activity, this method is better


Related articles: