Asynchronous Android读书笔记六Long-running Tasks with Service

In this chapter, we'll extend our toolkit by directly extending Service to take control of the level of concurrency applied to our long-running background tasks—how many threads are used to perform the work—and use various methods to send work to Services and receive results from them. 

  • Building responsive apps with Service

    Service does not provide any background threads and will run all of its callbacks directly on the main thread, just like Activity

    While it is possible to configure the service to launch in a separate process, that process will still run the Service callbacks on its own main thread and will be subject to the same constraints. The only difference is that our foreground process will not be shut down along with the misbehaving Service process. 

     we can only invoke IntentService via an Intent and itwill queue all work and process it on a single thread. 

    When we need more control over the level of concurrency 


  • Controlling concurrency with Executors

    we'll use Executor to create our own alternative to IntentService

    We'll allow subclasses to define the level of concurrency by passing an Executor tothe constructor. 

     public abstract class ConcurrentIntentService extends Service {
    
         private final Executor executor;
         public ConcurrentIntentService(Executor executor) {
    
           this.executor = executor;
         }
    
           protected abstract void onHandleIntent(Intent intent);
       }
    
    
    
    
    
     @Override
       public int onStartCommand(
    
         final Intent intent, int flags, int startId) {
         executor.execute(new Runnable(){
    
           @Override
           public void run() {
    
             onHandleIntent(intent);
           }
    

    });

         return START_REDELIVER_INTENT;
       }
    

    we should be responsible and stop the Service when it has no more work to do.We'll need to keep track of how many tasks are running at any given time and whenthe last one completes, invoke stopSelf

      private final CompletionHandler handler =
         new CompletionHandler();
    
       private int counter;
    
       @Override
       public void onStart(final Intent intent, int startId) {
    

    counter++;

         executor.execute(new Runnable(){
             @Override
    
             public void run() {
    

    try {

                 onHandleIntent(intent);
    
               } finally {
                 handler.sendMessage(Message.obtain(handler));
    

    }

    }});

    }

       private class CompletionHandler extends Handler {
         @Override
    
    public void handleMessage(Message msg) {
        if (--counter == 0) {
    
          Log.i(TAG, "0 tasks, stopping");
    
          stopSelf();
        } else {
    
          Log.i(TAG, counter + " active tasks");
        }
    

    }} 


  • Returning results with Messenger

    The Android framework provides theMessenger class, which wraps upHandlerand makes it possible to send messages from anywhere—including from remoteServices in other processes.Messenger implements Parcelable, which means wecan pass Messengers around in Intents, which we can't do withHandler directly. 

     Intent intent = new Intent(this, PrimesIntentService.class);
       intent.putExtra(PrimesIntentService.PARAM, primeToFind);
       intent.putExtra(PrimesIntentService.MSNGR, messenger);
       startService(intent);
    

    Sendingmessages withMessenger is very similar to sending them withHandler—we obtainaMessage with the appropriate parameters and then send it withMessenger.send

      @Override
       protected void onHandleIntent(Intent intent) {
    
         int primeToFind = intent.getIntExtra(PARAM, -1);
    
         Messenger messenger = intent.getParcelableExtra(MSNGR);
    
         try {
           if (primeToFind < 2) {
    
             messenger.send(Message.obtain(null, INVALID));
    

    } else {

             messenger.send(Message.obtain(
               null, RESULT, primeToFind, 0,
               calculateNthPrime(primeToFind)));
    
           }
         } catch (RemoteException anExc) {
    
           Log.e(TAG, "Unable to send message", anExc);
         }
    


      private static class PrimesHandler extends Handler {
         private TextView view;
         public void handleMessage(Message message) {
    
           if (message.what == PrimesIntentService.RESULT) {
             if (view != null) { // if we're attached
    
               view.setText(message.obj.toString());
             }
    

    }}

         public void attach(TextView view) {
           this.view = view;
    
         }
         public void detach() {
    
           this.view = null;
         }
    


  • Direct communication with local Services

    An Activityor Fragmentthat wants to directly interact with this Service needsto bind to it using thebindService method and supply a ServiceConnection to handle the connect/disconnect callbacks. 

    The ServiceConnection implementation simply casts the IBinder it receives tothe concrete class defined by theService, obtains a reference to the Service, and records it in a field of theActivity.

       public class LocalPrimesActivity extends Activity {
         private LocalPrimesService service;
         private ServiceConnection connection;
    
         private class Connection implements ServiceConnection {
           @Override
           public void onServiceConnected(
    
             ComponentName name, IBinder binder) {
    
       LocalPrimesService.Access access =
               ((LocalPrimesService.Access)binder);
    
             service = access.getService();
           }
    
           @Override
           public void onServiceDisconnected(ComponentName name) {
    
             service = null;
           }
    

    }}

    We can make the Activity bind and unbind during its onResume and onPause lifecycle methods:

       @Override
       protected void onResume() {
    
         super.onResume();
         bindService(
    
           new Intent(this, LocalPrimesService.class),
           connection = new Connection(),
           Context.BIND_AUTO_CREATE);
    

    }

       @Override
       protected void onPause() {
    
         super.onPause();
    
         unbindService(connection);
       }
    

    This is great—once the binding is made, we have a direct reference to the Service instance and can call its methods!  







  • Broadcasting results with Intents

    if the Activity and Service are a part of the same process, broadcasting is best done using a local broadcast, as this is more efficient and secure. 

     private void broadcastResult(String result) {
         Intent intent = new Intent(PRIMES_BROADCAST);
         intent.putExtra(RESULT, result);
                   
                   
        LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
    


    We only want this BroadcastReceiver to listen for results while our Activity is at the top of the stack and visible in the application, so we'll register and unregisterit in the onStart and onStop lifecycle methods.  


  • Detecting unhandled broadcasts

    Ideally, we'll display the results in the app if it is still in the foreground and send a notification otherwise. 

    If we're broadcasting results, the Service will need to know if anyone handled the broadcast and if not, send a notification. 


  • Applications of Services 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值