之前学习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 {
	@Override
	public void onCreate() {
		// TODO Auto-generated method stub
		Log.i("info", "Service--onCreate()");
		super.onCreate();
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		// TODO Auto-generated method stub
		Log.i("info", "Service--onStartCommand()");
		return super.onStartCommand(intent, flags, startId);
	}

	@Override
	public void onDestroy() {
		// TODO Auto-generated method stub
		Log.i("info", "Service--onDestroy()");
		super.onDestroy();
	}

	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		Log.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{
	@Override
	public void onCreate() {
		// TODO Auto-generated method stub
		Log.i("info", "BindService--onCreate()");
		super.onCreate();
	}
	public class MyBinder extends Binder{
		public MyBindService getService(){
			return MyBindService.this;
		}
	}
	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		Log.i("info", "BindService--onBind()");
		return new MyBinder();
	}
	@Override
	public void unbindService(ServiceConnection conn) {
		// TODO Auto-generated method stub
		Log.i("info", "BindService--unbindService()");
		super.unbindService(conn);
	}
	@Override
	public void onDestroy() {
		// TODO Auto-generated method stub
		Log.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 stub
			service = ((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 stub
			service = ((MyBinder)binder).getService();
		}
	};
	@Override
	protected 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;
		}
	}
	@Override
	protected void onDestroy() {
		// TODO Auto-generated method stub
		stopService(intent2);
		unbindService(conn);
		super.onDestroy();
	}
	@Override
	public 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里定义的View
	ACTIVITY_SERVICE -- ActivityManager -- 管理应用程序的系统状态
	POWER_SERVICE -- PowerManger -- 电源服务
	ALARM_SERVICE -- AlarmManager -- 闹钟服务
	NOTIFICATION_SERVICE -- NotificationManager -- 状态栏服务
	KEYGUARD_SERVICE -- KeyguardManager -- 键盘锁服务
	LOCATION_SERVICE -- LocationManager -- 位置服务,如GPS
	SEARCH_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方式启动就行。代码和上面的一样,就不给出了。
运行结果是什么呢?

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

 


Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐