2024年Android最全Android服务——Service(1),2024年最新腾讯游戏客户端开发面试

本文介绍了Android高级进阶中的服务架构,包括IntentService和自定义Service的实现,强调了系统化学习的重要性,以及服务的生命周期管理,如启动、停止、绑定和通知机制。同时鼓励技术交流和团队学习以促进技术提升。
摘要由CSDN通过智能技术生成

最后

在此为大家准备了四节优质的Android高级进阶视频:

架构师项目实战——全球首批Android开发者对Android架构的见解

附相关架构及资料

image.png

往期Android高级架构资料、源码、笔记、视频。高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter全方面的Android进阶实践技术,群内还有技术大牛一起讨论交流解决问题。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

}

/**

  • The IntentService calls this method from the default worker thread with

  • the intent that started the service. When this method returns, IntentService

  • stops the service, as appropriate.

*/

@Override

protected void onHandleIntent(Intent intent) {

  // Normally we would do some work here, like download a file.

  // For our sample, we just sleep for 5 seconds.

  long endTime = System.currentTimeMillis() + 5*1000;

  while (System.currentTimeMillis() < endTime) {

      synchronized (this) {

          try {

              wait(endTime - System.currentTimeMillis());

          } catch (Exception e) {

          }

      }

  }

}

}




您只需要一个构造函数和一个 onHandleIntent() 实现即可。



如果您决定还重写其他回调方法(如 onCreate()、onStartCommand() 或 onDestroy()),请确保调用超类实现,以便 IntentService 能够妥善处理工作线程的生命周期。



例如,onStartCommand() 必须返回默认实现(即,如何将 Intent 传递给 onHandleIntent()):



@Override

public int onStartCommand(Intent intent, int flags, int startId) {

Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();

return super.onStartCommand(intent,flags,startId);

}




除 onHandleIntent() 之外,您无需从中调用超类的唯一方法就是 onBind()(仅当服务允许绑定时,才需要实现该方法)。



在下一部分中,您将了解如何在扩展 Service 基类时实现同类服务。该基类包含更多代码,但如需同时处理多个启动请求,则更适合使用该基类。



扩展服务类

-----



正如上一部分中所述,使用 IntentService 显著简化了启动服务的实现。但是,若要求服务执行多线程(而不是通过工作队列处理启动请求),则可扩展 Service 类来处理每个 Intent。



为了便于比较,以下提供了 Service 类实现的代码示例,该类执行的工作与上述使用 IntentService 的示例完全相同。也就是说,对于每个启动请求,它均使用工作线程执行作业,且每次仅处理一个请求。



public class HelloService extends Service {

private Looper mServiceLooper;

private ServiceHandler mServiceHandler;

// Handler that receives messages from the thread

private final class ServiceHandler extends Handler {

  public ServiceHandler(Looper looper) {

      super(looper);

  }

  @Override

  public void handleMessage(Message msg) {

      // Normally we would do some work here, like download a file.

      // For our sample, we just sleep for 5 seconds.

      long endTime = System.currentTimeMillis() + 5*1000;

      while (System.currentTimeMillis() < endTime) {

          synchronized (this) {

              try {

                  wait(endTime - System.currentTimeMillis());

              } catch (Exception e) {

              }

          }

      }

      // Stop the service using the startId, so that we don't stop

      // the service in the middle of handling another job

      stopSelf(msg.arg1);

  }

}

@Override

public void onCreate() {

// Start up the thread running the service.  Note that we create a

// separate thread because the service normally runs in the process's

// main thread, which we don't want to block.  We also make it

// background priority so CPU-intensive work will not disrupt our UI.

HandlerThread thread = new HandlerThread("ServiceStartArguments",

        Process.THREAD_PRIORITY_BACKGROUND);

thread.start();



// Get the HandlerThread's Looper and use it for our Handler

mServiceLooper = thread.getLooper();

mServiceHandler = new ServiceHandler(mServiceLooper);

}

@Override

public int onStartCommand(Intent intent, int flags, int startId) {

  Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();



  // For each start request, send a message to start a job and deliver the

  // start ID so we know which request we're stopping when we finish the job

  Message msg = mServiceHandler.obtainMessage();

  msg.arg1 = startId;

  mServiceHandler.sendMessage(msg);



  // If we get killed, after returning from here, restart

  return START_STICKY;

}

@Override

public IBinder onBind(Intent intent) {

  // We don't provide binding, so return null

  return null;

}

@Override

public void onDestroy() {

Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();

}

}




正如您所见,与使用 IntentService 相比,这需要执行更多工作。



但是,因为是由您自己处理对 onStartCommand() 的每个调用,因此可以同时执行多个请求。此示例并未这样做,但如果您希望如此,则可为每个请求创建一个新线程,然后立即运行这些线程(而不是等待上一个请求完成)。



请注意,onStartCommand() 方法必须返回整型数。整型数是一个值,用于描述系统应该如何在服务终止的情况下继续运行服务(如上所述,IntentService 的默认实现将为您处理这种情况,不过您可以对其进行修改)。从 onStartCommand() 返回的值必须是以下常量之一:



*   START\_NOT\_STICKY

    

    > 如果系统在 onStartCommand() 返回后终止服务,则除非有挂起 Intent 要传递,否则系统不会重建服务。这是最安全的选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行服务。

    

*   START\_STICKY

    

    > 如果系统在 onStartCommand() 返回后终止服务,则会重建服务并调用 onStartCommand(),但绝对不会重新传递最后一个 Intent。相反,除非有挂起 Intent 要启动服务(在这种情况下,将传递这些 Intent ),否则系统会通过空 Intent 调用 onStartCommand()。这适用于不执行命令、但无限期运行并等待作业的媒体播放器(或类似服务)。

    

*   START\_REDELIVER\_INTENT

    

    > 如果系统在 onStartCommand() 返回后终止服务,则会重建服务,并通过传递给服务的最后一个 Intent 调用 onStartCommand()。任何挂起 Intent 均依次传递。这适用于主动执行应该立即恢复的作业(例如下载文件)的服务。

    



有关这些返回值的更多详细信息,请查阅每个常量链接的参考文档。



启动服务

----



您可以通过将 Intent(指定要启动的服务)传递给 startService(),从 Activity 或其他应用组件启动服务。Android 系统调用服务的 onStartCommand() 方法,并向其传递 Intent。(切勿直接调用 onStartCommand()。)



例如,Activity 可以结合使用显式 Intent 与 startService(),启动上文中的示例服务 (HelloSevice):



Intent intent = new Intent(this, HelloService.class);

startService(intent);




startService() 方法将立即返回,且 Android 系统调用服务的 onStartCommand() 方法。如果服务尚未运行,则系统会先调用 onCreate(),然后再调用 onStartCommand()。



如果服务亦未提供绑定,则使用 startService() 传递的 Intent 是应用组件与服务之间唯一的通信模式。但是,如果您希望服务返回结果,则启动服务的客户端可以为广播创建一个 PendingIntent (使用 getBroadcast()),并通过启动服务的 Intent 传递给服务。然后,服务就可以使用广播传递结果。



多个服务启动请求会导致多次对服务的 onStartCommand() 进行相应的调用。但是,要停止服务,只需一个服务停止请求(使用 stopSelf() 或 stopService())即可。



停止服务

----



启动服务必须管理自己的生命周期。也就是说,除非系统必须回收内存资源,否则系统不会停止或销毁服务,而且服务在 onStartCommand() 返回后会继续运行。因此,服务必须通过调用 stopSelf() 自行停止运行,或者由另一个组件通过调用 stopService() 来停止它。



一旦请求使用 stopSelf() 或 stopService() 停止服务,系统就会尽快销毁服务。



但是,如果服务同时处理多个 onStartCommand() 请求,则您不应在处理完一个启动请求之后停止服务,因为您可能已经收到了新的启动请求(在第一个请求结束时停止服务会终止第二个请求)。为了避免这一问题,您可以使用 stopSelf(int) 确保服务停止请求始终基于最近的启动请求。也就说,在调用 stopSelf(int) 时,传递与停止请求的 ID 对应的启动请求的 ID(传递给 onStartCommand() 的 startId) 。然后,如果在您能够调用 stopSelf(int) 之前服务收到了新的启动请求, ID 就不匹配,服务也就不会停止。



*   **注意:**

    

    > 为了避免浪费系统资源和消耗电池电量,应用必须在工作完成之后停止其服务。 如有必要,其他组件可以通过调用 stopService() 来停止服务。即使为服务启用了绑定,一旦服务收到对 onStartCommand() 的调用,您始终仍须亲自停止服务。

    



如需了解有关服务生命周期的详细信息,请参阅下面有关管理服务生命周期的部分。



创建绑定服务

======



绑定服务允许应用组件通过调用 bindService() 与其绑定,以便创建长期连接(通常不允许组件通过调用 startService() 来启动它)。 如需与 Activity 和其他应用组件中的服务进行交互,或者需要通过进程间通信 (IPC) 向其他应用公开某些应用功能,则应创建绑定服务。 要创建绑定服务,必须实现 onBind() 回调方法以返回 IBinder,用于定义与服务通信的接口。然后,其他应用组件可以调用 bindService() 来检索该接口,并开始对服务调用方法。服务只用于与其绑定的应用组件,因此如果没有组件绑定到服务,则系统会销毁服务(您不必按通过 onStartCommand() 启动的服务那样来停止绑定服务)。 要创建绑定服务,首先必须定义指定客户端如何与服务通信的接口。 服务与客户端之间的这个接口必须是 IBinder 的实现,并且服务必须从 onBind() 回调方法返回它。一旦客户端收到 IBinder,即可开始通过该接口与服务进行交互。 多个客户端可以同时绑定到服务。客户端完成与服务的交互后,会调用 unbindService() 取消绑定。一旦没有客户端绑定到该服务,系统就会销毁它。 有多种方法实现绑定服务,其实现比启动服务更为复杂,因此绑定服务将在有关绑定服务的单独文档中专门讨论。



向用户发送通知

=======



一旦运行起来,服务即可使用 Toast 通知或状态栏通知来通知用户所发生的事件。 Toast 通知是指出现在当前窗口的表面、片刻随即消失不见的消息,而状态栏通知则在状态栏提供内含消息的图标,用户可以选择该图标来采取操作(例如启动 Activity)。 通常,当某些后台工作已经完成(例如文件下载完成)且用户现在可以对其进行操作时,状态栏通知是最佳方法。 当用户从展开视图中选定通知时,通知即可启动 Activity(例如查看已下载的文件)。 如需了解详细信息,请参阅 Toast 通知或状态栏通知开发者指南。



在前台运行服务

=======



前台服务被认为是用户主动意识到的一种服务,因此在内存不足时,系统也不会考虑将其终止。 前台服务必须为状态栏提供通知,状态栏位于“正在进行”标题下方,这意味着除非服务停止或从前台删除,否则不能清除通知。



例如,应该将从服务播放音乐的音乐播放器设置为在前台运行,这是因为用户明确意识到其操作。 状态栏中的通知可能表示正在播放的歌曲,并允许用户启动 Activity 来与音乐播放器进行交互。



要请求让服务运行于前台,请调用 startForeground()。此方法取两个参数:唯一标识通知的整型数和状态栏的 Notification。例如:



Notification notification = new Notification(R.drawable.icon,getText(R.string.ticker_text), System.currentTimeMillis());

Intent notificationIntent = new Intent(this, ExampleActivity.class);

PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);

notification.setLatestEventInfo(this, getText(R.string.notification_title), getText(R.string.notification_message), pendingIntent);

startForeground(ONGOING_NOTIFICATION_ID, notification);




*   **注意:**

    

    > 提供给 startForeground() 的整型 ID 不得为 0。

    



要从前台删除服务,请调用 stopForeground()。此方法取一个布尔值,指示是否也删除状态栏通知。 此方法绝对不会停止服务。 但是,如果您在服务正在前台运行时将其停止,则通知也会被删除。



如需了解有关通知的详细信息,请参阅创建状态栏通知。



管理服务生命周期

========



服务的生命周期比 Activity 的生命周期要简单得多。但是,密切关注如何创建和销毁服务反而更加重要,因为服务可以在用户没有意识到的情况下运行于后台。



服务生命周期(从创建到销毁)可以遵循两条不同的路径:



*   启动服务

    

    > 该服务在其他组件调用 startService() 时创建,然后无限期运行,且必须通过调用 stopSelf() 来自行停止运行。此外,其他组件也可以通过调用 stopService() 来停止服务。服务停止后,系统会将其销毁。

    

*   绑定服务

    

    > 该服务在另一个组件(客户端)调用 bindService() 时创建。然后,客户端通过 IBinder 接口与服务进行通信。客户端可以通过调用 unbindService() 关闭连接。多个客户端可以绑定到相同服务,而且当所有绑定全部取消后,系统即会销毁该服务。 (服务不必自行停止运行。)

    



这两条路径并非完全独立。也就是说,您可以绑定到已经使用 startService() 启动的服务。例如,可以通过使用 Intent(标识要播放的音乐)调用 startService() 来启动后台音乐服务。随后,可能在用户需要稍加控制播放器或获取有关当前播放歌曲的信息时,Activity 可以通过调用 bindService() 绑定到服务。在这种情况下,除非所有客户端均取消绑定,否则 stopService() 或 stopSelf() 不会真正停止服务。



实现生命周期回调

--------



与 Activity 类似,服务也拥有生命周期回调方法,您可以实现这些方法来监控服务状态的变化并适时执行工作。 以下框架服务展示了每种生命周期方法:



public class ExampleService extends Service {

int mStartMode;       // indicates how to behave if the service is killed

IBinder mBinder;      // interface for clients that bind

boolean mAllowRebind; // indicates whether onRebind should be used



@Override

public void onCreate() {

    // The service is being created

}

@Override

public int onStartCommand(Intent intent, int flags, int startId) {

    // The service is starting, due to a call to startService()

    return mStartMode;

}

@Override

public IBinder onBind(Intent intent) {

    // A client is binding to the service with bindService()

    return mBinder;

}

@Override

public boolean onUnbind(Intent intent) {

    // All clients have unbound with unbindService()

    return mAllowRebind;

}

@Override

public void onRebind(Intent intent) {

文末

好了,今天的分享就到这里,如果你对在面试中遇到的问题,或者刚毕业及工作几年迷茫不知道该如何准备面试并突破现状提升自己,对于自己的未来还不够了解不知道给如何规划,可以来看看同行们都是如何突破现状,怎么学习的,来吸收他们的面试以及工作经验完善自己的之后的面试计划及职业规划。

这里放上一部分我工作以来以及参与过的大大小小的面试收集总结出来的一套进阶学习的视频及面试专题资料包,主要还是希望大家在如今大环境不好的情况下面试能够顺利一点,希望可以帮助到大家

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

bind(Intent intent) {

文末

好了,今天的分享就到这里,如果你对在面试中遇到的问题,或者刚毕业及工作几年迷茫不知道该如何准备面试并突破现状提升自己,对于自己的未来还不够了解不知道给如何规划,可以来看看同行们都是如何突破现状,怎么学习的,来吸收他们的面试以及工作经验完善自己的之后的面试计划及职业规划。

这里放上一部分我工作以来以及参与过的大大小小的面试收集总结出来的一套进阶学习的视频及面试专题资料包,主要还是希望大家在如今大环境不好的情况下面试能够顺利一点,希望可以帮助到大家

[外链图片转存中…(img-Lc5fws1O-1714829878740)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 25
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值