Android 服务与多线程编程详解
一、服务基本概念
服务(Service)是Android中实现程序后台运行的重要组件,它适合执行不需要与用户交互且需要长期运行的任务。服务有以下特点:
- 独立于UI:服务运行不依赖于任何用户界面,即使程序被切换到后台也能正常运行
- 进程依赖:服务依赖于创建它的应用程序进程,当应用进程被杀死时,所有依赖该进程的服务都会停止
- 主线程运行:服务不会自动开启线程,所有代码默认在主线程执行,需要手动创建子线程处理耗时任务
二、Android多线程编程
Android的多线程编程与Java基本相同,创建线程的方式也类似:
class MyThread extends Thread {
@Override
public void run() {
// 线程执行的代码
}
}
// 启动线程
new MyThread().start();
但需要注意Android的UI线程模型:
1. UI线程模型
Android采用单线程模型处理UI操作,主线程(也称为UI线程)负责:
- 绘制和更新界面
- 处理用户交互事件
- 执行Activity生命周期回调方法
2. 子线程更新UI的问题
直接在子线程中更新UI会导致异常:
// 错误示例:子线程更新UI
new Thread(() -> {
textView.setText("更新内容"); // 会抛出CalledFromWrongThreadException
}).start();
这是因为Android的UI组件不是线程安全的,所有UI操作必须在主线程执行。
三、异步消息处理机制
Android提供了异步消息处理机制来解决子线程与UI线程通信的问题,主要由4个部分组成:
- Message:线程间传递的消息,可携带少量数据
- Handler:消息的处理者,负责发送和处理消息
- MessageQueue:消息队列,存放所有通过Handler发送的消息
- Looper:消息循环器,不断从MessageQueue中取出消息并分发给Handler
1. 完整使用示例
public class MainActivity extends AppCompatActivity {
public static final int UPDATE_TEXT = 1;
private TextView textView;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_TEXT:
textView.setText("更新后的内容");
break;
// 可以处理更多消息类型
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.textView);
Button button = findViewById(R.id.button);
button.setOnClickListener(v -> {
// 创建子线程执行耗时操作
new Thread(() -> {
// 模拟耗时操作
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 发送消息到主线程更新UI
Message message = new Message();
message.what = UPDATE_TEXT;
handler.sendMessage(message);
}).start();
});
}
}
2. 更简洁的实现方式 - Runnable
除了使用Message,还可以直接传递Runnable对象:
// 在主线程创建Handler
Handler handler = new Handler(Looper.getMainLooper());
// 在子线程中
new Thread(() -> {
// 耗时操作...
// 在主线程执行UI更新
handler.post(() -> {
textView.setText("更新后的内容");
});
}).start();
四、服务详解
1. 定义服务
服务需要继承Service类并实现关键方法:
public class MyService extends Service {
private static final String TAG = "MyService";
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "服务创建了");
// 初始化操作
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "服务启动了");
// 执行后台任务
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "服务销毁了");
// 释放资源
}
@Override
public IBinder onBind(Intent intent) {
// 返回Binder对象用于Activity与Service通信
return null;
}
}
2. 服务的生命周期
- onCreate():服务第一次创建时调用
- onStartCommand():每次通过startService()启动服务时调用
- onBind():通过bindService()绑定服务时调用
- onUnbind():所有客户端解除绑定时调用
- onDestroy():服务销毁时调用
3. 启动和停止服务
启动服务:
Intent intent = new Intent(this, MyService.class);
startService(intent); // 启动服务
停止服务:
Intent intent = new Intent(this, MyService.class);
stopService(intent); // 停止服务
绑定服务:
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 获取Binder对象
}
@Override
public void onServiceDisconnected(ComponentName name) {
// 服务异常断开
}
};
Intent intent = new Intent(this, MyService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
解除绑定:
unbindService(connection);
五、活动与服务通信
1. 使用Binder实现通信
- 在Service中创建Binder子类:
public class MyService extends Service {
private DownloadBinder mBinder = new DownloadBinder();
class DownloadBinder extends Binder {
public void startDownload() {
Log.d(TAG, "开始下载");
}
public int getProgress() {
Log.d(TAG, "获取下载进度");
return 0;
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
- 在Activity中绑定服务并获取Binder:
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) {
}
};
// 绑定服务
Intent intent = new Intent(this, MyService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
2. 服务的销毁规则
必须同时调用stopService()
和unbindService()
才能完全销毁服务:
- 如果只调用
stopService()
,但仍有客户端绑定,服务不会销毁 - 如果只调用
unbindService()
,但服务是通过startService()
启动的,服务也不会销毁
六、前台服务
前台服务比普通服务具有更高的优先级,系统更不容易杀死它,适合执行重要任务。
1. 创建前台服务
- 在AndroidManifest.xml中声明权限:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- Android 13+ 需要 -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
- 在Service中创建通知并启动前台服务:
@Override
public void onCreate() {
super.onCreate();
// 创建通知渠道(Android 8.0+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
"channel_id",
"Channel Name",
NotificationManager.IMPORTANCE_DEFAULT
);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(channel);
}
// 创建PendingIntent
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(
this,
0,
intent,
PendingIntent.FLAG_IMMUTABLE
);
// 创建通知
Notification notification = new NotificationCompat.Builder(this, "channel_id")
.setContentTitle("前台服务")
.setContentText("服务正在运行")
.setSmallIcon(R.drawable.ic_notification)
.setContentIntent(pendingIntent)
.build();
// 启动前台服务
startForeground(1, notification); // 1是通知ID
}
- 启动前台服务:
// Android 9.0+ 需要使用startForegroundService()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(intent);
} else {
startService(intent);
}
2. Android 13+的通知权限
从Android 13开始,需要动态申请通知权限:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
ActivityCompat.requestPermissions(
this,
new String[]{Manifest.permission.POST_NOTIFICATIONS},
1
);
}
七、IntentService
IntentService是Service的子类,它解决了Service不能自动创建线程的问题:
- 自动创建子线程处理任务
- 任务执行完毕后自动停止服务
1. 创建IntentService
public class MyIntentService extends IntentService {
public MyIntentService() {
super("MyIntentService"); // 指定线程名称
}
@Override
protected void onHandleIntent(Intent intent) {
// 在这里执行耗时操作
// 这个方法运行在子线程中
}
}
2. 使用IntentService
Intent intent = new Intent(this, MyIntentService.class);
startService(intent); // 启动服务,会自动创建线程执行onHandleIntent()
八、总结
- 服务:适合执行后台任务,但需要注意生命周期管理和资源释放
- 多线程:Android UI操作必须在主线程执行,耗时操作应在子线程执行
- 通信机制:使用Handler、Binder等方式实现线程间和Activity-Service间通信
- 前台服务:适合重要任务,需要创建通知并处理权限问题
- IntentService:简化了Service的使用,自动处理线程和生命周期
通过合理使用服务和多线程技术,可以构建出响应迅速、功能完善的Android应用。