《第一行代码》阅读笔记(三十二)——探究服务
——第一行代码
服务( Service )是Android中实现程序后台运行的解决方案,它非常适合去执行那些不需要和用户交互而且还要求长期运行的任务。服务的运行不依赖于任何用户界面,即使程序被切换到后台,或者用户打开了另外一个应用程序,服务仍然能够保持正常运行。
不过需要注意的是,服务并不是运行在一个独立的进程当中的,而是依赖于创建服务时所在的应用程序进程。当某个应用程序进程被杀掉时,所有依赖于该进程的服务也会停止运行。
另外,也不要被服务的后台概念所迷惑,实际上服务并不会自动开启线程,所有的代码都是默认运行在主线程当中的。也就是说,我们需要在服务的内部手动创建子线程,并在这里执行具体的任务,否则就有可能出现主线程被阻塞住的情况。
定义一个服务
服务作为四大组织之一,必须需要在AndroidManifest中注册才能使用,但是如果使用便捷方式注册服务,编辑器就可以帮我们自动注册。之后需要了解的就是一些自定义服务需要重写的方法。
如果你是自动生成的服务,那么会自带一个构造函数和onBind(),不过现在都不需要了解。之后需要重写三个方法才能让服务起作用,onCreate()、onStartCommand()和onDestroy()。这些方法顾名思义,值得注意的就是一般的逻辑需写在onStartCommand()方法中。具体的解释就放在生命周期里面说吧。
启动和停止服务
没有意外的,服务也和广播一样都是使用intent驱动的。
而传递方式和活动之间的显示传递,几乎一模一样。区别在于启动的方法和传递的参数,活动之间信息的intent接收的是环境和指定活动类,而服务那就是指定服务类了。启动的放也用startActivity()变成了startService(),并且还多了一个stopService()用于停止服务。具体代码可以查阅书籍,作者解释的十分清晰。
书中还指出startService()和stopService()均是由活动类对服务做出判断,而服务如何让自己停止呢?就需要调用stopSelf()。
——第一行代码
onCreate()方法是在服务第一次创建的时候调用的,而onStartCommand()方法则在每次启动服务的时候都会调用,由于刚才我们是第一次点击Start Service按钮,服务此时还未创建过,所以两个方法都会执行,之后如果你再连续多点击几次Start Service按钮,你就会发现只有onStartCommand()方法可以得到执行了。
活动和服务之间的通信
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/one"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="1"/>
<Button
android:id="@+id/two"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="2"/>
<Button
android:id="@+id/three"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="3"/>
<Button
android:id="@+id/four"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="4"/>
</LinearLayout>
MyService
package com.firstcode.servicetest;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
public class MyService extends Service {
private static final String TAG = "MyService";
private DownloadBinder mBinder = new DownloadBinder();
class DownloadBinder extends Binder {
public void startDownload() {
Log.i(TAG, "startDownload: ");
}
public int getProgress() {
Log.i(TAG, "getProgress: ");
return 0;
}
}
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@Override
public void onCreate() {
Log.i(TAG, "onCreate: ");
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onStartCommand: ");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.i(TAG, "onDestroy: ");
super.onDestroy();
}
}
MainActivity
package com.firstcode.servicetest;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private MyService.DownloadBinder downloadBinder;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
downloadBinder = (MyService.DownloadBinder) service;
downloadBinder.startDownload();
downloadBinder.getProgress();
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i("MyService", "onServiceDisconnected: ");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.one);
Button buttontwo = findViewById(R.id.two);
Button buttonthree = findViewById(R.id.three);
Button buttonfour = findViewById(R.id.four);
button.setOnClickListener(this);
buttontwo.setOnClickListener(this);
buttonthree.setOnClickListener(this);
buttonfour.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.one:
Intent intent = new Intent(this, MyService.class);
startService(intent);
break;
case R.id.two:
Intent intent1 = new Intent(this, MyService.class);
stopService(intent1);
break;
case R.id.three:
Intent intent2 =new Intent(this, MyService.class);
bindService(intent2, connection, BIND_AUTO_CREATE);
break;
case R.id.four:
unbindService(connection);
break;
default:
break;
}
}
}
以上就是本次案例的全部代码
实现活动和服务之间的交互的关键在于在服务中定义了一个内部类,该类继承Binder。在这个类中可以定义各种方法,对服务中的数据进行操作。通过onBind()将实体类返回。然后在活动中,使用ServiceConnection声明连接,连接中就可以把服务强制转换成我们生命的内部类,然后调用其方法。之后使用bindService( )将环境和服务以及产生的连接自动绑定。即可实现活动和服务之间的交互。
服务的生命周期
——第一行代码
之前我们学习过了活动以及碎片的生命周期。类似地,服务也有自己的生命周期,前面我们使用到的onCreate()、onStartCommand()、onBind()和 onDestroy()等方法都是在服务的生命周期内可能回调的方法。
一旦在项目的任何位置调用了Context的startService( )方法,相应的服务就会启动起来,并回调onStartCommand()方法。 如果这个服务之前还没有创建过,onCreate()方法会先于onStartCommand()方法执行。服务启动了之后会一直保持运行状态,直到stopService()或stopSelf()方法被调用。注意,虽然每调用一次startService()方法,onStartCommand()就会执行一次,但实际上每个服务都只会存在一个实例。所以不管你调用了多少次startService()方法,只需调用一次stopService()或stopSelf()方法,服务就会停止下来了。
另外,还可以调用Context的bindService( )来获取一个服务的持久连接,这时就会回调服务中的onBind()方法。类似地,如果这个服务之前还没有创建过,onCreate()方法会先于onBind()方法执行。之后,调用方可以获取到onBind()方法里返回的IBinder对象的实例,这样就能自由地和服务进行通信了。只要调用方和服务之间的连接没有断开,服务就会一直保持运行状态。
当调用了startService() 方法后,又去调用stopService()方法,这时服务中的onDestroy()方法就会执行,表示服务已经销毁了。类似地,当调用了bindService()方法后,又去调用unbindService()方法,onDestroy()方法也会执行,这两种情况都很好理解。但是需要注意,我们是完全有可能对一个服务既调用了startService() 方法,又调用了bindService()方法的,这种情况下该如何才能让服务销毁掉呢?根据Android系统的机制,一
个服务只要被启动或者被绑定了之后,就会一直处于运行状态,必须要让以上两种条件同时不满足,服务才能被销毁。所以,这种情况下要同时调用stopService( )和unbindService()方法,onDestroy()方法才会执行。
前台服务
IntentService
这两个扩充使用大家有兴趣可以看看