概览
10. 服务
10.1 Service概述
Service能够在后台长时间运行,组件能够绑定到Service并与之交互,甚至执行进程间通信(IPC)。Service能够在后台处理网络事务、播放音乐、执行文件操作或者Content Provider通信。
10.2 Android多线程
10.2.1 线程的基本用法
Android多线程基本用法与Java中多线程基本一致
详细可以参考 Java多线程基础 这里不做赘述
10.2.2 子线程中更新UI
Android中的UI线程不安全,所以不能在子线程中做更新UI操作
所以就用到异步消息处理机制Handler
-
Handler类:用于发送消息和处理消息,使用sendMessage()发送消息,重写handleMessage()用来处理消息
-
Message 类:用于在线程之间传递消息,在内部携带少量的信息,在不同线程间交换数据
使用what字段设置消息类型
arg1,arg2这两个字段携带一些整形数据
obj字段携带一个Object对象 -
MessageQueue:消息队列,用于存放所有通过Handler发送的消息,每个线程只有一个MessageQueue
-
Looper:循环的在MessageQueue中取消息,根据消息发送过来的时间决定消息取出的先后,每个线程只有一个Looper对象
代码示例:
子线程发送消息切换到主线程
//在主活动中定义Handler,在按钮的点击事件中开启一个线程处理具体逻辑,此时由主线程切换到子线程。当想要更新UI时,发送消息,主线程接收到消息进行处理,此时由子线程切换到主线程进行UI操作
public class MainActivity extends AppCompatActivity {
private static final int COMPLETE = 0;
private TextView textView;
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn = (Button)findViewById(R.id.btn);
textView = (TextView)findViewById(R.id.text_view);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG,"btn_click");
//在子线程中如果想更新ui,利用Message发送消息,并且可以携带数据
new Thread(new Runnable() {
@Override
public void run() {
Log.d(TAG,"change_ui");
//对消息对象进行复用
Message msg = Message.obtain();
msg.what = COMPLETE;
msg.arg1 = 1;
handler.sendMessage(msg);
}
}).start();
}
});
}
private final Handler handler = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(@NonNull Message msg) {
Log.d(TAG,"change_complete");
switch (msg.what){
case COMPLETE:
Log.d(TAG,"arg1:"+msg.arg1);
textView.setText("complete"+msg.arg1);
break;
default:
break;
}
}
};
}
可以看到是从子线程切换到主线程的
主线程发送消息切换到子线程
public class SecondActivity extends AppCompatActivity {
private static final int COMPLETE = 0;
private static final int TRANSPORT = 1;
private TextView textView;
private static final String TAG = "SecondActivity";
private HandlerThread childHandlerThread;
private Handler childHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Button btn = (Button)findViewById(R.id.btn);
textView = (TextView)findViewById(R.id.text_view);
childHandlerThread = new HandlerThread("thread0");
childHandlerThread.start();
childHandler = new Handler(childHandlerThread.getLooper()){
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what){
case COMPLETE:
Log.d(TAG,"complete");
break;
case TRANSPORT:
Log.d(TAG,""+msg.obj);
default:
break;
}
}
};
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG,"update UI");
Message msg = Message.obtain();
msg.what = TRANSPORT;
msg.obj = "start";
childHandler.sendMessage(msg);
Message msg2 = Message.obtain();
msg2.what = COMPLETE;
//延迟3秒发送消息
childHandler.sendMessageDelayed(msg2,3000L);
}
});
}
}
可以看到是从主线程切换到子线程的
10.2.3 使用AsyncTask
使用AsyncTask流程:
-
自定义类继承自AsyncTask,并指定3个泛型参数
- Params:执行AsyncTask时需要传入的参数,用于在后台任务中使用
- Progress:后台执行时,使用这里指定的泛型作为进度单位
- Result:当任务执行完毕后,使用这里指定的泛型作为返回值类型
-
需要重写的方法:
- onPreExecute(): 在后台任务开始执行前调用,用于界面的初始化,例如显示一个进度条对话框
- doInBackground(Void… voids):后台任务 在子线程中运行,处理耗时任务,这个方法中不能进行UI操作
如果需要更新UI,需要调用publishProgress()方法 - onProgressUpdate(Integer… values):在后台任务中调用了publishProgress()方法,该方法就会被调用,可以对UI进行操作,利用参数中的数值就可以对界面进行更新
- onPostExecute(Boolean aBoolean):当后台任务执行完并通过return返回时,该方法会被调用。返回的数据作为参数传递到该方法中可以利用返回的数据进行UI操作
代码示例:
public class DownloadTask extends AsyncTask<Void,Integer,Boolean> {
/**
* 后台任务
* 在子线程中运行,处理耗时任务,这个方法中不能进行UI操作
* 如果需要更新UI,需要调用publishProgress()方法
* @param voids
* @return
*/
@Override
protected Boolean doInBackground(Void... voids) {
return null;
}
/**
* 在后台任务开始执行前调用,用于界面的初始化,例如显示一个进度条对话框
*/
@Override
protected void onPreExecute() {
}
/**
* 当后台任务执行完并通过return返回时,该方法会被调用。返回的数据作为参数传递到该方法中
* 可以利用返回的数据进行UI操作
* @param aBoolean
*/
@Override
protected void onPostExecute(Boolean aBoolean) {
super.onPostExecute(aBoolean);
}
/**
* 在后台任务中调用了publishProgress()方法,该方法就会被调用,可以对UI进行操作,利用参数中的数值就可以对界面进行更新
* @param values
*/
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
}
}
//在主活动中启动任务
new DownloadTask().execute()
10.3 Service的基本用法
10.3.1 定义一个服务
服务的基本用法:
如果手动创建Service需要去Manifest文件中注册
定义一个服务:在所在包下右键New->Service->Service并重写常用的4个方法
- onBind(Intent intent):绑定服务时使用
- onCreate():在服务创建的时候调用
- onStartCommand(Intent intent, int flags, int startId):在每次服务启动的时候调用
- onDestroy():在服务销毁的时候调用
代码示例:
- 手动定义一个MyService类并继承于Service
public class MyService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
- 在Manifest文件中注册service
<service android:name=".MyService"
android:enabled="true"
android:exported="true"/>
如果是AS自动创建,则只需要重写方法,不用注册service
10.3.2 启动和停止服务
- 启动服务:
Intent startIntent = new Intent(MainActivity.this,MyService.class);
startService(startIntent);
- 停止服务:
Intent stopIntent = new Intent(MainActivity.this,MyService.class);
stopService(stopIntent);
让服务自己停下来:在服务类中任何一个地方调用 stopSelf()方法就可以停止服务
10.3.3 活动和服务进行通信
流程:
- 创建一个内部类继承自Binder,并在onBind()方法中返回该类的实例对象
- 在主活动中创建 ServiceConnection的匿名类,并重写2个方法
onServiceConnected():在活动与服务成功绑定时调用
onServiceDisconnected():在活动与服务解绑时调用 - 绑定服务
Intent bindIntent = new Intent(MainActivity.this,MyService.class);
bindService(bindIntent,connection,BIND_AUTO_CREATE); 三个参数- intent对象
- ServiceConnection实例对象
- 标志位,传入BIND_AUTO_CREATE表示在活动和服务进行绑定后自动创建服务
- 解绑服务
unbindService(connection); 直接传入ServiceConnection实例对象进行解绑
代码示例:
//在刚才的MyService类中创建内部类MyBinder类并继承于Binder
//用于提供具体的方法,并在onBind()方法中返回MyBinder实例
private MyBinder mBinder = new MyBinder();
public static class MyBinder extends Binder{
//服务里提供具体的方法的类
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
//在主活动中定义变量MyBinder,以及匿名类ServiceConnection
private MyService.MyBinder myBinder;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//赋值
myBinder = (MyService.MyBinder) service;
//TODO: 调用服务中的方法
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
//在Oncreate()方法中,按钮的点击事件中 绑定服务
Intent intent = new Intent(ServiceActivity.this,MyService.class);
bindService(intent,connection,BIND_AUTO_CREATE);
//解绑服务,服务会销毁
unbindService(connection);
10.3.4 服务的生命周期
每个服务都只存在一个实例
- onCreate():在服务创建时调用
- onStartCommand():服务启动时调用
- onBind():服务绑定时调用
- onDestroy():服务被销毁时调用
Service的生命周期可以分成三个不同的路径:
-
通过startService()方法启动Service
其生命周期 onCreate() - onStartCommand() - onDestroy()
当调用startService()方法时,Service被创建,并且无限期运行,其自身必须调用stopSelf()或者其他组件调用stopService()方法来停止Service。当Service停止时,系统将其销毁。 -
通过bindService()方法启动Service,服务还没有被创建
其生命周期 onCreate() - onBind() - onDestroy()
当调用bindService()方法时,Service被创建。客户端通过IBinder接口与Service通信。客户端通过unbindService()方法关闭连接。当都解除绑定时,系统销毁Service(Service不需要被停止)。 -
通过startService()方法,bindService()方法来启动服务
需要同时调用stopService()方法和unbindService()方法来销毁服务
10.3.5 使用前台服务
在onCreate()方法中,构建出Notification对象,后使用startForeground(通知的id,Notification对象)方法让Service变成一个前台服务
代码示例:
在onCreate()方法中:
//将该注解写在方法上
//@RequiresApi(api = Build.VERSION_CODES.O)
String channelId = "channel_id_01";
String channelName = "channel";
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
NotificationChannel channel = new NotificationChannel(channelId,channelName,NotificationManager.IMPORTANCE_DEFAULT);
notificationManager.createNotificationChannel(channel);
Notification notification = new Notification.Builder(this,channelId)
.setContentTitle("service")
.setContentText("service text")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher))
.build();
startForeground(1,notification);
10.3.6 使用IntentService
在运行结束后会自动停止,IntentService使用流程:
新建类继承自IntentService,并重写常用的2个方法,在无参构造函数中调用父类的有参构造函数
onHandleIntent():处理具体的逻辑
onDestroy():自动停止服务时调用
代码示例:
public class MyIntentService extends IntentService {
public MyIntentService() {
super("MyIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
//处理具体逻辑
}
@Override
public void onDestroy() {
}
}