美文网首页
Service全面讲解(应用方面)

Service全面讲解(应用方面)

作者: Jowney | 来源:发表于2018-08-23 17:01 被阅读8次

    一、Service介绍

    Service是Android中实现程序后台运行的解决方案,非常适合用于去执行哪些不需要和用户交互而且还要求长期运行的任务。可以和其他组件进行交互。

    默认情况下,Service与是在主线程中运行的,如果是在服务中完成耗时操作的话,容易造成主线程阻塞。

    Service可以在很多场合使用,比如播放多媒体的时候用户启动了其他Activity,此时要在后台继续播放;比如检测SD卡上文件的变化;比如在后台记录你的地理信息位置的改变等等,总之服务是藏在后台的。


    Server的生命周期

    二、Service使用

    Service可以有两种启动方式:一种是startService(),另一种是bindService()。我们这先用第一种方式启动Service,但onBind()方法是Service中唯一的一个抽象方法,所以必须要在子类里实现,所以先写出来放那儿不用管。

    //MyService.java
    public class MyService extends Service {  
           
         public static final String TAG = "MyService";  
       
         //创建服务时调用
         @Override  
         public void onCreate() {  
             super.onCreate();  
            Log.d(TAG, "onCreate");  
            Log.d("TAG","Service线程的ID是:"+Thread.currentThread().getName());
         }  
       
        
    /*
    onStartCommand方法执行时,返回的是一个int型。这个整型可以有三个返回值:
    1、START_NOT_STICKY:“非粘性的”。使用这个返回值时,如果在执行完
    onStartCommand方法后,服务被异常kill掉,系统不会自动重启该服务。
    
    2、START_STICKY:如果Service进程被kill掉,保留Service的状态为开始状态,
    但不保留递送的intent对象。随后系统会尝试重新创建Service,由于服务状态为开始状态,
    所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。
    如果在此期间没有任何启动命令被传递到Service,那么参数Intent将为null。
    
    3、START_REDELIVER_INTENT:重传Intent。使用这个返回值时,系统会自动重启该服务,并将Intent的值传入。
    */
         @Override  
         public int onStartCommand(Intent intent, int flags, int startId) {  
          Log.d(TAG, "onStartCommand");  
             return super.onStartCommand(intent, flags, startId);  
         }  
           
         //销毁服务时调用
         @Override  
         public void onDestroy() {  
             super.onDestroy();  
            Log.d(TAG, "onDestroy");  
        
         }  
       
         @Override  
         public IBinder onBind(Intent intent) {  
             return null;  
         }  
     }
    

    这里只重写了在onCreate()、onStartCommand()和onDestroy()三个重要的方法。
    并在每个方法里面调用了打印日志

    //AndroidMainfest.xml
    <service android:name=".MyService"/>        
    
    //MainActivity.java
    public class MainActivity extends AppCompatActivity {
    
      ···
         @Override  
           public void onClick(View v) {  
            switch (v.getId()) {  
            case R.id.button_start_service:  
            //开启服务
                Intent startIntent = new Intent(this, MyService.class);  
                startService(startIntent);  
                break;  
            case R.id.button_stop_service:  
          //暂停服务
                Intent stopIntent = new Intent(this, MyService.class);  
                stopService(stopIntent);  
                break;  
            default:  
                break;  
            }  
        }  
      ···
    }
    

    Service启动:

    1、运行上面的程序点击button_start_service按钮,启动服务,后台打印日志如下:

         onCreate
         Service线程的ID是:main
         onStartCommand
    

    2、再连续点三次button_start_service按钮,后台增加的日志如下

         onStartCommand
         onStartCommand
         onStartCommand
    

    3.总结:服务对象同时只会有一个,onCreate()方法只会在Service第一次被创建的时候调用,而onStartCommand()方法在每次启动服务的时候都会调用。

    Service结束:

    1、点击button_stop_service按钮,MyService服务就停止掉了,打印日志如下

         onCreate
         Service线程的ID是:main
         onStartCommand
         onStartCommand
         onStartCommand
         onStartCommand
         onDestroy
    

    2、停止一个Service服务有两种方法:
    (1)在外部使用stopService()
    (2)在服务内部(onStartCommand方法内部)使用stopSelf()方法。

    三、IntentService使用

    为什么要改IntentService?我们在第一段中就已经说了,Service中的代码默认运行在主线程中,如果直接在Service里执行一些耗时操作,容易造成ANR(Application Not Responding)异常,所以还需要自己手动创建线程。

    为了能减少麻烦,IntentService就出生了,他会自己创建子线程

    public class MyIntentService extends IntentService{
        public MyIntentService() {
            super("MyIntentService");//调用父类有参构造函数。这里我们手动给服务起个名字为:MyIntentService
            // TODO Auto-generated constructor stub
        }
    
        //该方法在会在一个单独的线程中执行,来完成工作任务。任务结束后,该Service自动停止
        @Override
        protected void onHandleIntent(Intent intent) {
            // TODO Auto-generated method stub
            for(int i = 0;i<3;i++) {
                //打印当前线程的id
                Log.d("MyIntentService","IntentService线程的id是:"+Thread.currentThread().getId());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }        
        }
    
        @Override
        public void onDestroy() {
            // TODO Auto-generated method stub
            super.onDestroy();
            Log.d("MyIntentService","onDestroy");
        }
    }
    
    <service android:name=".MyIntentService"> </service>
    
     case R.id.button_start_intentservice:
                 Intent intentService = new Intent(this,MyIntentService.class);
                 startService(intentService);
               default:
    

    IntentService启动后打印日志:

     IntentService线程的id是:122
     IntentService线程的id是:122
     IntentService线程的id是:122
     onDestroy
    

    1、这里首先要提供一个无参的构造方法,并且必须在其内部调用父类的有参构造方法,手动将服务的名字改为“MyIntentService”。

    2、然后在子类中实现onHandleIntent()这个抽象方法,可以在这个方法里去处理一些具体的逻辑,我们就用三次for循环,打印当前线程的id,每次延时1秒。

    3、因为这个服务在运行结束后会自动停止,所以我们在onDestroy()方法中打印日志验证一下。

    四、Service与子线程

    Android的后台就是指,它的运行是完全不依赖UI的。即使Activity被销毁,或者程序被关闭,只要进程还在,Service就可以继续运行

    例子:比如说一些应用程序,始终需要与服务器之间始终保持着心跳连接,就可以使用Service来实现。你可能又会问,Service既然是运行在主线程里,在这里一直执行着心跳连接,难道就不会阻塞主线程的运行吗?当然会,但是我们可以在Service中再创建一个子线程,然后在这里去处理耗时逻辑就没问题了。

    既然在Service里也要创建一个子线程,那为什么不直接在Activity里创建呢?

    这是因为Activity很难对Thread进行控制,当Activity被销毁之后,就没有任何其它的办法可以再重新获取到之前创建的子线程的实例;而且在一个Activity中创建的子线程,另一个Activity无法对其进行操作。但是Service就不同了,所有的Activity都可以与Service进行关联,然后可以很方便地操作其中的方法,即使Activity被销毁了,之后只要重新与Service建立关联,就又能够获取到原有的Service中Binder的实例。因此,使用Service来处理后台任务,Activity就可以放心地finish,完全不需要担心无法对后台任务进行控制的情况。

    五、使用bindService方法完成Service和Activity之间的通信(同进程)

    //MyServer.java
    public class MyService extends Service {  
          
        public static final String TAG = "MyService";  
      
        private MyBinder mBinder;  
      
        @Override  
        public void onCreate() {  
            super.onCreate();  
            mBinder = new MyBinder();  
            Log.d(TAG, "onCreate");  
        }  
      
        @Override  
        public int onStartCommand(Intent intent, int flags, int startId) {  
            Log.d(TAG, "onStartCommand");  
            return super.onStartCommand(intent, flags, startId);  
        }  
      
        @Override  
        public void onDestroy() {  
            super.onDestroy();  
            Log.d(TAG, "onDestroy");  
        }  
      
        @Override  
        public IBinder onBind(Intent intent) {  
            return mBinder;  //在这里返回新建的MyBinder类
        }  
      
        //MyBinder类,继承Binder:让里面的方法执行下载任务,并获取下载进度
        class MyBinder extends Binder {  
      
            public void startDownload() {  
                Log.d("TAG", "startDownload() executed");  
                // 执行具体的下载任务  
            }
            public int getProgress(){
                Log.d("TAG", "getProgress() executed");  
                return 0;
            }
      
        }  
      
    }
    
    <service android:name=".MyService" > </service> 
    
    public class MainActivity extends Activity implements OnClickListener {
        private Button button_start_service;
        private Button button_stop_service;
        private Button button_bind_service;
        private Button button_unbind_service;
    
        private MyService.MyBinder myBinder;
        
        //匿名内部类:服务连接对象
        private ServiceConnection connection = new ServiceConnection() {
            
            //当服务异常终止时会调用。注意,解除绑定服务时不会调用
            @Override
            public void onServiceDisconnected(ComponentName name) {
            }
            
            //和服务绑定成功后,服务会回调该方法
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                myBinder = (MyService.MyBinder) service;
                //在Activity中调用Service里面的方法
                myBinder.startDownload();
                myBinder.getProgress();
            }
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            button_start_service = (Button) findViewById(R.id.button_start_service);
            button_stop_service = (Button) findViewById(R.id.button_stop_service);
            button_bind_service = (Button) findViewById(R.id.button_bind_service);
            button_unbind_service = (Button) findViewById(R.id.button_unbind_service);
            
            button_start_service.setOnClickListener(this);
            button_stop_service.setOnClickListener(this);
            button_bind_service.setOnClickListener(this);
            button_unbind_service.setOnClickListener(this);
        }
    
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
     case R.id.button_start_service:
                Intent startIntent = new Intent(this, MyService.class);
                startService(startIntent);
                break;
            case R.id.button_stop_service:
                Intent stopIntent = new Intent(this, MyService.class);
                stopService(stopIntent);
                break;
            case R.id.button_bind_service:
                Intent bindIntent = new Intent(this, MyService.class);
                bindService(bindIntent, connection, BIND_AUTO_CREATE);
                break;
            case R.id.button_unbind_service:
                unbindService(connection);
                break;
    
            default:
                break;
            }
        }
    
    }
    

    任何一个Service在整个应用程序范围内都是通用的,即MyService不仅可以和MainActivity建立关联,还可以和任何一个Activity建立关联,而且在建立关联时它们都可以获取到相同的MyBinder实例。

    1、只点击了button_bind_service按钮,oncreate()方法得到了执行,但onStartCommand()方法不会执行。打印日志:

    onCreate
    startDownload() executed
    getProgress() executed
    

    2、 只点击了button_bind_service按钮后再点击了button_unbind_service按钮

    onCreate
    startDownload() executed
    getProgress() executed
    onDestroy
    

    3|、那么重新启动应用如果我们既点击了button_start_service按钮,又点击了button_bind_service按钮会怎么样呢?
    那么不管你是单独点击button_stop_service按钮还是button_unbind_service按钮,Service都不会被销毁,必要将button_stop_service按钮和button_unbind_service按钮都点击一下(没有先后顺序),Service才会被销毁。

    4、
    假设现在Service和Activity已经相关联了,点击button_unbind_service按钮能够解除绑定,如果继续点击button_unbind_service按钮,程序会异常退出,这说明代码不够完善,我们需要在代码中加一个判断是否绑定的标记mBound。在改MainActivity中增加一部分代码,最终改MainActivity的完整代码如下:

    
    public class MainActivity extends Activity implements OnClickListener {
        private Button button_start_service;
        private Button button_stop_service;
        private Button button_bind_service;
        private Button button_unbind_service;
        private MyService.MyBinder myBinder;
        
        boolean mBound = false; //一开始,并没有和Service绑定.这个参数是用来显示绑定状态
        
        //匿名内部类:服务连接对象
        private ServiceConnection connection = new ServiceConnection() {
            
            //当服务异常终止时会调用。注意,解除绑定服务时不会调用
            @Override
            public void onServiceDisconnected(ComponentName name) {
                mBound = false; //服务异常终止时,状态为未绑定
            }
            
            //和服务绑定成功后,服务会回调该方法
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                myBinder = (MyService.MyBinder) service;
                //在Activity中调用Service里面的方法
                myBinder.startDownload();
                myBinder.getProgress();
                mBound = true; //true说明是绑定状态
            }
        };
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            button_start_service = (Button) findViewById(R.id.button1_start_service);
            button_stop_service = (Button) findViewById(R.id.button2_stop_service);
            button_bind_service = (Button) findViewById(R.id.button3_bind_service);
            button_unbind_service = (Button) findViewById(R.id.button4_unbind_service);
            button_start_service.setOnClickListener(this);
            button_stop_service.setOnClickListener(this);
            button_bind_service.setOnClickListener(this);
            button_unbind_service.setOnClickListener(this);
        }
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
            case R.id.button_start_service:
                Intent startIntent = new Intent(this, MyService.class);
                startService(startIntent);
                break;
            case R.id.button_stop_service:
                Intent stopIntent = new Intent(this, MyService.class);
                stopService(stopIntent);
                break;
            case R.id.button_bind_service:
                Intent bindIntent = new Intent(this, MyService.class);
                bindService(bindIntent, connection, BIND_AUTO_CREATE);
                break;
            case R.id.button_unbind_service:
                //如果和Service是绑定的状态,就解除绑定。
                if(mBound){
                    unbindService(connection);
                    mBound=false;
                }
                break;
    
            default:
                break;
            }
        }
    }
    

    五、startServer与bindServer的区别

    区别一:生命周期:
    通过started方式的服务会一直运行在后台,需要由组件本身或外部组件来停止服务才会以结束运行
    bind方式的服务,生命周期就要依赖绑定的组件

    区别二:参数传递:
    started服务可以给启动的服务对象传递参数,但无法获取服务中方法的返回值
    bind服务可以给启动的服务对象传递参数,也可以通过绑定的业务对象获取返回结果

    实际开发中的技巧:
    第一次先使用started方式来启动一个服务
    之后可以使用bind的方式绑定服务,从而可以直接调用业务方法获取返回值

    注意:
    一旦在项目的任何位置调用了Context的startService()方法,相应的服务就会启动起来,并回调onstartCommand()方法。如果这个服务之前还没有创建过,onCreate()方法会先于onstartCommand()方法执行。服务启动过后,会一直保持运行状态,直到stopService()或stopself()方法被调用。注意虽然每次调用一次startService()方法,onstartCommand()方法就会以执行一次,但实际上每个服务都只会存在一个实例。所以不管你调用了多少次startService()方法,只需调用一次stopService()或stopself()方法,服务就会停止。

    另外,还可以调用Context的bindService()来获取一个服务的持久连接,这时就会回调服务中的onBind()方法。类似地,如果这个服务之前还没有创建过,onCreate()方法会先于onBind()方法执行。之后调用方可以获取到onBind()方法里返回的IBinder对象的实例,这样,就能自由地和服务进行通信了。只要调用方和服务之间的连接没有断开,服务就会一直保持运行状态。

    六、使用bindService完成IPC进程间通信:(在同一个APP内模拟)

    相关文章

      网友评论

          本文标题:Service全面讲解(应用方面)

          本文链接:https://www.haomeiwen.com/subject/mpfmqftx.html