• 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
    
    >>>按时吃饭,多喝热水,适量锻炼,祝你平安
  • 相关阅读:
    *** 实现冒泡排序模板
    *** 实现stack模板
    python uses xml
    [转]给未来的电子通信工程师
    *** strRevert.cpp
    *** strlen.cpp
    *** checkRevStr.cpp 查看字符串是否是回文
    *** 自己代码:实现字符串比较
    *** 自写代码:查找两个字符串的最大公共子串
    *** 自写代码:在字符串中插入连续字符的个数
  • 原文地址:https://www.cnblogs.com/sinjin/p/14885431.html
Copyright © 2020-2023  润新知