Service

WHAT

  1. 不需要交互但是需要长期运行的后台任务。
  2. 服务的运行不依赖于任何用户界面,即使程序被切换到后台,或者用户打开了另外一个应用程序,服务仍然能够保持正常运行。
  3. 服务并不是运行在一个独立的进程当中的,而是依赖于创建服务时所在的
    应用程序进程。当某个应用程序进程被杀掉时,所有依赖于该进程的服务也会停止运行。
  4. 不要被服务的后台概念所迷惑,实际上服务并不会自动开启线程,所有的代码都是默认运行在主线程当中的。也就是说,我们需要在服务的内部手动创建子线程,并在这里执行具体的任务,否则就有可能出现主线程被阻塞住的情况。

注意事项

  1. Service不是更好的AsyncTask
    Service不是用来执行异步、后台操作的,它被创造的目的就是在Activity不可见时仍能执行逻辑,要把Service想成不可见的Activity。

要记住每个Service都是有使用成本的特殊组件,不只是对于APP,更是对整个系统。

  1. 默认情况下Service在主线程运行
    可以选择让Service在其他线程运行,但除非必要情况时应尽量避免这么做,因为这么做是有代价的。

  2. IntentService并不神奇
    IntentService是通过创建一个HandlerThread并在其中排列逻辑来工作的,可以很轻松的跳出Service实现这种方式。

IntentService是一个很简单的类,只有164行代码,去掉注释74行,可以自己去看看。

  1. 一个Service同时只能有一个实例
    不管怎么new,一个Service永远只能有一个实例,即使外部APP/进程与它交互也是这样。

  2. Service很容易被干掉
    不要以为内存少是很极端的情况,应该在编写Service代码时让它可以很优雅的处理系统对它的重启,这是生命周期的一部分。

可以把Service标记为foreground让它更难被干掉,但应该只在必要时这么做。

注意当执行onCreate()、onStartCommand()、onDestroy()方法时,Service被视作foreground虽然它并不是。

下面这个链接可以告诉你你的进程有多么可能被干掉。
安卓开发者平台

  1. 一个Service可以在同时被启动与绑定
    明确的停止绑定了其他组件的Service并不会使它结束。解绑Service的所有组件而不结束它也不会使它停止。还有就是不论你调用startService()多少次,只需调用一次stopService()或stopSelf()就可以结束它。具体参考下图:
    Service LifeCycle

  2. STARTFLAGREDELIVERY可以避免输入数据丢失
    如果在启动Service时传入数据并在onStartCommand()中返回STARTFLAGREDELIVERY可以有效地避免在Service处理数据被杀死时丢失数据。

  3. 前台的通知可以被部分遮盖
    当一个前台的Service要发送一个通知时,可以传入一个优先级值PRIORITY_MIN来在状态栏中隐藏它,而在通知栏中仍然可见。

  4. Service可以启动Activity
    就像不是Activity的所有Context一样,Service可以在添加FLAGACTIVITYNEW_TASK标记时启动Activity。

还可以显示Toast或状态栏通知,一样的道理。

  1. 可以引入单一职责原则
    最好不要在Service中写逻辑代码,而是分离在另一个类中,这样除了可以复用外,说不定还有其他好处。

场景

服务在后台监听蓝牙打印机的状态,当打印机异常(缺纸)时,弹出提示

弹出 Toast

使用 hanlder 传递 Toast


hanlder.post(new Runnable() {
    @Override
    public void run() {
        Toast.makeText(DialogService.this, "打印机缺纸", 1).show();
     }
    });

弹出 Dialog

Dialog的显示是需要依附于一个Activity,如果需要在 Servcie 中显示需要把 Dialog 设置成一个系统级 Dialog(TYPE_SYSTEM_ALERT),即全局性质的提示框.

在安卓8及以上版本,需要 使用 TYPE_APPLICATION_OVERLAY 权限才能在 后台 或者 其他窗口 弹出。

  private Handler mHandler;
//在 Service 生命周期方法 onCreate() 中初始化 mHandler
  mHandler = new Handler(Looper.getMainLooper());
//在子线程中想要 Toast 的地方添加如下
  mHandler.post(new Runnable() {
          @Override
          public void run() {
            //show dialog
            justShowDialog();
          }
        });
 
  private void justShowDialog() {
    AlertDialog.Builder builder = new AlertDialog.Builder(getApplicationContext())
        .setIcon(android.R.drawable.ic_dialog_info)
        .setTitle("service中弹出Dialog了")
        .setMessage("是否关闭dialog?")
        .setPositiveButton("确定",
            new DialogInterface.OnClickListener() {
              public void onClick(DialogInterface dialog,
                        int whichButton) {
              }
            })
        .setNegativeButton("取消",
            new DialogInterface.OnClickListener() {
              public void onClick(DialogInterface dialog,
                        int whichButton) {
              }
            });
    //下面这行代码放到子线程中会 Can't create handler inside thread that has not called Looper.prepare()
    AlertDialog dialog = builder.create();
    //设置点击其他地方不可取消此 Dialog
    dialog.setCancelable(false);
    dialog.setCanceledOnTouchOutside(false);
    //8.0系统加强后台管理,禁止在其他应用和窗口弹提醒弹窗,如果要弹,必须使用TYPE_APPLICATION_OVERLAY,否则弹不出
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
      dialog.getWindow().setType((WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY));
    }else {
      dialog.getWindow().setType((WindowManager.LayoutParams.TYPE_SYSTEM_ALERT));
    }
    dialog.show();
  }

参考
https://www.jb51.net/article/172238.htm

错误 Can't create handler inside thread that has not called Looper.prepare()

new Thread(new Runnable() {
                @Override
                public void run() {
                    Looper.perpare();//增加部分
                    timer = new Timer(mTotalTime, TimeSetted.SECOND_TO_MILL);
                    timer.start();
                    Log.d(TAG,"Countdown start");
                    Looper.loop();//增加部分
                }
            }).start();
//版权声明:本文为CSDN博主「雷霆管理层」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
//原文链接:https://blog.csdn.net/weixin_42694582/article/details/81535083
posted @ 2021-06-15 14:45  MOSSLIFE  阅读(126)  评论(0编辑  收藏  举报