您的位置:首页 > 移动开发 > Android开发

Android基础学习之Service

2015-10-11 17:15 477 查看
Android四大应用组件:

Activity 可见可交互 前台 界面处理

Service 不可见不可交互 后台管理(耗时任务)

BroadcastRceiver 通信机制(活动和服务,应用和应用,应用和系统)

ContentProvider 数据共享机制

它们之间的共同之处:

用法相似

跨进程交互

使用intent来交互

Service类

java.lang.Object

android.content.Context

android.content.ContextWrapper

android.app.Service

ServiceConnection 接口

private ServiceConnection conn=new ServiceConnection() {

@Override  //当检测到C/S连接断开调用该方法
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub

}

@Override //1.当检测到客户端(activity)和服务端(service)连接成功就会调用该方法
//2.当调用该方法是会传入一个IBinder对象service
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub

}
};


Service的特点:

1)与Activity是同级

2)不是一个单独的进程(默认没有特别指定,就在自己应用的进程空间中)

3)不是一个单独的线程(默认没有特别指定,就在主线程中运行,service中代码不能阻塞)

4)后台工作,不可见

服务的分类:

本地服务:同一个进程访问,当进程结束一起结束

远程服务:在不同进程访问,通过AIDL(接口)实现 当访问进程结束还可以在后台继续运行。

跨进程的进行服务访问图示:



service用两种用法 :

1.非绑定

2.绑定







service使用:

用法1:非绑定方式

1.启动

一般从活动中调用 startService() ==> 服务调用 onCreate()(创建时只调用一次)

onStartCommand() (可调用多次)

2.停止

一般从活动中调用 stopService() ==> 服务调用 onDestroy() (只调用一次) 或 服务直接调用stopSelf()

如果有绑定服务,不能停止服务

用法2:绑定方式

启动

绑定

一般从活动中调用 bindService() ==>服务调用 onBind() (只调用一次) 如果服务没有创建一般自动创建

松绑

一般从活动中调用 unbindService() ==>服务调用 onUnBind() 如果所有客户端都松绑服务还会调用

onDestroy()

停止

非绑定方式:



绑定方式:



注:图中ICount接口为自定义接口

基于本地服务示例

示例1:

使用非绑定的方式实现使用服务端运行两个子线程,分别做播放音乐和打印信息。

服务端:

public class MyService extends Service {
private static final String TAG = "ql debug";
private MediaPlayer mediaPlayer;
private Thread printThread;//打印线程
private int cnt = -1;
private boolean active = false;//服务运行标识位

public MyService() {
}

@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}

@Override
public void onCreate() {
super.onCreate();
mediaPlayer = MediaPlayer.create(this, R.raw.hold_my_hand);
mediaPlayer.start();
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
cnt = intent.getIntExtra("number", -1);//得到活动传过来的数值
active = true;
printThread = new Thread() {
@Override
public void run() {
while (active) {
cnt--;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d(TAG, "progress value--------->" + cnt);
}
}
};
printThread.start();
return super.onStartCommand(intent, flags, startId);
}

@Override
public void onDestroy() {
super.onDestroy();
mediaPlayer.stop();
active=false;
Log.e(TAG,"service destroy");
}
}


活动端:

public class MainActivity extends Activity implements OnClickListener {
int number = 100;
private Button btn_satrtService;
private Button btn_stopService;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

btn_satrtService = (Button) findViewById(R.id.btn_satrtService);
btn_stopService = (Button) findViewById(R.id.btn_stopService);
btn_satrtService.setOnClickListener(this);
btn_stopService.setOnClickListener(this);
}

@Override
public void onClick(View v) {
Intent intent = new Intent();
switch (v.getId()) {
case R.id.btn_satrtService:
intent.setAction("com.ql.servicedemo.action");
intent.putExtra("number", number);
startService(intent);// 启动服务
number++;
break;
case R.id.btn_stopService:
intent.setAction("com.ql.servicedemo.action");
stopService(intent);//停止服务
break;

default:
break;
}
}
}


清单文件进行服务的注册和定义动作字符串:(组件的注册在< application>< /application>标签中)

<service
android:name="com.ql.servicedemo.MyService"
android:enabled="true"
android:exported="true" >
<intent-filter >
<action android:name="com.ql.servicedemo.action"/>
</intent-filter>
</service>


示例2:

使用绑定的方式实现使用服务执行任务,并返回反馈信息。服务做打印任务,活动可以获取服务任务进度。

首先定义一个接口(因为Binder子类MyBinder已经继承),两个方法分别是取值和设置值:

public interface ICount {
public int getCount();
public void setCount(int cnt);
}


服务端:

public class MyService extends Service {
private static final String TAG = "ql debug";
private int cnt = 0;
private boolean active = false;

private class MyBinder extends Binder implements ICount{

@Override
public int getCount() {
return cnt;
}

@Override
public void setCount(int cnt) {
MyService.this.cnt = cnt;
}
}

MyBinder binder = new MyBinder();

public MyService() {
}

@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.返回到服务的通信通道。
return binder;
}

@Override
public void onCreate() {
super.onCreate();
new Thread() {
@Override
public void run() {
active = true;
while (active) {
cnt++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d(TAG, "progress value--------->" + cnt);
}
}
}.start();
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {

return super.onStartCommand(intent, flags, startId);
}

@Override
public void onDestroy() {
super.onDestroy();
active=false;
Log.e(TAG,"service destroy");
}
}


活动端:

public class MainActivity extends Activity implements OnClickListener {
int number = 100;
private Button btn_satrtService;
private Button btn_stopService;
private TextView txt_result;

private ICount count;

private class MyConn implements ServiceConnection {

@Override// 当远程绑定成功传入IBinder对象是一个代理对象service
public void onServiceConnected(ComponentName name, IBinder binder) {
count = (ICount) binder;
}

@Override//在service被系统意外回收才会调用
public void onServiceDisconnected(ComponentName name) {
count = null;
}
}

MyConn conn = new MyConn();

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Intent service=new Intent("com.ql.servicedemo2.action");
bindService(service, conn, BIND_AUTO_CREATE);//绑定服务

txt_result = (TextView) findViewById(R.id.txt_result);

btn_satrtService = (Button) findViewById(R.id.btn_satrtService);
btn_stopService = (Button) findViewById(R.id.btn_stopService);
btn_satrtService.setOnClickListener(this);
btn_stopService.setOnClickListener(this);
}

@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_satrtService:
txt_result.setText("来自service子线程的计数值:" + count.getCount());//获取服务任务进度
break;
case R.id.btn_stopService:
count.setCount(500);//改变服务任务的值
break;

default:
break;
}
}

@Override
protected void onDestroy() {
super.onDestroy();
unbindService(conn);//松绑服务
}
}


清单文件服务的注册和动作设置见示例1。

示例3:

使用广播进行活动和服务进行通信,需要创建广播的子类和在服务端注册广播接收器。

本例原理图示



java代码:

服务端

public class MyService extends Service {
//两个常量,读和写
private static final String READREQ = "read service data request";
private static final String WRITEREQ = "write service data request";
private static final String TAG = "ql debug";
private int cnt = 0;
private boolean active = false;//服务存活标识位(优化处理)

/**
* 用于通信的广播类
*
* @author qinlang
*
*/
private class MyReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
String command = intent.getStringExtra("command");
Log.v(TAG, "service receive command:" + command);
Intent i = new Intent("com.activity.receiver.action");
if (READREQ.equals(command)) {
i.putExtra("count", cnt);
sendBroadcast(i);//发送广播
}
if (WRITEREQ.equals(command)) {
cnt = 0;
}
}
}

MyReceiver receiver = new MyReceiver();
IntentFilter filter = new IntentFilter();

public MyService() {
}

@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.返回到服务的通信通道。
// throw new UnsupportedOperationException("Not yet implemented");
return null;
}

@Override
public void onCreate() {
super.onCreate();
//动态注册广播
filter.addAction("com.service.receiver.action");
registerReceiver(receiver, filter);//注册activity用的广播接收器

new Thread() {
@Override
public void run() {
active = true;
while (active) {
cnt++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d(TAG, "progress value--------->" + cnt);
}
}
}.start();
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {

return super.onStartCommand(intent, flags, startId);
}

@Override
public void onDestroy() {
super.onDestroy();
active = false;
unregisterReceiver(receiver);//移除广播注册
Log.e(TAG, "service destroy");
}
}


活动端

public class MainActivity extends Activity implements OnClickListener {
private static final String READREQ = "read service data request";
private static final String WRITEREQ = "write service data request";
int number = 100;
private Button btn_satrtService;
private Button btn_stopService;
private TextView txt_result;

/**
* 用于通信的广播类
* @author qinlang
*
*/
private class MyReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
// 这里可以操作UI线程的视图显示
txt_result.setText("service端子线程当前计数值为:" + intent.getIntExtra("count", -1));
}
}

MyReceiver receiver = new MyReceiver();
IntentFilter filter = new IntentFilter();

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//动态注册广播
filter.addAction("com.activity.receiver.action");
registerReceiver(receiver, filter);//注册service用的广播接收器

Intent service = new Intent("com.ql.servicedemo3.action");
startService(service);//启动服务

txt_result = (TextView) findViewById(R.id.txt_result);

btn_satrtService = (Button) findViewById(R.id.btn_satrtService);
btn_stopService = (Button) findViewById(R.id.btn_stopService);
btn_satrtService.setOnClickListener(this);
btn_stopService.setOnClickListener(this);
}

@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_satrtService:
sendBroadcastToService(READREQ);//读取服务端任务进度
break;
case R.id.btn_stopService:
sendBroadcastToService(WRITEREQ);//设置服务端任务值
break;

default:
break;
}
}

private void sendBroadcastToService(String command) {
Intent intent=new Intent("com.service.receiver.action");
intent.putExtra("command", command);
sendBroadcast(intent);//发送广播
}

@Override
protected void onDestroy() {
super.onDestroy();
}
}


效果:



基于远程服务

基于远程,就是服务和使用服务的活动不是一个进程(一个app)内。在android中每个应用都运行自己进程中,一个应用不能直接访问其他应用内存空间。为了在应用之间进行通信,android提供一种IPC的实现:AIDL (android interface definition language),是IPC轻量级实现。采用java开发语法规则来定义AIDL文件,并提供一个工具(aidl.exe)生成stub存根(java代码文件)。

AIDL 用来让应用(client端)能够被其他应用service(server端)的方法。

AIDL是一种代理模式,应用在远程代理的实现。

代理模式示意图:



应用使用原理图:



.aidl文件过程:



AIDL支持有限的数据类型:

1.java 简单类型 int

2.java 引用类型 String CharSquence

3.List和Map

如果自定义类型在AIDL中使用,必须定义自定义类型的aidl文件。

AIDL文件语法:

类似java 接口定义

支持java 常见类型

自定义类型必须Pacelable序列化(Parcelable接口是在android中实现序列化的接口)

创建AIDL步骤:

1.工程中创建.aidl文件 类似java语法

不能加public

客户端设置 in

服务端设置 out

客户/服务端 inout

支持基本数据类型,不支持 流类 InputStream类.

2.如果.aidl文件编写正确ADT生成java接口文件

不用关心具体内容自动维护

远程调用步骤

客户端:

1.定义aidl文件(代理对象) 定义需要服务端提供功能方法

2.通过ServiceConnection接口的onServiceConnected() 获得代理对象,用aidl生成java代码中方法来获得

private class AddServiceConn implements ServiceConnection{

@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 当远程绑定成功传人IBinder对象是一个代理对象service
proxy=IAddService.Stub.asInterface(service);
proxy.foo1();//方法一
proxy.foo2();//方法二
proxy.foo3();//方法三
//...
}

@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
proxy=null;
}
}


3.使用代理对象访问服务端的方法

服务端:

1.定义aidl文件(代理对象) 定义需要服务端提供功能方法

2.在onBind() 定义重写stub中的方法来构造stub子类对象

return new YourService.Stub(){

void foo1(){}

void foo2(){}

void foo3(){}

}

示例1:

使用代理对象来远程连接服务,示例实现服务的加法任务。

注:.aidl文件包名,cilent端必须要和service端一致,否则报错

如:



本例原理图和示意图:





代码:

服务端:

(别忘了注册服务和设置服务动作字符串)

1.编写.aidl文件,文件名IAddService.aidl。

package com.ql.testservice1;
interface IAddService{
int add(in int value1,in int value2);
}


2.服务java代码

public class MyService extends Service {
public MyService() {
}

@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return new IAddService.Stub() {

@Override
public int add(int value1, int value2) throws RemoteException {
return value1 + value2;
}
};
}
}


client端:

1.编写.aidl文件(与服务端的一致)

2.活动java代码

public class MainActivity extends Activity {
private IAddService proxy;// 代理对象

private class ServiceConn implements ServiceConnection {

@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 当远程绑定成功传入IBinder对象是一个代理对象service
proxy = IAddService.Stub.asInterface(service);
}

@Override
public void onServiceDisconnected(ComponentName name) {
proxy = null;
}
}

ServiceConn conn = new ServiceConn();

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 绑定服务
Intent intent = new Intent("com.ql.testservice.action");
bindService(intent, conn, BIND_AUTO_CREATE);

final EditText editText1 = (EditText) findViewById(R.id.editText1);
final EditText editText2 = (EditText) findViewById(R.id.editText2);
final TextView textView = (TextView) findViewById(R.id.textView3);
Button button = (Button) findViewById(R.id.button1);
button.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
int v1, v2, res = -1;
v1 = Integer.parseInt(editText1.getText().toString().trim());
v2 = Integer.parseInt(editText2.getText().toString().trim());
try {
res = proxy.add(v1, v2);//步骤3的“使用代理对象访问服务端的方法”
} catch (RemoteException e) {
e.printStackTrace();
}
textView.setText("计算结果:" + res);
}
});
}
}


效果:



示例2

本例演示自定义类型在AIDL中的使用,自定义类型中有三个对象。

用法步骤:

1.编写自定义bean类,必须实现Parcelable接口进行序列化

2.编写bean类的.aidl文件

3.加入.aidl代理文件

自定义bean类写法步骤:

1.实现Parcelable接口,重写writeToParcel(Parcel dest, int flags)方法。

2.要有一个带Parcel类型参数的构造方法

public XXX(Parcel in) {

}

3.定义常量CREATOR给aidl生成的代码用到的(这是个写死了的写法,照着写。范型类XXX为你的bean类)

public static final Parcelable.Creator<XXX> CREATOR = new Parcelable.Creator<XXX>() {

@Override
public XXX createFromParcel(Parcel in) {
return new XXX(in);
}

@Override
public XXX[] newArray(int size) {
return new XXX[size];
}
};


测试demo

1.定义bean类,文件名:Product.java

//必须实现Parcelable接口否则AIDL编译出错
public class Product implements Parcelable {
private int id;
private String name;
private float price;

public Product() {

}

//2.将Parcel对象中数据取出来.
public Product(Parcel in) {
id = in.readInt();
name = in.readString();
price = in.readFloat();
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public float getPrice() {
return price;
}

public void setPrice(float price) {
this.price = price;
}

@Override
public int describeContents() {
return 0;
}

@Override//1.将序列化用的数据写Parcel对象,dest是输出参数
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeString(name);
dest.writeFloat(price);
}

// 3.定义常量 给aidl生成代码用到的.
public static final Parcelable.Creator<Product> CREATOR = new Parcelable.Creator<Product>() {

@Override
public Product createFromParcel(Parcel in) {
return new Product(in);
}

@Override
public Product[] newArray(int size) {
return new Product[size];
}
};
}


2.编写bean的.aidl文件,文件名:Product.aidl

package com.ql.testservice2;
parcelable Product;


3.加入代理.aidl文件,文件名:IAddService.aidl

要导入具体包名和类名,它不会自动寻找类所在位置

说明:

文件中定义了一个Product类型(自定义类型)的getProduct()方法,一个Map类型的getMap()方法。

package com.ql.testservice2;
import com.ql.testservice2.Product;
interface IAddService{
int add(in int value1,in int value2);
Product getProduct();
Map getMap(in String country,in Product product);
}


4.服务端java代码

public class MyService extends Service {
public MyService() {
}

@Override
public IBinder onBind(Intent intent) {
//返回IBinder对象  对应客户端用来生成代理对象用的.
return new IAddService.Stub() {

@Override
public Product getProduct() throws RemoteException {
Product product=new Product();
product.setId(1000);
product.setName("宝马");
product.setPrice(1000000);
return product;
}

@Override
public Map getMap(String country, Product product) throws RemoteException {
Map map=new HashMap();
map.put("country", country);
map.put("id",product.getId());
map.put("name", product.getName());
map.put("price", product.getPrice());
return map;
}

@Override
public int add(int value1, int value2) throws RemoteException {
return value1 + value2;
}
};
}
}


以下为另一个应用的代码,用于测试连接远程服务

1.自定义bean,自定义bean的.aidl 文件和代理.aidl文件和服务端的一致(包名也要一致)。

2.活动测试代码

public class MainActivity extends Activity {
private IAddService proxy;// 代理对象

private class ServiceConn implements ServiceConnection {

@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 当远程绑定成功传入IBinder对象是一个代理对象service
proxy = IAddService.Stub.asInterface(service);
}

@Override//在service被系统意外回收才会调用
public void onServiceDisconnected(ComponentName name) {
proxy = null;
}

}

ServiceConn conn = new ServiceConn();

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 绑定服务
Intent intent = new Intent("com.ql.testservice2.action");
bindService(intent, conn, BIND_AUTO_CREATE);

final EditText editText1 = (EditText) findViewById(R.id.editText1);
final EditText editText2 = (EditText) findViewById(R.id.editText2);
final TextView textView = (TextView) findViewById(R.id.textView3);
final TextView textView2 = (TextView) findViewById(R.id.result2);
Button button = (Button) findViewById(R.id.button1);
Button button2 = (Button) findViewById(R.id.button2);
button.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
int v1, v2, res = -1;
v1 = Integer.parseInt(editText1.getText().toString().trim());
v2 = Integer.parseInt(editText2.getText().toString().trim());
try {
res = proxy.add(v1, v2);
} catch (RemoteException e) {
e.printStackTrace();
}
textView.setText("计算结果:" + res);
}
});

button2.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
try {
Product product = proxy.getProduct();
StringBuilder builder = new StringBuilder();
builder.append("product:" + product + "\n");
builder.append("id:" + product.getId() + "\n");
builder.append("name:" + product.getName() + "\n");
builder.append("price:" + product.getPrice() + "\n");
Map map = proxy.getMap("china", product);
builder.append("map object:" + map);
textView2.setText(builder.toString());

} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
}


效果:



(此处未整理)

当service被意外销毁时如何处理?

1.使用本地绑定service(activity和service绑定) 随activity销毁而销毁.

2.服务被系统意外销毁后,继续服务处理

public int onStartCommand (Intent intent, int flags, int startId)

返回值决定重启服务的方式:

2.1 START_STICKY 默认 销毁服务,重新创建服务会按空的intent对象执行任务

2.2 START_NOT_STICKY 不重启

2.3 START_STICKY_COMPATIBILITY 根据之前intent来启动

service 要指定其他进程运行,可设置:

android:process=”:remote” 可将服务放在单独的进程中,参数:用包名:retmote(如无包名表示本进程)

上文中代理模式示意图demo在这里:

main.java

public class Main {
public static void main(String[] args) {
Printable p=new PrintProxy("rose");
System.out.println("现在是:"+p.getPrinterName());
p.setPrinterName("alice");
System.out.println("现在是"+p.getPrinterName());
p.print("hello proxy world");

}

}


Printable.java

public interface Printable {
public abstract void setPrinterName(String name);
public abstract String getPrinterName();
public abstract void print(String string);
}


Printer.java

public class Printer implements Printable{
private String name;
public Printer() {
// TODO Auto-generated constructor stub
heavyJob("正在产生Printer对象实例");
}

public Printer(String name) {
// TODO Auto-generated constructor stub
this.name=name;
heavyJob("正在产生Printer对象实例");

}

private void heavyJob(String string) {
// TODO Auto-generated method stub
System.out.println(string);
for(int i=0;i<5;i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.print(".");
}
System.out.println("完成");
}

@Override
public void setPrinterName(String name) {
// TODO Auto-generated method stub
this.name=name;
}

@Override
public String getPrinterName() {
// TODO Auto-generated method stub
return this.name;
}

@Override
public void print(String string) {
// TODO Auto-generated method stub
System.out.println("<<<"+this.name+">>>");
System.out.println(string);
}
}


PrintProxy.java

//代理:要用再建立
//不需要本人处理的工作交出去,需要建立一个代理
//代理能力有限的,遇到超出能力范围还是找本人来处理

public class PrintProxy implements Printable{
private String name;
private Printer real;//委托对象

public PrintProxy() {
// TODO Auto-generated constructor stub
}
public PrintProxy(String name) {
// TODO Auto-generated constructor stub
this.name=name;
}

@Override
public void setPrinterName(String name) {
// TODO Auto-generated method stub
if(real!=null){
real.setPrinterName(name);
}
this.name=name;
}

@Override
public String getPrinterName() {
// TODO Auto-generated method stub
return this.name;
}

@Override
public void print(String string) {
// TODO Auto-generated method stub
realize();
real.print(string);

}

private void realize(){ //产生本人对象
if(real==null){
real=new Printer(name);
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android aidl service 代理