进程优先级
讲解 Service 之前,先了解一下进程优先级
在 Android 系统中,当系统资源(内存资源)不足时,系统会自动清理一部分应用程序占用的内存,以释放出更多的可用内存空间,运行当前需要执行的应用程序
当系统自动清理应用程序占用的内存时,会按照进程的优先级,从低到高进行清理,即优先级较低的进程则最优先被清理,而优先级较高的则越不容易被清理
进程的优先级,从高到低依次是:
1、前台进程,表现为进程中存在可见并可控的 Activity
2、可见进程,表现为进程中存在局部可见却不可控的 Activity
3、服务进程,表现为存在正在运行的 Service
4、后台进程,表现为该进程中的所有 Activity 均已被置于后台,即不可见也不可控
5、空进程,表现为已经退出的进程
如果我正在使用 QQ,那么 QQ 属于前台进程,如果回到桌面,就是后台进程,如果直接按返回键退出,那么 QQ 就是空进程
Service使用
Service 是 Android 系统的核心组件之一
Service 是没有界面的,后台工作(看不见)的类
Service 是运行在主线程的,所以,虽然 Service 适合做长期的任务,但是,也必须开始子线程来完成相关任务。Service 可以用于在后台执行的操作,例如检查版本更新,长时间的任务。
Service 是单例的(实例唯一:在同一时间内只存在一个对象)
启动Service
调用startService(Intent)
方法即可启动 Service
停止Service
1、在 Service 类以外,调用stopService(intent)
方法即可停止 Service
2、在 Service 类内部,调用stopSelf()
方法,即可停止当前 Service
Service生命周期
onCreate()
:当第一次创建 Service 对象时被调用
onStartCommand()
:每次激活 Service 组件时被调用
onDestroy()
:销毁服务
栗子1:观察service生命周期
新增 WorkService
public class WorkService extends Service {
public WorkService() {
super.onCreate();
Log.d("Service", "WorkService@" + hashCode() + ".onCreate()");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("Service", "WorkService@" + hashCode() + ".onStartCommand()");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.d("Service", "WorkService@" + hashCode() + ".onDestroy()");
super.onDestroy();
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
}
通过 Android Studio 的右键 new 一个 Service 会自动在 AndroidManifest中注册
<service
android:name=".WorkService"
android:enabled="true"
android:exported="true"/>
activity_main布局文件中增加两个 Button 分别用来激活和停止 Service
<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/btn_start_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="激活Service" />
<Button
android:id="@+id/btn_stop_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="停止Service" />
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button btnStart;
private Button btnStop;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnStart = findViewById(R.id.btn_start_service);
btnStart.setOnClickListener(this);
btnStop = findViewById(R.id.btn_stop_service);
btnStop.setOnClickListener(this);
}
@Override
public void onClick(View v) {
Intent intent = new Intent(this, WorkService.class);
switch (v.getId()) {
case R.id.btn_start_service:
startService(intent);
break;
case R.id.btn_stop_service:
stopService(intent);
break;
}
}
}
运行程序:
当启动一个Service的时候,会调用该 Service中 的onCreate()
和onStartCommand()
onCreate()
方法只会在 Service 第一次被创建的时候调用
如果当前Service已经被创建过了,不管怎样调用startService()
方法,onCreate()
方法都不会再执行
Activity和Service传递数据
开始这里的内容之前,你需要知道 Activity 和 Activity 之间是如何传递数据的,如果不清楚可以查看:Intent在Android系统的作用
栗子2:Activity和Service传递数据
期待实现的功能:我们修改 activity_main.xml 的布局,在页面增加一些信息输入的控件,然后点击 Button 把页面输入信息通过 Activity 传个 Service。
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="15dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="请输入用户名" />
<EditText
android:id="@+id/et_username"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="请选择性别" />
<RadioGroup
android:id="@+id/rg_male"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RadioButton
android:id="@+id/rb_male"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="男" />
<RadioButton
android:id="@+id/rb_female"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="女" />
</RadioGroup>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="请输入年龄" />
<EditText
android:id="@+id/et_age"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number" />
<Button
android:id="@+id/btn_submit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="提交" />
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private EditText etUserName;
private RadioButton rbGenderMale;
private EditText etAge;
private Button btnSubmit;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
etUserName = findViewById(R.id.et_username);
rbGenderMale = findViewById(R.id.rb_male);
etAge = findViewById(R.id.et_age);
btnSubmit = findViewById(R.id.btn_submit);
btnSubmit.setOnClickListener(this);
}
@Override
public void onClick(View v) {
Toast.makeText(this, "clicked", Toast.LENGTH_SHORT).show();
String username = etUserName.getText().toString().trim();
String gender = rbGenderMale.isChecked() ? "男" : "女";
int age = Integer.parseInt(etAge.getText().toString());
Intent intent = new Intent(this, WorkService.class);
intent.putExtra("_username", username);
intent.putExtra("_gender", gender);
intent.putExtra("_age", age);
startService(intent);
}
}
修改刚才的 WorkService,仅修改 onStartCommand()
方法即可。其中 WorkService 的onStartCommand()
方法的第一个参数就是Intent
直接取值即可。
WorkService
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("Service", "WorkService@" + hashCode() + ".onStartCommand()");
String u = intent.getStringExtra("_username");
String g = intent.getStringExtra("_gender");
int a = intent.getIntExtra("_age", 0);
Log.d("Service", "姓名:" + u + " 性别:" + g + " 年龄:" + a);
return super.onStartCommand(intent, flags, startId);
}
运行程序:
Service粘性
Service 的粘性表现为当 Service 意外终止后,在一定时间后会自动重新启动
Service是否粘性,由 onStartCommand() 方法的返回值决定,取值可以是
Service.START_STICKY
–>粘性Service.START_NOT_STICKY
–>非粘性Service.START_STICKY_COMPATIBILITY
–>兼容模式粘性Service.START_REDELIVER_INTENT
–>粘性的,且重启时还拥有此前的 Intent 数据(重新传递 Intent )
【小结】
如果希望 Service 是非粘性的,取值为 Service.START_NOT_STICKY
如果希望 Service 是粘性的,则使用父类方法返回值
如果 Service 是粘性的,默认情况下,每次自动重启时,并不会携带 Intent 对象,即自动重启时,第一个参数 Intent,为null。
如果希望 Service 是粘性的,且自动重启时还拥有此前的 Intent 数据,则取值为Service.START_REDELIVER_INTENT