二、Service 执行耗时工作
一、Service 开启和停止
二、Service 执行耗时工作
文章目录
Service 执行耗时工作
根据Service
开启和关闭打印日志,可知,Activity
中点击按钮函数和Service
中onCreate
、onStartCommand
和onDestroy
都在同一进程和线程执行。所以,Service
和Activity
一样,都在主线程运行。
2024-01-01 08:44:29.672 11694-11694 com.ieenin...inActivity com.ieening.blogsservicedemo D click start service button to start service
2024-01-01 08:44:29.675 11694-11694 com.ieenin...topService com.ieening.blogsservicedemo D executing StartStopService constructor
2024-01-01 08:44:29.677 11694-11694 com.ieenin...topService com.ieening.blogsservicedemo D executing StartStopService onCreate method
2024-01-01 08:44:29.684 11694-11694 com.ieenin...topService com.ieening.blogsservicedemo D executing onStartCommand method
2024-01-01 08:44:40.759 11694-11694 com.ieenin...inActivity com.ieening.blogsservicedemo D click restart service button to restart service
2024-01-01 08:44:40.768 11694-11694 com.ieenin...topService com.ieening.blogsservicedemo D executing onStartCommand method
2024-01-01 08:44:47.630 11694-11694 com.ieenin...inActivity com.ieening.blogsservicedemo D click stop service button to stop service
2024-01-01 08:44:47.634 11694-11694 com.ieenin...topService com.ieening.blogsservicedemo D executing onDestroy method
查看下面ActivityThread.java
源代码,可知,Service
的onCreate
、onBind
、onUnbind
等方法都是在主线程里被调用的。既然Service
各个方法是在主线程里执行,那么想要实现计数功能就必须开启子线程来完成此事。
public final void scheduleCreateService(IBinder token,
ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
updateProcessState(processState, false);
CreateServiceData s = new CreateServiceData();
s.token = token;
s.info = info;
// 发送创建 service 消息
sendMessage(H.CREATE_SERVICE, s);
}
private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
if (DEBUG_MESSAGES) {
Slog.v(TAG,
"SCHEDULE " + what + " " + mH.codeToString(what) + ": " + arg1 + " / " + obj);
}
Message msg = Message.obtain();
msg.what = what;
msg.obj = obj;
msg.arg1 = arg1;
msg.arg2 = arg2;
if (async) {
msg.setAsynchronous(true);
}
mH.sendMessage(msg); // mH 为 ActivityThread 里构造的 Handler,也就是说在主线程里构造的 Handler
}
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
......
case CREATE_SERVICE: // 创建 Service
if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
("serviceCreate: " + String.valueOf(msg.obj)));
}
handleCreateService((CreateServiceData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case BIND_SERVICE: // 绑定 Service
if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind: "
+ String.valueOf(msg.obj));
}
handleBindService((BindServiceData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case UNBIND_SERVICE: // 解绑 Service
if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceUnbind: "
+ String.valueOf(msg.obj));
}
handleUnbindService((BindServiceData)msg.obj);
schedulePurgeIdler();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case SERVICE_ARGS:
if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
("serviceStart: " + String.valueOf(msg.obj)));
}
handleServiceArgs((ServiceArgsData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case STOP_SERVICE: // 停止 Service
if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceStop: "
+ String.valueOf(msg.obj));
}
handleStopService((IBinder)msg.obj);
schedulePurgeIdler();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
......
}
Object obj = msg.obj;
if (obj instanceof SomeArgs) {
((SomeArgs) obj).recycle();
}
if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
}
}
Service
一直在计数,计数结果怎么通知给调用者呢,此处假设调用者是Activity
。根据前面介绍,可知有两种方式:
- 如果是显示开启的
Service
,则Service
可选择广播将数据发送给Activity
- 如果是绑定开启的
Service
,Activity
拿到IBinder
引用,进而拿到Service
引用,最终可以调用getCount()
获得计数值,并更新UI
。
2.1 显示开启Service+广播更新Activity
代码1
com/ieening/blogsservicedemo/services/StartStopCounterService.java
package com.ieening.blogsservicedemo.services;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
public class StartStopCounterService extends Service {
private boolean serviceThreadStopFlag = false;
public static final String COUNT = "count";
public static final String COUNTER_SERVICE_BROADCAST = "com.ieening.blogsservicedemo.COUNTER_SERVICE_BROADCAST";
private final String TAG = StartStopCounterService.class.getName();
private static int count = 0;
public StartStopCounterService() {
Log.d(TAG, "executing StartStopCounterService constructor");
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
serviceThreadStopFlag = false;
Thread serviceThread = new Thread(() -> {
for (int i = 0; i < 30; i++) {
if (serviceThreadStopFlag) {
break;
}
Log.d(TAG, "executing StartStopCounterService onCreate method, count= " + count);
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
count++;
Intent intent = new Intent(COUNTER_SERVICE_BROADCAST);
intent.putExtra(COUNT, count);
intent.setPackage(getPackageName());
sendBroadcast(intent);
}
Log.d(TAG, "executing StartStopCounterService onCreate method, Service stopped");
stopSelf();
});
serviceThread.start();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
serviceThreadStopFlag = false;
count = 0;
Intent sendedIntent = new Intent(COUNTER_SERVICE_BROADCAST);
sendedIntent.putExtra(COUNT, count);
sendedIntent.setPackage(getPackageName()); // 一定需要加上,变为显示广播
sendBroadcast(sendedIntent);
Log.d(TAG, "executing StartStopCounterService onStartCommand method");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
serviceThreadStopFlag = true;
Log.d(TAG, "executing StartStopCounterService onDestroy method");
super.onDestroy();
}
}
代码1中StartStopCounterService
类在onCreate
中开启子线程,在count
每次更新后,发出广播。
代码2
com/ieening/blogsservicedemo/receivers/CounterServiceReceiver.java
package com.ieening.blogsservicedemo.receivers;
import static com.ieening.blogsservicedemo.services.StartStopCounterService.COUNT;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class CounterServiceReceiver extends BroadcastReceiver {
private static final String TAG = CounterServiceReceiver.class.getName();
public static final String SERVICE_COUNT_ACTION = "com.ieening.blogsservicedemo.COUNTER_SERVICE_COUNT";
@Override
public void onReceive(Context context, Intent intent) {
int count = intent.getIntExtra(COUNT, -1);
Intent activity_intent = new Intent(SERVICE_COUNT_ACTION);
activity_intent.putExtra(COUNT, count);
activity_intent.setPackage(context.getPackageName());
context.sendBroadcast(activity_intent);
Log.d(TAG, "executing CounterServiceReceiver onReceive method, count=" + count);
}
}
代码2处理代码1中发出的广播,并将结果转发。
代码3
com/ieening/blogsservicedemo/MainActivity.java
public class MainActivity extends AppCompatActivity {
........
@Override
protected void onCreate(Bundle savedInstanceState) {
........
// ! 注册 Receiver
counterServiceReceiver = new CounterServiceReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(SERVICE_COUNT_ACTION);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
registerReceiver(counterServiceReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
} else {
registerReceiver(counterServiceReceiver, filter);
}
binding.startCounterServiceButton.setOnClickListener(v -> {
Log.d(TAG, "click start counter service button to start counter service");
// ! 显示开启 Service
Intent intent = new Intent(this, StartStopCounterService.class);
startService(intent);
v.setEnabled(false);
binding.restartCounterServiceButton.setEnabled(true);
binding.stopCounterServiceButton.setEnabled(true);
});
binding.restartCounterServiceButton.setOnClickListener(v -> {
Log.d(TAG, "click restart counter service button to restart counter service");
// ! 显示重启 Service
Intent intent = new Intent(this, StartStopCounterService.class);
startService(intent);
});
binding.stopCounterServiceButton.setOnClickListener(v -> {
Log.d(TAG, "click stop counter service button to stop counter service");
// ! 显示停止 Service
Intent stopIntent = new Intent(this, StartStopCounterService.class);
stopService(stopIntent);
v.setEnabled(false);
binding.startCounterServiceButton.setEnabled(true);
binding.restartCounterServiceButton.setEnabled(false);
});
......
}
private class CounterServiceReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (SERVICE_COUNT_ACTION.equals(intent.getAction())) {
int count = intent.getIntExtra(COUNT, -1);
if (count != -1) {
binding.startStopCounterServiceCounterText.setText(Integer.toString(count));
}
Log.d(TAG, "executing MainActivity CounterServiceReceiver onReceive, count=" + count);
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(counterServiceReceiver);
}
}
代码3中Activity
自定义内部类CounterServiceReceiver
作为广播Receiver
接受处理信息,并在onCreate
和onDestroy
中注册和取消注册广播Receiver
。这样,每次在StartStopCounterService
中发出的广播,Activity
中都可以接受并获取count
更新值。
2.2 绑定 Service+Binder
代码4
com/ieening/blogsservicedemo/services/BindUnbindCounterService.java
package com.ieening.blogsservicedemo.services;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
import com.ieening.blogsservicedemo.binders.BindUnbindServiceBinder;
public class BindUnbindCounterService extends Service {
private final String TAG = BindUnbindCounterService.class.getName();
private boolean serviceThreadStopFlag = false;
public int getCount() {
return count;
}
private int count = 0;
public BindUnbindCounterService() {
Log.d(TAG, "executing BindUnbindCounterService constructor");
}
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "executing BindUnbindCounterService onBind");
return new BindUnbindServiceBinder(this);
}
@Override
public void onCreate() {
// Service初次创建会调用该方法,我们可以做一些初始化操作,与 onDestroy相对
Log.d(TAG, "executing BindUnbindCounterService onCreate");
serviceThreadStopFlag = false;
super.onCreate();
Thread serviceThread = new Thread(() -> {
for (int i = 0; i < 30; i++) {
if (serviceThreadStopFlag) {
break;
}
Log.d(TAG, "executing BindUnbindCounterService onCreate method, count= " + count);
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
count++;
}
Log.d(TAG, "executing BindUnbindCounterService onCreate method, Service stopped");
stopSelf();
});
serviceThread.start();
}
@Override
public void onDestroy() {
serviceThreadStopFlag = true;
// 每次销毁 Service 时都会调用该方法,在该方法内,可以添加释放资源的操作,与 onCreate 相对
Log.d(TAG, "executing BindUnbindService onDestroy");
super.onDestroy();
}
}
代码4中定义BindUnbindCounterService
类,onCreate
中新建子线程并更新count
,定义getCount
函数,对外开放count
结果。
代码5
com/ieening/blogsservicedemo/binders/BindUnbindServiceBinder.java
package com.ieening.blogsservicedemo.binders;
import android.app.Service;
import android.os.Binder;
import android.util.Log;
public class BindUnbindServiceBinder extends Binder {
private final String TAG = BindUnbindServiceBinder.class.getName();
private final Service service;
public BindUnbindServiceBinder(Service service) { // ! 持有 Service 引用
Log.d(TAG, "executing BindUnbindServiceBinder constructor");
this.service = service;
}
public Service getService() { // ! 返回 Service 引用
Log.d(TAG, "executing BindUnbindServiceBinder getService");
return service;
}
}
Binder
类,持有Service
实例,供Activity
使用。
代码6
com/ieening/blogsservicedemo/MainActivity.java
public class MainActivity extends AppCompatActivity {
......
private BindUnbindCounterService bindUnbindCounterService;
........
@Override
protected void onCreate(Bundle savedInstanceState) {
........
// ! 绑定 Activity 和 Service
ServiceConnection counterServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "executing ServiceConnection onServiceConnected");
BindUnbindServiceBinder bindUnbindServiceBinder = (BindUnbindServiceBinder) service;
// 获取 MyService 引用
bindUnbindCounterService = (BindUnbindCounterService) bindUnbindServiceBinder.getService();
}
@Override
public void onServiceDisconnected(ComponentName name) {
bindUnbindCounterService = null;
Log.d(TAG, "executing ServiceConnection onServiceDisconnected");
// Service 被销毁时调用(内存不足等,正常解绑不会走这)
}
};
binding.bindCounterServiceButton.setOnClickListener(v -> {
Log.d(TAG, "click bind counter service button to bind counter service");
Intent bindIntent = new Intent(this, BindUnbindCounterService.class);
bindService(bindIntent, counterServiceConnection, BIND_AUTO_CREATE);
binding.bindCounterServiceButton.setEnabled(false);
binding.unbindCounterServiceButton.setEnabled(true);
binding.counterServiceGetCountButton.setEnabled(true);
});
binding.unbindCounterServiceButton.setOnClickListener(v -> {
Log.d(TAG, "click unbind counter service button to unbind counter service");
unbindService(counterServiceConnection);
binding.bindCounterServiceButton.setEnabled(true);
binding.unbindCounterServiceButton.setEnabled(false);
binding.counterServiceGetCountButton.setEnabled(false);
});
binding.counterServiceGetCountButton.setOnClickListener(v -> {
if (!Objects.isNull(bindUnbindCounterService)) {
binding.bindUnbindCounterServiceCounterText.setText(Integer.toString(bindUnbindCounterService.getCount()));
}
});
......
}
}
在ServiceConnection counterServiceConnection
中 onServiceConnected
获取bindUnbindServiceBinder
,通过bindUnbindServiceBinder
获取BindUnbindCounterService
实例,从而可以直接调用getCount
获取子线程更新的数据。
2.3 Service、Thread和Manager
2.3.1 Service与Thread
既然Service
无法直接执行耗时操作,那么需要Service
干嘛呢,还不如直接开启子线程执行任务呢? 我们都知道Service
是长时间在后台运行。 实际上说的是Service
的生命周期,也就是说Service
对象一直存在,当我们需要使用Service
的时候,通过Intent
或者IBinder
就能找到它,进而使用它提供的功能。同样实现计数功能,如果直接在Activity
里开启Thread
计数,当Activity
退出的时候,要把Thread
关闭了。再次开启Activity
时,已经找不到Thread
引用了,无法继续上次的累计计数。再者,就算不考虑内存泄漏,Activity
退出时候不关闭Thread
,再次开启Activity
的时候,依然找不到Thread
引用。另外如果想将计数功能抽出来,供多个Activity
使用,直接使用Thread
也无法实现多Activity共用计数功能。
2.3.2 Service与Manager
既然维护Thread
全局引用方法不太推荐,那么实现一个单例的Manager
(管理类)来持有Thread
,进而使用Thread
执行耗时任务,而外界通过调用这个Manager
来获取数据,代码如下:
代码7
com/ieening/blogsservicedemo/manager/CountManager.java
package com.ieening.blogsservicedemo.manager;
import android.util.Log;
import java.util.Objects;
public class CountManager {
private static final String TAG = CountManager.class.getName();
private static volatile CountManager instance;
public int getCount() {
return count;
}
private int count = 0;
private boolean stopCountFlag = false;
public static CountManager getInstance() {
if (Objects.isNull(instance)) {
synchronized (CountManager.class) {
if (Objects.isNull(instance)) {
instance = new CountManager();
}
}
}
return instance;
}
public void startCount() {
stopCountFlag = false;
new Thread(() -> {
for (int i = 0; i < 30; i++) {
if (stopCountFlag) {
break;
}
count++;
Log.d(TAG, "executing startCount, count=" + count);
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
public void stopCount() {
stopCountFlag = true;
}
}
事实上不少的项目都是采用Activity + Manager
方式来实现页面展示 + 数据获取。 Activity
展示UI
,后台通过Manager
获取数据,如从数据库获取或者从网络获取等,最后将数据反馈给Activity
用以刷新UI
。 到此你可能疑惑了,都有了Manager
了,Service
还有使用的必要吗? 答案是肯定的。 Service
作为Android
四大组件之一,是广泛使用于Android
系统里的。
Service
可以调整优先级,尽可能避免在资源紧张的时候被销毁- 借助
Service + Binder
,实现Android
进程间通信
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!