Android基础(五) Service全解析----看不见的Activity
android应用,android开发,android,java,sdk2016-06-07
作为Android四大组件之一,Service(服务)也经常运用于我们的日常使用中,它与Activity的区别在于:Service一直在后台运行,没有用户界面,所以绝不会到前台来。但Service被启动起来之后,它就和Activity一样,完全具有自己的生命周期。在关于程序中是选择用Activity还是Service的一个选择标准就是:如果某个程序组件需要运行时向用户呈现某种用户界面,或者该程序需要与用户交互,就需要使用Activity,否则就该考虑使用Service。
就像开发Activity需要两个步骤:①开发Activity子类;②在AndroidManifest文件中配置Activity。开发Service也需要两个步骤:①定义一个继承Service的子类;②在AndroidManifest文件中配置该Service。
public class FirstService extends Service { @Nullable @Override /** * Service中唯一的一个抽象方法,子类必须实现, * 该方法返回一个IBinder对象,应用程序可通过该对象与Service通信 */ public IBinder onBind(Intent intent) { return null; } /** * 在Service第一次被创建后将立即回调该方法 */ @Override public void onCreate() { super.onCreate(); Log.i("TAG","Service is Created"); } /** * 每次客户端调用startService(Intent)方法启动该Service时都会回调该方法 * @return */ @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i("TAG","Service is Started"); return super.onStartCommand(intent, flags, startId); } /** * 在该Service被关闭之前将会回调该方法 */ @Override public void onDestroy() { Log.i("TAG","Service is Destroyed!"); super.onDestroy(); } }
这个Service类重写了Service组件的onCreate()、onStartCommand()、onDestroy()、onBind()方法,重写这些方法时只是简单地输出了一条字符串,除此之外什么也没干。
当然,为了启动这个服务我们还是要在Activity中写明的:
package com.bigmoney.www.servicetest; import android.content.Intent; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; public class MainActivity extends AppCompatActivity implements View.OnClickListener { private Button serviceStart; private Button serviceStop; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); serviceStart = (Button) findViewById(R.id.button1); serviceStop = (Button) findViewById(R.id.button2); serviceStart.setOnClickListener(this); serviceStop.setOnClickListener(this); } @Override public void onClick(View v) { Intent intent = new Intent(this,FirstService.class); switch (v.getId()){ case R.id.button1: startService(intent) ;//用意图启动服务 case R.id.button2: stopService(intent); //用意图停止服务 default: break; } } }
由activity中的代码可知,点击第一个按钮后,后台将打印出如下log:
再点击第二个按钮后,后台会有如下log:
本程序中启动Service的方法属于“通过Context的startService()方法”,通过此方法启动Service,访问者与Service之间没有关联,即使访问者退出了,Service也仍然运行。
(有人可能注意到了这里使用了显式Intent(直接指定要启动的目标组件的实现类)来启动Service,而并没有使用隐式,并不是我偷懒,而是从Android5.0开始,Google要求必须使用显式Intent启动Service组件)。
Service也是context中的一份子,可以把Service看成是一个长期运行在后台的,没有界面的Activity。
是的,也许你看到这里会想服务难道还有另外一种启动方式?是的,在讲另外一种启动方式之前,我们先来明确下为什么需要使用服务:
一般情况下,如果我们想让某一块逻辑长期运行在后台,那么可以把它放到服务里面去执行。这也决定了服务的用途:①长期在后台监听设备接入;②在后台轮巡服务器获取数据(股票类软件等需要频繁刷新操作);③在后台播放音乐(音乐播放器)……等等。
①通过Context的startService()方法:通过该方法启动Service,访问者与Service之间没有关联,即使访问者退出了,Service也仍然运行;
②通过Context的BindService()方法:使用该方法启动Service,访问者与Service绑定在一起,访问者一旦退出,Service也就终止了。
用第①种方式启动的代码上面已经给出,启动、关闭Service十分简单,调用Context里定义的startService()、stopService()方法即可启动、关闭Service。如果在不关闭Service的情况下,连续三次单击“开启Service”,程序将连续三次启动Service,可以看到logcat中连续三次打印出“Service is Started”的log信息。可以由此看出,每当Service被创建时会回调onCreate()方法,每次Service被启动时会回调onStartCommand()方法——多次启动一个已有的Service组件将不会再回调onCreate()方法,但每次启动时都会回调onStartCommand()方法。
当程序通过startService()和stopService()启动、关闭Service时,Service与访问者之间基本不存在太多的关联,因此Service和访问者之间也无法进行通信、交换数据。
如果Service和访问者之间需要进行方法调用或者交换数据,则应该使用bindService()或者unbindService()方法启动、关闭Service。这里就需要用到我们的第②中启动方式了,也叫“绑定Service”。
实例代码如下:
ServiceDemo类:
package com.bigmoney.www.sevvvvv; import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.support.annotation.Nullable; import android.util.Log; import android.widget.Toast; /** * Created by hasee on 2016/3/19. * @param */ public class ServiceDemo extends Service { /** * 这是服务内部的代理,用于当activity与服务绑定之后,返回给activity */ class MyBinder extends Binder { /** * 内部类就通过自己的方法去调用服务的方法,这个内部类方法实际上就是给外部访问的 * 也就是给activity访问的 * @param name * @param money */ public void callMethodInService(String name, int money){ methodInService(name, money); }; } private void methodInService(String name, int money) { Toast.makeText(this,name+"有"+money+"钱",Toast.LENGTH_LONG).show(); } /** * 当服务成功绑定之后,就返回这里的内部对象 * 所谓的“绑定”就是把activity和Service放到一个Map中去形成映射关系 * @param intent * @return */ @Nullable @Override public IBinder onBind(Intent intent) { return new MyBinder(); } @Override public void onCreate() { super.onCreate(); Log.i("vim","onCreate---"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i("vim","onStartCommand---"); return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { super.onDestroy(); Log.i("vim", "onDestroy---"); } }
MainActivity类:
package com.bigmoney.www.sevvvvv; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.support.v7.app.AppCompatActivity; import android.view.View; public class MainActivity extends AppCompatActivity { ServiceDemo.MyBinder binder; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } class Myconn implements ServiceConnection { /** * 当服务成功绑定上之后调用,里面有一个参数非常重要就是第二个参数, * 这个参数就是服务里面的onBind方法返回的内部对象 */ @Override public void onServiceConnected(ComponentName name, IBinder service) { //这里的service实际上就是ServiceDemo类 binder = (ServiceDemo.MyBinder) service; } @Override public void onServiceDisconnected(ComponentName name) { } } Myconn myconn = new Myconn(); //绑定服务 public void bind(View v) { //启动服务---startService //绑定服务---bindService Intent intent = new Intent(this, ServiceDemo.class); //参数二:用于接收当前服务的一些状态,是启动还是停止了 //参数三:服务不存在,就创建然后绑定,存在则直接绑定 bindService(intent, myconn, BIND_AUTO_CREATE); } //调用服务中的方法 public void call(View v) { //调用了Service里面的呼叫服务的方法 binder.callMethodInService("张三", 1000); } //解除绑定 public void unbind(View v) { Intent intent = new Intent(this, ServiceDemo.class); unbindService(myconn); } }
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.bigmoney.www.sevvvvv.MainActivity"> <Button android:onClick="bind" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="绑定服务" /> <Button android:onClick="call" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="调用服务中的方法" /> <Button android:onClick="unbind" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="解除绑定服务" /> </LinearLayout>
最后运行起来如图:
当我们点击“绑定服务”后会打印如下log:
当我们点击“调用服务中的方法”时,会有如下效果:
当我们点击“解出绑定服务”时,
上面的代码示范了如何在Activity中绑定本地Service,该程序的Service类需要“真正”实现onBind()方法,并让该方法返回一个有效的IBinder对象。
public IBinder onBind(Intent intent) { return new MyBinder(); }
这段代码返回了一个可访问该Service状态数据的IBinder对象,该对象将被传给该Service的访问者。
Service中的内部类MyBinder通过继承Binder类实现了一个IBinder对象,这对于绑定本地Service并与之通信是一种常见场景。
对于Service的onBinder()方法所返回的IBinder对象来说,它可被当成该Service组件所返回的代理对象,Service允许客户端通过该IBinder对象来访问Service内部的数据,这样即可实现客户端与Service之间的通信。与多次调用startService()方法启动Service不同的是,多次调用bindService()方法并不会执行重复绑定。对于前一个实例程序,用户每单击“启动服务”按钮一次,系统就会回调Service的onStartCommand()方法一样;对于这个示例程序,不管用户单击“绑定服务”多少次,系统只会回调用Service的onBind()方法一次。
这里,我们可以总结下绑定服务的步骤:
1、定义服务类、声明服务中的方法
public class ServiceDemo extends Service{...}
2、在AndroidManifest文件中注册服务
3、定义一个内部类,继承Binder这种类型
class MyBinder extends Binder{...}
4、在onBind方法里面返回内部类对象
public IBinder onBind(Intent intent) {
return new MyBinder();
}
5、在activity里面绑定服务
public void bind(View v){...}
6、在onServiceConnected方法里面获取到内部对象
class MyConn implements ServiceConnection{
public void onServiceConnected(ComponentName name, IBinder service) {// IBinder service = new MyBinder();
binder = (MyBinder) service;
}
7、根据内部对象调用内部类中的方法,由它去调用服务中的方法
public void call(View v){
binder.callMethodInService("张三",
10000);
}
在官方文档中,服务的生命周期如上:
常规的service整体的生命周期是从onCreate()被调用开始,到onDestroy()方法返回为止。
和activity一样,service在onCreate()中进行它的初始化工作,在onDestroy()中释放残留的资源。
比如,一个音乐播放service可以在onCreate()中创建播放音乐的线程,在onDestory()中停止这个线程。
onCreate() 和 onDestroy()会被所有的service调用,不论service是通过startService()还是bindService()建立。
绑定service积极活动的生命周期是从onStartCommand() 或onBind()被调用开始,它们各自处理由startService()或 bindService()方法传过来的Intent对象。
如果service是被开启的,那么它的活动生命周期和整个生命周期一同结束。
如果service是被绑定的,它们它的活动生命周期是在onUnbind()方法返回后结束。
注意:尽管一个被开启的service是通过调用 stopSelf() 或 stopService()来停止的,没有一个对应的回调函数与之对应,即没有onStop()回调方法。所以,当调用了停止的方法,除非这个service和客户组件绑定,否则系统将会直接销毁它,onDestory()方法会被调用,并且是这个时候唯一会被调用的回调方法。
① 生命周期不同
> startService : onCreate -- onStartCommand -- onDestroy
> bindService : onCreate - onBind -- onUnBind -- onDestroy
② 与服务通讯方式不同
> startService : 通过intent传递code来区分。(无法与服务进行通讯)
> bindService : 通过内部代理对象操作服务中的方法
③ 与开启者的关系不同
> startService :即使开启者(activity)已经被销毁了, 服务依然运行着
> bindService : 当开启销毁的时候,服务也跟着销毁。
④ 在设置应用显示不同
> startService: 在设置应用里面有显示当前正在运行的服务
> bindService : 在设置应用里面没有显示。
如果使用startService启动服务,是可以让服务在后台一直运行,但是没有办法与服务进行通讯; 如果是用的是bindService启动服务,可以与服务进行通讯,但是没有办法让服务一直在后台运行。能不能让服务长期运行在后台,并且还能与服务进行通讯?答案是可以的。
具体操作步骤如下:
1. 启动服务
2. 绑定服务
3. 调用服务方法
4. 解除绑定
5. 停止服务
以上两个服务类代码都是模板代码,直接套用即可,这里不做赘述。
注意: 每次与服务进行通讯完毕,都记得解除绑定服务,否则服务有可能在退出界面的时候停止。
还有一种远程服务,我将在下片博文里为大家详细介绍。