乐趣区

关于android:Android笔记Android-Service-服务

一、Service 简介
Service 是 android 零碎中的四大组件之一(Activity、Service、BroadcastReceiver、ContentProvider),它跟 Activity 的级别差不多,但不能自己运行只能后盾运行,并且能够和其余组件进行交互。service 能够在很多场合的利用中应用,比方播放多媒体的时候用户启动了其余 Activity 这个时候程序要在后盾持续播放,比方检测 SD 卡上文件的变动,再或者在后盾记录你地理信息地位的扭转等等,总之服务总是藏在后盾的。

Service 的启动有两种形式:context.startService() 和 context.bindService()

二、Service 启动流程
context.startService() 启动流程:

context.startService() -> onCreate() -> onStart() -> Service running -> context.stopService() -> onDestroy() -> Service stop

如果 Service 还没有运行,则 android 先调用 onCreate(),而后调用 onStart();

如果 Service 曾经运行,则只调用 onStart(),所以一个 Service 的 onStart 办法可能会反复调用屡次。

如果 stopService 的时候会间接 onDestroy,如果是调用者本人间接退出而没有调用 stopService 的话,Service 会始终在后盾运行,该 Service 的调用者再启动起来后能够通过 stopService 敞开 Service。

所以调用 startService 的生命周期为:onCreate –> onStart (可屡次调用) –> onDestroy

context.bindService()启动流程:

context.bindService() -> onCreate() -> onBind() -> Service running -> onUnbind() -> onDestroy() -> Service stop

onBind()将返回给客户端一个 IBind 接口实例,IBind 容许客户端回调服务的办法,比方失去 Service 的实例、运行状态或其余操作。这个时候把调用者(Context,例如 Activity)会和 Service 绑定在一起,Context 退出了,Srevice 就会调用 onUnbind->onDestroy 相应退出。

所以调用 bindService 的生命周期为:onCreate –> onBind(只一次,不可屡次绑定) –> onUnbind –> onDestory。

在 Service 每一次的开启敞开过程中,只有 onStart 可被屡次调用(通过屡次 startService 调用),其余 onCreate,onBind,onUnbind,onDestory 在一个生命周期中只能被调用一次。

三、Service 生命周期
Service 的生命周期并不像 Activity 那么简单,它只继承了 onCreate()、onStart()、onDestroy() 三个办法

当咱们第一次启动 Service 时,先后调用了 onCreate()、onStart()这两个办法;当进行 Service 时,则执行 onDestroy()办法。

这里须要留神的是,如果 Service 曾经启动了,当咱们再次启动 Service 时,不会在执行 onCreate()办法,而是间接执行 onStart()办法。

它能够通过 Service.stopSelf()办法或者 Service.stopSelfResult()办法来进行本人,只有调用一次 stopService()办法便能够进行服务,无论调用了多少次的启动服务办法。

四、Service 示例
上面我做了一个简略的音乐播放的利用,别离应用 startService 和 bindService 来启动本地的服务。

Activity

public class PlayMusicService extends Activity implements OnClickListener {  
  
    private Button playBtn;  
    private Button stopBtn;  
    private Button pauseBtn;  
    private Button exitBtn;  
    private Button closeBtn;  
  
    private Intent intent;  
      
    @Override  
    public void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);  
        setContentView(R.layout.music_service);  
  
        playBtn = (Button) findViewById(R.id.play);  
        stopBtn = (Button) findViewById(R.id.stop);  
        pauseBtn = (Button) findViewById(R.id.pause);  
        exitBtn = (Button) findViewById(R.id.exit);  
        closeBtn = (Button) findViewById(R.id.close);  
          
        playBtn.setOnClickListener(this);  
        stopBtn.setOnClickListener(this);  
        pauseBtn.setOnClickListener(this);  
        exitBtn.setOnClickListener(this);  
        closeBtn.setOnClickListener(this);  
  
    }  
  
    @Override  
    public void onClick(View v) {  
        int op = -1;  
        intent = new Intent("com.homer.service.musicService");  
  
        switch (v.getId()) {  
        case R.id.play:                             // play music  
            op = 1;  
            break;  
        case R.id.stop:                             // stop music  
            op = 2;  
            break;  
        case R.id.pause:                            // pause music  
            op = 3;  
            break;  
        case R.id.close:                            // close activity  
            this.finish();  
            break;  
        case R.id.exit:                             // stopService  
            op = 4;  
            stopService(intent);  
            this.finish();  
            break;  
        }  
  
        Bundle bundle = new Bundle();  
        bundle.putInt("op", op);  
        intent.putExtras(bundle);  
          
        startService(intent);                           // startService  
    }  
      
    @Override  
    public void onDestroy(){super.onDestroy();  
  
        if(intent != null){stopService(intent);  
        }  
    }  
}  

Service

public class MusicService extends Service {  
    private static final String TAG = "MyService";  
      
    private MediaPlayer mediaPlayer;  
  
    @Override  
    public IBinder onBind(Intent arg0) {return null;}  
  
    @Override  
    public void onCreate() {Log.v(TAG, "onCreate");  
        Toast.makeText(this, "show media player", Toast.LENGTH_SHORT).show();  
  
        if (mediaPlayer == null) {mediaPlayer = MediaPlayer.create(this, R.raw.tmp);  
            mediaPlayer.setLooping(false);  
        }  
    }  
  
    @Override  
    public void onDestroy() {Log.v(TAG, "onDestroy");  
        Toast.makeText(this, "stop media player", Toast.LENGTH_SHORT);  
        if (mediaPlayer != null) {mediaPlayer.stop();  
            mediaPlayer.release();}  
    }  
  
    @Override  
    public void onStart(Intent intent, int startId) {Log.v(TAG, "onStart");  
        if (intent != null) {Bundle bundle = intent.getExtras();  
            if (bundle != null) {int op = bundle.getInt("op");  
                switch (op) {  
                case 1:  
                    play();  
                    break;  
                case 2:  
                    stop();  
                    break;  
                case 3:  
                    pause();  
                    break;  
                }  
            }  
        }  
    }  
  
    public void play() {if (!mediaPlayer.isPlaying()) {mediaPlayer.start();  
        }  
    }  
  
    public void pause() {if (mediaPlayer != null && mediaPlayer.isPlaying()) {mediaPlayer.pause();  
        }  
    }  
  
    public void stop() {if (mediaPlayer != null) {mediaPlayer.stop();  
            try {mediaPlayer.prepare();  // 在调用 stop 后如果须要再次通过 start 进行播放, 须要之前调用 prepare 函数  
            } catch (IOException ex) {ex.printStackTrace();  
            }  
        }  
    }  
}  

AndroidManifest.xml

注册 activity

<activity  
    android:name=".service.PlayMusicService"  
    android:label="@string/app_name" />  

注册 service

<service  
    android:name=".service.MusicService"  
    android:enabled="true" >  
    <intent-filter>  
        <action android:name="com.homer.service.musicService" />  
    </intent-filter>  
</service> 

五、代码解析
1、Activity 中,PlayMusicService 中通过重写 OnClickListener 接口 onClick() 办法实现对播放音乐的管制,把音乐各种操作用数字通过 Intent 传递给 service

而后通过结构一个 Intent,intent = new Intent(“com.homer.service.musicService”);

其中,com.homer.service.musicService 是 AndroidManifest.xml 对 service 的定义,即下面“注册 service”

2、Activity 中,音乐播放的管制,利用 Bundle 绑定数字 op 后,通过 startService(intent); 服务后发送进来
Bundle bundle = new Bundle();
bundle.putInt(“op”, op);
intent.putExtras(bundle);

startService(intent);

3、Service 中,会解决 Activity 启动的 startService(intent); 服务,顺次调用 service 的启动过程:onCreate –> onStart(可屡次调用) –> onDestroy

onCreate(),创立 mediaPlayer

onStart(),通过获取 Bundle bundle = intent.getExtras();,提取 int op = bundle.getInt(“op”);,而后执行响应的音乐播放操作

onDestroy(),进行并开释 mediaPlayer 音乐资源,如果当执行 context.stopService()时调用此办法

4、Activity 中,onClick()函数中 close 与 exit 是执行含意是不同的:

close:只是执行了 this.finish(); 敞开了本 Activity 窗体,service 并没有被关掉,音乐仍然会持续在后盾播放

exit:先调用了 stopService(intent); 敞开了 service 服务,在 Service 中会调用 3 中的 onDestroy()进行并开释音乐资源,后才执行 this.finish(); 敞开了本 Activity 窗体

六、拓展常识(过程和申明周期)
Android 操作系统尝试尽可能长时间的放弃利用的过程,但当可用内存很低时最终要移走一部分过程。怎么确定那些程序能够运行,那些要被销毁,Android 让每一个过程在一个重要级的根底上运行,重要级低的过程最有可能被淘汰,一共有 5 级,上面这个列表就是依照重要性排列的:

1 一个前台过程显示的是用户此时须要解决和显示的。下列的条件有任何一个成立,这个过程都被认为是在前台运行的。
a 与用户正产生交互的。
b 它管制一个与用户交互的必须的根本的服务。
c 有一个正在调用生命周期的回调函数的 service(如 onCreate()、onStar()、onDestroy())
d 它有一个正在运行 onReceive()办法的播送接管对象。
只有多数的前台过程能够在任何给定的工夫内运行,销毁他们是零碎万不得已的、最初的抉择——当内存不够零碎持续运行上来时。通常,在这一点上,设施曾经达到了内存分页状态,所以杀掉一些前台过程来保障可能响应用户的需要。

2 一个可用过程没有任何前台组件,但它依然能够影响到用户的界面。上面两种状况产生时,能够称该过程为可用过程。
它是一个非前台的 activity,但对用户依然可用(onPause()办法曾经被调用)这是可能产生的,例如:前台的 activity 是一个容许上一个 activity 可见的对话框,即以后 activity 半透明,能看到前一个 activity 的界面,它是一个服务于可用 activity 的服务。

3 一个服务过程是一个通过调用 startService()办法启动的服务,并且不属于前两种状况。只管服务过程没有间接被用户看到,但他们的确是用户所关怀的,比方后盾播放音乐或网络下载数据。所以零碎保障他们的运行,直到不能保障所有的前台可见程序都失常运行时才会终止他们。

4 一个后盾过程就是一个非以后正在运行的 activity(activity 的 onStop()办法曾经被调用),他们不会对用户体验造成间接的影响,当没有足够内存来运行前台可见程序时,他们将会被终止。通常,后盾过程会有很多个在运行, 所以他们保护一个 LRU 最近应用程序列表来保障常常运行的 activity 能最初一个被终止。如果一个 activity 正确的实现了生命周期的办法,并且保留它以后状态,杀死这些过程将不会影响到用户体验。

5 一个空线程没有运行任何可用应用程序组,保留他们的惟一起因是为了设立一个缓存机制,来放慢组件启动的工夫。零碎常常杀死这些内存来均衡零碎的整个零碎的资源,过程缓存和根本外围缓存之间的资源。
Android 把过程里优先级最高的 activity 或服务,作为这个过程的优先级。例如,一个过程领有一个服务和一个可见的 activity,那么这个过程将会被定义为可见过程,而不是服务过程。

此外,如果别的过程依赖某一个过程的话,那么被依赖的过程会进步优先级。一个过程服务于另一个过程,那么提供服务的过程不会低于取得服务的过程。例如,如果过程 A 的一个内容提供商服务于过程 B 的一个客户端,或者过程 A 的一个 service 被过程 B 的一个组件绑定,那么过程 A 至多领有和过程 B 一样的优先级,或者更高。

因为一个运行服务的过程的优先级高于运行后盾 activity 的过程,一个 activity 会筹备一个长时间运行的操作来启动一个服务,而不是启动一个线程–尤其是这个操作可能会拖垮这个 activity。例如后盾播放音乐的同时,通过照相机向服务器发送一张照片,启动一个服务会保障这个操作至多运行在 service 过程的优先级下,无论这个 activity 产生了什么,播送接收者应该作为一个空服务而不是简略的把耗时的操作独自放在一个线程里。

退出移动版