Android:Service简介
来源:互联网 发布:中航控制软件下载 编辑:程序博客网 时间:2024/06/01 18:24
Android:Service简介
Service是Android系统提供的四种组件之一,是运行在后台的一种服务程序,没有可视化界面。Android 支持两种服务类型的服务:本地服务和远程服务。
•本地服务 LocalService 用于应用程序内部。
它可以启动并运行,直至有人停止了它或它自己停止。在这种方式下,它以调用Context.startService()启动,而以调用Context.stopService()结束。它可以调用Service.stopSelf() 或 Service.stopSelfResult()来自己停止。不论调用了多少次startService()方法,你只需要调用一次stopService()来停止服务。
用于实现应用程序自己的一些耗时任务,比如查询升级信息,并不占用应用程序比如Activity所属线程,而是单开线程后台执行,这样用户体验比较好。
•远程服务 RemoteService 用于android系统内部的应用程序之间。
它可以通过自己定义并暴露出来的接口进行程序操作。客户端建立一个到服务对象的连接,并通过那个连接来调用服务。连接以调用Context.bindService()方法建立,以调用 Context.unbindService()关闭。多个客户端可以绑定至同一个服务。如果服务此时还没有加载,bindService()会先加载它。
生命周期:
service进程的优先级
Android系统会尽量保持拥有service的进程运行,只要在该service已经被启动(start)或者客户端连接(bindService)到它。当内存不足时,会保持拥有service的进程具有较高的优先级。
1.如果service正在调用onCreate,onStartCommand或者onDestory方法,那么用于当前service的进程则变为前台进程以避免被killed。
2.如果当前service已经被启动(start),拥有它的进程则比那些用户可见的进程优先级低一些,但是比那些不可见的进程更重要,这就意味着service一般不会被killed.
3.如果客户端已经连接到service (bindService),那么拥有Service的进程则拥有最高的优先级,可以认为service是可见的。
4.如果service可以使用startForeground(int,Notification)方法来将service设置为前台状态,那么系统就认为是对用户可见的,并不会在内存不足时killed。
Android:本地服务
在Activity中,使用context.startService() 来启动本地服务,调用context.stopService()来停止服务。本地服务的生命周期为:onCreate -->onStart(可多次调用) -->onDestroy
Service代码:
public class LocalService extends Service {
private static final String TAG = "LocalService";
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind");
return null;
}
@Override
public void onCreate() {
Log.i(TAG,"onCreate");
super.onCreate();
}
@Override
public void onDestroy() {
Log.i(TAG,"onDestroy");
super.onDestroy();
}
@Override
public void onStart(Intent intent, int startId) {
Log.i(TAG,"onStart");
super.onStart(intent, startId);
}
}
Activity代码:
public class ServiceActivity extendsActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.servicedemo);
((Button)findViewById(R.id.startLocalService)).setOnClickListener(
newView.OnClickListener(){
@Override
publicvoid onClick(View view) {
// TODO Auto-generated method stub
startService(new Intent("com.demo.SERVICE_DEMO"));
}
});
((Button)findViewById(R.id.stopLocalService)).setOnClickListener(
newView.OnClickListener(){
@Override
publicvoid onClick(View view) {
// TODO Auto-generated method stub
stopService(new Intent("com.demo.SERVICE_DEMO"));
}
});
}
}
AndroidManifest.xml:
<serviceandroid:name=".LocalService">
<intent-filter>
<actionandroid:name="com.demo.SERVICE_DEMO" />
<categoryandroid:name="android.intent.category.default" />
</intent-filter>
</service>
在第一次运行startService时,会调用onCreate和onStart方法,在没有调用stopService前,再次运行startService,都只会调用onStart。而stopService时调用onDestroy。
Android:远程服务
在Android中, 每个应用程序都有自己的进程,当需要在不同的进程之间传递对象时,该如何实现呢? 显然, Java中是不支持跨进程内存共享的。因此要传递对象, 需要把对象解析成操作系统能够理解的数据格式,以达到跨界对象访问的目的。在JavaEE中,采用RMI通过序列化传递对象。在Android中, 则采用AIDL(Android Interface Definition Language:接口描述语言)方式实现。
AIDL是一种接口定义语言,用于约束两个进程间的通讯规则,供编译器生成代码,
实现Android设备上的两个进程间通信(IPC)。AIDL的IPC机制和EJB所采用的CORBA很类似,进程之间的通信信息,首先会被转换成AIDL协议消息,然后发送给对方,对方收到AIDL协议消息后再转换成相应的对象。由于进程之间的通信信息需要双向转换,所以android采用代理类在背后实现了信息的双向转换,代理类由android编译器生成,对开发人员来说是透明的。
假设A应用需要与B应用进行通信,调用B应用中的download(Stringpath)方法,
B应用以Service方式向A应用提供服务。需要下面四个步骤:
1> 在B应用中创建*.aidl文件,aidl文件的定义和接口的定义很相类,
如:在cn.flyfot.aidl包下创建IDownloadService.aidl文件,内容如下:
package cn.flyfot.aidl;
interface IDownloadService
{
void download(String path);
}
当完成aidl文件创建后,eclipse会自动在项目的gen目录中同步生成IDownloadService.java接口文件。接口文件中生成一个Stub的抽象类,里面包括aidl定义的方法,还包括一些其它辅助方法。值得关注的是asInterface(IBinderiBinder),它返回接口类型的实例,对于远程服务调用,远程服务返回给客户端的对象为代理对象,客户端在onServiceConnected(ComponentName name, IBinder service)方法引用该对象时不能直接强转成接口类型的实例,而应该使用asInterface(IBinderiBinder)进行类型转换。
编写Aidl文件时,需要注意下面几点:
1.接口名和aidl文件名相同。
2.接口和方法前不用加访问权限修饰符public,private,protected等,也不能用final,static。
3.Aidl默认支持的类型包话java基本类型(int、long、boolean等)和(String、List、Map、CharSequence),使用这些类型时不需要import声明。对于List和Map中的元素类型必须是Aidl支持的类型。如果使用自定义类型作为参数或返回值,自定义类型必须实现Parcelable接口。
4.自定义类型和AIDL生成的其它接口类型在aidl描述文件中,应该显式import,即便在该类和定义的包在同一个包中。
5.在aidl文件中所有非Java基本类型参数必须加上in、out、inout标记,以指明参数是输入参数、输出参数还是输入输出参数。
6.Java原始类型默认的标记为in,不能为其它标记。
2> 在B应用中实现aidl文件生成的接口(本例是IDownloadService),但并非直接实现接口,而是通过继承接口的Stub来实现(Stub抽象类内部实现了aidl接口),并且实现接口方法的代码。内容如下:
public class ServiceBinder extends IDownloadService.Stub
{
@Override
public void download(String path) throws RemoteException
{
Log.i("DownloadService", path);
}
}
3> 在B应用中创建一个Service(服务),在服务的onBind(Intent intent)方法中返回实现了aidl接口的对象(本例是ServiceBinder)。内容如下:
public class DownloadService extends Service
{
private ServiceBinder serviceBinder =new ServiceBinder();
@Override
public IBinderonBind(Intent intent)
{
return serviceBinder;
}
}
配置AndroidManifest.xml:
<serviceandroid:name=".DownloadService">
<intent-filter>
<actionandroid:name="cn.flyfot.process.aidl.DownloadService"/>
</intent-filter>
</service>
4> 把B应用中aidl文件所在package连同aidl文件一起拷贝到客户端A应用,eclipse会自动在A应用的gen目录中为aidl文件同步生成IDownloadService.java接口文件,接下来就可以在A应用中实现与B应用通信,代码如下:
public class ClientActivity extendsActivity
{
private IDownloadService downloadService;
@Override
public voidonCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
this.bindService(newIntent("cn.flyfot.process.aidl.DownloadService"),
this.serviceConnection, BIND_AUTO_CREATE);//绑定到服务
}
@Override
protected voidonDestroy()
{
super.onDestroy();
this.unbindService(serviceConnection);//解除服务
}
privateServiceConnection serviceConnection = new ServiceConnection()
{
@Override
public voidonServiceConnected(ComponentName name, IBinder service)
{
downloadService =IDownloadService.Stub.asInterface(service);
try
{
downloadService.download("http://www.flyfot.cn");
}
catch(RemoteException e)
{
Log.e("ClientActivity",e.toString());
}
}
@Override
public void onServiceDisconnected(ComponentName name)
{
downloadService =null;
}
};
}
补充案例:电话窃听和拦截应用
基于广播机制,做一个简单的电话窃听和录音应用。
业务需求分析:
1.当手机处于开机状态,监听服务就要启动,对来电进行监听录音。
2.设置电话黑名单,当来电是黑名单电话,则直接挂断。
实现步骤:
android的新版本已经把Phone类给隐藏起来了,想要用代码实现挂断电话,就必须通过AIDL才行,
第一步:在程序中新建一个包,包名必须为:com.android.internal.telephony,因为要使用aidl,
第二步:在这个包里面新建一个名为ITelephony.aidl的文件,然后在文件里面写入代码:
package com.android.internal.telephony;
interface ITelephony{
boolean endCall();
void answerRingingCall();
}
然后保存,eclipse会自动在gen文件夹下生成一个ITelephony.java的类。
第三步:在程序中新建一个包,包名为:android.telephony;
第四步:在此包中创建NeighboringCellInfo.aidl文件:
package android.telephony;
parcelable NeighboringCellInfo;
定义一个电话监听的服务,对来电进行监听录音和拦截。具体代码如下:
PhoneListenerService:
importjava.lang.reflect.Method;
importcom.android.internal.telephony.ITelephony;
importandroid.app.Service;
importandroid.content.Context;
importandroid.content.Intent;
importandroid.media.MediaRecorder;
importandroid.os.Environment;
importandroid.os.IBinder;
importandroid.telephony.PhoneStateListener;
importandroid.telephony.TelephonyManager;
importandroid.util.Log;
importandroid.view.LayoutInflater;
importandroid.view.View;
importandroid.widget.Toast;
public class PhoneListenerServiceextendsService {
privateMediaRecorder recorder;
privateboolean recording =false;
@Override
publicIBinder onBind(Intent intent) {
returnnull;
}
@Override
publicvoid onCreate() {
Log.v("TAG","service onCreate()");
super.onCreate();
//电话服务管理
TelephonyManager manager = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
//监听电话状态
manager.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);
}
privatePhoneStateListener listener = newPhoneStateListener() {
/*
* @seeTelephonyManager#CALL_STATE_IDLE值为0
*
* @seeTelephonyManager#CALL_STATE_RINGING值为1
*
* @see TelephonyManager#CALL_STATE_OFFHOOK值为2
*/
@Override
publicvoid onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state,incomingNumber);
//打印电话状态改变信息
Log.v("TAG", "onCallStateChanged state=" + state);
switch(state) {
caseTelephonyManager.CALL_STATE_IDLE:// 没有来电或者挂断
stopRecord();
break;
caseTelephonyManager.CALL_STATE_RINGING:// 响铃时
stop(incomingNumber);
break;
caseTelephonyManager.CALL_STATE_OFFHOOK:// 接起电话
recordCalling();
break;
default:
break;
}
}
};
//停止录音
private voidstopRecord() {
Log.v("TAG","stopRecord");
if(recording) {
recorder.stop();
recorder.release();
recording=false;
}
}
//电话拦截
public voidstop(String s) {
try {
if(s.equals("110")) {
Toast.makeText(this, "拦截成功", 0).show();
Log.e("TAG", "此来电为黑名单号码,已被拦截!");
//调用ITelephony.endCall()结束通话
Method method = Class.forName("android.os.ServiceManager")
.getMethod("getService", String.class);
IBinder binder = (IBinder)method.invoke(null,
new Object[] { TELEPHONY_SERVICE });
ITelephony telephony =ITelephony.Stub.asInterface(binder);
telephony.endCall();
} else
Toast.makeText(this, "不需拦截", 0).show();
recording=false;
} catch(Exception e) {
e.printStackTrace();
}
}
//进行录音
privatevoid recordCalling() {
try {
Log.v("TAG","recordCalling");
recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC); // 读麦克风的声音
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);//输出格式.3gp
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);//编码方式
recorder.setOutputFile(Environment.getExternalStorageDirectory()
.getAbsolutePath()
+ "/"
+System.currentTimeMillis()
+ ".3gp");// 存放的位置是放在sdcard目录下
recorder.prepare();
recorder.start();
recording = true;
} catch(Exception e) {
e.printStackTrace();
}
}
}
我们知道服务不能自己启动,需要手动启动,所以我们需要一个广播,当手机刚开机,我们就发送广播,启动监听电话的服务。下面是一个发送广播的Receiver
BootCompleteReceiver:
importandroid.content.BroadcastReceiver;
importandroid.content.Context;
importandroid.content.Intent;
importandroid.util.Log;
public class BootCompleteReceiverextendsBroadcastReceiver {
@Override
publicvoid onReceive(Context context, Intent intent){
//运用广播开启监听这个服务
Log.v("TAG", "开机了!");
Intent i = new Intent(context, PhoneListenerService.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startService(i);
}
}
下面就是要在AndroidManifest.xml中配置相关的权限:具体代码如下:
AndroidManifest.xml
<?xml version="1.0"encoding="utf-8"?>
<manifestxmlns:android="http://schemas.android.com/apk/res/android"
package="cn.yj3g.L21_PhoneListener"
android:versionCode="1"
android:versionName="1.0">
<uses-sdkandroid:minSdkVersion="8" />
<applicationandroid:icon="@drawable/icon"android:label="@string/app_name">
<serviceandroid:name=".PhoneListenerService">
<intent-filter>
<actionandroid:name="cn.yj3g.L21_PhoneListener.PhoneListenerService"></action>
</intent-filter>
</service>
<receiverandroid:name=".BootCompleteReceiver">
<intent-filter>
<actionandroid:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
</application>
<!-- 读取电话状态权限-->
<uses-permissionandroid:name="android.permission.READ_PHONE_STATE"/>
<!-- 录音权限 -->
<uses-permissionandroid:name="android.permission.RECORD_AUDIO"/>
<!-- 向sdcard中写数据的权限 -->
<uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- 开机启动广播的权限 -->
<uses-permissionandroid:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!-- 挂断电话时需要的权限 -->
<uses-permission android:name="android.permission.CALL_PHONE"/>
</manifest>
这样一个监听电话的应用就做好了。当安装此应用的手机开机时,来电就处于监听状态,这样就可以不动声色的对该手机的来电进行监听录音或者拦截。
本文来自上海Android培训官方课件,感谢上海it培训机构提供。
- android service简介
- android service简介 .
- android Service简介
- Android Service 简介
- Android:Service简介
- Android基础service简介
- Android Service简介
- android之service简介
- Android---41---Service简介
- Android Service简介
- Android Service简介
- Android Service简介
- Android -- Service基础简介
- android Service简介备忘
- [Android][Service简介]
- android基本组件 Service 简介
- Android Service的简介(自用)
- Android Service的绑定简介
- HtmlHelper使用大全
- 关于学习android的精品视频及网站地址
- Linux的文件权限与目录配置
- What's the difference between a URI and a URL?
- 变换矩阵
- Android:Service简介
- WordToPdf,word转pdf
- 计算机与抽象
- 解决Binary XML file line #6: : Error inflating class <unknown> 的问题
- 花生壳远程控制桌面
- bestcode#6—1003 HDU 4983 Goffi and GCD 【欧拉函数】
- Parcelable
- Android硬件加速的一些问题和错误
- CAS实现单点登录(SSO)经典完整教程