Android Service binding process Complete analysis _android

Source: Internet
Author: User
Tags static class

Usually we use service to communicate with it, when we want to communicate with the service, then the service to be in a binding state. The client can then get a binder to communicate with the server, a process that is natural.

Do you really know the binding process of the service? Why can it be binder and service communication?
In the same way, look at a picture to learn roughly, the gray background box is the same class method, as follows:

We know that the Bindservice method of invoking the context can bind a service, while Contextimpl is the implementation class for the context. Then from the source point of view to analyze the service binding process.

Of course, it starts with the Bindservice method of Contextimpl, as follows:

@Override Public
Boolean bindservice (Intent service, serviceconnection conn,
  int flags) {
 Warnifcallingfromsystemprocess ();
 return Bindservicecommon (SERVICE, Conn, flags, Mmainthread.gethandler (),
   process.myuserhandle ());

In the Bindservice method, the Bindservicecommon method is also transferred, and the Intent,serviceconnection object is passed in.

Then look at the implementation of the Bindservicecommon method.

Private Boolean Bindservicecommon (Intent service, serviceconnection conn, int flags, Handler Handler, Userhandle user
  ) {iserviceconnection sd;
  if (conn = = null) {throw new IllegalArgumentException ("Connection is null");
  } if (Mpackageinfo!= null) {SD = Mpackageinfo.getservicedispatcher (conn, Getoutercontext (), Handler, flags);
  else {throw new runtimeexception ("not supported in system context");
  } validateserviceintent (service);
    try {IBinder token = Getactivitytoken (); if (token = = null && (flags&bind_auto_create) = = 0 && mpackageinfo!= null && mpackage Info.getapplicationinfo (). targetsdkversion < Android.os.Build.VERSION_CODES.
    Ice_cream_sandwich) {flags |= bind_waive_priority;
    } service.preparetoleaveprocess (this); int res = Activitymanagernative.getdefault (). Bindservice (Mmainthread.getapplicationthread (), GetActivityToken (), SE Rvice, Service.resolvetypeifneedEd (Getcontentresolver ()), SD, Flags, Getoppackagename (), User.getidentifier ());
    if (Res < 0) {throw new SecurityException ("Not allowed to bind to service" + service);
  return res!= 0;
  catch (RemoteException e) {throw e.rethrowfromsystemserver (); }
}

In the preceding code, the Getservicedispatcher method of the Mpackageinfo (Loadedapk object) is invoked. The name of the Getservicedispatcher method can be seen as obtaining a "service distributor". In fact, it is based on this "service distributor" to obtain a binder object.

Now you see the implementation of the Getservicedispatcher method.

Public final iserviceconnection Getservicedispatcher (serviceconnection C, Context
    , Handler Handler, int Flags) {
  synchronized (mservices) {
    loadedapk.servicedispatcher sd = null;
    Arraymap<serviceconnection, loadedapk.servicedispatcher> map = mservices.get (context);
    if (map!= null) {
      SD = Map.get (c);
    }
    if (SD = = null) {
      SD = new Servicedispatcher (c, context, Handler, flags);
      if (map = = null) {
        map = new arraymap<serviceconnection, loadedapk.servicedispatcher> ();
        Mservices.put (context, map);
      Map.put (c, SD);
    } else {
      sd.validate (context, handler);
    }
    return sd.getiserviceconnection ();
  }

From the realization of Getservicedispatcher method, we can know that serviceconnection and Servicedispatcher constitute the mapping relationship. When the storage set is not empty, the corresponding Servicedispatcher object is taken out according to the key that is passed in, namely serviceconnection.
When you remove the Servicedispatcher object, the last line of code is critical,

return Sd.getiserviceconnection ();

The Getiserviceconnection method of the Servicedispatcher object was invoked. This method must be to get a iserviceconnection.

Iserviceconnection getiserviceconnection () {return
  miserviceconnection;
}

So what's miserviceconnection? Now we can look at the Servicedispatcher class. Servicedispatcher is the inner class of loadedapk, which encapsulates innerconnection and serviceconnection. As follows:

Static Final class Servicedispatcher {private final servicedispatcher.innerconnection miserviceconnection;
  Private final serviceconnection mconnection;
  Private final context Mcontext;
  Private final Handler Mactivitythread;
  Private final serviceconnectionleaked mlocation;

  private final int mflags;

  Private RuntimeException munbindlocation;

  Private Boolean Mforgotten;
    private static class ConnectionInfo {IBinder binder;
  Ibinder.deathrecipient Deathmonitor; private static class Innerconnection extends Iserviceconnection.stub {final Weakreference<loadedapk.servicedi

    Spatcher> Mdispatcher; Innerconnection (Loadedapk.servicedispatcher sd) {mdispatcher = new weakreference<loadedapk.servicedispatcher>
    (SD); public void connected (componentname name, IBinder service) throws RemoteException {Loadedapk.servicedispatch
      Er sd = mdispatcher.get ();
      if (SD!= null) {sd.connected (name, service); }
    }
  Private final Arraymap<componentname, servicedispatcher.connectioninfo> mactiveconnections = new ArrayMa

  P<componentname, servicedispatcher.connectioninfo> (); Servicedispatcher (Serviceconnection conn, context, Handler activitythread, int flags) {Miserviceconnect
    Ion = new Innerconnection (this);
    Mconnection = conn;
    Mcontext = context;
    Mactivitythread = Activitythread;
    Mlocation = new serviceconnectionleaked (null);
    Mlocation.fillinstacktrace ();
  Mflags = flags;

 }//code omitted}

First see the Servicedispatcher construction method, a servicedispatcher associated with a Innerconnection object. And what about Innerconnection? , it is a binder, there is a very important connected method. As for why to use binder, because communication with service may be cross process.

Well, here's a summary: Invoking the Bindservice method binding service will go to the Bindservicecommon method.
In the Bindservicecommon method, the loadedapk Getservicedispatcher method is invoked and the serviceconnection is passed in, According to this serviceconnection, the Servicedispatcher object that is mapped is removed, Finally, the Getiserviceconnection method that calls the Servicedispatcher object gets its associated Innerconnection object and returns. A simple understanding is to use serviceconnection for innerconnection.

Now back to the Bindservicecommon method, you can see that the process of binding the service goes to the Bindservice method of Activitymanagernative.getdefault (), In fact, from the thrown exception type RemoteException can also know that communication with the service may be a cross process, this is very good to understand.

and Activitymanagernative.getdefault () is Activitymanagerservice, then continue to follow Activitymanagerservice Bindservice method can be, as follows:

public int Bindservice (iapplicationthread caller, IBinder token, Intent service,
    String Resolvedtype, iserviceconnection connection, int flags, String callingpackage,
    int userId) throws Transactiontoolargeexception C3/>enforcenotisolatedcaller ("Bindservice");

  Refuse possible leaked file descriptors
  if (service!= null && service.hasfiledescriptors () = = True) {
    throw new IllegalArgumentException ("File descriptors passed in Intent");
  }

  if (callingpackage = = null) {
    throw new IllegalArgumentException ("Callingpackage cannot be null");
  }

  Synchronized (this) {return
    mservices.bindservicelocked (caller, token, service,
        Resolvedtype, connection, Flags, Callingpackage, userId);
  }


In the above code, the process of binding the service to the Activeservices bindservicelocked method, then follow the Activeservices bindservicelocked method to see. As follows:

 int bindservicelocked (iapplicationthread caller, IBinder token, Intent service, Stri Ng Resolvedtype, final iserviceconnection connection, int flags, String callingpackage, final int userId) throws Trans actiontoolargeexception {//code omitted Connectionrecord c = new Connectionrecord (b, activity, connection, F

     Lags, Clientlabel, clientintent);
     IBinder binder = Connection.asbinder ();
     Arraylist<connectionrecord> clist = S.connections.get (binder);
       if (clist = = null) {clist = new arraylist<connectionrecord> ();
     S.connections.put (binder, CList);

     } clist.add (c);
      The code omits if ((flags&context.bind_auto_create)!= 0) {s.lastactivity = Systemclock.uptimemillis (); if (bringupservicelocked (S, Service.getflags (), CALLERFG, False, permissionsreviewrequired)!= null) {R
      Eturn 0;
}//code omitted return 1; }

The connection object is encapsulated in Connectionrecord, where connection is the Innerconnection object mentioned above. This step is very important.

And then call the Bringupservicelocked method, then explore the Bringupservicelocked method,

Private String bringupservicelocked (Servicerecord r, int intentflags, Boolean execinfg,
    boolean whilerestarting, Boolean permissionsreviewrequired)
    throws transactiontoolargeexception {

    //code ellipsis

    if (app!= null & & App.thread!= null) {
      try {
        app.addpackage (r.appinfo.packagename, R.appinfo.versioncode, Mam.mprocessstats);
        Realstartservicelocked (r, app, EXECINFG);
        return null;
      } catch (Transactiontoolargeexception e) {
        throw e;
      } catch (RemoteException e) {
        SLOG.W (TAG, "Exception When starting service "+ R.shortname, E);
      }

      If a Dead object exception is thrown-fall through to
      //Restart the application.
    }

    Code omits return

  null;
}

You can see that the Realstartservicelocked method is invoked to really start the service.

Then follow up the realstartservicelocked method to explore, as follows:

 private final void realstartservicelocked (Servicerecord R, Processrecord app, Boolea N execinfg) throws remoteexception {//code omitted App.thread.scheduleCreateService (R, R.serviceinfo, Mam.compatib
    Ilityinfoforpackagelocked (R.serviceinfo.applicationinfo), app.repprocstate);
    R.postnotification ();

   created = true;

  Code omitted requestservicebindingslocked (r, EXECINFG);

  updateserviceclientactivitieslocked (app, NULL, TRUE); If the service is in the started state, and there are no//pending arguments, then fake
  () 'll//be called. if (r.startrequested && r.callstart && r.pendingstarts.size () = = 0) {r.pendingstarts.add (new ServiceR Ecord.
  StartItem (R, False, R.makenextstartid (), NULL, NULL));

} sendserviceargslocked (R, Execinfg, True); Code omitted} 

This will call the App.thread Schedulecreateservice method to create a service and then callback the service lifecycle method, but bind the service?
In the above code, find a Requestservicebindingslocked method, from the name of the request to bind the service meaning, then it is true.

Private final void requestservicebindingslocked (Servicerecord R, Boolean execinfg)
    throws transactiontoolargeexception {for
  (int i=r.bindings.size ()-1; i>=0; i--) {
    Intentbindrecord IBR = R.bindings.valueat (i);
    if (!requestservicebindinglocked (R, IBR, EXECINFG, false)) {break
      ;
    }
  }
}

Yi, I again hold CTRL + the left mouse button, point in Requestservicebindinglocked method. As follows:

Private Final Boolean requestservicebindinglocked (Servicerecord R, Intentbindrecord I,
    boolean execinfg, Boolean rebind) throws Transactiontoolargeexception {
  if (R.app = null | | r.app.thread = NULL) {
    //If service is not CU Rrently running, can ' t yet bind.
    return false;
  }
  if ((!i.requested | | rebind) && i.apps.size () > 0) {
    try {
      bumpserviceexecutinglocked (R, Execinfg, "Bi nd ");
      R.app.forceprocessstateupto (activitymanager.process_state_service);
      R.app.thread.schedulebindservice (R, I.intent.getintent (), rebind,
          r.app.repprocstate);
      if (!rebind) {
        i.requested = true;
      }
      I.hasbound = true;
      I.dorebind = false;
    }

  The code omits return

  true;
}

R.app.thread called the Schedulebindservice method to bind the service, and R.app.thread is Applicationthread, now focus on the Applicationthread, The Schedulebindservice method is as follows:

Public final void Schedulebindservice (IBinder token, Intent Intent,
    boolean rebind, int processstate) {
  Updateprocessstate (Processstate, false);
  Bindservicedata s = new Bindservicedata ();
  S.token = token;
  S.intent = Intent;
  S.rebind = rebind;

  if (debug_service)
    slog.v (TAG, "Schedulebindservice token=" + token + "intent=" + Intent + "uid="
        + BINDER.GETCA Llinguid () + "pid=" + binder.getcallingpid ());
  SendMessage (H.bind_service, s);
}

Encapsulates information about the service to be bound and sends a message to the main thread,

public void Handlemessage (message msg) {
  if (debug_messages) slog.v (TAG, ">>> handling:" + codetostring (msg . what));
  Switch (msg.what) {

  //code omitted case

    Bind_service:
      trace.tracebegin (Trace.trace_tag_activity_manager, " Servicebind ");
      Handlebindservice ((bindservicedata) msg.obj);
      Trace.traceend (Trace.trace_tag_activity_manager);
      break;

  Code omitted

  }
}

The Handlebindservice method is called, and the binding is complete.

private void Handlebindservice (Bindservicedata data) {Service s = mservices.get (Data.token);
  if (debug_service) slog.v (TAG, "Handlebindservice s=" + S + "rebind=" + data.rebind);
      if (s!= null) {try {Data.intent.setExtrasClassLoader (S.getclassloader ());
      Data.intent.prepareToEnterProcess ();
          try {if (!data.rebind) {IBinder binder = S.onbind (data.intent);
        Activitymanagernative.getdefault (). PublishService (Data.token, data.intent, binder);
          else {s.onrebind (data.intent);
        Activitymanagernative.getdefault (). servicedoneexecuting (Data.token, Service_done_executing_anon, 0, 0);
      } ensurejitenabled ();
      catch (RemoteException ex) {throw ex.rethrowfromsystemserver ();
            } catch (Exception e) {if (!minstrumentation.onexception (S, e)) {throw new RuntimeException ( "Unable to bind to service" + S + "with" + Data.intent + ":" + e.tostring (), E);
 }
    }
  }
}

The service is obtained according to the token, and then the call callback Onbind method. It's over?

But the Onbind method returns a binder is used for what?
Let's take a look at what Activitymanagernative.getdefault () called the PublishService method, and then back to Activitymanagerservice.

public void PublishService (IBinder token, Intent Intent, IBinder service) {
  //refuse-possible leaked file descriptors
  if (intent!= null && intent.hasfiledescriptors () = = True) {
    throw new IllegalArgumentException ("File de Scriptors passed in Intent ");
  }

  Synchronized (this) {
    if (!) ( Token instanceof Servicerecord)) {
      throw new IllegalArgumentException ("Invalid service token");
    }
    Mservices.publishservicelocked ((Servicerecord) token, intent, service);
  }


will be handed over to activeservices processing, go to the Publishservicelocked method, that see Activeservices publishservicelocked method,

void publishservicelocked (Servicerecord R, Intent Intent, IBinder service) {final long origid = Binder.clearcallingide
  Ntity ();
    try {if (debug_service) slog.v (Tag_service, "PUBLISHING" + R + "" + Intent + ":" + SERVICE);
      if (r!= null) {Intent.filtercomparison filter = new Intent.filtercomparison (Intent);
      Intentbindrecord B = r.bindings.get (filter);
        if (b!= null &&!b.received) {b.binder = service;
        B.requested = true;
        B.received = true; for (int conni=r.connections.size ()-1; conni>=0; conni--) {arraylist<connectionrecord> clist = R.conne
          Ctions.valueat (Conni);
            for (int i=0; i<clist.size (); i++) {Connectionrecord c = clist.get (i); if (!filter.equals (c.binding.intent.intent)) {if (Debug_service) slog.v (Tag_service, "No
              T publishing to: "+ C); if (debug_service) slog.v (TAG_service, "Bound Intent:" + c.binding.intent.intent);
              if (debug_service) slog.v (Tag_service, "published intent:" + intent);
            Continue
            } if (Debug_service) slog.v (Tag_service, "Publishing to:" + C);
            try {c.conn.connected (r.name, service);

 }//code omitted}

What is C.conn? It's a Innerconnection object, yes, that's the binder, the code is posted, and in the Activeservices bindservicelocked method, The Innerconnection object is encapsulated in the Connectionrecord. So now that it calls the connected method, it's well understood.

The connected method of Innerconnection is as follows:

public void connected (componentname name, IBinder service) throws RemoteException {
  Loadedapk.servicedispatcher sd = Mdispatcher.get ();
  if (SD!= null) {
    sd.connected (name, service);
  }
}

The Servicedispatcher connected method is invoked, as follows

public void connected (componentname name, IBinder service) {
  if (mactivitythread!= null) {
    mactivitythread.post (New Runconnection (name, service, 0);
  } else {
    doconnected (name, service);
  }
}

As a result, Activitythread delivers a message to the main thread, which resolves the process of communicating across processes.
Then you should have guessed it. Runconnection must have a onserviceconnected method in the main thread callback,

Private Final class Runconnection implements Runnable {
  runconnection (componentname name, ibinder service, int comman d) {
    mname = name;
    Mservice = Service;
    Mcommand = command;

  public void Run () {
    if (Mcommand = 0) {
      doconnected (mname, Mservice);
    } else if (Mcommand = 1) {
      dodeat H (Mname, Mservice);
    }

  Final componentname Mname;
  Final IBinder Mservice;
  final int mcommand;
}

Hey, don't you show up? Continue to follow the Doconnected method,

public void doconnected (componentname name, IBinder service) {servicedispatcher.connectioninfo old;

  Servicedispatcher.connectioninfo info; Synchronized (this) {if (Mforgotten) {//We unbound before receiving the connection/no ignore
      Ction received.
    Return
    Old = Mactiveconnections.get (name); if (old!= null && old.binder = = Service) {//Huh, already have this one.
      Oh well!
    Return
      } if (service!= null) {//A new service is being connected ... set it all up.
      info = new ConnectionInfo ();
      Info.binder = Service;
      Info.deathmonitor = new Deathmonitor (name, service);
        try {service.linktodeath (info.deathmonitor, 0);
      Mactiveconnections.put (name, info); The catch (RemoteException e) {//This service is dead before we got it ... just//don ' t do anything with
        It.
        Mactiveconnections.remove (name);
      Return
   }} else {   The named service is being disconnected ... clean up.
    Mactiveconnections.remove (name);
    } if (old!= null) {Old.binder.unlinkToDeath (old.deathmonitor, 0);
  }//If There was a old service and it is not disconnected.
  if (old!= null) {mconnection.onservicedisconnected (name);
  }//If There is a new service, it's now connected.
  if (service!= null) {mconnection.onserviceconnected (name, service);

 }
}

In the last if judgment, finally found the Onserviceconnected Method!

To sum up, when the service callback Onbind method, actually not finished, will go to Activitymanagerservice, and then in the Activeservices publishservicelocked method, Remove the Innerconnection object from the Connectionrecord. With the Innerconnection object, it recalls its connected. In the innerconnection connected method, the Servicedispatcher connected method is called, and the Servicedispatcher method throws a message to the main thread. Switches to the main thread and then recalls the Onserviceconnected method of the Serviceconnected object passed in by the client in the main thread.

At this point, the service's binding process has been analyzed.

The above is the entire content of this article, I hope to help you learn, but also hope that we support the cloud habitat community.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.