[Android四大组件] Service详解,你不知道的都在这里

Service 服务

  • 什么是服务

    • 官方的解释: Service 就是一个可以在后台执行长时间运行操作而不提供用户界面的应用组件.

    • 通俗易懂: 也就是厨房里默默工作的厨师们 .

    • 服务的生命周期:

      这里写图片描述


  • 服务基本用法

    首先新建一个DemoService继承Service, 并重写父类的onCreate()、onStartCommand()和 onDestroy()方法 , 代码如下 :

    public class DemoService extends Service{
    
        public static final String TAG = "DemoService";
    
         // 首次创建服务, 此方法执行一次性设置程序, 若服务已在运行, 则不会调用此方法
        @Override
        public void onCreate(){
            super.onCreate();
            Log.d(TAG,"service onCreate()");
        }
    
        // 当其他组件调用startService()启动服务时, 此方法会被调用, 需要调用stopSelf()或stopService()来停止服务
        @Override
        public int onStartCommand(Intent intent, int flags,int startId){ 
            Log.d(TAG,"service onStartCommand()");
            return super.onStartCommand(intent,flags,startId);
        }
    
        // 服务不再使用就会被销毁
        @Override
        public void onDestroy(){
            Log.d(TAG,"service onDestroy()");
            super.onDestroy();
        }
    
        // 当其他组件调用bindService()来绑定服务, 此方法被调用, 所以此方法必须返回IBinder提供一个接口,返回null则不允许被绑定.
        @Override
        public IBinder onBind(Intent intent){
            return null;
        }
    
    }

    然后我们到AndroidManifest.xml去注册这个服务, 代码如下 :

    <?xml version="1.0" encoding="utf-8"?>  
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
    package="com.example.servicedemo"  
    android:versionCode="1"  
    android:versionName="1.0" >  
    
    <application  
        android:allowBackup="true"  
        android:icon="@drawable/ic_launcher"  
        android:label="@string/app_name"  
        android:theme="@style/AppTheme" >  
    
        ……  
    
        <service android:name="com.example.servicedemo.DemoService" >  
        </service>  
    </application>  
    </manifest> 

    ==进阶==, 为了确保应用的安全性,请始终使用显式 Intent 启动或绑定 Service,且不要为服务声明 Intent 过滤器。

    此外,还可以通过添加 android:exported 属性并将其设置为 “false”,确保服务仅适用于您的应用。这可以有效阻止其他应用启动您的服务,即便在使用显式 Intent 时也如此。


    然后我们再到Activity中加入两个Button, 一个是启动服务, 一个是停止服务,代码如下:

        public class MainActivity extends Activity implements View.OnClickListener {  
    
            private Button mStartService;  
            private Button mStopService;  
    
            @Override  
            protected void onCreate(Bundle savedInstanceState) {  
                super.onCreate(savedInstanceState);  
                setContentView(R.layout.activity_main);  
                mStartService = (Button) findViewById(R.id.service_start);  
                mStopService = (Button) findViewById(R.id.service_stop);  
                mStartService.setOnClickListener(this);  
                mStopService.setOnClickListener(this);  
            }  
    
            @Override  
            public void onClick(View v) {  
                switch (v.getId()) {  
                case R.id.start_service:  
                    Intent startIntent = new Intent(this, DemoService.class);  
                    startService(startIntent);  
                    break;  
                case R.id.stop_service:  
                    Intent stopIntent = new Intent(this, DemoService.class);  
                    stopService(stopIntent);  
                    break;  
                default:  
                    break;  
                }  
            }  
        }

    当我们运行这个app的时候, 点击开启服务的时候, logcat输出了”service onCreate()” 和 “service onStartCommand()”.

    当我们再次点击开启服务的时候, 只输出” service onStartCommand()”, 原因就在上面DemoService的注释里, 你们去找吧, 哈哈;

    但是好想我们的Service无脑地在后台运行是不是有点浪费内存啊,接下来讲讲如何使用Service吧。

  • Service 的通信

    • 扩展Binder类

      虽然我们可以在onCreate() 和 onStartCommand()方法去执行一些操作 ,但是这样子好像服务就成了 留守儿童了, 没人跟它聊天,告诉它要做什么了.

      不知道大家发现了DemoService上有一个onBind方法我们好像没有用到, 这就是联系的关键方法.

      首先, 我们先创建一个类继承Binder, 然后添加一个操作的方法 , 如下:

          class DemoBinder extends Binder{
      
              public void compute(){
                  Log.d(TAG,"Binder compute() ");
              }
          }

      然后在DemoService 类中的onBind方法中返回DemoBinder的实例.

      接下来就是在Activity中添加两个按钮 , 一个是绑定服务 ,一个是取消绑定, 再使用bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
      以下是Activity中增加的一些重要代码:

      private DemoBinder mBinder;
      
      private ServiceConnection mConnection = new ServiceConnection() {
      
          @Override
          public void onServiceConnected(ComponentName className,
                  IBinder service) {
              mBinder=(DemoBinder)service;
              mBinder.compute();
          }
      
          @Override
          public void onServiceDisconnected(ComponentName arg0) {
      
          }
      };
      
      //onClick方法中
      //绑定服务
      Intent intent =new Intent(this,DemoService.class);
      bindService(intent , mConnection ,Context.BIND_AUTO_CREATE);
      
      //解除绑定
      unbindService(mConnection);

      可以看出我们创建了一个ServiceConnection的匿名类, 并且重写了两个方法, 这两个方法会子啊service被绑定和解绑的时候调用, 在onServiceConnected()这个方法中,我们获得DemoBinder的实例,就可以调用它里面的方法, 所以就可以建立起了关联.

      在bindService()这个方法中,有三个参数, 第一个是Intent对象,第二个是前面创建的ServiceConnection对象的实例, 第三个是标志位, 这里的参数意思就是Service绑定后, 自动创建Service, 即Service中的onCreate方法被调用,但onStartCommand方法不会被调用.


    • 使用Messenger

      官方解释: 如需让服务与远程进程通信,则可使用 Messenger 为您的服务提供接口。利用此方法,您无需使用 AIDL 便可执行进程间通信 (IPC)。

      用法跟扩展Binder类有小小区别: 并不是直接调用服务的方法, 而是通过Handler来做出判断,官方源代码如下:

              public class MessengerService extends Service {
          /** Command to the service to display a message */
          static final int MSG_SAY_HELLO = 1;
      
          /**
           * Handler of incoming messages from clients.
           */
          class IncomingHandler extends Handler {
              @Override
              public void handleMessage(Message msg) {
                  switch (msg.what) {
                      case MSG_SAY_HELLO:
                          Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
                          break;
                      default:
                          super.handleMessage(msg);
                  }
              }
          }
      
          /**
           * Target we publish for clients to send messages to IncomingHandler.
           */
          final Messenger mMessenger = new Messenger(new IncomingHandler());
      
          /**
           * When binding to the service, we return an interface to our messenger
           * for sending messages to the service.
           */
          @Override
          public IBinder onBind(Intent intent) {
              Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
              return mMessenger.getBinder();
          }
      }

      然后在Activity中代码如下:

              public class ActivityMessenger extends Activity {
          Messenger mService = null;
      
          private ServiceConnection mConnection = new ServiceConnection() {
              public void onServiceConnected(ComponentName className, IBinder service) {
                  mService = new Messenger(service);
              }
      
              public void onServiceDisconnected(ComponentName className) {
                  mService = null;
              }
          };
      
          public void sayHello(View v) {
              Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
              try {
                  mService.send(msg);
              } catch (RemoteException e) {
                  e.printStackTrace();
              }
          }
      
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.main);
          }
      
          @Override
          protected void onStart() {
              super.onStart();
              bindService(new Intent(this, MessengerService.class), mConnection,
                  Context.BIND_AUTO_CREATE);
          }
      
          @Override
          protected void onStop() {
              super.onStop();
                  unbindService(mConnection);
          }
      }

      上面的sayHello()方法就是在xml布局文件中为button设置的onclick方法了..


    • AIDL

      还有一种方法就是使用AIDL这种语言来实现, 由于篇幅问题, 我会再开一篇来写这个AIDL

      注:大多数应用“都不会”使用 AIDL 来创建绑定服务,因为它可能要求具备多线程处理能力,并可能导致实现的复杂性增加。因此,AIDL 并不适合大多数应用,本文也不会阐述如何将其用于您的服务。如果您确定自己需要直接使用 AIDL,请参阅 AIDL 文档。


    • 注意

      • 只有 Activity、服务和内容提供程序可以绑定到服务,您无法从广播接收器绑定到服务。

  • Service和Thread

    官方解释: 简单来说, 服务就是用户不与其交互它也可以在后台运行的组件。

    通俗易懂:吃饭的时候 ,一般都是通过服务员点菜,服务员再告诉厨师。

    但是,Service是运行在主线程的。所以我们一般写Service都是写成下面这个样子:

        @Override  
    public int onStartCommand(Intent intent, int flags, int startId) {  
            new Thread(new Runnable() {  
                @Override  
                public void run() {  
                    // 开始执行后台任务  
                }  
            }).start();  
            return super.onStartCommand(intent, flags, startId);  
        }  
    
    class MyBinder extends Binder {  
    
        public void startDownload() {  
            new Thread(new Runnable() {  
                @Override  
                public void run() {  
                    // 执行具体的下载任务  
                }  
            }).start();  
        }    
    }  

  • 官方 IntentService

    由于大多数启动服务都不必同时处理多个请求(这种多线程情况也很危险), 因此使用IntentService类实现服务也许比较好.

    以下IntentService的实例:

        public class HelloIntentService extends IntentService {
          public HelloIntentService() {
              super("HelloIntentService");
          }
    
          @Override
          protected void onHandleIntent(Intent intent) {
              try {
                  Thread.sleep(5000);
              } catch (InterruptedException e) {
                  // Restore interrupt status.
                  Thread.currentThread().interrupt();
              }
          }
        }

    以下是IntentService的操作流程:

    • 创建默认的工作线程,用于在应用的主线程外执行传递给 onStartCommand() 的所有 Intent。

    • 创建工作队列,用于将 Intent 逐一传递给 onHandleIntent() 实现,这样您就永远不必担心多线程问题。

    • 在处理完所有启动请求后停止服务,因此您永远不必调用 stopSelf()。

    • 提供 onBind() 的默认实现(返回 null)。

    • 提供 onStartCommand() 的默认实现,可将 Intent 依次发送到工作队列和 onHandleIntent() 实现。

    注意, onStartCommand()方法依旧返回默认实现, 即return super.onStartCommand(…);


  • 创建前台服务

    前台服务就是我们所说的通知或者桌面小部件这类的, 这里就简单演示下通知吧.
    
    NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentTitle("my notification")
                .setContentText("hello world");
    Intent intent = new Intent(this, MainActivity.class);
    TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
    stackBuilder.addParentStack(MainActivity.class);
    stackBuilder.addNextIntent(intent);
    PendingIntent pendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
    mBuilder.setContentIntent(pendingIntent);
    NotificationManager notificationManager= (NotificationManager) etSystemService(Context.NOTIFICATION_SERVICE);
    notificationManager.notify(1,mBuilder.build());

    NotificaitonManager.notify(), 这个方法传入两个参数, 唯一标识通知的整数型, 和状态栏的Notification.

    若要移除服务, 可调用notificaiton.cancel()方法来移除通知..

    除非发生以下情况之一,否则通知仍然可见:

    • 用户单独或通过使用“全部清除”清除了该通知(如果通知可以清除)。

    • 用户点击通知,且您在创建通知时调用了 setAutoCancel()。

    • 您针对特定的通知 ID 调用了 cancel()。此方法还会删除当前通知。

    • 您调用了 cancelAll() 方法,该方法将删除之前发出的所有通知

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android Jetpack是Google提供的一套用于加速Android应用开发的工具包,其中包括了许多架构组件,其中之一就是ViewModel。 ViewModel是一种设计模式,用于保存和管理与UI相关的数据。在传统的Android开发中,当屏幕旋转或者因为其他原因导致Activity或Fragment重建时,之前保存的临时数据就会丢失。而ViewModel的出现解决了这个问题。 ViewModel的主要作用是将数据与UI组件分离。它的工作方式是创建一个ViewModel类,并在其中保存需要与UI组件交互的数据。这样,当屏幕旋转或重建时,ViewModel实例不会销毁,数据也会得到保留。然后,在Activity或Fragment中,通过获取ViewModel实例,可以轻松地访问这些数据。 使用ViewModel的好处有很多。首先,它可以避免内存泄漏,因为ViewModel的生命周期与Activity或Fragment无关。其次,它可以节省资源,因为当Activity或Fragment销毁时,ViewModel实例可以被系统缓存起来,下次再创建时可以直接返回该实例。另外,由于ViewModel保存了与UI相关的数据,可以减少因为屏幕旋转导致的数据重复加载的问题。 在使用ViewModel时,你可以选择使用Android Jetpack中的其他架构组件来进一步提高开发效率,比如通过LiveData实现数据的观察和通知,或者通过DataBinding来实现UI与数据的自动绑定。 总之,ViewModel是Android Jetpack中非常重要的一个架构组件,它的出现实现了数据与UI的解耦,提高了开发效率,并且解决了数据丢失的问题。希望通过这篇文档的详解,你对ViewModel有了更深入的理解。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值