一、Service的两种使用方式
本文介绍的是app A 启动 app B的服务的方式。本篇内容相关代码已经上传Github:BinderDemo
这是一篇很基础的帖子,本身是不想写的,我想写的是《Binder的原理》,但是我略微思索一下,发现事情不简单。要了解Binder,当然离不开Service,我干脆再说一下《Service启动原理》吧,既然已经说了这么多了,我干脆再简单说一下Service启动方式吧,于是就有了现在的这篇文章……上面的两篇我后面补充吧。
首先声明,startService和bindService中贴的log可能由于使用隐式/显式启动/绑定服务的方式不同 ,日志有所出入,具体看1.5版本差异。
首先声明,startService和bindService中贴的log可能由于使用隐式/显式启动/绑定服务的方式不同 ,日志有所出入,具体看1.5版本差异。
首先声明,startService和bindService中贴的log可能由于使用隐式/显式启动/绑定服务的方式不同 ,日志有所出入,具体看1.5版本差异。
1.1 首先,准备app B
- 首先继承系统Service,实现自己的Service;
public class SnapService extends Service {
private static final L.TAG TAG = new L.TAG("SnapServer");
@Nullable
@Override
public IBinder onBind(Intent intent) {
L.i(TAG, "onBind: ");
return null;
}
@Override
public boolean onUnbind(Intent intent) {
L.i(TAG, "onUnbind: ");
return super.onUnbind(intent);
}
@Override
public void onCreate() {
super.onCreate();
L.i(TAG, "onCreate: ");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onStartCommand: ");
}
@Override
public void onDestroy() {
super.onDestroy();
L.i(TAG, "onDestroy: ");
}
}
- 2.将Service添加到Manifest;
<service
android:name=".SnapService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.demoserver.action" />
</intent-filter>
</service>
注意 exported属性为true,否则不允许外部启动; 添加action 。
1.2 准备app A
暂时只需要创建好并添加一个ClientMainActivity就够了,先准备以下代码:
//app B的包名
private final static String TARGET_APP_PACKAGE = "com.example.demoserver";
//app B 目标Service的名字
private final static String TARGET_SERVICE_NAME = TARGET_APP_PACKAGE + ".SnapService";
private final static String TARGET_SERVICE_ACTION = TARGET_APP_PACKAGE + ".action";
接下来开始正文,我们在app A 中启动app B的服务
1.3 启动服务——startService
1. 显式启动
private void startService(){
// --- 显式启动 ---
Intent intent = new Intent();
// intent.setClassName(TARGET_APP_PACKAGE, TARGET_SERVICE_NAME);
intent.setComponent(new ComponentName(
TARGET_APP_PACKAGE, //这个参数是另外一个app的包名
TARGET_SERVICE_NAME));//这个是要启动的Service的全路径名
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(intent);
} else {
startService(intent);
}
}
2. 隐式启动
private void startService(){
// --- 隐式启动 ---
Intent intent = new Intent();
intent.setPackage(TARGET_APP_PACKAGE);
intent.setAction(TARGET_SERVICE_ACTION);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(intent);
} else {
startService(intent);
}
3. 服务前台化
Android8.0之后,官方为了防止app后台造成的网络、cpu、内存等性能消耗影响用户体验所以做了限制,不允许启动后台服务,常规方式启动服务超过5s就会闪退。而我们需要做的就是服务前台化:
- manifest申请服务前台化权限:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
- Service启动后调用
startForeground
@Override
public void onCreate() {
super.onCreate();
L.i(TAG, "onCreate: ");
//服务前台化
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
sendNotification();
}
}
private void sendNotification() {
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_ID, NotificationManager.IMPORTANCE_HIGH);
channel.enableLights(true);//设置提示灯
channel.setLightColor(Color.BLUE);//设置提示灯颜色
channel.setShowBadge(true);//显示logo
channel.setDescription("test");//设置描述
channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);//锁屏可见
manager.createNotificationChannel(channel);
Notification testNotification= new Notification.Builder(this)
.setChannelId(CHANNEL_ID)
.setContentText("服务前台化")
.setContentText("服务已经至于前台了")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
.build();
//id 不能是0
startForeground(1, testNotification);
}
客户端使用start或者bind方式启动服务后就可以看到通知栏有通知代表服务已经前台化,我就不再贴出结果了。
服务前台化具体解释可参见大神的帖子
调用方法后服务被启动:
2021-06-20 01:40:15.181 15231-15231/com.example.demoserver I/Server_SnapServer: onCreate:
2021-06-20 01:40:15.182 15231-15231/com.example.demoserver I/Server_SnapServer: onStartCommand: action = com.example.demoserver.action
4. 停止服务
@Snap
public void stopService() {
Intent intent = new Intent();
intent.setPackage(TARGET_APP_PACKAGE);
intent.setAction(TARGET_SERVICE_ACTION);
stopService(intent);
}
日志输出:
2021-06-20 02:28:05.198 21142-21142/com.example.binderdemo D/Client_SnapUtil: - snapSnaped : stopService
2021-06-20 02:29:09.405 21289-21289/com.example.demoserver I/Server_SnapServer: onDestroy:
1.4 绑定服务——bindService
准备代码:
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
L.i(TAG, "onServiceConnected: " + name.toShortString());
mMessageController = MessageController.Stub.asInterface(service);
mIsServiceConnected = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
L.i(TAG, "onServiceDisconnected: " + name.toShortString());
mIsServiceConnected = false;
}
};
1.显式绑定
// --- 显式绑定 ---
Intent serviceIntent = new Intent();
// intent.setClassName(TARGET_APP_PACKAGE, TARGET_SERVICE_NAME);
serviceIntent.setComponent(new ComponentName(
TARGET_APP_PACKAGE, //这个参数是另外一个app的包名
TARGET_SERVICE_NAME));//这个是要启动的Service的全路径名
boolean success = bindService(serviceIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
L.d(TAG, "bindService: success = " + success);
2.隐式绑定
// --- 隐式启动 ---
Intent serviceIntent = new Intent();
serviceIntent.setPackage(TARGET_APP_PACKAGE);
serviceIntent.setAction(TARGET_SERVICE_ACTION);
boolean success = bindService(serviceIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
L.d(TAG, "bindService: success = " + success);
调用方法后:
2021-06-20 02:13:57.678 16983-16983/com.example.binderdemo D/Client_SnapUtil: - snapSnaped : bindService
2021-06-20 02:13:57.689 16983-16983/com.example.binderdemo D/Client_ClientMainActivity: bindService: success = true
2021-06-20 02:13:57.701 16983-16983/com.example.binderdemo I/Client_ClientMainActivity: onServiceConnected: {com.example.demoserver/com.example.demoserver.SnapService}
-----------------------------------
2021-06-20 02:13:57.695 15679-15679/com.example.demoserver I/Server_SnapServer: onCreate:
2021-06-20 02:13:57.699 15679-15679/com.example.demoserver I/Server_SnapServer: onBind:
3.解绑服务
public void unBindService() {
unbindService(mServiceConnection);
}
日志输出:
2021-06-20 02:30:53.705 21428-21428/com.example.demoserver I/Server_SnapServer: onUnbind:
1.5 版本差异
- Android 7.1
- 显式startService执行onCreate(),隐式startService还执行onStartCommand();
- 直接bindService不成功,必须在显式/隐式startService之后才能通过显式bindService绑定service。
- unBindService之后仅仅执行onUnBind();
- startService和bindService一起执行后:如果先执行stopService,则不执行onDestroy,执行完unBindService之后一并执行onUnbind()和onDestroy();若先后执行unBindService和stopService,则onUnBind()和onDestroy()一并先后执行。
Android 8.0
- 不申请权限,不进行服务前台化。显式startService之后仅仅执行
onCreate(),隐式startService(),执行onCreate()->onStartCommand();显式bindService 隐式bindService 之后正常执行onCreate()->onBind();- 申请权限,服务前台化。显式/隐式startService/startForeGroundService,显式/隐式bindService,看起来能启动Service,但是模拟器它SystemUI崩溃了,app都没有报错,杀掉竟然启动不起来。
奇了怪了,很可能通知创建的有问题,或者模拟器有问题,我后续换台模拟器再试试。
- Andorid 8.1, Android 9.0,Android 10
- 显式startForeGroundService仅仅执行onCreate() ,隐式startForeGroundService()之后还会执行onStartCommand();
- 显式/隐式bindService,unBindService执行onCreate()->onBind()->onUnbind()->onDestroy();
- startService和bindService一起执行后:如果先执行stopService,则不执行onDestroy,执行完unBindService之后一并执行onUnbind()和onDestroy();若先后执行unBindService和stopService,则onUnBind()和onDestroy()一并先后执行。
Android 11
- 此版本很特殊,只能通过显式bindService方式启动服务,但是却没有执行相关生命周期方法
- 无法stopService和unBindService;
ps:我不知道是代码问题还是我打开方式不对,不论是模拟器还是真机都不能从外部直接启动app B 的Service,我看google文档好像没有看到这块儿有变更,麻烦知道的大佬烦请告知一下。
二、startService和bindService异同
2.1 生命周期不同
- startService方式:
- 如上1.3StartService 所讲 Service被启动和停止后日志输出可以看到,该方式Service的生命周期为
onCreate()->onStartCommand…onDestroy。- tartService可以多次调用,但是onCreate()只执行一遍,也就是Service只会创建一次,每次调用onStartCommand都会被调用。
- bindService方式:
- 如上1.4 bindService Service 的bind和unBind日志输出可以看到,这种方式Service的生命周期为:
onCreate()->onBind()…onUnBind- bindService只有第一次调用会创建Service,执行onCreate()和onBind(),连续多次调用并不会执行onCreate()和onBind();
2.2 Service独立性
- startService方式:
Service一旦被启动,在调用者没有调用stopService()的前提下,其生命周期就和调用者的生命周期不再关联。即使调用者被杀掉,Service也可以独立生存。
- bindService方式:
与startService方式不同的是,这种方式,service的生命周期是和调用者关联在一起的,如果调用者进程被终结后,服务便会终止。
2.3 调用服务的方式
- startService之后可以发送action与服务交互。
- bindService之后,除了发送action还 可以获得服务的完整方法(Binder的实现类),可以直接交互;
三、使用ADB向Service发送命
我们平时肯定会有这样的情况,项目里有一些方便我们平时调试或测试同学测试使用的功能,这些功能可能需要做个UI或者手动开启比较麻烦,这个时候,Service是不是也可以派上用场(和测试小姐姐装x)。
- 准备action和参数
public interface SnapAction {
String ACTION_SWITCH_THEME = "ACTION_SWITCH_THEME";
String EXTRA_INT_THEME = "theme";
/**
* 0或不传值为打开,1为关闭
*/
String ACTION_ENABLE_LOG = "ACTION_ENABLE_LOG";
String EXTRA_INT_ENABLE_LOG = "enable_log";
}
- Service内重写onStartCommand()
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
final String action = intent.getAction();
if (!TextUtils.isEmpty(action)) {
L.i(TAG, "onStartCommand: action = " + action);
switch (action) {
case SnapAction.ACTION_ENABLE_LOG:
int enableLog = intent.getIntExtra(SnapAction.EXTRA_INT_ENABLE_LOG, 0);
SnapConfig.LOG_ENABLE = enableLog == 0;
L.i(TAG, "enable_log = " + enableLog);
break;
case SnapAction.ACTION_SWITCH_THEME:
int theme = intent.getIntExtra(SnapAction.EXTRA_INT_THEME, 0);
L.d(TAG, "switch_theme: theme = " + theme);
break;
default:
break;
}
}
return super.onStartCommand(intent, flags, startId);
}
- 终端通过ADB发送命令:
后面是包名/service完整路径名,–ei表示int类型的参数,后面是参数键值对;
adb shell am startservice -n com.example.demoserver/com.example.demoserver.SnapService -a ACTION_ENABLE_LOG --ei enable_log 0
服务端收到命令:
2021-06-21 19:11:51.523 22476-22476/com.example.demoserver I/Server_SnapServer: onStartCommand: action = ACTION_ENABLE_LOG
2021-06-21 19:11:51.524 22476-22476/com.example.demoserver I/Server_SnapServer: enable_log = 0
好了,以上就是service的简单使用方式,本篇内容相关代码已经上传[Github:BinderDemo],如有不对的地方,欢迎指正。
后续我一定会不上Service启动原理,以及Binder使用方式及原理,敬请期待
本文详细介绍了Android中Service的使用,包括显式和隐式启动Service、前台服务的实现、服务的绑定与解绑。通过示例代码展示了不同API版本下的行为差异,并阐述了startService与bindService在生命周期和独立性上的区别。同时,演示了如何通过ADB命令与Service交互,提供了一种调试和测试服务的方法。
1068

被折叠的 条评论
为什么被折叠?



