• android学习笔记五。1、Service深入学习


    一、Service,服务是没有界面而在后台长期运行的程序,可以看做是后台的Activity。

      1、在Android中按返回键退出一个应用并不会(内存充足时)直接销毁一个进程,所以其中的子线程也可以在后台运行,这种线程和服务的区别是优先级不同,如果内存不足,被销毁的顺序不同。而且即使服务所在进程被销毁,如果内存充足会自动重新运行。

    作为Android四大组件之一,Service只要继承Service类,并在清单文件中配置即可使用。

      !!!2、有两种方式开启Service,①使用startService();②使用bindService()。两者的区别在于

        ①前者创建一个服务之后,就与这个服务没有关联了,因为没有方法可以在获取这个服务的实例了。但是进行联系还需要复杂的操作,并且还要区分进程内绑定本地服务和进程外绑定远程服务

        ②通过绑定开启的service,一旦调用这个服务的activity被销毁则这个服务也被销毁。即服务不能长期存在。

     1 import android.app.Activity;
     2 import android.content.ComponentName;
     3 import android.content.Intent;
     4 import android.content.ServiceConnection;
     5 import android.os.Bundle;
     6 import android.os.IBinder;
     7 import android.view.View;
     8 
     9 public class MainActivity extends Activity {
    10     private MyConn conn ;
    11     private IMiddlePerson mp;
    12 
    13     @Override
    14     protected void onCreate(Bundle savedInstanceState) {
    15         super.onCreate(savedInstanceState);
    16         setContentView(R.layout.activity_main);
    17     }
    18     
    19     @Override
    20     protected void onDestroy() {
    21         System.out.println("啊啊啊,我是activity,我挂了");
    22         super.onDestroy();
    23     }
    24 
    25     //绑定服务
    26     public void bind(View view){
    27         //3.activity采用绑定的方式去开启服务。
    28             //激活一个服务的意图
    29         Intent intent = new Intent(this,MyService.class);
    30             //保持联系的连接对象
    31         conn = new MyConn();
    32         bindService(intent, conn, BIND_AUTO_CREATE);
    33         
    34     }
    35     //解除绑定服务
    36     public void unbind(View view){
    37         unbindService(conn);
    38     }
    39     
    40     private class MyConn implements ServiceConnection{
    41         //4. 当服务被连接的时候调用,服务被成功绑定的时候调用。IBinder就是绑定的Service的onBind()的返回值,
    42         //!!!这就是联系所在
    43         @Override
    44         public void onServiceConnected(ComponentName name, IBinder service) {
    45             System.out.println("在activity里面成功得到了中间人");
    46             mp = (IMiddlePerson) service;
    47         }
    48         //当服务失去连接的时候调用(一般进程挂了,服务被异常杀死)
    49         @Override
    50         public void onServiceDisconnected(ComponentName name) {
    51             
    52         }
    53     }
    54 
    55     //调用服务里面的方法。
    56     public void call(View view){
    57         //5.通过中间人调用服务里面的方法。
    58         mp.callMethodInService(55);
    59     }
    60 }
    调用方
     1 import android.app.Service;
     2 import android.content.Intent;
     3 import android.os.Binder;
     4 import android.os.IBinder;
     5 import android.widget.Toast;
     6 
     7 public class MyService extends Service {
     8 
     9     //2.实现服务成功绑定的代码 ,返回一个中间人(一个符合IBinder要求的类,这里就是下面的内部类)。
    10     @Override
    11     public IBinder onBind(Intent arg0) {
    12         System.out.println("服务被成功绑定了。。。。");
    13         return new MiddlePerson();
    14     }
    15     
    16     @Override
    17     public boolean onUnbind(Intent intent) {
    18         System.out.println("onunbind");
    19         return super.onUnbind(intent);
    20     }
    21     
    22     @Override
    23     public void onCreate() {
    24         System.out.println("oncreate");
    25         super.onCreate();
    26     }
    27     
    28     @Override
    29     public int onStartCommand(Intent intent, int flags, int startId) {
    30         System.out.println("onstartcommand");
    31         return super.onStartCommand(intent, flags, startId);
    32     }
    33     
    34     @Override
    35     public void onDestroy() {
    36         System.out.println("ondestory");
    37         super.onDestroy();
    38     }
    39     
    40     /**
    41      * 这是服务里面的一个方法
    42      */
    43     public void methodInService(){
    44         Toast.makeText(this, "哈哈,服务给你办好了暂住证。", 0).show();
    45     }
    46     
    47     //1.第一步服务要暴露方法 必须要有一个中间人
    48     private class MiddlePerson extends Binder implements IMiddlePerson{
    49         /**
    50          * 代办暂住证
    51          * @param money 给钱 50块钱以上才给办。
    52          */
    53         public void callMethodInService(int money){
    54             if(money>=50){
    55                 methodInService();
    56             }else{
    57                 Toast.makeText(getApplicationContext(), "多准备点钱。", 0).show();
    58             }
    59         }
    60         /**
    61          * 陪领导打麻将
    62          */
    63         public void playMajiang(){
    64             System.out.println("陪领导打麻将。");
    65         }
    66     }
    67 }
    service方

    注意:上面的service方的中间人内部类是私有的,所以向外传递时不能直接将其抛出,所以在这里使用了一个接口作为中转,将需要暴露的方法通过接口暴露。

      为了解决上面两种开启服务的缺点,可以使用混合的启动方式,即先startService()然后bindService(),此时关闭同样需要混合关闭

    参考:http://blog.csdn.net/ryantang03/article/details/7770939

      3、Service和Activity的异同点:

        Service虽然叫服务,运行在后台,但是并不是新开了一个线程,而是依然运行在Activity的线程中,即UI线程中,所以如果在Service中执行耗时的操作,也会出现ANR异常。所以,Service和Activity一样,需要在子线程中进行耗时操作

        那Service和Activity的区别在哪里?为什么需要有Service呢?

        这是因为Activity退出之后,其创建的子线程都会销毁,而Service可以继续运行在后台,而且通过绑定,当Activity再次启动时,可以获取一个Binder实例来获取后台运行数据。

      4、其他类型的Service:

        ①前台Service:Service通常运行在后台,但是其优先级较低,在系统内存不足时可能会被回收。这时可以考虑使用前台Service来确保Service不会由于内存不足而回收。前台Service并没有什么特别的,只是在启动时需要创建一个notification在通知栏上进行显示。

     1         Notification.Builder localBuilder = new Notification.Builder(this);
     2         localBuilder.setContentIntent(PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), 0));
     3         localBuilder.setAutoCancel(false);
     4         localBuilder.setSmallIcon(R.drawable.pi);
     5         localBuilder.setTicker("Foreground Service Start");
     6         localBuilder.setContentTitle("Socket服务端");
     7         localBuilder.setContentText("正在运行...");
     8 
     9             //调用这一句开启前台服务
    10         startForeground(1, localBuilder.getNotification());
    前台Service

        ②IntentServvice:自动创建新消息线程HandlerThread的Service,将消息需要处理的数据通过Intent进行传递。具体的处理逻辑写在onHandleIntent()中,这是对外的接口。

     1 public class TestService3 extends IntentService {  
     2     private final String TAG = "hehe";  
     3     //必须实现父类的构造方法  
     4     public TestService3()  
     5     {  
     6         super("TestService3");  
     7     }  
     8   
     9     //必须重写的核心方法  
    10     @Override  
    11     protected void onHandleIntent(Intent intent) {  
    12         //Intent是从Activity发过来的,携带识别参数,根据参数不同执行不同的任务  
    13         String action = intent.getExtras().getString("param");  
    14         if(action.equals("s1"))Log.i(TAG,"启动service1");  
    15         else if(action.equals("s2"))Log.i(TAG,"启动service2");  
    16         else if(action.equals("s3"))Log.i(TAG,"启动service3");  
    17           
    18         //让服务休眠2秒  
    19         try{  
    20             Thread.sleep(2000);  
    21         }catch(InterruptedException e){e.printStackTrace();}          
    22     }  
    23   
    24     //重写其他方法,用于查看方法的调用顺序  
    25     @Override  
    26     public IBinder onBind(Intent intent) {  
    27         Log.i(TAG,"onBind");  
    28         return super.onBind(intent);  
    29     }  
    30   
    31     @Override  
    32     public void onCreate() {  
    33         Log.i(TAG,"onCreate");  
    34         super.onCreate();  
    35     }  
    36   
    37     @Override  
    38     public int onStartCommand(Intent intent, int flags, int startId) {  
    39         Log.i(TAG,"onStartCommand");  
    40         return super.onStartCommand(intent, flags, startId);  
    41     }  
    42   
    43   
    44     @Override  
    45     public void setIntentRedelivery(boolean enabled) {  
    46         super.setIntentRedelivery(enabled);  
    47         Log.i(TAG,"setIntentRedelivery");  
    48     }  
    49       
    50     @Override  
    51     public void onDestroy() {  
    52         Log.i(TAG,"onDestroy");  
    53         super.onDestroy();  
    54     }  
    55       
    56 } 
    IntentService示例

          这是android自带的一种对原始Service进行封装的类,直接使用的Service的优点是:

            a、创建独立线程,可以直接安全的进行操作,而无需创建线程。线程使用的是HandlerThread,针对HandlerThread,IntentServvice内部实现了一个ServiceHandler类继承自Handler,将消息源与生成的子线程进行绑定。对此具体介绍见http://www.cnblogs.com/songfeilong2325/p/5412424.html

            b、在该消息处理完成之后,自动调用Service的stopSelf()将该Service进行销毁。

     1     private final class ServiceHandler extends Handler {
     2         public ServiceHandler(Looper looper) {
     3             super(looper);
     4         }
     5 
     6         @Override
     7         public void handleMessage(Message msg) {
     8             //调用这个方法处理消息,!!!所以这个方法是必须要继承实现
     9             onHandleIntent((Intent)msg.obj);
    10             //结束Service
    11             stopSelf(msg.arg1);
    12         }
    13     }
    ServiceHandler

        ③远程Service:就是在清单文件中<service>的android:process=":remote"进行设置,此时开启服务时就是在一个新的进程中。而此时使用start方式开启Service和原来一样,但是bind开启服务进行交互的方式需要改变,需要使用android的跨进程通讯机制

          a、使用Messenger:这是和Handler不同的进程间消息机制。同样使用Handler进行消息处理,但是使用Messenger来发送消息和获取Binder对象。

     1 public class MessengerService extends Service {
     2     /** Command to the service to display a message */
     3     static final int MSG_SAY_HELLO = 1;
     4 
     5     /**
     6      * 通过handler处理消息
     7      */
     8     class IncomingHandler extends Handler {
     9         @Override
    10         public void handleMessage(Message msg) {
    11             switch (msg.what) {
    12                 case MSG_SAY_HELLO:
    13                     Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
    14                     break;
    15                 default:
    16                     super.handleMessage(msg);
    17             }
    18         }
    19     }
    20 
    21     /**
    22      *!!! 一个Messenger 对象,在Service端就是用于在onBind()中获取BInder对象
    23      */
    24     final Messenger mMessenger = new Messenger(new IncomingHandler());
    25 
    26     @Override
    27     public IBinder onBind(Intent intent) {
    28         Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
    29         return mMessenger.getBinder();
    30     }
    31 }
    Service端
     1 public class ActivityMessenger extends Activity {
     2     /** Messenger for communicating with the service. */
     3     Messenger mService = null;
     4 
     5     boolean mBound;
     6 
     7     /**
     8      * 创建一个连接对象,用于和Service组件进行连接
     9      */
    10     private ServiceConnection mConnection = new ServiceConnection() {
    11         public void onServiceConnected(ComponentName className, IBinder service) {
    12             // !!!在这里初始化Messenger对象,用于下面发送消息
    13             mService = new Messenger(service);
    14             mBound = true;
    15         }
    16 
    17         public void onServiceDisconnected(ComponentName className) {
    18             mService = null;
    19             mBound = false;
    20         }
    21     };
    22 
    23     public void sayHello(View v) {
    24         if (!mBound) return;
    25         // Create and send a message to the service, using a supported 'what' value
    26         Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
    27         try {
    28             mService.send(msg);
    29         } catch (RemoteException e) {
    30             e.printStackTrace();
    31         }
    32     }
    33 
    34     @Override
    35     protected void onCreate(Bundle savedInstanceState) {
    36         super.onCreate(savedInstanceState);
    37         setContentView(R.layout.main);
    38     }
    39 
    40     @Override
    41     protected void onStart() {
    42         super.onStart();
    43         // Bind to the service
    44         bindService(new Intent(this, MessengerService.class), mConnection,
    45             Context.BIND_AUTO_CREATE);
    46     }
    47 
    48     @Override
    49     protected void onStop() {
    50         super.onStop();
    51         // Unbind from the service
    52         if (mBound) {
    53             unbindService(mConnection);
    54             mBound = false;
    55         }
    56     }
    57 }
    调用端

          b、使用AIDL方式:(Android Interface Definition Language)是Android接口定义语言的意思。使用了一个接口文件(类似.java文件但是以.aidl结尾)。关于AIDL的具体使用参考http://blog.csdn.net/lmj623565791/article/details/38461079http://blog.csdn.net/guolin_blog/article/details/9797169

       注意:使用Messenger的内部也是用AIDL的方式实现的,只是进行了简化。参考:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0723/3216.html

      5、注意点:

        ①如果一个service没有绑定(bind),直接调用unBind()会报错,java.lang.IllegalArgumentException: Service not registered

        ②绑定方式需要的ServiceConnection类需要实现两个,一个在Activity和service建立联系时调用,另一个在解除关联时调用。但是后者onServiceDisconnected方法在正常情况下是不被调用的,它的调用时机是当Service服务被异外销毁时,例如内存的资源不足时这个方法才被自动调用

        ③为了确保安全,Service应该使用显示Intent来启动,而且只应该被自身应用启动,通过添加 android:exported 属性并将其设置为 "false",确保服务仅适用于您的应用

      6、深入探究---Binder机制

  • 相关阅读:
    python学习笔记 day14 各种推导式
    python学习笔记 day14 生成器表达式
    python学习笔记 day14 生成器进阶(二)
    python学习笔记 day14 生成器进阶
    python学习笔记 day13 迭代器
    python 学习笔记 day12 作业讲解--员工信息表
    PAT L3-020 至多删三个字符
    2019省赛训练组队赛3.26周二---FJUT 2016
    PAT L3-007 天梯地图
    POJ 2234 Matches Game
  • 原文地址:https://www.cnblogs.com/songfeilong2325/p/4854042.html
Copyright © 2020-2023  润新知