Android组件之服务的详解

作者:Cristiano_san 时间:2021-10-14 00:05:30 

目录
  • 一、服务的概念

  • 二、Android的多线程编程

    • 2.1 线程的基本用法

    • 2.2 在子线程中更新UI

      • 更新方式一

      • 更新方式二

    • 2.3 解析异步消息处理机制

      • 2.4 使用AsyncTask

      • 三、服务的基本用法

        • 3.1 首先定义一个服务

          • 3.2 MyService类里重写几个方法

            • 3.3 在注册文件中完成对服务的注册

              • 3.4 启动和停止服务

                • 3.5 活动和服务进行通信

                • 四、服务的生命周期

                  • 五、服务的更多技巧

                    • 5.1 使用前台服务

                      • 5.2 服务中的多线程问题&IntentService

                      一、服务的概念

                      1、服务是实现程序后台运行的解决方案,主要执行那些不需要和用户交互而且还要求长期运行的任务

                      2、服务是依赖于创建服务时所在的应用程序进程而存在的,而不是运行在一个独立的进程当中

                      3、服务的后台≠创建子线程,需要在服务的内部手动创建子线程,并且在这里执行具体的任务,否则可能会出现主线程被堵塞的情况

                      二、Android的多线程编程

                      2.1 线程的基本用法

                      • 线程声明方式一:


                      //定义方式
                      class myThread extends Thread{
                       @Override
                       public void run(){
                       //具体的处理逻辑
                       }
                      }

                      //启动方式
                      new myThread().start();
                      • 线程声明方式二:


                      //定义方式
                      class myThread implements Runnable{
                      @Override
                       public void run(){
                       //具体的处理逻辑
                       }
                      }

                      //启动方式
                      myThread my = new myThread();
                      new Thread(my).start();
                      • 线程声明方式三:


                      new Thread(new Runnable() {
                                 @Override
                                 public void run() {

                      }
                      }).start();

                      2.2 在子线程中更新UI

                      更新方式一

                      利用runOnUiThread()方法,该方法可以要执行的操作回调到主线程中进行操作,使用方法:

                      以更新TextView为例:


                      @Override
                         protected void onCreate(Bundle savedInstanceState) {
                             .....
                             change.setOnClickListener(new View.OnClickListener() {
                                 @Override
                                 public void onClick(View view) {
                                     new Thread(new Runnable() {
                                         @Override
                                         public void run() {
                                             changeUI("next to meet you");
                                         }
                                     }).start();
                                 }
                             });
                         }
                         public void changeUI(String date){
                             runOnUiThread(new Runnable() {
                                 @Override
                                 public void run() {
                                     text.setText(date);
                                 }
                             });
                         }

                      1、我们在子线程中调用changeUI()方法,并将更改后的数据传过去
                      2、在changeUI()方法中,调用runOnUiThread()方法将进程调回到主线程中,然后调用setText()方法进行更新UI操作

                      具体使用方法,博客:理解 Activity.runOnUiThread

                      更新方式二

                      利用Android自带的异步消息处理机制


                      public class MainActivity extends AppCompatActivity {
                         public static final int UPDATE_TEXT = 1;
                         private TextView text;

                      private Handler handler = new Handler(){
                             public void handleMessage(Message msg){
                                 switch (msg.what){
                                     case UPDATE_TEXT:
                                         //对UI进行操作
                                         text.setText("Nice to meet you");
                                         break;
                                     default:
                                         break;
                                 }
                             }
                         };

                      @Override
                         protected void onCreate(Bundle savedInstanceState) {
                             ....
                             change.setOnClickListener(new View.OnClickListener() {
                                 @Override
                                 public void onClick(View view) {
                                     new Thread(new Runnable() {
                                         @Override
                                         public void run() {
                                             Message message = new Message();
                                             message.what = UPDATE_TEXT;
                                             handler.sendMessage(message);
                                         }
                                     }).start();
                                 }
                             });
                         }
                      }

                      1、首先定义一个字段UPDATE_TEXT表示更新的具体动作
                      2、创建Handler的对象,并重写父类的handleMessage()方法,在该方法中对Message进行处理
                      3、判断Message的what的值,进行相应的UI操作
                      4、回到子线程,首先获取一个Message 的对象,对what字段赋值
                      5、调用Handler对象的sendMessage()将这个Message 发送出去
                      6、在Handler收到这个消息以后就会调用handleMessage()对消息进行处理,且现在已经是在主线程中处理不会出现问题

                      2.3 解析异步消息处理机制

                      1、处理机制组成:Message、Handler、MessageQueue、Looper

                      2、Message:

                      • 1)作用:在线程之间传递信息

                      • 2)可以在内部携带少量信息,利用其字段,如what,arg1,rg2,obj字段存储,用于在不同线程之间交换数据

                      3、Handler:

                      • 1)作用:主要用于发送和处理消息

                      • 2)发送消息使用Handler的sendMessage()方法

                      • 3)发出的消息经过处理以后,传递到Handler的handleMessage()方法中

                      4、MessageQueue:

                      • 1)作用:主要用于存放所有通过Handler发送的消息。每个线程中只有一个MessageQueue对象

                      5、Looper:

                      • 1)作用:每个线程中MessageQueue的管家

                      • 2)在调用其loop()方法后,进入无限循环,在循环中每发现MessageQueue中存在一条消息就将其取出,传递到Handler的handleMessage()方法中进行处理

                      • 3)每个线程中只有一个Looper对象

                      6、整个异步流程:

                      Android组件之服务的详解

                      • 1)首先主线程中创建一个Handler的对象,并重写父类的handleMessage()方法

                      • 2)在子线程要进行UI操作时,创建一个Message对象,通过Handler的sendMessage()方法出去

                      • 3)这条消息会被添加到MessageQueue队列中等待

                      • 4)Looper从MessageQueue中取出一条消息,传递到Handler的handleMessage()方法中进行处理

                      2.4 使用AsyncTask

                      1、AsyncTask的实现原理是基于异步消息处理机制的
                      2、AsyncTask是一个抽象类,在子类继承这个类时指定3个泛型参数:

                      • Params:在执行AsyncTask时需传入的参数,可用于在后台任务中使用

                      • Progress:后台任务执行时,若需要在界面上显示当前进度,这里就指定的泛型作为进度单位

                      • Result:当任务执行完毕,若需要对结果进行返回,则使用这里指定的泛型作为返回值类型

                      • 如:


                      public class DownloadTask extends AsyncTask<Void,Integer,Boolean> {
                         ....
                      }

                      第一个泛型参数指定为Void,表示在执行AsyncTask时不需要传入参数给后台任务
                      第二个参数指定为Integer,表示使用整型数据来作为进度显示单位
                      第三个参数指定为Boolean,表示用boolean类型数据来反馈执行结果

                      3、在继承后,还需重写如下方法

                      • onPreExecute() :在后台任务开始执行之前调用,用于进行一些界面上的初始化操作,如显示一个进度条对话框等

                      • doInBackground(Params… ) :该方法中所有的代码都会在子线程中运行。后台任务一旦完成通过return语句将任务的执行结果返回,若AsyncTask第三个参数指定为Void,则可以不返回任务执行结果。若需要更新UI元素,则调用publishProgress(Progress…)方法来完成

                      • onProgressUpdate(Progress… ):当在后台任务中调用publishProgress(Progress…)方法,该方法就会被调用,该方法所携带的参数就是在后台任务中传递过来的,在这个方法中利用参数中的数值可以对UI进行操作

                      • onPostExecute(Result) :后台任务执行完毕并返回return语句进行返回时,这个方法就会被调用,返回的数据作为参数会传递到此方法中,可以利用返回的数据来进行一些UI操作,如提醒任务执行的结果,或关闭进度条对话框等具体框架:


                      public class DownloadTask extends AsyncTask<Void,Integer,Boolean> {
                         @Override
                         protected void onPreExecute() {
                             progressDialog.show();//显示进度对话框
                         }

                      @Override
                         protected Boolean doInBackground(Void... voids) {//在该方法里执行下载任务
                             try{
                                 while(true){
                                     int percent = doDownload();//虚构一个方法,返回一个值表示下载进度
                                     publishProgress(percent);//调用publishProgress方法,并将进度数值传进去
                                     if(percent >= 100)
                                         break;
                                 }
                             }catch (Exception e){
                                 e.printStackTrace();
                             }
                             return true;
                         }

                      @Override
                         protected void onProgressUpdate(Integer... values) {//该方法所带的参数就是publishProgress()方法的进度数值
                             progressDialog.setMessage("Downloaded" + values[0] + "%");
                         }

                      @Override
                         protected void onPostExecute(Boolean aBoolean) {//后台任务完成后该方法就会被调用
                             progressDialog.dismiss();//关闭进度对话框
                             //提示下载结果
                             if (aBoolean){
                                 Toast.makeText(context,"Succeeded",Toast.LENGTH_SHORT).show();
                             }else {
                                 Toast.makeText(context,"Failed",Toast.LENGTH_SHORT).show();
                             }
                         }
                      }

                      4、启动方法:


                      new DownloadTask().execute();

                      三、服务的基本用法

                      3.1 首先定义一个服务

                      Android组件之服务的详解

                      在上述界面中:Exported表示是否允许除了当前程序之外的其他程序访问这个服务;Enabled表示是否启动这个服务

                      3.2 MyService类里重写几个方法


                      public class MyService extends Service {
                         public MyService() {
                         }
                         @Override
                         public IBinder onBind(Intent intent) {
                             // TODO: Return the communication channel to the service.
                             throw new UnsupportedOperationException("Not yet implemented");
                         }

                      @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();
                         }//服务被销毁时调用
                      }

                      1、onCreate()方法在服务被创建时调用
                      2、onStartCommand()方法在每次服务启动时调用,若需要服务一旦启动就立刻执行某个动作,就可以将逻辑卸载这个方法里面
                      3、onDestroy()方法在服务被销毁时调用
                      4、onCreate()方法在服务第一次创建时调用,onStartCommand()方法是在每次启动服务时调用(若在一次服务中调用了onDestroy()方法,那么这个服务就无了,再点开始按钮就会调用onCreate()方法)

                      3.3 在注册文件中完成对服务的注册


                      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                         package="com.example.testservices" >
                         <application
                             .....
                             <service
                                 android:name=".MyService"
                                 android:enabled="true"
                                 android:exported="true" >
                             </service>
                             .....
                         </application>
                      </manifest>

                      3.4 启动和停止服务

                      首先要创建一个活动和两个用于启动服务的按钮,创建完成后通过Intent实现活动和服务之间的“桥梁”搭建


                      Button start = findViewById(R.id.start_service);
                             Button stop = findViewById(R.id.stop_service);
                             start.setOnClickListener(new View.OnClickListener() {
                                 @Override
                                 public void onClick(View view) {
                                     Intent startIntent = new Intent(MainActivity2.this,MyService.class);
                                     startService(startIntent);//启动服务
                                 }
                             });
                             stop.setOnClickListener(new View.OnClickListener() {
                                 @Override
                                 public void onClick(View view) {
                                     Intent stopIntent = new Intent(MainActivity2.this,MyService.class);
                                     stopService(stopIntent);//停止服务
                                 }
                             });

                      关于让服务停止,如果不点击停止按钮服务会一直处于运行状态。可以通过在MyService中指定的地方调用 stopSelf()方法 实现服务的自动停止

                      然后在MyService里定义一些日志


                      public class MyService extends Service {
                         final static String TAG = "MyService";
                        ....
                         @Override
                         public void onCreate() {
                             super.onCreate();
                             Log.d(TAG,"onCreate executed");
                         }//服务被创建时调用

                      @Override
                         public int onStartCommand(Intent intent, int flags, int startId) {
                             Log.d(TAG,"onStartCommand executed");
                             return super.onStartCommand(intent, flags, startId);
                         }//每次服务启动时调用,若需要服务一旦启动就立刻执行某个动作,就可以将逻辑卸载这个方法里面

                      @Override
                         public void onDestroy() {
                             super.onDestroy();
                             Log.d(TAG,"onDestroy executed");
                         }//服务被销毁时调用
                      }

                      运行结果:

                      Android组件之服务的详解

                      3.5 活动和服务进行通信

                      以下载功能为例:在服务中提供一个下载功能,在活动中决定何时开始下载以及随时查看下载进度

                      1、首先在MyService中创建一个Binder的子类内部类,对下载功能进行管理:


                      class DownloadBinder extends Binder{
                             public void startDownload(){
                                 Log.d(TAG,"startDownload executed");
                             }//开始下载的模拟方法
                             public int getProgress(){
                                 Log.d(TAG,"getProgress executed");
                                 return 0;
                             }//查看下载进度的模拟方法
                         }

                      2、然后同样地,在MyService里获取到DownloadBinder 的实例,并且通过 onBind()方法 返回这个实例(最关键的一步)


                      private DownloadBinder binder = new DownloadBinder();
                      .....
                         @Override
                         public IBinder onBind(Intent intent) {
                             // TODO: Return the communication channel to the service.
                             return binder;
                         }

                      3、在活动中定义两个按钮,分别用于绑定服务和服务解绑

                      4、在活动中创建一个ServiceConnection的匿名类,并重写onServiceConnected()、onServiceDisconnected()方法,这两个方法会在活动与服务成功绑定和断开时调用


                      public class MainActivity2 extends AppCompatActivity {
                         private MyService.DownloadBinder binder;
                         private ServiceConnection connection = new ServiceConnection() {
                             @Override
                             public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                                 binder = (MyService.DownloadBinder) iBinder;
                                 binder.startDownload();
                                 binder.getProgress();
                                 //在这里通过转型获取到Binder实例,通过这个实例,就可以在活动中根据具体的场景来调用DownloadBinder中任何public的方法
                             }
                             @Override
                             public void onServiceDisconnected(ComponentName componentName) {

                      }
                         };
                         @Override
                         protected void onCreate(Bundle savedInstanceState) {
                             .....
                         }
                      }

                      5、在按钮事件里完成活动与服务之间的绑定和解绑


                      bind.setOnClickListener(new View.OnClickListener() {
                                 @Override
                                 public void onClick(View view) {
                                     Intent bindIntent = new Intent(MainActivity2.this,MyService.class);
                                     bindService(bindIntent,connection,BIND_AUTO_CREATE);//绑定服务
                                     //BIND_AUTO_CREATE是一个标志位,该标志位表示在活动和服务进行绑定后自动创建服务,即MyService里的onCreate()方法会得到执行,onStartCommand()方法不会得到执行
                                 }
                             });
                             unbind.setOnClickListener(new View.OnClickListener() {
                                 @Override
                                 public void onClick(View view) {
                                     unbindService(connection);//解除绑定
                                 }
                             });

                      四、服务的生命周期

                      Android组件之服务的详解

                      Android组件之服务的详解

                      五、服务的更多技巧

                      5.1 使用前台服务

                      1、前台服务的作用:可以使服务一直保持运行状态,而不会由于系统内存不足的原因导致被回收
                      2、跟普通服务的区别:一直有一个正在运行的图标在系统的状态栏显示,下拉状态栏后可以看到更详细的信息
                      3、具体实现:


                      public class MyService extends Service {
                         .....
                         @Override
                         public void onCreate() {
                             super.onCreate();
                             Log.d(TAG,"onCreate executed");
                             Intent intent = new Intent(this,MainActivity2.class);
                             PendingIntent pi = PendingIntent.getActivity(this,0,intent,0);
                             NotificationManager manager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
                             Notification notification = new NotificationCompat.Builder(this,"YonC")
                                     .setContentTitle("This is content title")
                                     .setContentText("his is content text")
                                     .setWhen(System.currentTimeMillis())
                                     .setContentIntent(pi)
                                     .build();
                             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                                 NotificationChannel channel = new NotificationChannel("YonC", "YonC",NotificationManager.IMPORTANCE_HIGH);
                                 manager.createNotificationChannel(channel);
                             }
                             manager.notify(1,notification);
                             startForeground(1,notification);
                         }//服务被创建时调用
                      .....
                      }

                      1、这里利用PendingIntent 和Notification 创建一个通知的形式创建了一个前台活动
                      2、首先通过notify()方法将该通知显现出来
                      3、然后调用startForeground()方法让MyService变成一个前台服务,该方法接收两个参数,第一个参数是通知的id,
                      类似于notify()方法的第一个参数;第二个参数为构建出的Notification对象

                      4、注意还需要申请权限


                      <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

                      5.2 服务中的多线程问题&IntentService

                      1、ANR:即Application Not Responding,直接在服务中处理一些耗时的逻辑就会出现此类情况
                      2、IntentService是服务的子类之一,在该类中的onHandleIntent()方法中的逻辑都是在子线程中执行,因此可以将服务中一些耗时的逻辑都放在这个方法中从而避免ANR问题
                      3、具体实现:


                      public class MyIntentService extends IntentService {
                         public MyIntentService(){
                             super("MyIntentService");
                         }//这个无参的构造函数和调用父类有参构造函数是必须有的
                         @Override
                         protected void onHandleIntent(@Nullable Intent intent) {
                             //该方法里的逻辑都是在子线程中执行的
                         }
                      }

                      son.setOnClickListener(new View.OnClickListener() {
                                 @Override
                                 public void onClick(View view) {
                                     Intent intent = new Intent(MainActivity2.this,MyIntentService.class);
                                     startService(intent);
                                 }
                             });

                      在按钮注册事件中引用Intent “建桥”,再调用startService()方法开启服务


                         <service android:name=".MyIntentService"/>

                      完成该服务的注册

                      来源:https://blog.csdn.net/Cristiano_san/article/details/119885779

                      标签:Android,组件,服务
                      0
                      投稿

                      猜你喜欢

                    • Spring Cloud Gateway 默认的filter功能和执行顺序介绍

                      2021-07-01 21:31:46
                    • C#开发中常用的加密解密方法汇总

                      2021-09-06 23:35:49
                    • OpenTelemetry初识及调用链Trace详解

                      2021-08-03 12:37:14
                    • C# Winform实现捕获窗体最小化、最大化、关闭按钮事件的方法

                      2021-10-18 08:01:29
                    • 一文带你了解C#中抽象方法与虚方法的区别

                      2023-07-23 00:14:32
                    • Spring Boot配置线程池拒绝策略的场景分析(妥善处理好溢出的任务)

                      2022-08-05 07:12:24
                    • 详解Java数据结构和算法(有序数组和二分查找)

                      2023-04-08 13:38:07
                    • interrupt()和线程终止方式_动力节点Java学院整理

                      2021-09-27 08:50:53
                    • Java中去除字符串中所有空格的几种方法

                      2023-11-24 04:59:24
                    • Mybatis如何实现@Select等注解动态组合SQL语句

                      2022-04-13 16:42:05
                    • 为什么不建议使用Java自定义Object作为HashMap的key

                      2021-09-21 06:15:05
                    • IDEA2020.1个性化设置的实现

                      2023-10-17 09:31:34
                    • 解决使用ProcessBuilder踩到的坑及注意事项

                      2023-11-24 01:37:55
                    • Java @Deprecated注解的作用及传递性

                      2023-08-11 12:55:05
                    • 简单理解java泛型的本质(非类型擦除)

                      2023-10-13 03:54:34
                    • C# web.config之<customErrors>节点说明案例详解

                      2023-07-06 20:20:15
                    • Jackson序列化和反序列化忽略字段操作

                      2022-08-29 14:01:14
                    • 一文带你了解Java选择排序的原理与实现

                      2022-05-13 21:01:31
                    • Java try()语句实现try-with-resources异常管理机制操作

                      2022-02-06 18:29:16
                    • Java流程控制break和continue

                      2023-06-16 09:49:54
                    • asp之家 软件编程 m.aspxhome.com