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);
}
}
}