android service 學習(上)

 
Service是android 系統中的一種組件,它跟Activity的級別差不多,但是他不能自己運行,只能後臺運行,並且可以和其他組件進行交互。Service的啓動有兩種方式:context.startService() context.bindService()。
 
使用context.startService() 啓動Service是會會經歷:
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()啓動Service會經歷:
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可以在和多場合的應用中使用,比如播放多媒體的時候用戶啓動了其他Activity這個時候程序要在後臺繼續播放,比如檢測SD卡上文件的變化,再或者在後臺記錄你地理信息位置的改變等等,總之服務嘛,總是藏在後頭的。
 
下面我做了一個簡單的音樂播放的應用,分別使用startService和bindService來啓動本地的服務。
而在下一篇《android service 學習(下) 》會介紹通過AIDL對Service進行遠程調用。
 
下面是整個應用啓動界面:
先從使用startService啓動Service學起
 
 
首先編寫一個Activity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public class PlayMusic extends Activity implements OnClickListener {
    private static final String TAG = "PlayMusic";
    private Button playBtn;
    private Button stopBtn;
    private Button pauseBtn;
    private Button exitBtn;
    private Button closeBtn;
 
    //....(詳見源碼)
 
@Override
    public void onClick(View v) {
        int op = -1;
        Intent intent = new Intent("org.allin.android.musicService");
         
        //廣播用
//      Intent intent = new Intent("org.allin.android.musicReceiver");
         
        switch (v.getId()) {
        case R.id.play:
            Log.d(TAG, "onClick: playing muic");
            op = 1;
            break;
        case R.id.stop:
            Log.d(TAG, "onClick: stoping music");
            op = 2;
            break;
        case R.id.pause:
            Log.d(TAG, "onClick: pausing music");
            op = 3;
            break;
        case R.id.close:
            Log.d(TAG, "onClick: close");
            this.finish();
            break;
        case R.id.exit:
            Log.d(TAG, "onClick: exit");
            op = 4;
            stopService(intent);
            this.finish();
            break;
        }
         
        Bundle bundle  = new Bundle();
        bundle.putInt("op", op);
        intent.putExtras(bundle);
        startService(intent);
         
//      sendBroadcast(intent);
    }
 
 
}


 
通過重寫onClick方法來實現對播放音樂的控制。這裏把播放音樂的各種操作用數字的方式通過Intent傳遞給service。 
構造一個Intent ,ntent intent = new Intent("org.allin.android.musicService");
"org.allin.android.musicService"是在AndroidManifest.xml文件中對service類的定義

1
2
3
4
5
<service android:enabled="true" android:name=".MusicService">
<intent-filter>
<action android:name="org.allin.android.musicService" />
</intent-filter>
</service>

把操作碼放在Bundle中 
Bundle bundle  = new Bundle();
bundle.putInt("op", op);
intent.putExtras(bundle);
最後使用startService(intent);啓動服務。 
下面看看Service是怎麼實現的。
 
MusicService.java
 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
/**
 * @author allin.dev
 * http://allin.cnblogs.com/
 *
 */
public class MusicService extends Service {
 
    private static final String TAG = "MyService";
    private MediaPlayer mediaPlayer;
 
    /*
     * (non-Javadoc)
     *
     * @see android.app.Service#onBind(android.content.Intent)
     */
    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }
 
    @Override
    public void onCreate() {
        Log.v(TAG, "onCreate");
        if (mediaPlayer == null) {
            mediaPlayer = MediaPlayer.create(this, R.raw.tmp);
            mediaPlayer.setLooping(false);
        }
    }
 
    @Override
    public void onDestroy() {
        Log.v(TAG, "onDestroy");
        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 {
                // 在調用stop後如果需要再次通過start進行播放,需要之前調用prepare函數
                mediaPlayer.prepare();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
 
}
 
 
服務 使用了系統自帶MediaPlayer進行音樂的播放控制。 當調用了startService後服務會先調用onCreate,我們在裏面對MediaPlayer進行初始化。接着會調用onStart,可以看到傳遞給startService()的Intent對象會傳遞給onStart()方法,這樣我們就可以得到intent裏面的操作碼: 
Iundle bundle = intent.getExtras(); 
int op = bundle.getInt("op");
然後更具定義好的操作碼進行相應的f播放操作。啓動後界面如下圖: 
 
圖中的”close”和“exit”是不同的,close只是調用finish()退出當前的Activity,但是Service並沒有關掉,音樂會繼續播放。而exit就是調用了stopService(intent);來停止服務,Service會調用onDestroy()方法來對mediaPlayer進行停止和釋放資源。
 
有時候如果服務只提供一些操作接口,我們也可以通過廣播的g方式來啓動服務。
首先要定義一個Receiver,並繼承BroadcastReceiver,然後在AndroidManifest.xml中進行註冊:
1
2
3
4
5
<receiver android:name=".MusicReceiver">
<intent-filter>
<action android:name="org.allin.android.musicReceiver" />
</intent-filter>
</receiver>
 
Receiver的實現:
 
MusicReceiver.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
 * @author allin.dev
 * http://allin.cnblogs.com/
 *
 */
public class MusicReceiver extends BroadcastReceiver {
 
    private static final String TAG = "MusicReceiver";
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(TAG, "onReceive");
        Intent it = new Intent("org.allin.android.musicService");
        Bundle bundle = intent.getExtras();
        it.putExtras(bundle);
         
        if(bundle != null){
            int op = bundle.getInt("op");
            if(op == 4){
                context.stopService(it);
            }else{
                context.startService(it);
            }
        }
         
    }
 
}
 
然後對PlayMusic中的onclick方法進行些改造,把Intent指向Receiver
Intent intent = new Intent("org.allin.android.musicReceiver");
intent中綁定的操作碼都不變,再調用sendBroadcast(intent);把intentg廣播出去。
MusicReceiver接受到廣播後根據操作碼進行相應的操作。




接下來的例子就是使用bindService來啓動Service
首先一樣是寫一個Activity
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
public class PlayBindMusic extends Activity implements OnClickListener {
 
    private static final String TAG = "PlayBindMusic";
    private Button playBtn;
    private Button stopBtn;
    private Button pauseBtn;
    private Button exitBtn;
     
    private BindMusicService musicService;
 
    @Override
    public void onClick(View v) {
 
        switch (v.getId()) {
        case R.id.play:
            Log.d(TAG, "onClick: binding srvice");
            musicService.play();
            break;
        case R.id.stop:
            Log.d(TAG, "onClick: stoping srvice");
            if(musicService != null){
                musicService.stop();
            }
            break;
        case R.id.pause:
            Log.d(TAG, "onClick: pausing srvice");
            if(musicService != null){
                musicService.pause();
            }
            break;
        case R.id.exit:
            Log.d(TAG, "onClick: exit");
            this.finish();
            break;
        }
    }
 
 
private void connection(){
        Log.d(TAG, "connecting.....");
        Intent intent = new Intent("org.allin.android.bindService");
        bindService(intent, sc, Context.BIND_AUTO_CREATE);
         
    }
private ServiceConnection sc = new ServiceConnection() {
        @Override
        public void onServiceDisconnected(ComponentName name) {
            musicService = null;
            Log.d(TAG, "in onServiceDisconnected");
        }
         
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            musicService = ((BindMusicService.MyBinder)(service)).getService();
            if(musicService != null){
                musicService.play();
            }
             
            Log.d(TAG, "in onServiceConnected");
        }
    };
}

 

 

這裏使用了bindService(intent, sc, Context.BIND_AUTO_CREATE);來啓動服務的,
我們需要定義ServiceConnectionnn,並實現裏面的方法,當服務綁定成功後會調用ServiceConnectionnn中的回調函數:
public void onServiceConnected(ComponentName name, IBinder service),
回調函數裏面使用musicService = ((BindMusicService.MyBinder)(service)).getService();來獲取BindMusicService服務對象,有了BindMusicService實例對象,就可以調用服務提供的各種控制音樂播放的哦功能。
下面看看BindMusicService.java的實現:

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/**
 * @author allin.dev
 * http://allin.cnblogs.com/
 */
public class BindMusicService extends Service {
 
    private static final String TAG = "MyService";
    private MediaPlayer mediaPlayer;
 
    private final IBinder binder = new MyBinder();
 
    public class MyBinder extends Binder {
        BindMusicService getService() {
            return BindMusicService.this;
        }
    }
 
    /*
     * (non-Javadoc)
     *
     * @see android.app.Service#onBind(android.content.Intent)
     */
    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "onBind");
        play();
        return binder;
    }
 
    @Override
    public void onCreate() {
        super.onCreate();
         
        Log.d(TAG, "onCreate");
        Toast.makeText(this, "show media player", Toast.LENGTH_SHORT).show();
         
         
    }
 
    @Override
    public void onDestroy() {
        super.onDestroy();
         
        Log.d(TAG, "onDestroy");
        Toast.makeText(this, "stop media player", Toast.LENGTH_SHORT);
        if(mediaPlayer != null){
            mediaPlayer.stop();
            mediaPlayer.release();
        }
    }
 
     
    public void play() {
        if (mediaPlayer == null) {
            mediaPlayer = MediaPlayer.create(this, R.raw.tmp);
            mediaPlayer.setLooping(false);
        }
        if (!mediaPlayer.isPlaying()) {
            mediaPlayer.start();
        }
    }
 
    public void pause() {
        if (mediaPlayer != null && mediaPlayer.isPlaying()) {
            mediaPlayer.pause();
        }
    }
 
    public void stop() {
        if (mediaPlayer != null) {
            mediaPlayer.stop();
            try {
                // 在調用stop後如果需要再次通過start進行播放,需要之前調用prepare函數
                mediaPlayer.prepare();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
 
}



我們看到Service中有個返回IBinder對象的onBind方法,這個方法會在Service被綁定到其他程序上時被調用,而這個IBinder對象和之前看到的onServiceConnected方法中傳入的那個IBinder是同一個東西。應用和Service間就依靠這個IBinder對象進行通信。
啓動後的界面如下圖:

[源碼下載]

下一篇《android service 學習(下) 》會介紹通過AIDL對Service進行遠程調用

發佈了287 篇原創文章 · 獲贊 216 · 訪問量 175萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章