安卓服务与多线程

发布于:2025-07-27 ⋅ 阅读:(14) ⋅ 点赞:(0)

Android 服务与多线程编程详解

一、服务基本概念

服务(Service)是Android中实现程序后台运行的重要组件,它适合执行不需要与用户交互且需要长期运行的任务。服务有以下特点:

  1. 独立于UI:服务运行不依赖于任何用户界面,即使程序被切换到后台也能正常运行
  2. 进程依赖:服务依赖于创建它的应用程序进程,当应用进程被杀死时,所有依赖该进程的服务都会停止
  3. 主线程运行:服务不会自动开启线程,所有代码默认在主线程执行,需要手动创建子线程处理耗时任务

二、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个部分组成:

  1. Message:线程间传递的消息,可携带少量数据
  2. Handler:消息的处理者,负责发送和处理消息
  3. MessageQueue:消息队列,存放所有通过Handler发送的消息
  4. 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实现通信

  1. 在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;
    }
}
  1. 在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. 创建前台服务

  1. 在AndroidManifest.xml中声明权限:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- Android 13+ 需要 -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
  1. 在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
}
  1. 启动前台服务:
// 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. 自动创建子线程处理任务
  2. 任务执行完毕后自动停止服务

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()

八、总结

  1. 服务:适合执行后台任务,但需要注意生命周期管理和资源释放
  2. 多线程:Android UI操作必须在主线程执行,耗时操作应在子线程执行
  3. 通信机制:使用Handler、Binder等方式实现线程间和Activity-Service间通信
  4. 前台服务:适合重要任务,需要创建通知并处理权限问题
  5. IntentService:简化了Service的使用,自动处理线程和生命周期

通过合理使用服务和多线程技术,可以构建出响应迅速、功能完善的Android应用。


网站公告

今日签到

点亮在社区的每一天
去签到