薯拾

《第一行代码android》第10章

2020-10-04

10. 服务

ServiceAndroid中实现程序后台运行的解决方案,适合不需要和用户交互且要求长期运行的任务;但是服务并不是运行在独立的进程中,依赖于创建服务时所在的应用程序进程;服务默认运行在主线程中不会自动开启线程,需要在服务内部手动创建子线程。

10.1 Android 多线程

基本使用

线程的基本使用就是java的方式:

1
2
3
4
class MyThread extends Thread{
public void run(){......}
}
new MyThread().run();
1
2
3
4
class MyThread implements Runnable{
public void run(){......}
}
new Thread(new MyThread()).start();

继承Thread类的方式对应自定义线程,实现Runnable接口强调将任务进行抽象。

子线程中更新UI

Android不允许在子线程更新UI,已测试证实。提供了异步消息处理机制处理需要根据任务执行结果更新UI的场景。也就是将消息从子线程传递给主线程,主线程进行UI更新。runOnUiThread方法实际是以上流程的封装实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private Handler handler = new Handler(){
public void handleMessage(Message msg){
switch(msg.what){
case 123:
updateUI();
break;
}
}
}
/* 子线程 */
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.what = 123;
handler.sendMessage(message);
}
}).start();

AsyncTask

AsyncTask也是对异步机制的封装。这是一个抽象类,需要定义子类去继承,继承时三个参数分别是:

  • Params: 执行AsyncTask时需要传入的参数,这个类型和下面doInBackground的参数类型一致
  • Progress: 后台执行任务时更新UI的进度,此类型和onProgressUpdatepublishProgress的参数类型一致;
  • Result: 任务执行完成返回结果的类型,与doInBackground的返回值,onPostExecuteonCancelled的参数类型一致
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class DownloadTask extends AsyncTask<String, Integer, Integer> {
    /* 后台任务开始之前,进行一些界面初始化,主线程执行 */
    protected void onPreExecute(){}
    /* 子线程执行耗时操作,不可操作UI,可通过publishProgress更新进度 */
    protected Integer doInBackground(String... params){}
    /* 主线程处理信息,对应handleMessage */
    protected void onProgressUpdate(Integer... values){}
    /* 主线程接收子线程执行结果 */
    protected void onPostExecute(Integer status) {}
    }

10.2 服务基本用法

继承Service,并通过intentcontextstartServicestopService控制,也可以在service内部通过stopSelf停止自己。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class DownloadService extends Service{
private DownloadBinder mBinder = new DownloadBinder();
/* 第一次创建时调用 */
public void onCreate() {}
/* 每次启动时调用,未stopService多次startService时,不会调用onCreate */
public int onStartCommand(Intent intent, int flags, int startId) {}
/* 服务销毁时调用 */
public void onDestroy() {}

public IBinder onBind(Intent intent) {
return mBinder;
}
/* binder 相当于服务对外提供的接口,通过onBind向外暴露 */
class DownloadBinder extends Binder {
public void startDownload(){}
public int getProgress(){}
}
}

在主线程中,通过在conncetion中获取bindServiceunBindService的回调可在此获取到服务提供的binder,进而调用binder提供的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private MyService.DownloadBinder downloadBinder;
private ServiceConnection connection = new ServiceConnection() {
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
downloadBinder = (DownloadService.DownloadBinder) iBinder;
downloadBinder.startDownload();
downloadBinder.getProgress();
}
public void onServiceDisconnected(ComponentName componentName) {}
};
/* 绑定服务 */
Intent intentBind = new Intent(this, MyService.class);
bindService(intentBind, connection, BIND_AUTO_CREATE);
/* 解绑服务 */
unbindService(connection);
如图展示了通过`serviceConnection`调用`service`的`binder`的时序图,目前只能确定调用先后书序,`context`的确切机制还不清楚。需要注意`onServiceDisconnected`是在服务发生异常退出时才会调用(使用1/0直接程序崩溃了,还未实验触发成功),直接`unbindService`不会触发。

10.3 技巧

前台服务

内存不足时可能会回收后台运行的服务,前台服务则不会,前台服务会在状态栏显示。使用notification载入intent然后startForeground这个notification即可,注意事项与notification一致。

1
2
3
4
5
6
7
8
9
10
11
12
public class DownloadService extends Service{
public void onCreate() {
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0);
NotificationChannel mChannel = new NotificationChannel(id, name, NotificationManager.IMPORTANCE_LOW);
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.createNotificationChannel(mChannel);
notification = new Notification.Builder(this, id).todo().build();
/** 创建完成notification*/
startForeground(1, notification);
}
}

IntentService

使得开发者不用手动开启线程和停止服务,自动创建异步自动停止的服务。从API30弃用,受Android 8添加的后台限制,官网推荐workManagerjobIntentManager

1
2
3
4
public class MyIntentService extends IntentService {
protected void onHandleIntent(@Nullable Intent intent) {}
public void onDestroy() {}
}
Tags: android
使用支付宝打赏
使用微信打赏

若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏

扫描二维码,分享此文章