拓展:
Android之Service与IntentService的比较
http://blog.csdn.net/smile3670/article/details/7702521
Java并发编程:volatile关键字解析
http://www.cnblogs.com/dolphin0520/p/3920373.html
java中volatile关键字的含义
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对象
//注意要强转
TelephonyManager manager = (TelephonyManager)
this
.getSystemService(
this
.TELEPHONY_SERVICE);
//通过TelephonyManager来获取通话的状态?????????????????????/
manager.listen(
new
MyPoneListener(), PhoneStateListener.LISTEN_CALL_STATE);
//注意常量是PhoneStateListener的常量。
}
//定义一个类继承PhoneListener
private
class
MyPoneListener
extends
PhoneStateListener{
//复写它的一个监听通话状态的方法
@Override
public
void
onCallStateChanged(
int
state, String incomingNumber) {
//判断状态
/*int CALL_STATE_IDLE Device call state: No activity.
int CALL_STATE_OFFHOOK Device call state: Off-hook.
int CALL_STATE_RINGING Device call state: Ringing.*/
//代表空闲状态
if
(state == TelephonyManager.CALL_STATE_IDLE)
{
System.out.println(
"结束录音"
);
if
(recorder !=
null
)
{
recorder.stop();
recorder.reset();
// You can reuse the object by going back to setAudioSource() step
recorder.release();
// Now the object cannot be reused
}
}
//接听状态
else
if
(state == TelephonyManager.CALL_STATE_OFFHOOK)
{
System.out.println(
"开始录音"
);
if
(recorder !=
null
)
{
recorder.start();
// Recording is now started
}
}
//响铃状态
if
(state == TelephonyManager.CALL_STATE_RINGING)
{
System.out.println(
"准备录音,创建录音机。"
);
recorder =
new
MediaRecorder();
//设置录音录制的是双方的还是单方的。
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
//设置录音的保存格式3GP
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"
/>
<!-- SD卡读写权限 -->
<
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();
}
// 定义一个Binder的子类对象<中间人对象>
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);
// 1.通过startService方法开启服务 为了让音乐盒可以长期的运行,即按下回退键服务不会随之一起销毁。
Intent intent =
new
Intent(
this
, PlayService.
class
);
startService(intent);
conn =
new
MyConn();
// 2.通过bindService方法开启服务 为了调用服务里的方法
bindService(intent, conn, BIND_AUTO_CREATE);
// 3.在activity里的onDestroy方法里注销绑定服务。
}
// 定义一个类实现ServiceConnection接口
private
class
MyConn
implements
ServiceConnection {
@Override
public
void
onServiceConnected(ComponentName name, IBinder service) {
// 注意要最终是要用Iservice的特有方法,所以要强转成Iservice.
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
;
}
}
// 在activity里的onDestroy方法里注销绑定服务
@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) {
// TODO Auto-generated method stub
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"
)))
{
// Toast.makeText(getApplicationContext(),"密码错误.....", 0).show();
System.out.println(
"sdggsgggs"
);
return
false
;
}
if
(money <
4000
)
{
// Toast.makeText(getApplicationContext(),"豆子数不足4000.....", 0).show();
System.out.println(
"QQQQQQQQQQQ"
);
return
false
;
}
return
true
;
}
//定义一个中间人对象与调用本地服务不一样,直接继承Stub类就可以了。
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
;
//怎么才能将其它应用的IService得到呢,使用AIDL技术。
private
IService serviceBinder =
null
;
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//绑定服务-----------------注意这个逻辑要写在onCreate方法里,因为它需要一定的时间。
//由于是跨应用访问,要使用隐式意图。
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();
}
}
//定义一个类实现ServiceConnection接口
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(
"服务连接失败。。。。"
);
}
}
//解除绑定Service
@Override
protected
void
onDestroy() {
unbindService(conn);
super
.onDestroy();
}
}
|
本文转自屠夫章哥 51CTO博客,原文链接:http://blog.51cto.com/4259297/1679401,如需转载请自行联系原作者