清晰地理解Service

目的:

1.Service的start和bind状态有什么区别

2.同一个Service,先startService,然后再bindService,如何把它停止掉

3.有注意到Service的onStartCommand方法的返回值么?不同返回值有什么区别

4.Service的生命周期方法onCreate,onStart,onBind等运行在哪个线程中?


介绍:

大多数朋友肯定都知道Service,它属于计算型组件,就是处理复杂的计算,或者下载,就是在后台长期运行,还有一个特点:生命周期很长,没有界面。作为Android的四大组件的一员,可看出其重要性。我得理解是:service和activity是兄弟,一个有界面,一个没有界面而儿。它在日常生活中,我们也肯定接触得到,Service能在后台中进行一些长期运行的服务,例如听歌 和下载等等。通常在应用管理查看手机运行中的app,都会看到一个app都有两到四个服务。

用法:

第一种方式:通过startService启动Service

1.首先新建一个TestService继承Service,必须重写onBind方法,这里我们重写onCreate,onStartCommand,onDestroy方法,如下图:
public class TestService extends Service {
    private String TAG = "TestService";


    @Override
    public void onCreate(){
        super.onCreate();
        Log.d(TAG,"concreate()方法执行");
    }

    @Override
    public int onStartCommand(Intent intent,int flags,int startId){
        Log.d(TAG,"onstartcommand()方法执行");
        return super.onStartCommand(intent,flags,startId);
    }


    @Override
    public void onDestroy(){
        Log.d(TAG,"ondestroy执行");
        super.onDestroy();
    }


    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG,"onBind()方法执行");
        return null;
    }
}

在每个方法分别打印一句话,就是为了验证开启服务和关闭服务都走哪个方法。
2.程序的主布局文件:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.nova.servicedemo.activity.MainActivity">

    <TextView
        android:id="@+id/tv_startservice"
        android:layout_width="200dp"
        android:layout_height="40dp"
        android:text="开始服务"
        android:layout_centerInParent="true"
        android:background="@drawable/bg_startservice"
        android:gravity="center"
        android:textColor="@color/white"/>

    <TextView
        android:id="@+id/tv_stopservice"
        android:layout_width="200dp"
        android:layout_height="40dp"
        android:layout_centerHorizontal="true"
        android:layout_below="@id/tv_startservice"
        android:layout_marginTop="20dp"
        android:background="@drawable/bg_stopservice"
        android:text="停止服务"
        android:gravity="center"
        android:textColor="#FFFFFF"/>
     <TextView
        android:id="@+id/tv_finish
        android:layout_width="200dp"
        android:layout_height="40dp"
        android:layout_centerHorizontal="true"
        android:layout_below="@id/tv_stopservice"
        android:text="finish"
        android:gravity="center"/>

</RelativeLayout>可看到只有两个TextView,一个是开启服务,另一个是停止服务。

3.在主界面里面加入启动Service和停止Service的逻辑,如下所示:

public class MainActivity extends Activity {

    @Bind(R.id.tv_startservice)
    TextView tv_startservice;

    @Bind(R.id.tv_stopservice)
    TextView tv_stopservice;

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

    @OnClick({R.id.tv_stopservice,R.id.tv_startservice,R.id.tv_finish})
    void Onclick(View v){
        switch(v.getId()){
            case R.id.tv_startservice:
                Intent startIntent = new Intent(MainActivity.this, TestService.class);
                startService(startIntent);
                break;
            case R.id.tv_stopservice:
                Intent stopIntent = new Intent(MainActivity.this,TestService.class);
                stopService(stopIntent);
                break;
            case R.id.tv_finish:
finish();
break; } }}逻辑很简单,就是新建一个intent启动service和停止service.
4.最后也要在AndroidManifest.xml中进行注册:
 <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".activity.MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <service android:name=".service.TestService"/>
    </application>

现在运行程序,点击开启服务,看到KogCat打印:


可看到当启动一个service的时候,会调用该Service的onCreate()和onStartCommand()方法。这时候如果再点击一次开始服务,看看LogCat打印什么


可见onCreate()方法没有执行,只执行onStartCommand()方法。也就是说,当用start方式来启动服务时,如果该Service已经启动了,如果再次调用startService()方法,onCreate()方法都不会执行。点击停止服务的时候,会执行onDestroy()方法,就不贴图了。
当服务启动了,点击finish,service没有执行任何方法,也就是说service没有销毁,后台一直启动。

第二种方式:通过bindService启动Service

1.bindService启动的服务和调用者是client-server模式,调用者是client,service是server,service只有一个,但是绑定到service上面的client可以有多个。
2.client可以通过IBinder接口获取Service实例,实现在client端直接调用Service中的方法,startService方法启动中无法实现。
3.bindService启动服务的生命周期和其绑定的client息息相关,当client销毁时,client会自动和Service解除绑定,client也可以调用context的unbindService()方法与Service解除绑定,如果Service没有和任何的client绑定时,会自行销毁。
总而言之,通过bind启动,可以和组件建立关联。

修改Service中的方法,上面有个onBind()方法没有用到,这个方法时用于和组件建立关联:

public class TestService extends Service {
    private String TAG = "TestService";

    //通过binder实现调用者client与Service之间通信
    private Mbinder mBinder = new Mbinder();


    @Override
    public void onCreate(){
        super.onCreate();
        Log.d(TAG,"concreate()方法执行");
    }

    @Override
    public int onStartCommand(Intent intent,int flags,int startId){
        Log.d(TAG,"onstartcommand()方法执行");
        return super.onStartCommand(intent,flags,startId);
    }


    @Override
    public void onDestroy(){
        Log.d(TAG,"ondestroy执行");
        super.onDestroy();
    }


    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG,"onBind()方法执行");
        return mBinder;
    }

    class Mbinder extends Binder {
        public void hello(){
            Log.d(TAG,"hello方法执行");
        }
    }
}

修改xml中的代码,添加用于bind启动服务和取消绑定的方式:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.nova.servicedemo.activity.MainActivity">

    <TextView
        android:id="@+id/tv_startservice"
        android:layout_width="200dp"
        android:layout_height="40dp"
        android:text="开始服务"
        android:layout_centerHorizontal="true"
        android:background="@drawable/bg_startservice"
        android:gravity="center"
        android:textColor="@color/white"
        android:clickable="true"
        android:layout_marginTop="20dp"/>

    <TextView
        android:id="@+id/tv_stopservice"
        android:layout_width="200dp"
        android:layout_height="40dp"
        android:layout_centerHorizontal="true"
        android:layout_below="@id/tv_startservice"
        android:layout_marginTop="20dp"
        android:background="@drawable/bg_stopservice"
        android:text="停止服务"
        android:gravity="center"
        android:textColor="#FFFFFF"/>


    <TextView
        android:id="@+id/tv_bindservice"
        android:layout_width="200dp"
        android:layout_height="40dp"
        android:layout_centerHorizontal="true"
        android:layout_below="@id/tv_stopservice"
        android:gravity="center"
        android:text="使用bind开启服务"
        android:layout_marginTop="20dp"
        android:background="@drawable/bg_startservice"
        android:textColor="@color/white"/>

    <TextView
        android:id="@+id/tv_unbindservice"
        android:layout_width="200dp"
        android:layout_height="40dp"
        android:layout_centerHorizontal="true"
        android:layout_below="@id/tv_bindservice"
        android:background="@drawable/bg_stopservice"
        android:layout_marginTop="20dp"
        android:gravity="center"
        android:text="使用unbind service停止"
        android:textColor="@color/white"/>

    <TextView
        android:id="@+id/tv_finish"
        android:layout_width="200dp"
        android:layout_height="40dp"
        android:text="finish界面"
        android:layout_marginTop="20dp"
        android:layout_centerHorizontal="true"
        android:background="@drawable/bg_startservice"
        android:layout_below="@id/tv_unbindservice"
        android:textColor="@color/white"
        android:gravity="center"/>

</RelativeLayout>



下面继续修改主界面代码,让MainActivity和TestService之间建立关联,思想就是:创建ServiceConnection类型实例,重写onServiceConnected()方法和onServiceDisconnected()方法,当执行到onServiceConnected回调时,这样就可以实现client和Service的连接。当onServiceDisconnected回调被执行时,表示client和Server断开连接,这个方法可以处理断开连接后的事情。通过IBinder实例得到Service实例对象,有了这个实例,Activity和Service之间就变得紧密,就可以在activity中调用Mbinder的方法。

public class MainActivity extends Activity {

    @Bind(R.id.tv_startservice)
    TextView tv_startservice;//使用start方式启动服务

    @Bind(R.id.tv_stopservice)
    TextView tv_stopservice;//停止服务

    @Bind(R.id.tv_bindservice)
    TextView tv_bindservice;//使用bind方式开启服务
    @Bind(R.id.tv_unbindservice)
    TextView tv_unbindservice;//取消绑定

    @Bind(R.id.tv_finish)
    TextView tv_finish;

    private TestService.Mbinder mBinder;
    private String TAG = "TestService";

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

    @OnClick({R.id.tv_stopservice,R.id.tv_startservice,R.id.tv_bindservice,R.id.tv_unbindservice,R.id.tv_finish})
    public void Onclick(View v){
        switch(v.getId()){
            case R.id.tv_startservice:
                Intent startIntent = new Intent(MainActivity.this, TestService.class);
                startService(startIntent);
                break;
            case R.id.tv_stopservice:
                Intent stopIntent = new Intent(MainActivity.this,TestService.class);
                stopService(stopIntent);
                break;
            case R.id.tv_bindservice:
                Intent bindIntent = new Intent(MainActivity.this,TestService.class);
                bindService(bindIntent,conn,BIND_AUTO_CREATE);
                break;
            case R.id.tv_unbindservice:
                unbindService(conn);
                break;
            case R.id.tv_finish:
                finish();
                break;

        }

    }

    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mBinder = (TestService.Mbinder)service;
            mBinder.hello();
            Log.d(TAG,"connected()方法执行");
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.d(TAG,"disconnected()方法执行");
        }
    };



}


通过调用bindService()方法将activity和service进行绑定,bindService()方法有三个参数,第一个参数是构建出的intent对象,第二个是ServiceConnention实例,第三个是标志位,BIND_AUTO_CREATE表示在activity和Service建立关联后自动创建Service.
下面点击bindService,看下log的输出:



发现当执行bindService之后:
1.如果Service不存在,则执行Service执行onCreate(),onBind()方法,client实例ServiceConnection执行onServiceConnected()方法。
2.如果Service存在,不会执行任何方法。

当点击unbindservice之后:
会执行service的ondestroy()方法


测试步骤2:

step1:点击bindService
step2:点击finish
发现:



发现ondestroy()方法执行了,也就是说如果使用bind开启服务,如果组件销毁了,service也会自动销毁。


如何销毁Service

step1:点击startService
step2:点击BindService
如何销毁Service?



上图是同时点击start和bind开启服务所执行的方法。

现在点击stopService:
发现没有执行onDestroy()方法,也就是没有销毁Service.
现在再点击unbundservice:



发现service销毁了。
现在重复上面操作,开启start和bind同时开启服务,但是这次先点击unbindservice,看看service有没有销毁:
发现先按unbindservice service没有销毁,再按stopservice,发现service销毁了:



由此可得出结论:
当如果同一个Service,先startService,然后再bindService,销毁service的方法是:stopservice和unbindservice要同时执行。

论onStartCommand方法的返回值

留意到onStartCommand这个方法最后是返回int值,点击源码发现:会返回的值有四种:



1.START_STICKY
表示Service运行的进程被Android系统强制杀掉之后,Android系统会将该service依然设置为started(运行状态),保留service的状态为开始状态,但不保留递送的intent对象。随后系统会尝试重新创建service,由于服务状态为开始状态,所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令被传递到service,那么参数Intent将为null。也就是说如果service可以任意时刻运行或结束都没什么问题,而且不需要intent信息,那么就可以在onStartCommand方法中返回START_STICKY.
2.START_NOT_STICKY
“非粘性的”。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统不会自动重启该服务
3.START_REDELIVER_INTENT
重传Intent。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,Android系统会再次将service在被杀掉之前最后一次传入onStartCommand方法中的intent再次保留下来并再次传入到重新创建后的service的onStartCommand方法中,这样就可以读到intent参数。
4.START_STICKY_COMPATIBILITY
START_STICKY的兼容版本,但不保证服务被kill后一定能重启。


系统的默认策略:


Service的生命周期方法onCreate,onStart,onBind等运行在哪个线程中?

现在我们在主界面和service的生命周期方法打印当前线程id的语句

在Mainactivity中打印:
Log.d("TestService","MianActivity 线程是:" +Thread.currentThread().getId());

在service的各个方法中打印:
Log.d("TestService","oncreate 线程是:" +Thread.currentThread().getId());


可看到:线程id是完全一样的,证实了service确实运行在主线程中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值