深入四大组件之Service

来源:互联网 发布:淘宝店铺pc端首页多高 编辑:程序博客网 时间:2024/06/05 23:15

  之前学习android的时候,说实话自己并没有特别认真学习Service。以至于等自己用到的时候一头雾水,“书到用时方恨少”这话一点也不假呀。为了多读书,所以就下定决心,好好的把Service深入的了解一下。

我划分了几个学习步骤,由简单认识到深入了解。我们给出官方API     Service官方API

 一:Service的概念特性

          (1)概念  :Service是后台运行的没有界面的优先级高于Activity的一个组件。

           我们该如何理解优先级高于Activity呢?或者说优先级高于Activity有什么用呢?
答:我们需要知道当我们的android手机内存不足的时候,系统就会杀掉一下Activity,所以我们的服务一般就不会被杀掉。即使被杀掉也会很容易就重新启动。除非我们自己去关闭我们的服务。

        (2)Service与Thread的区别:

          Service并不等同于thread ,因为Service相当于一个context。类似于Activity。其用法当然也与Activity类似,我们也需要在我们的配置文件中去配置。我在一篇博客里面看到别人是这样理解的。接下来我就拿过来用了。好东西就要整合整合嘛!区别链接

Service和Thread之间没有任何关系!

之所以有不少人会把它们联系起来,主要就是因为Service的后台概念。Thread我们大家都知道,是用于开启一个子线程,在这里去执行一些耗时操作就不会阻塞主线程的运行。而Service我们最初理解的时候,总会觉得它是用来处理一些后台任务的,一些比较耗时的操作也可以放在这里运行,这就会让人产生混淆了。但是,如果我告诉你Service其实是运行在主线程里的,你还会觉得它和Thread有什么关系吗?

其实,后台和子线程是两个完全不同的概念:

Android的后台就是指,它的运行是完全不依赖UI的。即使Activity被销毁,或者程序被关闭,只要进程还在,Service就可以继续运行。比如说一些应用程序,始终需要与服务器之间始终保持着心跳连接,就可以使用Service来实现。你可能又会问,Service既然是运行在主线程里,在这里一直执行着心跳连接,难道就不会阻塞主线程的运行吗?当然会,但是我们可以在Service中再创建一个子线程,然后在这里去处理耗时逻辑就没问题了。

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

       (3)注意项

        1、Service是运行在主线程当中的,所以我们不能用它来执行一些耗时操作。
         2、可以在Service中开启一个线程,用它来执行一些耗时操作。

        (4)服务的类型

服务有两种类型:本地服务(用在应用程序内部)和系统服务(用在应用程序与系统服务之间)。如何开启一个服务
             启动本地服务    startService     stopService    stopSelf    stopSelfResult
         bindService     unbindService
            启动系统服务     定义IBinder接口
      好了,我相信大家基本上对Service有了简单的了解了。接下来才是我们的干货。

二:Service的生命周期以及两种启动方式

和往常一样我们先给出Service的生命周期图

      从图中可以看到给出了两种启动方式的生命周期图。这两种启动方式有什么区别呢?
首先start的启动方式,启动后我们的启动源就和我们启动的服务没有任何关系了。我们也无法调用服务中我们定义的一些方法,我们举个例子,假如说我们要播放一首歌曲,我们在MainActivity中使用按钮调用SatrtService来播放一首歌曲。接下来我们的服务就开始播放。但是接下来我们的MainActivity就再也和StartService没有关系了。怎么停止,怎么暂停,MainActivity无法控制。也就是其生命周期不受我们控制。当然了,如果我们要向服务传递一些参数值,我们是没办法拿到处理的结果的。
Bind形式的话。我们可以建立联系。拿到Service对象后。对它进行操作,在Activity中通过拿到的Service对象调用Service中定义的一些方法。例如暂停音乐播放等等。一般在我们开发当中,也会经常使用这种方法。它的生命周期是有我们来控制的。我们不仅可以向我们的服务传递一些参数值,还可以拿到一些参数值。
      好了,言归正传。我们通过程序来看一下如何创建Service以及了解Service的生命周期。首先看start方式。

1、使用start方式启动服务

(1)创建我们的项目,创建我们的Satrt方式启动类MyStartService,(别忘了配置。和Activity方法配置一样)
package com.example.servicedemo;import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.util.Log;public class MyStartService extends Service {@Overridepublic void onCreate() {// TODO Auto-generated method stubLog.i("info", "Service--onCreate()");super.onCreate();}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {// TODO Auto-generated method stubLog.i("info", "Service--onStartCommand()");return super.onStartCommand(intent, flags, startId);}@Overridepublic void onDestroy() {// TODO Auto-generated method stubLog.i("info", "Service--onDestroy()");super.onDestroy();}@Overridepublic IBinder onBind(Intent intent) {// TODO Auto-generated method stubLog.i("info", "Service--onBind()");return null;}}
(2)在我们的MainActivity中修改布局文件
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical" >    <TextView        android:id="@+id/textView1"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="Start:" />    <Button        android:id="@+id/start"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:onClick="doClick"        android:text="StartService" />    <Button        android:id="@+id/stop"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:onClick="doClick"        android:text="StopService" />    <TextView        android:id="@+id/textView2"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="Bind:" />    <Button        android:id="@+id/bind"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:onClick="doClick"        android:text="BindService" />    <Button        android:id="@+id/play"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:onClick="doClick"        android:text="播放" />    <Button        android:id="@+id/pause"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:onClick="doClick"        android:text="暂停" />    <Button        android:id="@+id/next"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:onClick="doClick"        android:text="下一首" />    <Button        android:id="@+id/pervious"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:onClick="doClick"        android:text="上一首" />    <Button        android:id="@+id/unbind"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:onClick="doClick"        android:text="UnBindService" /></LinearLayout>
效果图为:
我们模拟了音乐播放的流程在Bind方法当中。由于接下来会用到,所以这里就先把布局写好了。由于我们首先演示的是start方式。所以我们只是使用最上面的两个按钮,大家能看明白就好。
          (3)执行start方式
定义好了之后,接下来该怎么做呢?我们在MainActivity类中,执行点击startService按钮就可以启动了
 intent1 = new Intent(MainActivity.this, MyStartService.class);startService(intent1);
上面的为启动的方式,当我们停止的时候直接一句话就可以了。点击stopService按钮
stopService(intent1);
运行结果是什么呢?

我们点击一次startSevice按钮执行  onCreate->onStartCommand方法。当我们多次点击startService按钮时发现,接下来只会重复调用onStartCommand方法。接下来点击stopService按钮,销毁我们的服务。好了,这种方式调用,非常的简单。大家一目了然

2、使用bind方式启动服务

(1)创建MyBindService类
package com.example.servicedemo;import android.app.Service;import android.content.Intent;import android.content.ServiceConnection;import android.os.Binder;import android.os.IBinder;import android.util.Log;public class MyBindService extends Service{@Overridepublic void onCreate() {// TODO Auto-generated method stubLog.i("info", "BindService--onCreate()");super.onCreate();}public class MyBinder extends Binder{public MyBindService getService(){return MyBindService.this;}}@Overridepublic IBinder onBind(Intent intent) {// TODO Auto-generated method stubLog.i("info", "BindService--onBind()");return new MyBinder();}@Overridepublic void unbindService(ServiceConnection conn) {// TODO Auto-generated method stubLog.i("info", "BindService--unbindService()");super.unbindService(conn);}@Overridepublic void onDestroy() {// TODO Auto-generated method stubLog.i("info", "BindService--onDestroy()");super.onDestroy();}public void Play(){Log.i("info", "播放");}public void Pause(){Log.i("info", "暂停");}public void Pervious(){Log.i("info", "上一首");}public void next(){Log.i("info", "下一首");}}
   (2)MyBindService类中的方法说明。
        —————Play()、Pause()、Pervious()、next()类是模拟音乐播放的方法。
        —————里面的内部类MyBind是获取当前的服务类数据。将来我们可以在MainActivity中通过这个内部类拿到我们的Service对象,从而实现在MainActivity类当中实现对Service的数据的回调。也就相当于得到我们的服务的数据。
(3)在MainActivity启动我们服务,点击BindService按钮
intent2 = new Intent(MainActivity.this, MyBindService.class);bindService(intent2, conn, Service.BIND_AUTO_CREATE);
        我们看与start方式启动有一些稍微的区别,就是bindService(intent2,conn,Service.BIND_AUTO_CREATE);
里面的三个参数分别代表什么含义呢?
           intent2—就是我们的意图
Service.BIND_AUTO_CREATE——使用此方式自动创建服务
conn——这个有点麻烦,接下来再重点分析
解除绑定unBindService(conn),这里我们先定义为unBindService(null).

(4)生命周期结果
我们看到执行onCreate->onBind
需要注意的是我们只能解除绑定一次,否则的话会报错。

(5)conn这里只是我们自己定义的一个名字。其实是一个ServiceConnection对象。我们可以通过这个对象的相关方法来得到Service对象,进而实现对Service的操作。
ServiceConnection conn = new ServiceConnection() {@Override//当服务跟启动源断开的时候 会自动回调public void onServiceDisconnected(ComponentName name) {// TODO Auto-generated method stub}@Override//当服务跟启动源连接的时候 会自动回调public void onServiceConnected(ComponentName name, IBinder binder) {// TODO Auto-generated method stubservice = ((MyBinder)binder).getService();}};
看注释应该就应该能明白。

好了,现在我们给出所有的MainActivity的源码
package com.example.servicedemo;import com.example.servicedemo.MyBindService.MyBinder;import android.app.Activity;import android.app.Service;import android.content.ComponentName;import android.content.Intent;import android.content.ServiceConnection;import android.os.Bundle;import android.os.IBinder;import android.view.Menu;import android.view.View;public class MainActivity extends Activity {Intent intent1;Intent intent2;MyBindService service;ServiceConnection conn = new ServiceConnection() {@Override//当服务跟启动源断开的时候 会自动回调public void onServiceDisconnected(ComponentName name) {// TODO Auto-generated method stub}@Override//当服务跟启动源连接的时候 会自动回调public void onServiceConnected(ComponentName name, IBinder binder) {// TODO Auto-generated method stubservice = ((MyBinder)binder).getService();}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);}public void doClick(View v){switch (v.getId()) {case R.id.start: intent1 = new Intent(MainActivity.this, MyStartService.class);startService(intent1);break;case R.id.stop:stopService(intent1);break;case R.id.play:service.Play();break;case R.id.pause:service.Pause();break;case R.id.pervious:service.Pervious();break;case R.id.next:service.next();break;case R.id.bind:intent2 = new Intent(MainActivity.this, MyBindService.class);startService(intent2);bindService(intent2, conn, Service.BIND_AUTO_CREATE);break;case R.id.unbind:unbindService(conn);break;}}@Overrideprotected void onDestroy() {// TODO Auto-generated method stubstopService(intent2);unbindService(conn);super.onDestroy();}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// Inflate the menu; this adds items to the action bar if it is present.getMenuInflater().inflate(R.menu.main, menu);return true;}}
真开心呀,写了一个小时终于写完一部分了。到此为止我们的Service基本用法算是完了。接下来就是更高级的了。我们写这篇博客的目的,也主要是为了学习下面的高级进阶。上面的主要是铺垫而已。

三、系统服务

  下面先列举一些常见的系统服务。再次给出官方的API文档    上面写的非常详细SystemService官方API
常用系统服务:后台Service在系统启动时被SystemServer开启:1.MountService:监听是否有SD卡安装及移除2.ClipboardService:提供剪切板功能3.PackageManagerService:提供软件包的安装移除及查看4.电量,网络连接状态等等使用的时候通过getSystemService()的方法来实现:这是一个Activity的一个方法,通过传入name->object>服务对象WINDOW_SERVICE -- WindowManager -- 管理打开的窗口程序LAYOUT_INFLATER_SERVICE -- LayoutInflater -- 取得XML里定义的ViewACTIVITY_SERVICE -- ActivityManager -- 管理应用程序的系统状态POWER_SERVICE -- PowerManger -- 电源服务ALARM_SERVICE -- AlarmManager -- 闹钟服务NOTIFICATION_SERVICE -- NotificationManager -- 状态栏服务KEYGUARD_SERVICE -- KeyguardManager -- 键盘锁服务LOCATION_SERVICE -- LocationManager -- 位置服务,如GPSSEARCH_SERVICE -- SearchManager -- 搜索服务VEBRATOR_SERVICE -- Vebrator -- 手机震动服务CONNECTIVITY_SERVICE -- Connectivity -- 网络连接服务WIFI_SERVICE -- WifiManager -- Wi-Fi服务TELEPHONY_SERVICE -- TelephonyManager -- 电话服务
      下面我们用代码演示一下如何使用这些服务。
     1、使用LAYOUT_INFLATER_SERVICE使用LayoutInflater把一个layout转换成view
     LayoutInflater inflater = (LayoutInflater) MainActivity.this.getSystemService(LAYOUT_INFLATER_SERVICE);     View view = inflater.inflate(R.layout.activity_main, null);     setContentView(view);
这个方法在平时我们的开发当中是非常有必要的。因为我们可以通过LayoutInflater动态的加载我们布局。
    2、判断网络的连接状态
判断网络是否连接public boolean isNetworkConnected(Context context) { // 判断网络的方法     if (context != null) {         ConnectivityManager mConnectivityManager =               (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);        NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo();// 获取网络连接状态信息        if (mNetworkInfo != null) {// 如果不为空           return mNetworkInfo.isAvailable();// 返回网络状态属于活动状态         }     }     return false; }
     3、发送短信服务:(别忘了权限)
<uses-permission android:name="android.permission.SEND_SMS"></uses-permission><uses-permission android:name="android.permission.READ_SMS"></uses-permission><uses-permission android:name="android.permission.RECEIVE_SMS"></uses-permission>

String phone = phoneEt.getText().toString();              String context = contextEt.getText().toString();              SmsManager manager = SmsManager.getDefault();              ArrayList<String> list = manager.divideMessage(context);  //因为一条短信有字数限制,因此要将长短信拆分              for(String text:list){                  manager.sendTextMessage(phone, null, text, null, null);              }              Toast.makeText(getApplicationContext(), "发送完毕", Toast.LENGTH_SHORT).show();  


此为调用系统的。
/** * 调起系统发短信功能 * @param phoneNumber * @param message */public void doSendSMSTo(String phoneNumber,String message){if(PhoneNumberUtils.isGlobalPhoneNumber(phoneNumber)){Intent intent = new Intent(Intent.ACTION_SENDTO, Uri.parse("smsto:"+phoneNumber));          intent.putExtra("sms_body", message);          startActivity(intent);}}
还有其他好多种短信发送的。我们给出一个链接。自己可以看看写的非常详细几种常见的发送短信方式
当然还有很多很多其他的系统服务,不过上面都已经给出官方的API文档的连接了,我一般都是用到什么服务就从上面去查,代码也很好看懂。还有别忘了,在我们的配置文件中配置我们的权限。

四、在Service里的耗时操作

          前面我们已经把,如何启动服务以及服务的生命周期做了一下介绍。还有系统的服务。我们在前面还提到过我们想要在Service里面执行耗时操作,就必须要在Service里面定义一个子线程。如果是你会怎么做呢?当然如果是我我肯定会直接定义。代码如下:
package com.example.servicetest;import android.app.Service;import android.content.Intent;import android.os.IBinder;public class MyService extends Service {            public static final String TAG = "MyService";         //服务执行的操作    @Override      public int onStartCommand(Intent intent, int flags, int startId) {          new Thread(new Runnable() {            public void run() {                //处理具体的逻辑                stopSelf();  //服务执行完毕后自动停止            }        }).start();                return super.onStartCommand(intent, flags, startId);      }    @Override    public IBinder onBind(Intent intent) {        // TODO Auto-generated method stub        return null;    }       }
        我在onStartCommand里面定义了一个子线程。当然在子线程处理完服务之后stopSelf自动停止服务。这是我们自然而然想到的办法,但是这种办法也有不妥,因为有时候我们老是忘记启动线程或者是停止服务。怎么办呢?谷歌给我们提供了一种方法,那就是IntentService类  。它可以为我们异步的创建服务,还可以自动的停止服务。是不是很高大上。好,接下来好好地认识一下他,如何去用。

IntentService的工作机制

     当我们每有一个耗时操作的时候,这些耗时操作就会形成一个队列,在IntentService的onHandleIntent中去执行。执行完队列的第一个耗时操作时,再去执行队列的第二个,以此类推。我们使用代码看看如何使用
package com.example.servicetest;import android.app.IntentService;import android.content.Intent;import android.util.Log;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");    }}
       上面这段我们可以看到,我们需要继承IntentService,以前我们都是直接继承Service。然后在onHandlerIntent重写方法当中执行耗时操作。在我们这个例子里面我们只是每过一秒就在后台打印当前的线程ID。我们还是一样在我们的MainActivity中启动Service。我们使用Start方式启动就行。代码和上面的一样,就不给出了。
运行结果是什么呢?

      现在应该都有了大致的了解了吧。

 


1 0
原创粉丝点击