记录:Service的使用

来源:互联网 发布:php5权威编程 pdf 编辑:程序博客网 时间:2024/05/18 00:07

摘要:记录一下 service的基本使用,基本是记录的 log 打印信息。其原理和深入的解析课对下面的参考链接进行相关的查阅

Service

  service是一个可以在后台执行长时间运行操作而不提供用户界面的应用组件。服务可以由其它应用组件启动。即使用户切换到其它应用。服务仍然在后台持续运行。此外,组件可以绑定绑定到服务,以与之进行交互。甚至是执行进程间通信。例如,服务可以处理网络事务、播放音乐、执行文件 I/O 或与 ContentProvider 交互。而所有这一切均可以在后台执行。

服务在其托管进程的主线程中运行,它既不创建自己的线程,也不在单独的进程中运行(除非另行指定)。 这意味着,如果服务将执行任何 CPU 密集型工作或阻止性操作(例如 MP3 播放或联网),则应在服务内创建新线程来完成这项工作。通过使用单独的线程,可以降低发生“应用无响应”(ANR) 错误的风险,而应用的主线程仍可继续专注于运行用户与 Activity 之间的交互

  要创建服务,必须创建 Service的子类(或使用它的现有子类)。在实现在中需要重写一些回调方法,以处理服务生命周期。

  • onStartCommand( )
  •   当另一组件(如:activity)通过调用 startService( )请求启动服务,系统将调用此方法。一旦执行方法,服务即会启动并可在后台无限期的运行。如果你实现此方法,则在服务工作完成后,需要通过调用 stopSelf( )或stopService( )来停止服务。(如果只是单一绑定服务,则无需实现此方法)
  • onBind( )
  •   当另一个组件通过 bindService( )与服务绑定时,系统将调用此方法。在此方法实现中,必须通过 IBinder 提供的一个接口,供客服端用来与服务进行通信。务必实现此方法,但如果并不希望 绑定,则返回 null
  • onCreate( )
  •   首次创建服务时,系统将调用此方法来执行一次性设置程序(在调用 onStartCommand( )或onBind( )之前)。如果服务已在运行,则不会调用此方法。
  • onDestroy( )
  •   当服务不在使用且将被销毁时,系统将调用此方法。服务应该实现此方法来清理所有资源,如线程、注册的监听器、接收器等..。这是服务接收的最后一个调用

  当內存过低且必须回收系统资源以供有用户焦点 Activity使用时,Android 系统才会强制停止服务。如果将服务绑定到具有用户焦点的 Activity,则他不太可能会终止;如果将服务声明为前台服务,则它几乎永远不会终止;如果服务已经启动并要长长时间运行,则系统会随者时间的推移降低服务在后台任务列表中的位置,而服务也将随之变的非常容易被终止;如果服务是启动服务,则必须将其设计为能够妥善处理系统对它的重启。系统终止服务后,在资源变得再次可用的时,系统便会重启服务(不过这还取决于 onStartCommand的返回值);

  毕竟是四组件,需要在 AnroidManifest.xml中注册:

    <service android:description="string resource"             android:enabled=["true" | "false"]             android:exported=["true" | "false"]             android:icon="drawable resource"             android:isolatedProcess=["true" | "false"]             android:label="string resource"             android:name="string"             android:permission="string"             android:process="string" >        . . .    </service>
Service注册 (可能用到的属性)android:description描述服务的字符串,建议设置为字符串的资源引用android:exported  其它应用程序的组件是否可以调用服务或与其进行交互。当为 false时,只有具有相同的用户 ID的相同应用程序或引用组件才能启动或绑定到该服务
  默认值取决与服务时候包含 <intent-filter>没有过路器以为着它只能通过指定其确切的类名来调用。这以为着该服务仅用于应用程序内部使用(因为其它人不会知道类名)。所以,在这种情况下,默认为 false,另一方面,存在<intent-filter>只在于外部使用,一次默认为 true
  此属性不是将服务暴露于其他应用程序的唯一方法。 您还可以使用权限限制可与服务交互的外部实体android:icon代表该服务该图标。此属性必须设置为包含图像的可绘制的引用。如果未设置,则会使用为整个应用 <application>指定的 iconandroid:isolatedProcess如果设置为 true,则此服务将在与系统其余部分隔离的特殊进程下运行,并且没有自己的权限。与它的唯一沟通是通过 ServiceApi (绑定和启动)android:label可以向用户显示服务的名称,如果未设置此属性,则使用 <application>的 lable 属性
  建议设置为字符串的资源引用android:name该 service的名称。android:permission权限申请android:process  通常,应用程序的所有组件都在应用程序创建的默认进程中运行。它与应用程序包具有相同的名称。但是组件也可以自己使用 process属性覆盖默认值,从而允许你跨多个进程分布的应用程序
  如果以冒号 ( : )开头,则会创建一个新的应用程序进程,如果以小写开头,将以该名称的全局进程运行,前提是它有权限执行此操作。 这允许不同应用程序中的组件共享进程,从而减少资源的使用。(如:remote”和”:remote”,前者的进程名称为:remote,而后者的进程名称为:App-packageName:remote)

  为了确保应用的安全性,请始终使用显示 intent 启动或 绑定 service,且不要为服务声明 <intent-filter>。这样启动那个服务存在以一定的不确定性,而如果不对这种不确定性的考量非常有必要。可使用 intent过滤器选着相应的组件名,但是,随后必须使用 setPackage( )方法设置包名,这样可以充分消除不确定性。

  此外还可以通过添加 android:exported 属性,并将其设置为 false,确保服务仅适用于自己的应用。可以有效的阻止其它应启动服务,即便是使用显示 intent时,而已是如此。

启动服务 startService( )

  启动服务有另一个组件通过 startService( )启动,会调用服务的 onStartCommand( )方法。服务启之后,其生命周期及独立于启动它的组件,并且可以在后台无限期地运行。即使启动服务的组件被销毁也不受影响。因此通过 stopSelf( )在工作完成后,停止运行。或者由另一个组件通过调用 stopSerice( )来停止它。

  应用组件(如:Activity)可以通过调用startActivity( )方法并传递 intent 对象(指定服务并携带所需的数据)来启动服务。服务通过onStartCommand( )方法来就收次 intent。

  startService( )由自己对 onStartCommand( )调用进行处理。因此,可以为每一个请求创建一个新的线程,然后立刻运行这些线程(而不是等待上一个请求完成,在执行下一个)。注意:onStartCommand( )方法必须返回一个整数行,整数型是一个值,用于描述系统应该在服务终止的情况下继续运行。从onStartCommand( )返回的值必须是一下常量之一:

  • START_NOT_STICKY
  •   如果系统在 onStartCommand( )返回后杀死这个服务,那么知道服务接收到新的 intent对象,这个服务才会被重新创建。这是最安全的选项,用来避免在不需要的时候运行你的服务
  • START_STICKY
  •   如果系统在onStartCommand( )返回后杀死这个服务,系统就会重新创建这个服务并且调用 onStartCommand( )方法。但它不会重新传递最后 intent的对象。系统会用一个 null 的intent对象来调用 onStartCommand( )方法。在这个情况下,除非有一些对象在等待启动服务。这适用于不执行命令的媒体播放器(或类似的服务),它只是无限期的运行着并等待工作的到来
  • START_REDELIVER_INTENT
  •   如果系统在 onStartCommand( )方法返回后,系统就重新创建这个服务,并且用发送给这个服务的最后 intent对象,调用 onStartCommand( )方法。任意等待中的 intent 对象会被一次发送,这适用于那些应该立刻恢复正在执行工作的服务,如下载文件

  一个测试的效demo,通过 DDMS直接杀死进程的日志打印

public class StartService extends Service {    private static final String TAG  = "info";    private static final String CLASS_NAME = "StartService " ;    public static final String TEST_NUM = "test_num" ;     int num ;    @Nullable    @Override    public IBinder onBind(Intent intent) {        return null;    }    @Override    public void onCreate() {        Log.i(TAG,CLASS_NAME+"onCreate");        super.onCreate();    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        Log.i(TAG,CLASS_NAME+"onStartCommand num = "+num);        num = intent.getIntExtra(TEST_NUM,0);        cousumeTime();//        return super.onStartCommand(intent, flags, startId);        return START_NOT_STICKY;    }    private void cousumeTime() {        new Thread(){            @Override            public void run() {                try {                    do  {                        Log.i(TAG, CLASS_NAME + "一 num = " + num);                        sleep(1000);                        num++;                    }while (num < 15);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }.start();    }    @Override    public void onDestroy() {        Log.i(TAG,CLASS_NAME+"onDestroy");        super.onDestroy();    }}

  START_STICKY

I/info: StartService onCreateI/info: StartService onStartCommand num = 0I/info: StartService 一 num = 5I/info: StartService 一 num = 6I/info: StartService 一 num = 7I/info: StartService 一 num = 8I/info: StartService onCreateI/info: StartService onStartCommand num = 0

  在打印以上 日志之后抛出异常

java.lang.NullPointerException: Attempt to invoke virtual method 'int android.content.Intent.getIntExtra(java.lang.String, int)' on a null object reference

START_NOT_STICKY

I/info: StartService onCreateI/info: StartService onStartCommand num = 0I/info: StartService 一 num = 5I/info: StartService 一 num = 6I/info: StartService 一 num = 7I/info: StartService 一 num = 8I/info: StartService 一 num = 9

START_NOT_STICKY

I/info: StartService onCreateI/info: StartService onStartCommand num = 0I/info: StartService 一 num = 5I/info: StartService 一 num = 6........................................I/info: StartService 一 num = 10I/info: StartService onCreateI/info: StartService onStartCommand num = 0I/info: StartService 一 num = 5I/info: StartService 一 num = 6I/info: StartService 一 num = 7........................................ I/info: StartService 一 num = 13 I/info: StartService 一 num = 14


  startService( )一经调用,startService( )方法将立刻返回,如果服务之前未运行,则系统先调用 onCreate( ),然后在调用onStartCommand( ),反之,则直接调用 onStartCommand( )。如果服务没有何绑定服务一起使用,则使用 startService( )传递的intent是应用组件与服务之间的唯一通信模式。但是,如果希望服务返回结果的话,则启动服务的客服端可以创建一个 pendingIntent(使用getBroadcast( )),并且通过启动服务的 intent 传递给服务。然后服务就可以使用广播传递结果。

  多个服务启动请求会导致多次对服务的 onStartCommand() 进行相应的调用。但是,要停止服务,只需一个服务停止请求(使用 stopSelf() 或 stopService())即可。启动服务必须管理自己的生命周期。也就是说,除非系统必须回收内存资源,否则系统不会停止或销毁服务,而且服务在 onStartCommand() 返回后会继续运行。一旦请求使用 stopSelf() 或 stopService() 停止服务,系统就会尽快销毁服务。

  但是,如果服务同时处理多个 onStartCommand() 请求,则您不应在处理完一个启动请求之后停止服务,因为您可能已经收到了新的启动请求(在第一个请求结束时停止服务会终止第二个请求)。为了避免这一问题,您可以使用 stopSelf(int) 确保服务停止请求始终基于最近的启动请求。也就说,在调用 stopSelf(int) 时,传递与停止请求的 ID 对应的启动请求的 ID(传递给 onStartCommand() 的 startId)。然后,如果在您能够调用 stopSelf(int) 之前服务收到了新的启动请求,ID 就不匹配,服务也就不会停止。

绑定服务 bindService( )

  绑定服务是客户端-服务器接口中的服务器。通过使用 bindservice( )与其绑定,以便创建长期连接(通常不允许组件通过 startService( )来启动它)。绑定服务可让组件(例如 Activity)绑定到服务、发送请求、接收响应,和其它应用组件中的服务中的服务进行交互,甚至执行进程间通信 (IPC)。 绑定服务通常只在为其他应用组件服务时处于活动状态,不会无限期在后台运行。

  要创建绑定服务,必须实现 onBind( )回调方法,该方法返回的 IBinder 对象定义了客户端用来与服务进行交互的编程接口。然后,在其它应用组件可以调用 bindservice( )来检索该接口,并调用方法。服务只用于与其绑定的应用组件。因此如果没有组件绑定服务,则系统会销毁服务(不必按通过 onStartCommand() 启动的服务那样来停止绑定服务)。

  创建绑定服务,通过调用 bindService() 绑定到服务。服务与客户端之间的这个接口必须是 IBinder 的实现,并且服务必须从 onBind() 回调方法返回它。调用时,它必须提供 ServiceConnection 的实现,后者会监控与服务的连接。bindService() 方法会立即无值返回。当 Android 系统创建客户端与服务之间的连接时,会对 ServiceConnection 调用 onServiceConnected(),向客户端传递用来与服务通信的 IBinder。 一旦客户端收到 IBinder,即可开始通过该接口与服务进行交互。一个ServiceConnection对象,该代表与服务的连接,它只有两个方法, onServiceConnected和onServiceDisconnected,其含义如下:

  • onServiceConnected(ComponentName name, IBinder service)
  •   系统会调用该方法以传递服务的 onBind() 方法返回的 IBinder。其中service便是服务端返回的IBinder实现类对象,通过该对象我们便调用服务端的公共方法。而ComponentName是一个封装了组件(Activity, Service, BroadcastReceiver, or ContentProvider)信息的类,如包名,组件描述等信息,较少使用该参数。
  • onServiceDisconnected(ComponentName name)
  •   Android 系统会在与服务的连接意外中断时(例如当服务崩溃或被终止时)调用该方法。注意:当客户端取消绑定时,系统“绝对不会”调用该方法。

  多个客服端可以同时绑定到一个服务。不过,只有在第一个客户端绑定时,系统才会调用服务的 onBind( )方法来检索 IBinder。系统随后无需再次调用 onBind( ),便可将同一 IBinder 传递至任何其它绑定客服端。当最后一个客户端取消与服务的绑定时,系统会将服务销毁(除非startService( )也启动了该服务)。

  当实现绑定服务时,最重要的环节是定义 onBin( 回调方法返回接口。可以通过以下三种方法定义接口,用以提供客服端与服务进行交互显示:

  • 继承Binder类
  •   如果服务是供自身的应用专用,并且在与客户端相同的进程中运行(常见情况),则应通过扩展Binder类,并从 onBind( )返回它的一个实例来创建接口。客服端收到 Binder后,可利用它直接访问Binder实现中乃至Service中可用的公共方法
      如果服务只是您的自有应用的后台工作线程,则优先采用这种方法。 不以这种方式创建接口的唯一原因是,您的服务被其他应用或不同的进程占用。
  • 使用Messenger
  •   如果让接口跨不同进程工作,则可以使用 messenger为服务创建接口。这中方式可以传递不同类型的 message对象。(也就是 Handler中的 Messenger)。Messenger随后可以与客服端分享一个IBinder,从而让客户端利用Messenger对象想服务发送命令。此外,客服端还可以自有Messenger,以便服务回传消息。
      这是执行进程间通信 (IPC) 的最简单方法,因为 Messenger 会在单一线程中创建包含所有请求的队列,也就是说Messenger是以串行的方式处理客户端发来的消息,这样我们就不必对服务进行线程安全设计了。
  • 使用 AIDL
  •   AIDL(Android 接口定义语言)执行所有将对象分解成原语的工作,操作系统可以识别这些原语并将它们编组到各进程中,以执行 IPC。 之前采用 Messenger 的方法实际上是以 AIDL 作为其底层结构。 如上所述,Messenger 会在单一线程中创建包含所有客户端请求的队列,以便服务一次接收一个请求。 不过,如果您想让服务同时处理多个请求,则可直接使用 AIDL。 在此情况下,您的服务必须具备多线程处理能力,并采用线程安全式设计。
      如需直接使用 AIDL,您必须创建一个定义编程接口的 .aidl 文件。Android SDK 工具利用该文件生成一个实现接口并处理 IPC 的抽象类,您随后可在服务内对其进行扩展。注意:大多数应用“都不会”使用 AIDL 来创建绑定服务,因为它可能要求具备多线程处理能力,并可能导致实现的复杂性增加。因此,AIDL 并不适合大多数应用 参考建议:传智播客的仿阿福音视频播放器采用就是 aidl 形式

  扩展Binder类

  如果服仅供本身引用使用,不需要跨进程工作,则可以实现自身的 Binder类,让客服端通过该类直接访问服务中的公共方法。注意:此方法只有在客户端和服务位于同一应用和进程内。这种情况下才有效。例如,对于需要将 Activity绑定在后台播放音乐的自身服务的音乐应用,此方法非常有效。

  以下是具体的设置方法:

  1. 在服务中,创建一个可以满足下列任意要求的 Binder实例:
    • 包含客户端课调用的公共方法
    • 返回当前service实例,其中包含客户端可调用的公共方法
    • 或返回有服务承载的其它类的实例,其中包含客户端可调用的公共方法
  2. 从 onBind( )回调方法返回此 Binder 实例
  3. 在客户端中,从 onServiceConnection( )回调方法接受 Binder,并使用提供的方法调用绑定服务
注意:之所以要求服务和客户端必须在同一应用内,是为了便于客户端转换返回的对象和正确调用其 API。服务和客户端还必须在同一进程内,因为此方法不执行任何跨进程编组。 
public class BindService extends Service {    private static final String TAG  ="info";    private static final String CLASS_NAME = "BindService ";    private final Random mRandom = new Random();    public class Bbinder extends Binder{        public BindService getService(){            return BindService.this;        }    }    private Bbinder binder = new Bbinder();    @Override    public IBinder onBind(Intent intent) {        Log.i(TAG,CLASS_NAME+"onBind");        return binder;    }    public int getRandomNum(){        int num  = mRandom.nextInt(100);        return num;    }    @Override    public void unbindService(ServiceConnection conn) {        Log.i(TAG,CLASS_NAME+"unbindService");        super.unbindService(conn);    }    @Override    public void onCreate() {        super.onCreate();        Log.i(TAG,CLASS_NAME+"onCreate");    }    @Override    public void onDestroy() {        Log.i(TAG,CLASS_NAME+"onDestroy");        super.onDestroy();    }}
public class BinderActivity extends AppCompatActivity implements View.OnClickListener {    private static final String TAG  ="info";    private static final String CLASS_NAME = "BinderActivity ";    private boolean isBound;    private BindService mService ;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_start_service);        initStadrtService();    }    private Button sStart_Service,sStop_Service,getRandom;    private void initStadrtService() {        sStart_Service = (Button) findViewById(R.id.sStart);        sStart_Service.setText("Bind Service");        sStop_Service = (Button) findViewById(R.id.sStop);        sStop_Service.setText("Unbind Service");        getRandom = (Button) findViewById(R.id.getRandom);        sStart_Service.setOnClickListener(this);        sStop_Service.setOnClickListener(this);        getRandom.setOnClickListener(this);    }    @Override    public void onClick(View v) {        Intent intent = new Intent(BinderActivity.this,StartService.class);        int id = v.getId();        switch (id){            case R.id.sStart:{                doBindService();            }            break;            case R.id.sStop:{                doUnbindService();            }            break;            case R.id.getRandom:{                if(isBound){                    Log.i(TAG,CLASS_NAME+"Random = "+mService.getRandomNum());                }                break;            }        }    }    private ServiceConnection mConnection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            Log.i(TAG,CLASS_NAME+"onServiceConnected componentName = "+name);            mService = ((BindService.Bbinder) service).getService();        }        @Override        public void onServiceDisconnected(ComponentName name) {            Log.i(TAG,CLASS_NAME+"onServiceDisconnected") ;            mService = null ;        }    };    private void doBindService() {        bindService(new Intent(BinderActivity.this,BindService.class),mConnection, Context.BIND_AUTO_CREATE);        isBound =true ;    }    private void doUnbindService() {        if(isBound){           unbindService(mConnection);            isBound = false;        }    }    @Override    protected void onDestroy() {        unbindService(mConnection);        super.onDestroy();    }}

  log的打印 点击顺序 3次 Bind Service按钮 –> 3次 getServiceRandom -> Unbind Service
 I/info: BindService onCreate I/info: BindService onBind I/info: BinderActivity onServiceConnected componentName = ComponentInfo{com.hu.smaple/com.hu.smaple.hu.service.BindService} I/info: BinderActivity Random = 73 I/info: BinderActivity Random = 69 I/info: BinderActivity Random = 89 I/info: BindService onDestroy
  在多次点击 Bind Service,没有像 startService那样多次调用 onStartCommand( )方法。其原由下面见参考链接
  • 使用Messenger
  •   如需让服务与远程进程(即不同进程间)通信,则可使用 Messenger 为您的服务提供接口。利用此方法,无需使用 AIDL 便可执行进程间通信 (IPC)
  以下是 Messenger 的使用方法摘要
  1. 服务实现一个 Handler,由其接受来自客户端的每个调用的回调
  2. Handler 用于创建 Messenger 对象(对Handler的引用)
  3. Messenger 创建一个 IBinder,服务通过 onBind() 使其返回客户端
  4. 客户端使用 IBinder 将 Messenger(引用服务的 Handler)实例化,然后使用后者将 Message 对象发送给服务
  5. 服务在其 Handler 中(具体地讲,是在 handleMessage() 方法中)接收每个 Message。
  这样,客户端并没有调用服务的“方法”。而客户端传递的“消息”(Message 对象)是服务在其 Handler 中接收的。
  demo来至于 ApiDemos-23,参考链接:Messenger:使用消息的跨进程通信
public class MessengerService extends Service {    private static final String TAG = "info";    private static final String CLASS_NAME = "MessengerService ";    NotificationManager mNM;    ArrayList<Messenger> mClients = new ArrayList<>();    int mValue = 0;    static final int MSG_REGISTER_CLIENT = 1;    static final int MSG_UNREGISTER_CLIENT = 2;    static final int MSG_SET_VALUE = 3;    class IncomingHandler extends Handler {        @Override        public void handleMessage(Message msg) {            switch (msg.what) {                case MSG_REGISTER_CLIENT:                    //客户端的信使添到集合                    mClients.add(msg.replyTo);                    break;                case MSG_UNREGISTER_CLIENT:                    //从集合中移除一个信使                    mClients.remove(msg.replyTo);                    break;                case MSG_SET_VALUE:                    //取出client传过来的 hashCode() arg1                    mValue = msg.arg1;                    for (int i = mClients.size() - 1; i >= 0; i--) {                        try {                            //客服端的信使 发送消息 (把client传过来的值,回传回去)                            mClients.get(i).send(Message.obtain(null, MSG_SET_VALUE,mValue, 0));                        } catch (RemoteException e) {                            mClients.remove(i);                        }                    }                    break;                default:                    super.handleMessage(msg);            }        }    }    //service 自己信使对象    final Messenger mMessenger = new Messenger(new IncomingHandler());    @Override    public void onCreate() {        Log.i(TAG, CLASS_NAME + "onCreate");        mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);        showNotification();    }    @Override    public void onDestroy() {        Log.i(TAG, CLASS_NAME + "onDestroy");        mNM.cancel(R.string.remote_service_started);    }    @Override    public IBinder onBind(Intent intent) {        //返回一个 binder        return mMessenger.getBinder();    }    private void showNotification() {        CharSequence text = getText(R.string.remote_service_started);        PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, MessengerServiceActivities.Controller.class), 0);        Notification notification = new Notification.Builder(this)                .setSmallIcon(R.drawable.stat_sample)                .setTicker(text)                .setWhen(System.currentTimeMillis())                .setContentTitle(getText(R.string.local_service_label))                .setContentText(text)                .setContentIntent(contentIntent)                .build();        mNM.notify(R.string.remote_service_started, notification);    }}
    public static class Binding extends Activity {        private static final String CLASS_NAME = "Messenger BindingService ";        //service中的信使        Messenger mService = null;        boolean mIsBound;        TextView mCallbackText;        class IncomingHandler extends Handler {            @Override            public void handleMessage(Message msg) {                switch (msg.what) {                    case MessengerService.MSG_SET_VALUE:                        mCallbackText.setText("Received from service: " + msg.arg1);                        break;                    default:                        super.handleMessage(msg);                }            }        }        //client 自己信使对象        final Messenger mMessenger = new Messenger(new IncomingHandler());        @Override        protected void onCreate(Bundle savedInstanceState) {            super.onCreate(savedInstanceState);            setContentView(R.layout.messenger_service_binding);            Button button = (Button) findViewById(R.id.bind);            button.setOnClickListener(mBindListener);            button = (Button) findViewById(R.id.unbind);            button.setOnClickListener(mUnbindListener);            mCallbackText = (TextView) findViewById(R.id.callback);            mCallbackText.setText("Not attached.");        }        private View.OnClickListener mBindListener = new View.OnClickListener() {            public void onClick(View v) {                Log.i(TAG,CLASS_NAME+"onClick-->Bind");                doBindService();            }        };        private View.OnClickListener mUnbindListener = new View.OnClickListener() {            public void onClick(View v) {                Log.i(TAG,CLASS_NAME+"onClick-->Unbind");                doUnbindService();            }        };        private ServiceConnection mConnection = new ServiceConnection() {            @Override            public void onServiceConnected(ComponentName name, IBinder service) {                Log.i(TAG, CLASS_NAME + "onServiceConnected");                //虽然是 new,但是是通过aidl,其实就会 service中的那个                mService = new Messenger(service);                mCallbackText.setText("Attached");                try {                    Message msg = Message.obtain(null, MessengerService.MSG_REGISTER_CLIENT);                    //将 自己(client)的信使设置到消息中,远程(service)接收到消息的同时也得到该(client)信使                    msg.replyTo = mMessenger;                    mService.send(msg);                    //把 hashCode()存放在 arg1上                    msg = Message.obtain(null, MessengerService.MSG_SET_VALUE, this.hashCode(), 0);                    mService.send(msg);                } catch (RemoteException e) {                    e.printStackTrace();                }            }            @Override            public void onServiceDisconnected(ComponentName name) {                Log.i(TAG,CLASS_NAME+"onServiceDisconnected");                mService = null ;                mCallbackText.setText("DisContented");            }        };        void doBindService(){            bindService(new Intent(Binding.this,MessengerService.class),mConnection, Context.BIND_AUTO_CREATE);            mIsBound = true;            mCallbackText.setText("Binding");        }        void doUnbindService(){            if(mIsBound){                if(mService != null){                    try {                        Message msg = Message.obtain(null,MessengerService.MSG_UNREGISTER_CLIENT);                        msg.replyTo = mMessenger;                        mService.send(msg);                    } catch (RemoteException e) {                        e.printStackTrace();                    }                }                unbindService(mConnection);                mIsBound = false;                mCallbackText.setText("Unbinding.");            }        }    }

  这里为了快进程在注册service的时候是使用 android:process
        <service android:name=".hu.service.MessengerService"            android:process=":remote"/>

  其中进程在DDMS可以查看到

  log打印 点击顺序 BindService –> UnbindSerive
I/info: MessengerService onDestroyI/info: Messenger BindingService onClick-->BindI/info: MessengerService onCreateI/info: Messenger BindingService onServiceConnectedI/info: Messenger BindingService onClick-->UnbindI/info: MessengerService onDestroy
注:通常情况下,切勿在 Activity 的 onResume() 和 onPause() 期间绑定和取消绑定,因为每一次生命周期转换都会发生这些回调,您应该使发生在这些转换期间的处理保持在最低水平。此外,如果您的应用内的多个 Activity 绑定到同一服务,并且其中两个 Activity 之间发生了转换,则如果当前 Activity 在下一个 Activity 绑定(恢复期间)之前取消绑定(暂停期间),系统可能会销毁服务并重建服务。 

bindService()和startService()混合使用

public class StartAndBindService extends Service {    private static final String TAG  ="info";    private static final String CLASS_NAME = "StartAndBindService ";    public class TestBinder extends Binder{         StartAndBindService getService(){            return  StartAndBindService.this;        }    }    private TestBinder mBinder = new TestBinder();    @Nullable    @Override    public IBinder onBind(Intent intent) {        Log.i(TAG,CLASS_NAME+"onBind") ;        return mBinder;    }    @Override    public void unbindService(ServiceConnection conn) {        Log.i(TAG,CLASS_NAME+"unbindService") ;        super.unbindService(conn);    }    @Override    public void onRebind(Intent intent) {        Log.i(TAG,CLASS_NAME+"onRebind") ;        super.onRebind(intent);    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        Log.i(TAG,CLASS_NAME+"onStartCommand") ;        return super.onStartCommand(intent, flags, startId);    }    @Override    public void onCreate() {        Log.i(TAG,CLASS_NAME+"onCreate") ;        super.onCreate();    }    @Override    public boolean stopService(Intent name) {        Log.i(TAG,CLASS_NAME+"stopService") ;        return super.stopService(name);    }    @Override    public void onDestroy() {        Log.i(TAG,CLASS_NAME+"onDestroy") ;        super.onDestroy();    }}
public  class BindAndStartActivity extends Activity implements View.OnClickListener {    private static final String TAG  ="info";    private static final String CLASS_NAME = "BindAndStartActivity ";        private Button bind, unBind, start, stop;        @Override        protected void onCreate(Bundle savedInstanceState) {            Log.i(TAG,CLASS_NAME+"onCreate");            super.onCreate(savedInstanceState);            setContentView(R.layout.activity_bind_start);            bind = (Button) findViewById(R.id.bind);            unBind = (Button) findViewById(R.id.unbind);            start = (Button) findViewById(R.id.start);            stop = (Button) findViewById(R.id.stop);            bind.setOnClickListener(this);            unBind.setOnClickListener(this);            start.setOnClickListener(this);            stop.setOnClickListener(this);        }        @Override        public void onClick(View v) {            int id = v.getId();            switch (id) {                case R.id.bind:                    doBindSerivice();                    break;                case R.id.unbind:                    doUnbindService();                    break;                case R.id.start:                    doStartService();                    break;                case R.id.stop:                    doStopService();                    break;                default:            }        }        private boolean isBound;        private StartAndBindService mService;        private ServiceConnection mConnection = new ServiceConnection() {            @Override            public void onServiceConnected(ComponentName name, IBinder service) {                mService = ((StartAndBindService.TestBinder) service).getService();            }            @Override            public void onServiceDisconnected(ComponentName name) {                mService = null;            }        };        private void doBindSerivice() {            Log.i(TAG,CLASS_NAME+"Bind-->onClick");            bindService(new Intent(BindAndStartActivity.this,StartAndBindService.class),mConnection, Context.BIND_AUTO_CREATE);            isBound = true;        }        private void doUnbindService() {            Log.i(TAG,CLASS_NAME+"Unbind-->onClick");            if(isBound) {                unbindService(mConnection);                isBound = false;            }        }        private void doStartService() {            Log.i(TAG,CLASS_NAME+"Start-->onClick");            startService(new Intent(BindAndStartActivity.this,StartAndBindService.class));        }        private void doStopService() {            Log.i(TAG,CLASS_NAME+"Stop-->onClick");            stopService(new Intent(BindAndStartActivity.this,StartAndBindService.class));        }        @Override        protected void onDestroy() {            Log.i(TAG,CLASS_NAME+"onDestroy");            super.onDestroy();            unbindService(mConnection);        }    @Override    public void onBackPressed() {        Log.i(TAG,CLASS_NAME+"Back-->onClick");        super.onBackPressed();    }}

  对应的 log打印 Bind –> start –> back退出activity –>重新进入activity
I/info: BindAndStartActivity onCreateI/info: BindAndStartActivity Bind-->onClickI/info: StartAndBindService onCreateI/info: StartAndBindService onBindI/info: BindAndStartActivity Start-->onClickI/info: StartAndBindService onStartCommandI/info: BindAndStartActivity Back-->onClickI/info: BindAndStartActivity onDestroyI/info: BindAndStartActivity onCreate

  对应的 log打印 Bind –> start –> stop–> back退出activity

I/info: BindAndStartActivity onCreateI/info: BindAndStartActivity Bind-->onClickI/info: StartAndBindService onCreateI/info: StartAndBindService onBindI/info: BindAndStartActivity Start-->onClickI/info: StartAndBindService onStartCommandI/info: BindAndStartActivity Stop-->onClickI/info: BindAndStartActivity Back-->onClickI/info: BindAndStartActivity onDestroyI/info: StartAndBindService onDestroy

  对应的 log打印 Start –> Bind –> back退出activity –>重新进入activity

I/info: BindAndStartActivity onCreateI/info: BindAndStartActivity Start-->onClickI/info: StartAndBindService onCreateI/info: StartAndBindService onStartCommandI/info: BindAndStartActivity Bind-->onClickI/info: StartAndBindService onBindI/info: BindAndStartActivity Back-->onClickI/info: BindAndStartActivity onDestroyI/info: BindAndStartActivity onCreate

  对应的 log打印 Start –> Bind –> stop –> back退出activity

I/info: BindAndStartActivity onCreateI/info: BindAndStartActivity Start-->onClickI/info: StartAndBindService onCreateI/info: StartAndBindService onStartCommandI/info: BindAndStartActivity Bind-->onClickI/info: StartAndBindService onBindI/info: BindAndStartActivity Stop-->onClickI/info: BindAndStartActivity Back-->onClickI/info: BindAndStartActivity onDestroyI/info: StartAndBindService onDestroy

前台Service

  前台服务被认为是用户主动意识到的一种服务,因此在内存不足时,系统也不会考虑将其终止。 前台服务必须为状态栏提供通知,放在“正在进行”标题下方,这意味着除非服务停止或从前台移除,否则不能清除通知。

  •   对于把服务设置运行于前台, 我们可以使用对应的了两个方法,分别是startForeground()和stopForeground()
  • startForeground(int id, Notification notification)
  •   该方法的作用是把当前服务设置为前台服务,其中id参数代表唯一标识通知的整型数,需要注意的是提供给 startForeground() 的整型 ID 不得为 0,而notification是一个状态栏的通知。
  • stopForeground(boolean removeNotification)
  •   该方法是用来从前台删除服务,此方法传入一个布尔值,指示是否也删除状态栏通知,true为删除。 注意该方法并不会停止服务。 但是,如果在服务正在前台运行时将其停止,则通知也会被删除。

  demo摘抄至 api-23

public class ForegroundService extends Service {    private static final String TAG = "info";    private static final String CLASS_NAME = "ForegroundService ";    static final String ACTION_FOREGROUND = "com.example.hu.foreground" ;    static final String ACTION_BACKGROUND = "com.example.hu.background" ;    private static final Class<?>[] mSetForegroundSignature = new Class[] {            boolean.class};    private static final Class<?>[] mStartForegroundSignature = new Class[] {            int.class, Notification.class};    private static final Class<?>[] mStopForegroundSignature = new Class[] {            boolean.class};    private NotificationManager mNM;    private Method mSetForeground;    private Method mStartForeground;    private Method mStopForeground;    private Object[] mSetForegroundArgs = new Object[1];    private Object[] mStartForegroundArgs = new Object[2];    private Object[] mStopForegroundArgs = new Object[1];    void invokeMethod(Method method,Object[] args){        try {            method.invoke(this,args);        }catch (InvocationTargetException e){            Log.i(TAG,CLASS_NAME+"Unable to invoke method",e);        }catch (IllegalAccessException e){            Log.i(TAG,CLASS_NAME+"Unable to invoke method",e);        }    }    void startForegroundCompat(int id, Notification notification){        Log.i(TAG,CLASS_NAME+"startForegroundCompat");        //新的API        if (mStartForeground != null) {            mStartForegroundArgs[0] = Integer.valueOf(id);            mStartForegroundArgs[1] = notification;            invokeMethod(mStartForeground, mStartForegroundArgs);            return;        }        //老的 API        mSetForegroundArgs[0] = Boolean.TRUE;        invokeMethod(mSetForeground, mSetForegroundArgs);        mNM.notify(id, notification);    }    void stopForegroundCompat(int id){        Log.i(TAG,CLASS_NAME+"stopForegroundCompat");        if (mStopForeground != null) {            mStopForegroundArgs[0] = Boolean.TRUE;            invokeMethod(mStopForeground, mStopForegroundArgs);            return;        }        //注意在改变前景状态之前取消,因为在那个点可以被杀死        mNM.cancel(id);        mSetForegroundArgs[0] = Boolean.FALSE;        invokeMethod(mSetForeground, mSetForegroundArgs);    }    @Override    public void onCreate() {        Log.i(TAG,CLASS_NAME+"onCreate");        mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);        try {            mStartForeground = getClass().getMethod("startForeground",mStartForegroundSignature);            mStopForeground = getClass().getMethod("stopForeground",mStopForegroundSignature);        } catch (NoSuchMethodException e) {//            e.printStackTrace();            mStartForeground = mStartForeground = null ;        }        try {            mSetForeground = getClass().getMethod("setForeground",                    mSetForegroundSignature);        } catch (NoSuchMethodException e) {            throw new IllegalStateException(                    "OS doesn't have Service.startForeground OR Service.setForeground!");        }    }    public void onDestroy() {        Log.i(TAG,CLASS_NAME+"onDestroy");        stopForegroundCompat(R.string.foreground_service_started);    }    @Override    public void onStart(Intent intent, int startId) {        Log.i(TAG,CLASS_NAME+"onStart");        handleCommand(intent);    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        Log.i(TAG,CLASS_NAME+"onStartCommand");        handleCommand(intent);        //设置为 粘性,一直运行。除非有明确的 停止        return START_STICKY;    }    private void handleCommand(Intent intent) {        if(ACTION_FOREGROUND.equals(intent.getAction())){            CharSequence text = getText(R.string.foreground_service_started);            PendingIntent contentIntent = PendingIntent.getActivity(this,0,new Intent(this,Controller.class),0);            //Notification            Notification notification = new Notification.Builder(this)                    .setSmallIcon(R.drawable.stat_sample)                    .setTicker(text)                    .setWhen(System.currentTimeMillis())                    .setContentTitle(getText(R.string.alarm_service_label))                    .setContentText(text)                    .setContentIntent(contentIntent)                    .build();            startForegroundCompat(R.string.foreground_service_started,notification);        }else if(ACTION_BACKGROUND.equals(intent.getAction())){            stopForegroundCompat(R.string.foreground_service_stop);        }    }    @Override    public IBinder onBind(Intent intent) {        return null;    }    public static class Controller extends Activity{        private static final String CLASS_NAME = "ForegroundService&Controller ";        @Override        protected void onCreate(Bundle savedInstanceState) {            super.onCreate(savedInstanceState);            setContentView(R.layout.foreground_service_controller);            Button button = (Button) findViewById(R.id.start_foreground);            button.setOnClickListener(mForegroundListener);            button = (Button) findViewById(R.id.start_background);            button.setOnClickListener(mBackgroundListener);            button = (Button) findViewById(R.id.stop);            button.setOnClickListener(mStopListener);            Log.i(TAG,CLASS_NAME+"onCreate");        }        private View.OnClickListener mForegroundListener = new View.OnClickListener() {            @Override            public void onClick(View v) {                Log.i(TAG,CLASS_NAME+"Foreground --> onClick");                Intent intent = new Intent(ForegroundService.ACTION_FOREGROUND);                intent.setClass(Controller.this,ForegroundService.class);                startService(intent);            }        };        private View.OnClickListener mBackgroundListener = new View.OnClickListener() {            @Override            public void onClick(View v) {                Log.i(TAG,CLASS_NAME+"Background --> onClick");                Intent intent = new Intent(ForegroundService.ACTION_BACKGROUND);                intent.setClass(Controller.this,ForegroundService.class);                startService(intent);            }        };        private View.OnClickListener mStopListener = new View.OnClickListener() {            @Override            public void onClick(View v) {                stopService(new Intent(Controller.this,ForegroundService.class));            }        };    }}

  对应的 log打印
I/info: ForegroundService&Controller Foreground --> onClickI/info: ForegroundService onCreateI/info: ForegroundService onStartCommandI/info: ForegroundService startForegroundCompatI/info: ForegroundService&Controller Background --> onClickI/info: ForegroundService onStartCommandI/info: ForegroundService stopForegroundCompat

IntentService

  IntentService是一个基于Service的一个类,用来处理异步的请求。你可以通过startService(Intent)来提交请求,该Service会在需要的时候创建,当完成所有的任务以后自己关闭,且请求是在工作线程处理的。我们使用了IntentService最起码有两个好处,一方面不需要自己去new Thread了;另一方面不需要考虑在什么时候关闭该Service了。

  IntentService 执行以下操作:

  1. 创建默认的工作线程,用于在应用的主线程传递给 onStartCommand() 的所有 Intent。
  2. 创建工作队列,用于将 Intent 逐一传递给 onHandleIntent() 实现,这样就永远不必担心多线程问题。
  3. 在处理完所有启动请求后停止服务,因此不必调用 stopSelf()。
  4. 提供 onBind() 的默认实现(返回 null)。
  5. 提供 onStartCommand() 的默认实现,可将 Intent 依次发送到工作队列和 onHandleIntent() 实现。

  如果您决定还重写其他回调方法(如 onCreate()、onStartCommand() 或 onDestroy()),请确保调用超类实现,以便 IntentService 能够妥善处理工作线程的生命周期。

public class MIntentService extends IntentService {    private static final String TAG = "info";    private static final String CLASS_NAME = "MIntentService ";    public static final String TEST_NUM = "test_unm";    public MIntentService() {        super("intentServiceworker");    }    @Override    public void onCreate() {        Log.i(TAG, "intentService onCreate");        super.onCreate();    }    @Override    protected void onHandleIntent(Intent intent) {        Log.i(TAG,"Current Worker Thread = "+Thread.currentThread().getId());        Log.i(TAG, CLASS_NAME + "onHandleIntent");        int num = intent.getIntExtra(TEST_NUM, 0);        do {            Log.i(TAG, CLASS_NAME + "num = " + num);            try {                Thread.sleep(1000);            } catch (InterruptedException e) {                e.printStackTrace();            }            num++;        } while (num < 15);    }    @Override    public void onDestroy() {        Log.i(TAG, "intentService  onDestroy");        super.onDestroy();    }}
    private void startIntentService() {        Log.i(TAG,CLASS_NAME+"Thread = "+Thread.currentThread().getId());        Intent intent = new Intent(this,MIntentService.class) ;        intent.putExtra(MIntentService.TEST_NUM,5);        startService(intent);    }

  log打印 只点击一次 start intent service
I/info: IntentServiceActivity Thread = 1I/info: intentService onCreateI/info: Current Worker Thread = 219I/info: MIntentService onHandleIntentI/info: MIntentService num = 5I/info: MIntentService num = 6................................................I/info: MIntentService num = 13I/info: MIntentService num = 14I/info: intentService  onDestroy

  log打印 间隔点击两次 start intent service

I/info: IntentServiceActivity Thread = 1I/info: intentService onCreateI/info: Current Worker Thread = 220I/info: MIntentService onHandleIntentI/info: MIntentService num = 5I/info: MIntentService num = 6I/info: MIntentService num = 7I/info: IntentServiceActivity Thread = 1I/info: MIntentService num = 8...........................I/info: MIntentService num = 13I/info: MIntentService num = 14I/info: Current Worker Thread = 220I/info: MIntentService onHandleIntentI/info: MIntentService num = 5I/info: MIntentService num = 6...........................I/info: MIntentService num = 13I/info: MIntentService num = 14I/info: intentService  onDestroy

参考资料


  • Android里Service的bindService()和startService()混合使用深入分析
  • Android中的Service:默默的奉献者 (1)
  • Android学习笔记(五一):服务Service(上)- IntentService
  • 关于Android Service真正的完全详解,你需要知道的一切
  • Messenger:使用消息的跨进程通信
  • Service类onStartCommand()返回值讲解.
  • Android IntentService完全解析 当Service遇到Handler
  • Android Service生命周期 Service里面的onStartCommand()方法详解
  • Android应用实例之—-基于Service与ContentProvider的音乐播放实例!
  • Android中bindService的细节之三:多次调用bindService(),为什么onBind()只执行一次? (备注:这是多篇文章)
  • https://developer.android.google.cn/guide/components/services.html

0 0
原创粉丝点击