文章目录
前言
今天学习了Service,来记录一下学习历程。
提示:以下是本篇文章正文内容,下面案例可供参考
一、startService
startService()生命周期(感兴趣的可以自己去验证一下,在每个重写方法中输出日志信息,然后运行,再在LogCat中查看日志信息):
onCreate() --> onStartCommand() --> Servicerunning --> onDestroy() --> Service shut down
特别注意:其中onStartCommand()方法是Service已经在启动状态时,用户每次startService(intent)时就会调用onStartCommand()。还有就是startService()不能调用Service的方法,因为它不执行onBind()
首先来讲讲startService,看我运行效果
<iframe allowfullscreen="true" data-mediaembed="csdn" id="sIPVEtUz-1634973309733" src="https://live.csdn.net/v/embed/179083"></iframe>startService
项目结构如下图所示
MainActivity代码如下
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button btn_start;
private Button btn_stop;
private Intent intent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_start = findViewById(R.id.btn_start);
btn_stop = findViewById(R.id.btn_stop);
btn_start.setOnClickListener(this);
btn_stop.setOnClickListener(this);
intent = new Intent(MainActivity.this,MyService.class);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_start:
Log.i("TAG", "onClick: Service启动");
startService(intent); //启动Service
Toast.makeText(MainActivity.this, "Service启动成功", Toast.LENGTH_SHORT).show();
break;
case R.id.btn_stop:
Log.i("TAG", "onClick: Service停止");
stopService(intent); //停止Service
Toast.makeText(MainActivity.this, "Service已经停止", Toast.LENGTH_SHORT).show();
break;
}
}
}
MainActivity的布局文件activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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=".MainActivity"
android:orientation="horizontal"
android:gravity="center">
<Button
android:id="@+id/btn_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="启动Service" />
<Button
android:id="@+id/btn_stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="停止Service"
android:layout_marginLeft="20dp"/>
</LinearLayout>
MyService代码如下
package com.example.mystartservice;
import android.app.ActivityManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
import java.util.ArrayList;
public class MyService extends Service {
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
int i = 1;
while (isRunning()){//如果Service正在运行
Log.i("Service", "run: "+i++);
try {
Thread.sleep(1500); //线程休眠1.5秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
}
//判断Service是否正在运行
public boolean isRunning(){
ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
//获取所有正在运行的Service
ArrayList<ActivityManager.RunningServiceInfo> runningService = (ArrayList<ActivityManager.RunningServiceInfo>)
activityManager.getRunningServices(20);
for (int i = 0;i < runningService.size();i++){
if(runningService.get(i).service.getClassName().toString().equals("com.example.mystartservice.MyService")){
//判断当前Service是否正在运行
return true;
}
}
return false;
}
}
二、bindService
bindService()生命周期(感兴趣的可以自己去验证一下,在每个重写方法中输出日志信息,然后运行,再在LogCat中查看日志信息):
onCreate() --> onBind() --> Clients are bound to service --> onUnbind --> onDestroy() --> Service shut down
执行bindService()时,在这个时候调用者和Service就绑定在一起了。调用者调用unbindService方法或者调用者不存在了(比如Activity被销毁了),Service就会执行 onUnbind --> onDestroy。不难发现,调用者和Service相当于共存亡了。
特别注意:unBindService()只能调用一次,否则会造成ServiceConnection泄露。
我写了个bindService测试,看我运行效果
<iframe allowfullscreen="true" data-mediaembed="csdn" id="7hlnB2hm-1634974528603" src="https://live.csdn.net/v/embed/179085"></iframe>bindService测试
MainActivity代码如下
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import java.util.List;
public class MainActivity extends AppCompatActivity {
BinderService binderService; //声明Service类对象
//文本框组件ID
int[] tvid = {R.id.tv1,R.id.tv2,R.id.tv3,R.id.tv4,
R.id.tv5,R.id.tv6,R.id.tv7,R.id.tv8,R.id.tv9};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn = findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//单击抽奖按钮,随机获取一组号码
List number = binderService.getRandomNumber();
for (int i=0;i < number.size();i++){
TextView tv = (TextView) findViewById(tvid[i]); //获取文本框组件
tv.setText(number.get(i).toString()); //显示生成的随即号码
}
}
});
}
//创建ServiceConnection对象
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
binderService = ((BinderService.MyBinder)service).getService(); //获取后台Service
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(MainActivity.this,BinderService.class);
bindService(intent,connection,BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(connection); //解绑Service
}
}
MainActivity的布局文件activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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=".MainActivity"
android:orientation="vertical"
android:gravity="center">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/tv1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:text="00" />
<TextView
android:id="@+id/tv2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:text="00" />
<TextView
android:id="@+id/tv3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:text="00" />
<TextView
android:id="@+id/tv4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:text="00" />
<TextView
android:id="@+id/tv5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:text="00" />
<TextView
android:id="@+id/tv6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:text="00" />
<TextView
android:id="@+id/tv7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:text="00" />
<TextView
android:id="@+id/tv8"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:text="00" />
<TextView
android:id="@+id/tv9"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="00"
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
android:layout_marginTop="30dp">
<Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="抽奖" />
</LinearLayout>
</LinearLayout>
BinderService代码如下
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class BinderService extends Service {
public BinderService(){
}
//创建MyBinder内部类
public class MyBinder extends Binder{
public BinderService getService(){//创建获取Service的方法
return BinderService.this; //返回当前的Service类
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new MyBinder(); //返回MyBinder Service对象
}
//自定义方法,用于生成随机数
public List getRandomNumber(){
List resArr = new ArrayList();
String strNumber = ""; //用于保存生成的随机数
for(int i = 0;i < 9;i++){
int number = new Random().nextInt(60)+1; //生成指定范围的随机整数
if(number < 10){
strNumber = "0"+String.valueOf(number);
}else {
strNumber = String.valueOf(number);
}
resArr.add(strNumber); //把转换后的字符串添加到List集合中
}
return resArr;
}
@Override
public void onDestroy() { //销毁Service
super.onDestroy();
}
}
在BinderService类中可能有人会疑惑(包括我)为什么要写一个内部类MyBinder继承Binder?
public class MyBinder extends Binder{
public BinderService getService(){//创建获取Service的方法
return BinderService.this; //返回当前的Service类
}
}
于是我点进Binder里查看源码,发现Binder是实现接口IBider的实现类
然后再去看MainActivity里创建了一个ServiceConnection对象,并重写了两个方法,你会发现
onServiceConnted()的两个参数中有一个IBinder接口对象
//创建ServiceConnection对象
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
binderService = ((BinderService.MyBinder)service).getService(); //获取后台Service
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
这里为了能调用MyBinder类里的getService方法,进行了强转,这样一梳理是不是就理解为什么要在BinderService里写内部类MyBinder了,onBind回调方法将返回给MainActivity一个IBinder接口实例,IBinder允许MainActivity回调服务的方法,比如得到Service运行的状态或其他操作。我们需要IBinder对象返回具体的Service对象才能操作,所以说具体的Service对象必须首先实现Binder对象。
总结
遇到的问题:在实现startService时,我是用Android Studio自带的Service
所以它会自动在AndroidManifest.xml写如下代码
<service
android:name=".MyService"
android:enabled="true"
android:exported="true"></service>
在实现bindService时就是自定义了一个BinderService,所以就忘记在AndroidManifest.xml注册
报了一个错误
所以千万别忘了还要在AndroidManifest.xml写如下代码
<service android:name=".BinderService"
android:enabled="true"
android:exported="true">
</service>
粗略讲了一下startService和bindService,如果还有哪里不懂可以自行百度看大佬的博客也可以留言,如果上面我哪里写的不对可以指出,我也会及时改正,谢谢。