Service详解

Service详解

Service作用

  Service是一个不提供用户交互的可以在后台长期执行操作的应用程序组件。其它的应用程序组件可以启动Service,当切换到其他应用程序,该service会在后台继续运行。其他组件也可以绑定到service与其交互,甚至可以执行进程间通信。

Service的两种形式

  • 开始状态
    当一个应用程序组件(例如Activity)通过调用startService()启动一个Service后,它就处于“开始状态”。一旦处于“开始状态”,一个Service可以无限期地在后台运行,甚至当启动它的组件已经被销毁。通常,一个“开始状态”的Service会执行一个单独的操作,不会返回结果给调用者。例如,通过网络上传或下载文件。当操作完成,这个Service应该停止它自己。
  • 绑定状态
    当一个应用程序组件通过调用bindService()绑定到一个Service,则它处于“绑定状态”。处于“绑定状态”的Service提供了client-server接口,其允许这些组件可以与该Service执行诸如发送请求,获取结果,进程间通信等交互。一个处于“绑定状态”的Service仅和绑定到的应用程序组件运行一样长的时间。多个组件可以每个绑定到该Service一次,但当所有这些组件与该Service解绑后,该Service才会被销毁。

注意:你的Service可以同时工作在两种形式。

在manifest中声明service

  像其他应用程序组件一样,所有的Service必须在manifest中进行声明。

声明代码
<manifest ... >
  ...
  <application ... >
      <service 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>
      ...
  </application>
</manifest>
声明说明
  • android:name属性是唯一必须的属性,它具体了Service的类名。一旦发布了应用程序,你不应该再更改这个名字,如果更改,可能导致代码出错,因为你的代码依赖明确的intents去启动或绑定到Service。
  • To ensure your app is secure, always use an explicit intent when starting or binding your Service and do not declare intent filters for the service. If it’s critical that you allow for some amount of ambiguity as to which service starts, you can supply intent filters for your services and exclude the component name from the Intent, but you then must set the package for the intent with setPackage(), which provides sufficient disambiguation for the target service.
声明属性
android:enabled
  • 该属性设置为“true”,则该Service可以被实例化并被使用,否则,该Service不可用。该属性默认为“true”。
  • <application>元素也有enabled属性,它作用于所有的应用程序组件。
android:exported
  • 该属性设置为“true”,则其他应用的组件可以触发该Service或与它交互,如果设置为“false”,则只有当前应用或有相同用户id的应用程序才可以启动或绑定到该Service。
  • 该属性默认值依赖于该Service是否包含intent filters。没有任何的intent filters意味着该Service只能被使用具体的类名触发。该种情况只限于应用程序内部使用(其他应用程序不能知道具体类名)。所以这种情况,默认值为“false”。另一方面,只要有至少有一个filter为外部所使用,则默认值为“true”。
  • 该属性不是唯一限制其他应用使用该Service的方式。可以使用权限来限制与该Service交互的实体。
android:icon

设置代表该Service的一个图标。如果没有设置则使用 <application>元素设置的icon属性。

android:isolatedProcess

该属性设置为“true”,则该Service会在一个特殊的进程下运行,该进程与其他系统进程分开,且没有自己的权限。与其通信的唯一方式就是通过Service API。

android:label

一个展示给用户的Service名字。如果没有设置则使用 <application>元素设置的label属性。

android:name
  • Service的名字,它必须是一个具体的类名。
  • 一旦你发布了应用程序,不应该再改变这个名字(除非你已经设置了android:exported="false")。
  • 这个名字没有默认值,它必须被定义。
android:permission
  • 设置一个要创建或绑定到该Service的实体必须具有的权限的名字。如果startService()bindService(),或stopService()的调用者没有被授予这个权限,则这些方法不能起效并且Intent对象不能传递到该Service。
  • 如果这个属性没有设置,则使用 <application>元素设置的permission属性。如果都没有设置,则该Service不受任何权限保护。
android:process
  • 设置该Service运行在的进程名。正常的,应用程序的所有组件运行在为该应用创建的一个默认进程中。它的名字与该应用的包名相同。<application>元素的process属性能为所有的组件设置不同的默认名字。各组件可以用它们自己的process属性覆盖这个默认值,这允许你将你的应用分为几个进程。
  • 如果指定到这个属性的名字以冒号(:)开始,则这个新进程对该应用是私有的,它在需要时被创建且该Service运行在这个进程中。如果这个进程名以小写字符开始,则这个Service运行在一个以这个名字命名的全局进程中。这允许不同应用程序的组件共享这个进程,以减少资源的使用。

Service生命周期

  Service的生命周期和activity的生命周期相似,但更简单。然而,要更关注Service的创建和销毁,因为Service运行在后台不能被用户注意到。
  Service的生命周期可以追寻两种不同的路径:

  • “开始状态”Service
    当其他组件调用startService()方法时,该Service被创建。该Service会无限期地运行,可以调用stopSelf()方法终止它。启动它的组件,也可以调用stopService()方法来终止该Service。当该Service停止,系统就会销毁它…
  • “绑定状态”Service
    当其他组件(client)调用bindService()方法时,该Service被创建。接下来该client可以通过IBinder接口与该Service通信。该Service能够调用unbindService()方法关闭连接。多个clients可以绑定到同一个Service,当所有它们解绑,系统可以销毁该Service。(该Service不需要终止它自己)
    这两条路径不是完全分离的。当已调用startService()方法启动Service后,还可以绑定到该Service。例如,一个后台音乐Service可以先通过调用startService()方法启动播放音乐。之后,当用户可能需要对播放器进行控制或想获取当前播放歌曲信息的时候,一个activity可以通过调用bindService()方法绑定到该Service。在这种情况下,调用stopService()stopSelf()方法不能终止该Service,直到所以的clients与该Service解绑。
Service生命周期回调(需要深入研究)
public class ExampleService extends Service {

    private static final String NAME = ExampleService.class.getSimpleName();
    private static final String TAG = "sxd";

    int mStartMode;       //指明该Service被杀死后的行为表现
    IBinder mBinder;      //clients绑定接口
    boolean mAllowRebind; //指明onRebind是否应该被使用

    @Override
    public void onCreate() {
        //该Service被首次创建时调用
        Log.i(TAG, NAME + "--onCreate");
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, NAME + "--onStartCommand");
        //该Service正在运行,client再次调用startService()时调用
        return mStartMode;
    }
    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, NAME + "--onBind");
        //client调用bindService()绑定到该Service时调用
        return mBinder;
    }
    @Override
    public boolean onUnbind(Intent intent) {
        Log.i(TAG, NAME + "--onUnbind");
        //所有已绑定到该Service的client调用unbindService()与该Service解绑时调用
        return mAllowRebind;
    }
    @Override
    public void onRebind(Intent intent) {
        Log.i(TAG, NAME + "--onRebind");
        //当onUnbind()被调用后,又有新的client调用bindService()来绑定该Service时调用
    }
    @Override
    public void onDestroy() {
        Log.i(TAG, NAME + "--onDestroy");
        //该Service不再使用,销毁时调用
    }

}

注意:不像activity的生命周期回调方法,你不被要求去调用这些回调方法的父类实现。

Service生命周期图解

这里写图片描述
图:左侧展示了调用startService()方法创建的Service的生命周期,右侧展示了调用bindService()方法创建的Service的生命周期。

管理”绑定状态”Service的生命周期

  当一个Service从所有的clients解绑,系统就会销毁它(除非它也调用了onStartCommand()方法启动)。如果一个Service是一个纯净的”绑定状态”Service,则你不需要管理它的生命周期-Android系统基于是否还绑定了其他clients来管理它。
  然而,如果你选择实现onStartCommand()回调方法,那么你必须明确地终止该Service,因为这个Service被考虑为”开始状态”Service。这种情况下,该Service运行直到调用了stopSelf()stopService()方法,不管它是否绑定了其他clients。
  如果你的Service处于”开始状态”且接受绑定,如果你想在下次client绑定到该Service时,收到onRebind()方法回调,则当系统调用onUnbind()方法时,应该返回true。onRebind()方法返回void,但该client仍会在onServiceConnected()方法中收到IBinder。下面是这种逻辑的生命周期图解:
这里写图片描述

权限

  当在manifest中使用<service>标签声明Service时,可以施加该Service的全局访问权限。这样做,其他应用需要在它们自己的manifest中声明相应的<uses-permission>元素以能够启动,终止或绑定到该Service。
  GINGERBREAD(Android 2.3)中,当使用Context.startService(Intent)时,你能够在Intent中设置Intent.FLAG_GRANT_READ_URI_PERMISSIONIntent.FLAG_GRANT_WRITE_URI_PERMISSION。这可以授予该Service对在Intent中的特定URIs临时的访问权限。访问持续到该Service调用了stopSelf(int)方法或直到该Service被完全终止。该方式对其他没有请求受保护Service权限的应用或该Service完全没有暴露的授权访问同样有用。
  一个Service通过权限保护独立IPC对它的调用。通过在执行这个调用之前调用checkCallingPermission(String)方法检测是否具有访问权限。

Service进程生命周期

Android进程优先级
  1. 前台进程( FOREGROUND_APP)
  2. 可视进程(VISIBLE_APP )
  3. 次要服务进程(SECONDARY_SERVER )
  4. 后台进程 (HIDDEN_APP)
  5. 内容供应节点(CONTENT_PROVIDER)
  6. 空进程(EMPTY_APP)

一个Service已经启动或有clients绑定到它,则系统会尝试保持该Service的宿主进程。当运行在低内存状态需要杀死已存在进程时,下面情况的Service宿主进程具有较高的优先级:

  • 如果Service正在onCreate(), onStartCommand()onDestroy()方法中执行代码,则该宿主进程被作为前台进程以确保代码执行而不被杀掉。
  • 如果一个Service已经被启动,则它的宿主进程比任何对用户可见的进程的优先级低,但比任何不可见的进程优先级高。因为,只有少数的进程对用户可见,这意味着该Service不会被杀死除非在低内存状态下。然而,由于用户不能直接感知到后台Service,这种状态其被考虑为可以被杀死的候补者,你应该为这种情况的发生做好准备。实际上,长时间运行的Services会增加被杀死的可能性。
  • 如果有clients绑定到了Service,则该Service的宿主进程至少和最重要的client同等重要。如果这些clinets中有一个对用户可见,则该Service被考虑为对用户可见。clients重要程度影响service重要程度的方式可以通过BIND_ABOVE_CLIENTBIND_ALLOW_OOM_MANAGEMENTBIND_WAIVE_PRIORITYBIND_IMPORTANTBIND_ADJUST_WITH_ACTIVITY来调整。
  • “开始状态”的Service能够使用startForeground(int, Notification)方法将该Service设置为前台状态,这种情况下,系统认为它是用户可以感知到的,因此在低内存状态不会被作为杀掉的候选(理论上,该Service还是会在面对前台应用对内存的极度要求的压力下被杀死,但这是可以不用关心的)。
  • 有其他组件运行在Service进程能够提高该Service宿主进程的优先级。
    注意这意味着在大部分时间里你的Service是可以正常运行的,但在内存极度紧缺的情况下,它会被系统杀死。如果这发生了,系统随后会尝试重启该Service。一个重要的效果是,如果你实现了onStartCommand()方法去安排一个在另一个线程或异步执行的任务,那么你可能想要使用START_FLAG_REDELIVERY以让系统重传一个Intent给你以使它不会在被处理时该Service被杀死导致其丢失。

onStartCommand()的返回值(需要深入研究)

  注意onStartCommand()必须返回一个整形值。这个整型值描述了当Service被杀死后,系统应该怎样继续该Service。这个返回值必须是下面各常量中的一个:

  • START_NOT_STICKY
    如果系统在onStartCommand()方法返回后杀死了该Service,则不会再重新创建该Service,除非有pending intents传递给它。对于避免你的Service在不必要的时候运行和你的应用能够简单重启未完成的工作的情况,这是一个安全的选项。
  • START_STICKY
    如果系统在onStartCommand()方法返回后杀死了该Service,则会重新创建该Service并调用onStartCommand()方法,但不会重传最后的Intent。系统会调用onStartCommand()附带null的intent,除非有pending intent去启动该Service,在这种情况下,会传递这些intents。
  • START_REDELIVER_INTENT
    如果系统在onStartCommand()方法返回后杀死了该Service,则会重新创建该Service并调用onStartCommand()方法附带最后传递到该Service的Intent。任何pending intents是被轮流地传递。

Service主要方法详解

  • public void onCreate ()
    该Service被第一次创建时调用,不要直接调用该方法。
  • public void onDestroy ()
    该Service不再使用,或被移除时调用。
  • public int onStartCommand (Intent intent, int flags, int startId)
    每次调用startService(Intent)方法时,该方法都会被调用。
    参数:
    intent:该Intent由startService(Intent)方法提供。如果该Service被杀死后重启,且之前返回了除START_STICKY_COMPATIBILITY之外的任何状态,则重启后该Intent为null。
    flags:启动请求的附加数据。当可取值为为0START_FLAG_REDELIVERYSTART_FLAG_RETRY
    startId:一个唯一的整型值代表了特定的启动请求。用于stopSelfResult(int)来终止特定Service。
    返回值描述了当Service被杀死后,系统应该怎样继续该Service
  • public abstract IBinder onBind (Intent intent)
    返回与该Service的通信渠道。如果clients不用绑定到该Service,则返回null。这个返回的IBinder,通常是用aidl描述的负责接口。
    参数intent是通过Context.bindService方法传递过来,用来绑定到该Service的。
    注意: any extras that were included with the Intent at that point will not be seen here.
  • public boolean onUnbind (Intent intent)
    所有已绑定到该Service的client调用unbindService()与该Service解绑时调用
  • public void onRebind (Intent intent)
    当onUnbind()被调用后,又有新的client调用bindService()来绑定该Service时调用
  • public void onConfigurationChanged (Configuration newConfig)
    当组件正在运行且设备配置发生改变时调用。
  • public void onLowMemory ()
  • public void onTrimMemory (int level)
    当系统决定到了释放不需要内存的良好时机时调用。
    level的可能取值为TRIM_MEMORY_COMPLETE, TRIM_MEMORY_MODERATE, TRIM_MEMORY_BACKGROUND, TRIM_MEMORY_UI_HIDDEN, TRIM_MEMORY_RUNNING_CRITICAL, TRIM_MEMORY_RUNNING_LOW, TRIM_MEMORY_RUNNING_MODERATE
  • public void onTaskRemoved (Intent rootIntent)
    当该Service正在运行且用户移除了源自该Service应用程序的任务时调用。如果你设置了ServiceInfo.FLAG_STOP_WITH_TASK则你将不会接收到该回调。该Service将被简单的终止。
    rootIntent:被用来创建被移除任务的原始root intent。
  • public final void startForeground (int id, Notification notification)
    使该Service运行在前台,当处于该状态时,提供了向用户展示正在运行的通知。
  • public final void stopForeground (boolean removeNotification)
    移除该Service的前台状态。
  • public final void stopSelf ()
    终止正在运行的Service
  • public final void stopSelf (int startId)
    stopSelfResult(int)的老版本
  • public final boolean stopSelfResult (int startId)
    终止最近一次由该startId被启动的Servive。

IntentService详解

  IntentService是Service的子类,用来处理异步请求。Clients通过调用startService(Intent)方法发送请求。该Service在需要时启动,用工作线程轮流处理每一个Intent,完成工作后终止自己。
  “工作队列处理器”模式常用来从应用程序主线程卸载任务。IntentService类简化了这个模式。为了使用它,继承IntentService类并实现onHandleIntent(Intent)方法。IntentService会接受Intents,创建工作线程,并在适当的时候终止该Service。
  所有请求在一个单独的工作线程中处理,每次只能处理一个请求。
  相对Service,IntentService做了如下处理:

  • 创建默认的工作线程来执行所有传递到onStartCommand()的intents,以将处理工作从应用程序主线程中分离出去。
  • 创建一个工作队列来向onHandleIntent()每次传递一个intent,所以你不需要担心多线程问题。
  • 当所有请求被处理完后,服务自动结束,不需要再调用stopSelf()方法结束服务。
  • 提供了返回值为null的默认的onBind()方法。
  • 提供了默认的onStartCommand()方法,以发送intent到工作队列,然后到onHandleIntent()方法。
IntentService主要方法
  • public int onStartCommand (Intent intent, int flags, int startId)
    使用IntentService时,该方法不需要被重载。
  • public void setIntentRedelivery (boolean enabled)
    设置intent重传行为。通常根据需要在构造函数中调用。
    如果设置为true,onStartCommand(Intent, int, int)方法返回START_REDELIVER_INTENT,这时如果进程在onHandleIntent(Intent)方法返回前死掉,则进程会被重启并重新传递intent。如果有多个intent已经被传递,则只有最近传递的一个intent能够保证被重传。
    如果设置为false(默认),onStartCommand(Intent, int, int)方法返回START_NOT_STICKY,这时如果进程死掉,则intent不会再被重传。
  • protected abstract void onHandleIntent (Intent intent)
    该方法在工作线程中被触发,以执行每个请求任务。
IntentService使用示例
IntentService基本使用示例
public class MyIntentService extends IntentService {

    private static final String NAME = MyIntentService.class.getSimpleName();
    private static final String TAG = "sxd";

    public static final String INTENT_KEY = "intent_key";

    public MyIntentService() {
        super(NAME);
    }

    @Override
    public void onCreate() {
        Log.i(TAG, NAME + "--onCreate");
        Log.i(TAG, NAME + "--onCreate++currentThread++id:" + Thread.currentThread().getId());
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, NAME + "--onStartCommand");
        Log.i(TAG, NAME + "--onStartCommand++currentThread++id:" + Thread.currentThread().getId());
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        Log.i(TAG, NAME + "--onHandleIntent++begin:" + intent.getStringExtra(INTENT_KEY));
        Log.i(TAG, NAME + "--onHandleIntent++currentThread++id:" + Thread.currentThread().getId());
        try {
            Thread.sleep(5 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Log.i(TAG, NAME + "--onHandleIntent++end:" + intent.getStringExtra(INTENT_KEY));
    }

    @Override
    public void onDestroy() {
        Log.i(TAG, NAME + "--onDestroy");
        Log.i(TAG, NAME + "--onDestroy++currentThread++id:" + Thread.currentThread().getId());
        super.onDestroy();
    }

}
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private static final String NAME = MainActivity.class.getSimpleName();
    private static final String TAG = "sxd";

    private Button mStartService;
    private Button mStopService;
    private Button mBindService;
    private Button mUnbindService;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        mStartService = (Button) this.findViewById(R.id.start_service);
        mStartService.setOnClickListener(this);
        mStopService = (Button) this.findViewById(R.id.stop_service);
        mStopService.setOnClickListener(this);
        mBindService = (Button) this.findViewById(R.id.bind_service);
        mBindService.setOnClickListener(this);
        mUnbindService = (Button) this.findViewById(R.id.unbind_service);
        mUnbindService.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.start_service:
                startService();
                break;
            case R.id.stop_service:
                stopService();
                break;
            /*case R.id.bind_service:
                bindService();
                break;
            case R.id.unbind_service:
                unbindService();
                break;*/
        }
    }

    private void startService() {
        Log.i(TAG, NAME + "--startService++mainThread++id:" + Thread.currentThread().getId());
        Intent intent = new Intent(this, MyIntentService.class);
        intent.putExtra(MyIntentService.INTENT_KEY, "1");
        startService(intent);
        intent.putExtra(MyIntentService.INTENT_KEY, "2");
        startService(intent);
        intent.putExtra(MyIntentService.INTENT_KEY, "3");
        startService(intent);
    }

    private void stopService() {
        Intent intent = new Intent(this, MyIntentService.class);
        stopService(intent);
    }

    /*private void bindService() {
        Intent intent = new Intent(this, MyService.class);
        bindService(intent, mConnection, BIND_AUTO_CREATE);
    }

    private void unbindService() {
        unbindService(mConnection);
    }*/

}

该示例程序,点击“启动服务”按钮,执行结果如下图
这里写图片描述
执行结果证明:

  • onHandleIntent(Intent)方法确实是在独立线程运行。
  • Intent请求确实是被逐个处理,且使用相同的工作线程处理。
  • Service确实是在所有请求处理完成后,自动结束。

该示例程序,点击“启动服务”按钮后,在所有请求未处理完成时,点击“停止服务”按钮,执行结果如下图
这里写图片描述
观察执行结果可知,当主动停止Service后,Service立即终止,但当前正在处理的任务不会立即终止,而是执行完成后终止,由于Service终止,则后续任务不会再被进行传递处理。

使用Service实现IntentService功能
public class ImitateIntentService extends Service {

    private static final String NAME = ImitateIntentService.class.getSimpleName();
    private static final String TAG = "sxd";

    public static final String INTENT_KEY = "intent_key";

    private Looper mServiceLooper;
    private ServiceHandler mServiceHandler;

    // Handler that receives messages from the thread
    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }
        @Override
        public void handleMessage(Message msg) {
            Bundle bundle = msg.getData();
            Log.i(TAG, NAME + "--ServiceHandler++begin:" + bundle.getString(INTENT_KEY));
            Log.i(TAG, NAME + "--ServiceHandler++currentThread++id:" + Thread.currentThread().getId());
            // Normally we would do some work here, like download a file.
            // For our sample, we just sleep for 5 seconds.
            try {
                Thread.sleep(5 * 1000);
            } catch (InterruptedException e) {
                // Restore interrupt status.
                Thread.currentThread().interrupt();
            }
            // Stop the service using the startId, so that we don't stop
            // the service in the middle of handling another job
            Log.i(TAG, NAME + "--ServiceHandler++end:" + bundle.getString(INTENT_KEY));
            stopSelf(msg.arg1);
        }
    }

    @Override
    public void onCreate() {
        Log.i(TAG, NAME + "--onCreate");
        Log.i(TAG, NAME + "--onCreate++currentThread++id:" + Thread.currentThread().getId());
        // Start up the thread running the service.  Note that we create a
        // separate thread because the service normally runs in the process's
        // main thread, which we don't want to block.  We also make it
        // background priority so CPU-intensive work will not disrupt our UI.
        HandlerThread thread = new HandlerThread(NAME, Process.THREAD_PRIORITY_BACKGROUND);
        thread.start();

        // Get the HandlerThread's Looper and use it for our Handler
        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, NAME + "--onStartCommand");
        Log.i(TAG, NAME + "--onStartCommand++currentThread++id:" + Thread.currentThread().getId());

        // For each start request, send a message to start a job and deliver the
        // start ID so we know which request we're stopping when we finish the job
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        Bundle bundle = new Bundle();
        bundle.putString(INTENT_KEY, intent.getStringExtra(INTENT_KEY));
        msg.setData(bundle);
        mServiceHandler.sendMessage(msg);

        // If we get killed, after returning from here, restart
        return START_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, NAME + "--onBind");
        Log.i(TAG, NAME + "--onBind++currentThread++id:" + Thread.currentThread().getId());
        // We don't provide binding, so return null
        return null;
    }

    @Override
    public void onDestroy() {
        Log.i(TAG, NAME + "--onDestroy");
        Log.i(TAG, NAME + "--onDestroy++currentThread++id:" + Thread.currentThread().getId());
    }
}

该示例程序使用Service和Handler,实现了IntentService功能。
该示例程序,点击“启动服务”按钮,执行结果如下图
这里写图片描述
执行结果和IntentService示例完全一样。

Service使用示例

本地Service的使用示例
public class MyService extends Service {

    private static final String NAME = MyService.class.getSimpleName();
    private static final String TAG = "sxd";

    private MyBinder mBinder = new MyBinder();

    @Override
    public void onCreate() {
        //该Service被首次创建时调用
        Log.i(TAG, NAME + "--onCreate");
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, NAME + "--onStartCommand");
        //该Service正在运行,client再次调用startService()时调用
        return super.onStartCommand(intent, flags, startId);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, NAME + "--onBind");
        //client调用bindService()绑定到该Service时调用
        return mBinder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.i(TAG, NAME + "--onUnbind");
        //所有已绑定到该Service的client调用unbindService()与该Service解绑时调用
        return super.onUnbind(intent);
    }

    @Override
    public void onRebind(Intent intent) {
        Log.i(TAG, NAME + "--onRebind");
        //当onUnbind()被调用后,又有新的client调用bindService()来绑定该Service时调用
        super.onRebind(intent);
    }

    @Override
    public void onDestroy() {
        Log.i(TAG, NAME + "--onDestroy");
        //该Service不再使用,销毁时调用
        super.onDestroy();
    }

    class MyBinder extends Binder {

        public void doSomething() {
            Log.i(TAG, NAME + "--MyBinder++doSomething()");
        }

    }

}
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private static final String NAME = MainActivity.class.getSimpleName();
    private static final String TAG = "sxd";

    private Button mStartService;
    private Button mStopService;
    private Button mBindService;
    private Button mUnbindService;

    private MyService.MyBinder myBinder;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        mStartService = (Button) this.findViewById(R.id.start_service);
        mStartService.setOnClickListener(this);
        mStopService = (Button) this.findViewById(R.id.stop_service);
        mStopService.setOnClickListener(this);
        mBindService = (Button) this.findViewById(R.id.bind_service);
        mBindService.setOnClickListener(this);
        mUnbindService = (Button) this.findViewById(R.id.unbind_service);
        mUnbindService.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.start_service:
                startService();
                break;
            case R.id.stop_service:
                stopService();
                break;
            case R.id.bind_service:
                bindService();
                break;
            case R.id.unbind_service:
                unbindService();
                break;
        }
    }

    private void startService() {
        Intent intent = new Intent(this, MyService.class);
        startService(intent);
    }

    private void stopService() {
        Intent intent = new Intent(this, MyService.class);
        stopService(intent);
    }

    private void bindService() {
        Intent intent = new Intent(this, MyService.class);
        bindService(intent, mConnection, BIND_AUTO_CREATE);
    }

    private void unbindService() {
        unbindService(mConnection);
    }

    private ServiceConnection mConnection = new ServiceConnection() {

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.i(TAG, NAME + "--onServiceDisconnected++name:" + name.getClassName());
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.i(TAG, NAME + "--onServiceConnected++name:" + name.getClassName());
            myBinder = (MyService.MyBinder) service;
            myBinder.doSomething();
        }
    };

}
远程Service的使用示例
应用程序内使用远程Service示例

IMyServiceAIDL.aidl文件

interface IMyServiceAIDL {

    int add(int a, int b);

}

  远程Service运行在独立进程中,Activity等组件要与其通信,则需要使用AIDL来实现跨进程通信。AIDL是Android接口定义语言。创建.aidl文件,定义需要进行的操作。.aidl文件编译后,会自动生成相应的.java文件。

MyService.java文件

public class MyService extends Service {

    private static final String NAME = MyService.class.getSimpleName();
    private static final String TAG = "sxd";

    @Override
    public void onCreate() {
        //该Service被首次创建时调用
        Log.i(TAG, NAME + "--onCreate");
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, NAME + "--onStartCommand");
        //该Service正在运行,client再次调用startService()时调用
        return super.onStartCommand(intent, flags, startId);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, NAME + "--onBind");
        //client调用bindService()绑定到该Service时调用
        return new MyServiceAIDLImpl();
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.i(TAG, NAME + "--onUnbind");
        //所有已绑定到该Service的client调用unbindService()与该Service解绑时调用
        return super.onUnbind(intent);
    }

    @Override
    public void onRebind(Intent intent) {
        Log.i(TAG, NAME + "--onRebind");
        //当onUnbind()被调用后,又有新的client调用bindService()来绑定该Service时调用
        super.onRebind(intent);
    }

    @Override
    public void onDestroy() {
        Log.i(TAG, NAME + "--onDestroy");
        //该Service不再使用,销毁时调用
        super.onDestroy();
    }

    class MyServiceAIDLImpl extends IMyServiceAIDL.Stub {

        @Override
        public int add(int a, int b) throws RemoteException {
            return a + b;
        }

    }

}

  onBind()方法中需要返回.aidl文件定义接口的实例。

在manifest中注册Service

<service
            android:name=".MyService"
            android:process=":remote">
            <intent-filter>
                <action android:name="com.example.sunxiaodong.remoteservice.MyService"/>
            </intent-filter>
        </service>

  Service注册时,使用android:process声明属性将该Service定义为远程Service。因为在其它应用程序中,不能获得该Service的具体类名,所以如果需要在其它应用程序中使用该远程Service,则需要为该Service定义action。

MainActivity.java文件

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private static final String NAME = MainActivity.class.getSimpleName();
    private static final String TAG = "sxd";

    private Button mStartService;
    private Button mStopService;
    private Button mBindService;
    private Button mUnbindService;

    private EditText mAddendOne;
    private EditText mAddendTwo;
    private TextView mResult;

    private IMyServiceAIDL mIMyServiceAIDL;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        mStartService = (Button) this.findViewById(R.id.start_service);
        mStartService.setOnClickListener(this);
        mStopService = (Button) this.findViewById(R.id.stop_service);
        mStopService.setOnClickListener(this);
        mBindService = (Button) this.findViewById(R.id.bind_service);
        mBindService.setOnClickListener(this);
        mUnbindService = (Button) this.findViewById(R.id.unbind_service);
        mUnbindService.setOnClickListener(this);
        mAddendOne = (EditText) this.findViewById(R.id.addend_one);
        mAddendTwo = (EditText) this.findViewById(R.id.addend_two);
        mResult = (TextView) this.findViewById(R.id.result);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.start_service:
                startService();
                break;
            case R.id.stop_service:
                stopService();
                break;
            case R.id.bind_service:
                bindService();
                break;
            case R.id.unbind_service:
                unbindService();
                break;
        }
    }

    private void startService() {
        Intent intent = new Intent(this, MyService.class);
        startService(intent);
    }

    private void stopService() {
        Intent intent = new Intent(this, MyService.class);
        stopService(intent);
    }

    private void bindService() {
        Intent intent = new Intent(this, MyService.class);//显示启动Service
        /*Intent intent = new Intent("com.example.sunxiaodong.remoteservice.MyService");
        intent.setPackage("com.example.sunxiaodong.remoteservice");//Android5.0后Service不能采用隐式启动,所以必须加上包名*/
        bindService(intent, mConnection, BIND_AUTO_CREATE);
    }

    private void unbindService() {
        unbindService(mConnection);
    }

    private ServiceConnection mConnection = new ServiceConnection() {

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mIMyServiceAIDL = IMyServiceAIDL.Stub.asInterface(service);
            try {
                int result = mIMyServiceAIDL.add(Integer.parseInt(mAddendOne.getText().toString()), Integer.parseInt(mAddendTwo.getText().toString()));
                mResult.setText(String.valueOf(result));
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    };

}

  使用IMyServiceAIDL.Stub.asInterface()方法将传入的IBinder对象传换成了IMyServiceAIDL对象,接下来就可以调用在IMyServiceAIDL.aidl文件中定义的所有接口了。
  该示例程序,填入两个加数后,点击“绑定服务”按钮,就可在结果位置,显示两个加数之和。

其他应用程序使用远程Service示例

  在该应用程序中,要使用其它应用程序的远程Service,需要将该远程Service的.aidl文件连同包名路径一起拷贝到该应用程序中。

client应用程序的MainActivity.java文件

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private static final String NAME = MainActivity.class.getSimpleName();
    private static final String TAG = "sxd";

    private Button mStartService;
    private Button mStopService;
    private Button mBindService;
    private Button mUnbindService;

    private EditText mAddendOne;
    private EditText mAddendTwo;
    private TextView mResult;

    private IMyServiceAIDL mIMyServiceAIDL;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        mStartService = (Button) this.findViewById(R.id.start_service);
        mStartService.setOnClickListener(this);
        mStopService = (Button) this.findViewById(R.id.stop_service);
        mStopService.setOnClickListener(this);
        mBindService = (Button) this.findViewById(R.id.bind_service);
        mBindService.setOnClickListener(this);
        mUnbindService = (Button) this.findViewById(R.id.unbind_service);
        mUnbindService.setOnClickListener(this);
        mAddendOne = (EditText) this.findViewById(R.id.addend_one);
        mAddendTwo = (EditText) this.findViewById(R.id.addend_two);
        mResult = (TextView) this.findViewById(R.id.result);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.start_service:
                startService();
                break;
            case R.id.stop_service:
                stopService();
                break;
            case R.id.bind_service:
                bindService();
                break;
            case R.id.unbind_service:
                unbindService();
                break;
        }
    }

    private void startService() {
        Intent intent = new Intent("com.example.sunxiaodong.remoteservice.MyService");
        intent.setPackage("com.example.sunxiaodong.remoteservice");//Android5.0后Service不能采用隐式启动,所以必须加上包名
        startService(intent);
    }

    private void stopService() {
        Intent intent = new Intent("com.example.sunxiaodong.remoteservice.MyService");
        intent.setPackage("com.example.sunxiaodong.remoteservice");//Android5.0后Service不能采用隐式启动,所以必须加上包名
        stopService(intent);
    }

    private void bindService() {
        Intent intent = new Intent("com.example.sunxiaodong.remoteservice.MyService");
        intent.setPackage("com.example.sunxiaodong.remoteservice");//Android5.0后Service不能采用隐式启动,所以必须加上包名
        bindService(intent, mConnection, BIND_AUTO_CREATE);
    }

    private void unbindService() {
        unbindService(mConnection);
    }

    private ServiceConnection mConnection = new ServiceConnection() {

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mIMyServiceAIDL = IMyServiceAIDL.Stub.asInterface(service);
            try {
                int result = mIMyServiceAIDL.add(Integer.parseInt(mAddendOne.getText().toString()), Integer.parseInt(mAddendTwo.getText().toString()));
                mResult.setText(String.valueOf(result));
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    };

}

  对远程Service的使用,基本上同相同应用程序内对远程Service的使用方法相同,只是在启动该Service时,需要使用action。

源码地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章