Android开发学习笔记之service

1、Service的种类

  

按运行地点分类:

类别 区别  优点 缺点   应用
本地服务(Local) 该服务依附在主进程上,  服务依附在主进程上而不是独立的进程,这样在一定程度上节约了资源,另外Local服务因为是在同一进程因此不需要IPC,也不需要AIDL。相应bindService会方便很多。  主进程被Kill后,服务便会终止。  非常常见的应用如:HTC的音乐播放服务,天天动听音乐播放服务。
远程服务(Remote 该服务是独立的进程,  服务为独立的进程,对应进程名格式为所在包名加上你指定的android:process字符串。由于是独立的进程,因此在Activity所在进程被Kill的时候,该服务依然在运行,不受其他进程影响,有利于为多个进程提供服务具有较高的灵活性。  该服务是独立的进程,会占用一定资源,并且使用AIDL进行IPC稍微麻烦一点。  一些提供系统服务的Service,这种Service是常驻的。

其实remote服务还是很少见的,并且一般都是系统服务。

  

按运行类型分类:

类别 区别 应用
前台服务 会在通知一栏显示 ONGOING 的 Notification, 当服务被终止的时候,通知一栏的 Notification 也会消失,这样对于用户有一定的通知作用。常见的如音乐播放服务。
后台服务 默认的服务即为后台服务,即不会在通知一栏显示 ONGOING 的 Notification。 当服务被终止的时候,用户是看不到效果的。某些不需要运行或终止提示的服务,如天气更新,日期同步,邮件同步等。

有同学可能会问,后台服务我们可以自己创建 ONGOING 的 Notification 这样就成为前台服务吗?答案是否定的,前台服务是在做了上述工作之后需要调用 startForeground ( android 2.0 及其以后版本 )或 setForeground (android 2.0 以前的版本)使服务成为 前台服务。这样做的好处在于,当服务被外部强制终止掉的时候,ONGOING 的 Notification 任然会移除掉。

 

按使用方式分类:

类别 区别
startService 启动的服务 主要用于启动一个服务执行后台任务,不进行通信。停止服务使用stopService
bindService 启动的服务 该方法启动的服务要进行通信。停止服务使用unbindService
startService 同时也 bindService 启动的服务 停止服务应同时使用stepService与unbindService

以上面三种方式启动的服务其生命周期也有区别,将在随后给出。


2、Service 与 Thread 的区别

  

很多时候,你可能会问,为什么要用 Service,而不用 Thread 呢,因为用 Thread 是很方便的,比起 Service 也方便多了,下面我详细的来解释一下。

 

1). Thread:Thread 是程序执行的最小单元,它是分配CPU的基本单位。可以用 Thread 来执行一些异步的操作。

2). Service:Service 是android的一种机制,当它运行的时候如果是Local Service,那么对应的 Service 是运行在主进程的 main 线程上的。如:onCreate,onStart 这些函数在被系统调用的时候都是在主进程的 main 线程上运行的。如果是Remote Service,那么对应的 Service 则是运行在独立进程的 main 线程上。因此请不要把 Service 理解成线程,它跟线程半毛钱的关系都没有!

  

既然这样,那么我们为什么要用 Service 呢?其实这跟 android 的系统机制有关,我们先拿 Thread 来说。Thread 的运行是独立于 Activity 的,也就是说当一个 Activity 被 finish 之后,如果你没有主动停止 Thread 或者 Thread 里的 run 方法没有执行完毕的话,Thread 也会一直执行。因此这里会出现一个问题:当 Activity 被 finish 之后,你不再持有该 Thread 的引用。另一方面,你没有办法在不同的 Activity 中对同一 Thread 进行控制。

  

举个例子:如果你的 Thread 需要不停地隔一段时间就要连接服务器做某种同步的话,该 Thread 需要在 Activity 没有start的时候也在运行。这个时候当你 start 一个 Activity 就没有办法在该 Activity 里面控制之前创建的 Thread。因此你便需要创建并启动一个 Service ,在 Service 里面创建、运行并控制该 Thread,这样便解决了该问题(因为任何 Activity 都可以控制同一 Service,而系统也只会创建一个对应 Service 的实例)。

  

因此你可以把 Service 想象成一种消息服务,而你可以在任何有 Context 的地方调用 Context.startService、Context.stopService、Context.bindService,Context.unbindService,来控制它,你也可以在 Service 里注册 BroadcastReceiver,在其他地方通过发送 broadcast 来控制它,当然这些都是 Thread 做不到的。


3.创建前台服务

      前台服务的优点上面已经说明,但设置服务为前台服务,我们需要注意在 sdk 2.0 及其以后版本使用的方法是 startForeground 与 stopForeground,之前版本使用的是 setForeground ,因此如果你应用程序的最低运行环境要求是 2.0,那么这里可以直接运用新方法,如果运行环境是2.0以下,那么为了保证向后兼容性,这里必须使用反射技术来调用新方法。

package com.newcj.test;
 
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
 
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
 
public class ForegroundService extends Service {
 
    private static final Class[] mStartForegroundSignature = new Class[] {
        int.class, Notification.class};
    private static final Class[] mStopForegroundSignature = new Class[] {
        boolean.class};
    private NotificationManager mNM;
    private Method mStartForeground;
    private Method mStopForeground;
    private Object[] mStartForegroundArgs = new Object[2];
    private Object[] mStopForegroundArgs = new Object[1];
     
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
     
    @Override
    public void onCreate() {
        super.onCreate();
        mNM = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
        try {
            mStartForeground = ForegroundService.class.getMethod("startForeground", mStartForegroundSignature);
            mStopForeground = ForegroundService.class.getMethod("stopForeground", mStopForegroundSignature);
        } catch (NoSuchMethodException e) {
            mStartForeground = mStopForeground = null;
        }
         // 我们并不需要为 notification.flags 设置 FLAG_ONGOING_EVENT,因为
         // 前台服务的 notification.flags 总是默认包含了那个标志位
        Notification notification = new Notification(R.drawable.icon, "Foreground Service Started.",
                System.currentTimeMillis());
        PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
                new Intent(this, Main.class), 0);
        notification.setLatestEventInfo(this, "Foreground Service",
                "Foreground Service Started.", contentIntent);
        // 注意使用  startForeground ,id 为 0 将不会显示 notification
        startForegroundCompat(1, notification);
    }
     
    @Override
    public void onDestroy() {
        super.onDestroy();
        stopForegroundCompat(1);
    }
     
    // 以兼容性方式开始前台服务
    private void startForegroundCompat(int id, Notification n){
        if(mStartForeground != null){
            mStartForegroundArgs[0] = id;
            mStartForegroundArgs[1] = n;
             
            try {
                mStartForeground.invoke(this, mStartForegroundArgs);
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
             
            return;
        }
        setForeground(true);
        mNM.notify(id, n);
    }
     
    // 以兼容性方式停止前台服务
    private void stopForegroundCompat(int id){
        if(mStopForeground != null){
            mStopForegroundArgs[0] = Boolean.TRUE;
             
            try {
                mStopForeground.invoke(this, mStopForegroundArgs);
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
            return;
        }
         
        //  在 setForeground 之前调用 cancel,因为我们有可能在取消前台服务之后
        //  的那一瞬间被kill掉。这个时候 notification 便永远不会从通知一栏移除
        mNM.cancel(id);
        setForeground(false);
    }
 
}

特别注意:

  1、使用 startForeground ,如果 id 为 0 ,那么 notification 将不会显示。

4. 通讯

Service与Activity的通讯手段很多,IBinder、BroaderCast,本文将介绍另一种手段:Messenger


官方文档解释:它引用了一个Handler对象,以便others(Messenger)能够向它发送消息(使用mMessenger.
send(Message msg)方法)。该类允许跨进程间基于Message的通信(即两个进程间可以通过Message进行通信),在服务端使用Handler创建一个Messenger,客户端持有这个Messenger就可以与服务端通信了。

以前我们使用Handler+Message的方式进行通信,都是在同一个进程中,从线程持有一个主线程的Handler对象,并向主线程发送消息。而Android既然可以使用bindler机制进行跨进行通信,所以我们当然可以将Handler与bindler结合起来进行跨进程发送消息。
查看API就可以发现,Messenger就是这种方式的实现。
一般使用方法如下:
1、远程通过mMessenger = new Messenger(mHandler) 创建一个信使对象
2、客户端使用bindlerService请求连接远程
3、远程onBind方法返回一个bindler return mMessenger.getBinder(); 
4.客户端使用远程返回的bindler得到一个信使(即得到远程信使)

?
1
2
3
4
public void onServiceConnected(ComponentName name, IBinder service) {   
               rMessenger = new Messenger(service);      
              ......
  }
这里虽然是new了一个Messenger,但我们查看它的实现
public Messenger(IBinder target) { mTarget = IMessenger.Stub.asInterface(target); } 
发现它的mTarget是通过Aidl得到的,实际上就是远程创建的那个。
5、客户端可以使用这个远程信使对象向远程发送消息:rMessenger.send(msg);
这样远程服务端的Handler对象就能收到消息了,然后可以在其handlerMessage(Message msg)方法中进行处理。(该Handler对象就是第一步服务端创建Messenger时使用的参数mHandler).
mHandler.obtainMessage()
经过这5个步骤貌似只有客户端向服务端发送消息,这样的消息传递是单向的,那么如何实现双向传递呢?

首先需要在第5步稍加修改,在send(msg)前通过msm.replyTo = mMessenger将自己的信使设置到消息中,这样服务端接收到消息时同时也得到了客户端的信使对象了,然后服务端可以通过/得到客户端的信使对象,并向它发送消息 cMessenger = msg.replyTo; cMessenger.send(message); 
即完成了从服务端向客户端发送消息的功能,这样客服端可以在自己的Handler对象的handlerMessage方法中接收服务端发送来的message进行处理。
双向通信宣告完成。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
public class Binding extends Activity implements View.OnClickListener {
     private Messenger mMessenger;
     private Messenger rMessenger;
     private TextView mCallbackText;
     private boolean isBound;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super .onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
         initView();
     }
 
     private Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
             case MessengerService.MSG_SET_VALUE:
                 mCallbackText.setText( "Received from service: " + msg.arg1);
                 break ;
             default :
                 break ;
             }
         }
     };
 
     private ServiceConnection connection = new ServiceConnection() {
         public void onServiceConnected(ComponentName name, IBinder service) {
             rMessenger = new Messenger(service);
             mMessenger = new Messenger(mHandler);
             sendInitMessage();
         }
 
         public void onServiceDisconnected(ComponentName name) {
             rMessenger = null ;
         }
     };
 
     /**
      * 使用服务端的信使向它发送一个消息。
      */
     private void sendInitMessage() {
         Message message = Message.obtain( null , MessengerService.MSG_SET_VALUE);
         message.replyTo = mMessenger;
         try {
             rMessenger.send(message);
         } catch (RemoteException e) {
             e.printStackTrace();
         }
     }
 
     public void onClick(View v) {
         Intent intent = new Intent( "com.app.messenger_service" );
         switch (v.getId()) {
         case R.id.bind:
             if (!isBound) {
                 isBound = bindService(intent, connection, BIND_AUTO_CREATE);
             }
             break ;
         case R.id.unbind:
             if (isBound) {
                 unbindService(connection);
                 isBound = false ;
             }
             break ;
         default :
             break ;
         }
     }
 
     private void initView() {
         findViewById(R.id.bind).setOnClickListener( this );
         findViewById(R.id.unbind).setOnClickListener( this );
         mCallbackText = (TextView) findViewById(R.id.callback);
         mCallbackText.setText( "Not attached." );
     }
}

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
public class MessengerService extends Service {
     private Messenger cMessenger;
     private boolean connecting = false ;
     static final int MSG_REGISTER_CLIENT = 1 ;
     static final int MSG_UNREGISTER_CLIENT = 2 ;
     static final int MSG_SET_VALUE = 3 ;
 
     private Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
             case MSG_SET_VALUE:
                 try {
                     Message message = Message.obtain( null ,
                             MessengerService.MSG_SET_VALUE);
                     message.arg1 = 0 ;
                     cMessenger = msg.replyTo;
                     cMessenger.send(message);
                 } catch (RemoteException e) {
                     e.printStackTrace();
                 }
                 break ;
             default :
                 super .handleMessage(msg);
             }
         }
     };
     private Messenger mMessenger = new Messenger(mHandler);
 
     @Override
     public void onDestroy() {
 
         super .onDestroy();
     }
 
     @Override
     public boolean onUnbind(Intent intent) {
         connecting = false ;
         return super .onUnbind(intent);
     }
 
     @Override
     public IBinder onBind(Intent intent) {
         connecting = true ;
         new Thread( new Runnable() {
             @Override
             public void run() {
                 int i = 0 ;
                 try {
                     while (connecting) {
                         Message message = Message.obtain( null , MSG_SET_VALUE);
                         Thread.sleep( 1000 );
                         message.arg1 = i++;
                         cMessenger.send(message);
                     }
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 } catch (RemoteException e) {
                     e.printStackTrace();
                 }
 
             }
         }).start();
         return mMessenger.getBinder();
     }
 
     @Override
     public void onRebind(Intent intent) {
         Toast.makeText( this , "onRebind" , Toast.LENGTH_SHORT).show();
     }
 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值