服务基础与进阶
服务基础一
xml文件:
<Button
android:onClick="start"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="启动" />
<Button
android:onClick="stop"
android:layout_marginTop="10dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="停止" />
创建一个继承自TestService的服务类:
public class TestService extends Service {
@Override
public IBinder onBind(Intent arg0) {
return null;
}
@Override
public void onCreate() {
System.out.println("onCreate");
super.onCreate();
}
@Override
public void onDestroy() {
System.out.println("onDestroy");
super.onDestroy();
}
@Override
@Deprecated
public void onStart(Intent intent, int startId) {
System.out.println("onStart");
super.onStart(intent, startId);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.out.println("onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
}
在清单文件中声明服务:
<service android:name=".TestService"></service>
回到MainActivity源文件:
public void start(View view){
Intent intent = new Intent(this, TestService.class);
startService(intent);
}
public void stop(View view){
Intent intent = new Intent(this, TestService.class);
stopService(intent);
}
由以上程序可知:服务只会被创建一次 如果服务已经创建了 并且没有销毁
多次调用 startService的方法 只会执行 onStartCommand() 和onStart()方法
服务停止
1.stopService()
2.程序管理器 手工的停止掉服务
使用服务窃听电话
项目中创建一个继承自Service的类PhoneStatusService:
public class PhoneStatusService extends Service {
/**
*长期在后台运行的组件,如果用户不手动的关闭,不会停止的
*/
@Override
public IBinder onBind(Intent arg0) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
Log.e("onCreate:", "服务被创建");
System.out.println("服务被创建");
//监听用户电话状态变化
//电话管理器 电话管理服务
TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
//监听手机的通话状态的变化
tm.listen(new MyPhoneStatusLinstener(), PhoneStateListener.LISTEN_CALL_STATE);
}
@SuppressWarnings("unused")
private class MyPhoneStatusLinstener extends PhoneStateListener{
MediaRecorder recorder;
@Override
public void onCallStateChanged(int state, String incomingNumber) {
try{
switch (state) {
case TelephonyManager.CALL_STATE_IDLE://空闲状态,没有通话没有铃声
if(recorder!=null){
recorder.stop();
recorder.reset();
recorder.release();
recorder = null;
}
break;
case TelephonyManager.CALL_STATE_RINGING://响铃状态
/*System.out.println("发现来电号码:"+ incomingNumber);
if("666".equals(incomingNumber)){
System.out.println("挂断电话。。。");
}*/
//1.创建出来一个录音机
recorder = new MediaRecorder();
//设置录制的音频 从话筒里面获取声音
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
recorder.setOutputFile("/sdcard/"+System.currentTimeMillis()+".3gp");
recorder.prepare();
break;
case TelephonyManager.CALL_STATE_OFFHOOK://通话状态
if(recorder != null){
recorder.start();
}
break;
}
}catch(Exception e){e.printStackTrace();}
super.onCallStateChanged(state, incomingNumber);
}
}
@Override
public void onDestroy() {
super.onDestroy();
Log.e("onDestroy:", "服务被销毁");
System.out.println("服务被销毁");
}
}
清单文件中声明服务:
在application中添加:<service android:name=".PhoneStatusService"></service>
因为涉及到读取电话状态、调用系统得录音机、操作SDCARD,所以要添加一下权限:
<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"/>
再在应用启动的时候开启服务:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//开启服务
Intent intent = new Intent(this, PhoneStatusService.class);
startService(intent);
}
进程与应用程序
应用程序:一组组件(activity service provider receiver)的集合。
一般情况下 一个应用程序 会对应一个 进程
一般情况下 关闭掉应用。(关闭掉所有的界面,关闭所有的activity)。
应用程序的进程 是不会被关闭的 仍然在后台长期的运行。
采用一组策略 帮助我们自动的管理进程。
进程 按照优先级分别为不同的等级:
1. 前台进程 用户可以看到这个进程里面某一个activity的界面,可以操作这个界面。
2. 可见进程 用户仍然可以看到这个进程 某个activity的界面,但是不可以操作这个界面。
3. 服务进程 如果一个应用程序 有一个服务在后台运行。
4. 后台进程 没有任何服务的进程 打开一个activity之后 按下home键 最小化
5. 空进程 没有任何活动组件 存在的进程。
new thread(){}.start();线程运行在进程中,要是进程不在线程就不在了。
服务运行的流程
本实例主要讲的是服务的绑定,我们不能通过实例化服务类来直接的访问服务类中的其他方法,
所以只能通过绑定服务来间接的访问到我们需要的方法。
新建一个xml文件:
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="start"
android:text="开启服务" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="stop"
android:text="停止服务" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="bind"
android:text="绑定服务" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="change"
android:text="调用服务里的方法" />
创建一个接口,用来代理服务类中的方法:
//美女代理人的接口
public interface IService {
public void changeTiaoWu(String twName);
}
创建一个继承自Service的服务类:
public class MeiNvService extends Service {
@Override
public IBinder onBind(Intent arg0) {
System.out.println("美女服务被绑定了...");
// 步骤2:服务在成功绑定的时候 会调用onbind方法返回一个ibinder对象
// 返回自定义的代理人对象
return new MyBinder();
}
private class MyBinder extends Binder implements IService {
public void yueMeiNvKanDianYing() {
}
public void yueMeiNvYouYong() {
}
@Override
public void changeTiaoWu(String twName) {
// 简介的利用代理人 调用了美女的方法
callTiaoWu(twName);
}
}
@Override
public void onCreate() {
super.onCreate();
System.out.println("服务开始了,美女开始跳舞了。。。");
}
// 更改跳舞
public void callTiaoWu(String twName) {
// 步骤5:利用 ibinder 对象间接的调用了服务里面的方法
Toast.makeText(getApplicationContext(), "开始跳舞" + twName, 0).show();
}
@Override
public void onDestroy() {
super.onDestroy();
System.out.println("服务销毁了,美女停止了跳舞!");
}
}
在MainActivity中:
//步骤4:在 activity 里面得到服务 ibinder 对象的引用
private IService myBinder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
//开启服务
public void start(View view){
Intent intent = new Intent(this, MeiNvService.class);
startService(intent);
}
//停止服务
public void stop(View view){
Intent intent = new Intent(this, MeiNvService.class);
stopService(intent);
}
//调用服务里面的方法,换一只舞
public void change(View view){
myBinder.changeTiaoWu("钢管舞");
}
public void bind(View view){
Intent intent = new Intent(this, MeiNvService.class);
//intent 激活服务的意图
//conn 代理中间人对象 用来跟服务简历联系不能为空
//BIND_AUTO_CREATE 在绑定服务的时候如果服务不存在就自动的创建
//步骤1:采用绑定服务的方式 开启服务
bindService(intent, new MyConn(), BIND_AUTO_CREATE);
}
private class MyConn implements ServiceConnection{
//在服务被成功绑定的时候调用的方法
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
System.out.println("美女把代理人返回回来了...");
//步骤3:服务返回的 ibinder 对象会被传递给 MyConn的回调方法
myBinder = (IService) service;
}
//在服务失去绑定的时候调用的方法 只有程序异常终止
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
记得最后在清单文件的application中声明服务:
<service android:name=".MeiNvService"></service>
为能更好的观察到服务的混合开启,在程序中可添加一下几个方法:
在MainActivity中:
//解除绑定服务的方法
public void unbind(View view){
unbindService(conn); //解绑的是MyConn的对象
}
为退出程序也能解绑服务通常在onDestroy里添加解绑方法:
protected void onDestroy(){
//因为解除绑定只能解除一次,要是先按解除绑定在按回退键的话log中会产生一片红,为防止这样的情况产生,可添加try
try{
unbindService(conn);
}catch(Exception e){
}
super.onDestroy();
}
在服务类中:
public boolean onUnbind(Intent intent){
System.out.println("onUnbind");
return super.onUnbind(intent);
}
总结:
1.后台长期运行的组件。
startService();
stopService();
bindService()绑定服务 可以得到服务的代理人对象,间接调用服务里面的方法。
绑定服务:间接调用服务里面的方法。
如果调用者activity被销毁了,服务也会跟着销毁
(不求同生,但求同时挂)
开启服务:不可以调用服务里面的方法。
如果调用者activity退出了,服务还会长期的在后台运行
需求:既要保证服务长期的后台运行,又想去调用服务里面的方法。
技巧:1.先开启服务 2.绑定服务
步骤:1.开启服务 startService() - onCreate();
2.绑定服务 bindService() - onBind();
3.关闭程序,调用者退出,服务被解绑。
4.stopService() 停止服务。
生命周期:
1.单独调用 startService() - onCreate();
stopService() - onDestroy();
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bind -> onCreate -> onBind
unbind -> onUnbind -> onDestroy
服务职能被解绑一次,多次的解除绑定服务 应用户会报错。
除了可以用绑定的方法调用服务的方法外还可以用广播来实现。
xml文件:
<Button
android:onClick="call"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="调用服务的方法" />
创建一个继承自Service的服务类:
public class MyService extends Service {
private MyReceiver receiver;
@Override
public IBinder onBind(Intent arg0) {
return null;
}
@Override
public void onCreate() {
//采用代码的方式注册一个广播接收者
receiver = new MyReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction("com.lcy.callmethod");
registerReceiver(receiver, filter);
super.onCreate();
}
private void methodInservice(){
Toast.makeText(this, "我是服务的方法", 1).show();
}
@Override
public void onDestroy() {
unregisterReceiver(receiver);
receiver = null;
super.onDestroy();
}
private class MyReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context arg0, Intent arg1) {
System.out.println("我是service内部的广播接收者");
methodInservice();
}
}
}
在配置文件中配置服务类:
<service android:name="com.ex.broadcastreceiveandservice.MyService"></service>
在MainActivity类中:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, MyService.class);
startService(intent);
}
public void call(View view){
//发送一个自定义的广播
Intent intent = new Intent();
intent.setAction("com.lcy.callmethod");
sendBroadcast(intent);
}
}
/**
* 四大组件:特定 都是要在清单文件配置
* activity 界面 用户交互的UI
* service 后台运行的 没有界面 长期后台存在 提高应用程序进程的优先级
* broadcast receiver 接收系统的广播事件 自定义一些广播(可以通过代码方式注册,也可以通过清单文件注册)。
* content provider 把自己私有的数据 暴露给别的应用程序。
*/
********************************************************************************************************
采用aidl绑定远程服务:
aidl:android interface definition language Andrid 接口定义语言
IPC:inter process communication 进程间通讯
~~~~~~~~~~~~~远程绑定服务的流程~~~~~~~~~~~~~~~~~~~~
1.在activity 调用 bindService() 去绑定服务
bindService(intent, new MyConn(), BIND_AUTO_CREATE);
需要传递一个叫 ServiceConntion 的接口参数
用来返回两个回调 当服务被成功绑定
当服务失去连接
2.在服务里需要重写方法 onBind() 在服务被绑定的时候调用返回一个IBinder接口 对象(代理人) 接口定义 需要改成aidl 用自动生成的 IService.stub
代理人 必须要实现一个方法,这个方法可以调用到服务的方法。
3.在activity 的onServiceConnected 得到中间人 使用aidl 自动生成 IService 利用IService.stub.asInterface();
4.调用中间人的方法
5.中间人调用服务的方法
模拟支付宝在线支付服务:
首先创建一个MyAliPayService项目:
先创建一个接口:
interface IService {
void callMethodInService();
}
为使这个接口能在不同的应用中得到共享,把它的文件扩展名改为 .aidl
再创建一个服务类:
public class AliPayService extends Service {
@Override
public IBinder onBind(Intent arg0) {
System.out.println("远程支付服务:onBind");
return new MyBinder();
}
private class MyBinder extends IService.Stub{
@Override
public void callMethodInService() {
methodInService();
}
}
private void methodInService(){
System.out.println("我是远程支付宝的服务,用来支付");
}
@Override
public void onCreate() {
System.out.println("远程支付服务:onCreate");
super.onCreate();
}
@Override
public void onDestroy() {
System.out.println("远程支付服务:onDestroy");
super.onDestroy();
}
@Override
public boolean onUnbind(Intent intent) {
System.out.println("远程支付服务:onUnbind");
return super.onUnbind(intent);
}
}
在清单文件中配置服务类:
<service android:name="com.ex.myalipayservice.AliPayService">
<intent-filter >
<action android:name="com.lcy.alipay"/>
</intent-filter>
</service>
再另常见一个项目MyYouXi:
xml文件:
<Button
android:onClick="bind"
android:id="@+id/btn1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="绑定到远程服务" />
<Button
android:onClick="call"
android:layout_below="@id/btn1"
android:layout_marginTop="10dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="调用远程服务方法" />
把aidl中的文件连同路径(所在的包)复制到src中:
在MainActivity类中:
public class MainActivity extends Activity {
private Intent intent;
private IService iService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intent = new Intent();
intent.setAction("com.lcy.alipay");
}
public void bind(View view){
bindService(intent, new MyConn(), BIND_AUTO_CREATE);
}
//引入aidl的时候文件的报名一定要相同
private class MyConn implements ServiceConnection{
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iService = IService.Stub.asInterface(service);//API提供接口强制类型转换的方法
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
public void call(View view){
try {
iService.callMethodInService();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}