【安卓移动应用开发】四大组件之——Service
1,安卓中进程的概念
1.1,Android中的进程与线程
1.每一个app就是一个进程,进程开启的时候默认就有一个主线程(UI线程)
2.线程之间可以做到数据共享,但是进程之间无法直接进行数据共享(但可通过组件进行)
3.主线程才能进行UI更新的操作
4.不要把耗时操作放在主线程,而是开启一个子线程去处理这些事情
1.2,开启一个子进程
下面介绍两种常用的方法来创建子线程,这两种方法都是基于Java的多线程来实现的。
1.2.1,继承Thread对象
步骤1:
编写子线程类,这里可以使用内部类,方便共享数据
/**
* 继承Thread类开启子线程
*/
class SonThread extends Thread {
/*
* 自定义方法,run方法调用,使得run方法更加简洁
*/
protected int CountSum() {
for (int i = 0; i < 1000; i++) {
sum += 1;
}
return sum;
}
/**
* 重写run方法
* 在run方法中调用子线程需要执行的方法
*/
@Override
public void run() {
super.run();
CountSum();
tv_log.setText(sum+"");
}
}
步骤2
通过start方法开启子线程
Thread mThread = new SonThread();
// 注意不是使用run方法来开启线程,而是使用start方法
// 直接使用run方法的话还是在主线程中运行
mThread.start();
1.2.2,实现Runnable接口
(考虑到Java的单继承,更加推荐此方法)
步骤1:
编写子线程类实现Runnable方法
class SonThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
sum += 1;
}
Log.d("zhiqiang","当前运行线程ID:"+Thread.currentThread().getId());
tv_log.setText(sum+"");
}
}
步骤2:
通过Thread的构造方法来构造Thread对象,并通过start方法开启子线程
Thread mThread = new Thread(new SonThread());
mThread.start();
注意,开启多个子线程,子线程的执行顺序与代码顺序无关
1.3,子线程更新UI
为了用户体验的流畅,安卓禁止在子线程中更新UI,在子线程直接更新UI会导致崩溃(某些情况下不会崩溃??但是还是要按照规范只在主线程中更新UI)。
1.3.1,Handler切换线程
下面介绍最为基础的方式----通过Handler来实现子线程间接更新UI的方法:
步骤1:
构建类,继承Handler类,实现handleMessage方法
// 类属性
protected static final int CHANGE_TEXT_MAG = 1;
protected class UIHandler extends Handler{
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what){
case CHANGE_TEXT_MAG:
// 实现更新UI的操作
tv_log.setText(msg.arg1+"");
break;
}
}
}
步骤2:
在主线程对Handler初始化
// 声明Handler类对象
private UIHandler uiHandler;
// 初始化Handler对象
uiHandler = new UIHandler();
Handler对象在那个线程中创建的,其中的方法就会在那个线程中执行
步骤3:
在子线程中创建Message类,向Handler发送消息
// 向Handler发送消息
Message message = new Message();
message.what = CHANGE_TEXT_MAG;
message.arg1 = sum;
uiHandler.sendMessage(message);
1.3.2,AsyncTask框架
使用上一节中的方法完成子线程更行UI操作太过麻烦,使用AsyncTask框架,能够省去繁琐的Handler线程切换。
步骤:1
编写类,继承AsyncTask类,并实现必要的方法
// 使用框架更新UI
protected class DownloadTask extends AsyncTask<String,Integer,Boolean>{
// 需要提前准备的任务,在doInBackground之前运行
@Override
protected void onPreExecute() {
super.onPreExecute();
Log.d("zhiqiang","运行--onPreExecute方法");
}
// 后台需要进行的操作,这也是最主要的方法 **
@Override
protected Boolean doInBackground(String... strings) {
String data = strings[0];
Log.d("zhiqiang","接受参数"+data+"\n开始执行doInBackground方法");
int prograss = 0;
while (prograss < 100){
prograss += 10;
// 通过publishProgress方法通知主进程当前线程进度
publishProgress(prograss);
Log.d("zhiqiang","当前下载进度:"+prograss);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
// 主线程运行过程中,使用该方法接受子线程运行进度(与publishProgress方法配合使用,通知一次,接收一次)
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
int progress = values[0];
tv_log.setText("下载进度:"+progress);
}
// 子线程任务结束会调用该方法(doInBackground方法结束时调用)
@Override
protected void onPostExecute(Boolean aBoolean) {
super.onPostExecute(aBoolean);
tv_log.setText("下载完成!!");
}
}
2,Service
2.1,Service的使用
2.1.1,创建Service
Java包上右键 > New > Service > Service
2.1.2,Service生命周期方法
// onCreate方法只会在初次创建的时候被调用(销毁之后再创建也会被调用)
@Override
public void onCreate() {
super.onCreate();
}
// onStartCommand方法每次调用startService方法的时候都会被调用
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
// 使用stopService方法销毁Service的时候会调用onDestroy方法
@Override
public void onDestroy() {
super.onDestroy();
}
2.1.3,Service的使用
通过startService和stopService来创建和关闭Service
// 1 > 使用Service创建Intent
Intent intent = new Intent(MainActivity.this,MyService.class);
// 2 > 调用startService方法传递Intent来开启Service
startService(intent);
/*在调用stopService方法之前再次调用startService方法,不会触发Service的onCreate方法*/
// 3 > 停止Service
stopService(intent);
通过bindService和unBindService来创建和关闭Service,这种方式创建的Service可以和很方便Activity进行通信
/* 1 > 编写类继承Binder类,并通过Service的onBind方法返回该类的实体对象*/
public class MyBinder extends Binder{
private int sum = 0;
public void Count(int num1,int num2){
sum = num1 + num2;
}
public int getSum(){
return sum;
}
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return new MyBinder();
}
// 2 > 使用Service创建Intent
Intent intent = new Intent(MainActivity.this,MyService.class);
// 3 > 在Activity中调用bindService方法,连接Service
bindService(intent,connection,BIND_AUTO_CREATE);
// 4 > 初始化Binder对象,在connection中对其进行赋值
private MyService.MyBinder mBinder = null;
// 5 > bindService方法中的connection参数需要手动编写ServiceConnection类
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 其中的参数service为连接的service中onBind方法返回的Binder对象
mBinder = (MyService.MyBinder)service;
// 调用service中bind类返回的方法
mBinder.Count(1,2);
}
@Override
public void onServiceDisconnected(ComponentName name) {
// 一般在Activity与Service被动非正常断开连接的时候,重新连接Service会被调用。。
}
};
// 6 > 使用unbindService方法,传递connection为参数,断开与Service的连接
unbindService(connection);
bindService和stopService,只会在第一次调用bindService的时候会调用bindService和onCreate方法,调用stopService的时候会调用onDestroy方法。不会调用onStartCommand方法
2.1.4,Service的线程
Service默认运行在主线程中,若想要在Service中运行耗时操作,需要手动创建子线程。
3,特殊的Service
3.1,前台Service
通过前台Service,service会在通知中显示,并且在Activity不可见的时候,继续在后台执行。
步骤1:
在AndroidManifest.xml配置文件中添加permission
<manifest ...>
<!--添加下方uses-permission配置-->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
...
<application ...>
...
</manifest>
步骤2:
编写Service,在适当的生命周期方法中将Service挂载到Notification上
public class MyService extends Service{
...
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 开启前台Service
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
NotificationChannel channel = new NotificationChannel("LearnService", "LearnService",NotificationManager.IMPORTANCE_DEFAULT);
manager.createNotificationChannel(channel);
}
// 定义点击通知后跳转的Activity
Intent mIntent = new Intent(MyService.this,MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(MyService.this,0,mIntent,0);
// 生成通知
Notification notification = (new NotificationCompat.Builder(MyService.this,"LearnService"))
.setContentTitle("setContentTitle")
.setContentText("setContentText")
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentIntent(pendingIntent)
.build();
// 关键步骤
startForeground(1,notification);
return super.onStartCommand(intent, flags, startId);
}
这样,通过Activity调用startService方法开启Service的时候,就会生成一个Service的通知了。该Service在退出App的时候不会一起关闭
3.2,IntentService
后台调用的Service,会自动创建子线程,且运行完会被自动回收
public class MyService extends IntentService {
也可以使用普通Service每次调用stopSelf,但是需要添加子线程。。
步骤1:
创建继承IntentService
再Java包上右键 > Service > Service(IntentService) 来创建 IntentService
步骤2:
在onHandleIntent方法中编写子线程中需要执行的操作
步骤2:
在Activity中调用启动IntentService
Intent intent = new Intent(MainActivity.this,MyIntentService.class);
startService(intent);
IntentService中子线程的任务完成之后,回自动的调用Destroy方法,结束子线程。而普通的Service创建子线程运行结束之后,不会自动关闭子线程