一、进程概念介绍
安卓的四大组件,都是运行在主线程的,不能运行耗时操作,否则ANR
1-1.进程优先级
1. Foreground process 【前台进程】
优先级最高,相当于Activity执行了onResume(),用户正在交互
2. Visible process 【可视进程】
一直影响用户看得见,相当于Activity执行了onPause()
3. Service process 【服务进程】
服务进程在后台运行,没有界面,通过startService()开启了一个服务
4. Background process 【后台进程】
用户不可见,相当于Activity执行了onStop(),但是Activity并没有销毁
5. Empty process 【空进程】
不会维持任何组件运行
二、start方式开启服务的特点
2-1.新建一个服务的过程
- 新建一个类,继承service
public class MyService extends Service {
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
// Called by the system when the service is first created. Do not call this method directly.
// 当服务第一次创建的时候调用
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
}
// 当服务启动的时候调用
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// TODO Auto-generated method stub
return super.onStartCommand(intent, flags, startId);
}
//当服务销毁时调用
@Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
}
}
- 修改清单文件如下
<application
....
<service android:name="com.elnui.day08_servicedemo.MyService">
</service>
</application>
2-2.开启和关闭服务
public void click1(View v){
Intent intent = new Intent(this,MyService.class);
startService(intent);
}
public void click2(View v){
Intent intent = new Intent(this,MyService.class);
stopService(intent);
}
2-3.特点
- 第一次开启服务,服务执行onCreat()和onStartCommand()
- 第二次开启服务,服务只执行onStartCommand()
- ==当服务开启后,就会在后台长期运,直到用户手工停止==
三、电话窃听器案例
3-1.新的API
1. SmsManage
2. TelephonyManager
- 拿到实例不能直接new,要使用 Context.getSystemService(Context.TELEPHONY_SERVICE)拿到
3-2.实现步骤
- 定义一个服务,开启服务,记得在清单文件添加service节点
- 在服务里onCreat()里获取TelephonyManager的实例
TelephonyManager TELManager = (TelephonyManager)getSystemService(TELEPHONY_SERVICE);
- 注册电话的监听
TELManager.listen(new MyPhoneListener(), PhoneStateListener.LISTEN_CALL_STATE);
- 定义一个类监听电话的状态
private class MyPhoneListener extends PhoneStateListener{
// 当电话设备的状态发生改变时调用
@Override
public void onCallStateChanged(int state, String incomingNumber) {
// TODO Auto-generated method stub
// 判断电话的状态
switch(state){
case TelephonyManager.CALL_STATE_IDLE: // 空闲状态
break;
case TelephonyManager.CALL_STATE_OFFHOOK: // 接听状态
break;
case TelephonyManager.CALL_STATE_RINGING: // 响铃状态
System.out.println("___准备录音机");
break;
}
super.onCallStateChanged(state, incomingNumber);
}
}
3-3.实现录音,MediaRecorder
//准备录音机
mRecorder = new MediaRecorder();
// 设置音频的来源
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); // 此处模拟器只能一MIC,单向的
// 设置输出的格式
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
// 设置存放的路径
mRecorder.setOutputFile("/mnt/sdcard/luyin.3gp");
// 设置音频的编码方式
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
try {
mRecorder.prepare();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 开启录音
mRecorder.start();
// 如果在空闲状态
if(mRecorder != null){
mRecorder.stop();
mRecorder.reset();
mRecorder.release();
}
3-4.把开启服务的代码放到广播中
public class BootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Intent intent1 = new Intent(context,LisenerService.class);
context.startService(intent);
}
}
四、使用服务注册特殊的广播接收者
操作特别频繁的广播事件,比如屏幕的解锁和加锁
4-1.实现步骤
[service]作用:管理广播接收者
public class BroadcastReceiverService extends Service {
private ScreenReceiver receiver;
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
@Override
public void onCreate() {
// 创建ScreenReceiver实例
receiver = new ScreenReceiver();
// 获取IntentFilter实例,目的添加Action
IntentFilter filter = new IntentFilter();
filter.addAction("android.intent.action.SCREEN_OFF");
filter.addAction("android.intent.action.SCREEN_ON");
// 动态注册Action
registerReceiver(receiver, filter);
super.onCreate();
}
@Override
public void onDestroy() {
// 当服务关闭时,取消注册广播接收者
unregisterReceiver(receiver);
super.onDestroy();
}
}
[开启服务]
Intent intent = new Intent(this,BroadcastReceiverService.class);
startService(intent);
[清单配置服务]
<service android:name="com.elnui.day08_flubroadcastreceiver.BroadcastReceiverService" >
</service>
五、bingService开启服务特点
5-1.bindService()
boolean android.content.ContextWrapper.bindService(Intent service, ServiceConnection conn, int flags)
5-2.代码实现
public void click3(View v){
Intent intent = new Intent(this,MyService.class);
conn = new MyConn();
bindService(intent, conn, BIND_AUTO_CREATE);
}
// 定义一个类监视服务的状态
/*
* 当onBind返回null的时候这个方法不会被调用
* */
private class MyConn implements ServiceConnection{
// 当服务连接成功时调用
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
System.out.println("__bindService's connected success!!");
}
// 失去连接时调用
@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
System.out.println("__bindService's disconnected success!!");
}
}
// 点击按钮解绑服务
public void click4(View v){
unbindService(conn);
}
@Override
protected void onDestroy() {
// 当Activity销毁的时候要解绑服务
unbindService(conn);
super.onDestroy();
}
5-3.总结
- 第一次绑定会执行服务的onBind()和onCreat()
- 当服务的onBind()返回null的时候ServiceConnection的onServiceConnected()不执行
- 第二次点击绑定服务按钮,服务没有反应
- ==同生同死(调用者和被调用者之间,即Activity和Service之间)==
- 服务不可以多次解绑,多次解绑会报异常
- 服务不能在设置页面找到,相当是隐形的服务
7.== 服务一旦开启,就会在后台一直运行,知道用户杀死==
六、为什么要引入bingService
目的是为了调用服务里面的方法
七、通过bingService方式调用服务方法里面的过程
- 在服务的内容定义一个方法,让Activity去调用
public void banZheng(int money){
if(money > 1000){
System.out.println("__开始办证");
}else{
System.out.println("这点钱还想办事?");
}
}
- 在服务的内容定义一个中间人对象IBinder(继承她的子类Binder)
public class MyBinder extends Binder{
public void callBanzheng(int m){
// 调用办证的方法
banZheng(m);
}
}
- 把定义的中间人对象在onBind()里返回
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return new MyBinder();
}
- 在MainActivity的onCreat()里调用bindService的目的是为了获取我们定义的中间人对象
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this,WhyBindservice.class);
conn = new MyCoon();
bindService(intent, conn, BIND_AUTO_CREATE);
}
- 获取中间人对象
private class MyCoon implements ServiceConnection{
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
binder = (MyBinder)service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
}
}
- 拿到了中间人对象就可以间接的调用服务里面的方法
public void click1(View v){
binder.callBanzheng(2000);
}
- 销毁Activity的时候记得销毁服务,避免漏气
八、通过接口方式调用服务里面的方法
好处:可以隐藏代码内部的细节,让程序员只需暴露自己只想暴露的方法
8-1.方法
- 定义一个接口,把想暴露的方法放在放在该接口内
- 定义的中间人对象实现我们定义的接口
- 在获取对象的时候
binder = (Iservice)service;
九、百度音乐盒框架
[主要代码]
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/* 混合方式开启服务 */
// 先调用startService()启动服务
Intent intent = new Intent(this, MusicService.class);
startService(intent);
coon = new MyCoon();
// 调用bindService,目的拿到IBinder对象
bindService(intent,coon,BIND_AUTO_CREATE);
}
十、aidl介绍
10-1.本地服务
运行在自己应用里面的服务
10-2.远程服务
运行在其他应用里面的服务
10-3.aidl(Android Interface definition language)
专门是用来进程间的通信
10-4.使用aidl步骤
- 把Iservice.java文件的后缀改为.aidl
- aidl这个语言不认识public,删除public
- 自动生成一个Iservice.java文件,系统自动帮我们生成了一个类Stub
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: F:\\Projects\\stu_Android\\day08_remoteService\\src\\com\\elnui\\day08_remoteservice\\Iservice.aidl
*/
package com.elnui.day08_remoteservice;
public interface Iservice extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.elnui.day08_remoteservice.Iservice
{
private static final java.lang.String DESCRIPTOR = "com.elnui.day08_remoteservice.Iservice";
/** Construct the stub at attach it to the interface. */
//..................................................
- 我们自己定义的IBinder对象直接继承Stub
- 保证2应用的aidl文件时同一个,保证aidl文件所在的包名相同
- 通过
iservice = Stub.asInterface(service);
拿到IBinder对象
7. 两个应用的清单配置
[local] 不用配置
[remote]
<service android:name="com.elnui.day08_remoteservice.RemoteService">
<intent-filter >
<action android:name="com.elnui.callRemoteService"/>
</intent-filter>
</service>
其中[remote]清单文件里面的 ,
是由[local]绑定IBinder是指定的,如下
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// bindService
Intent intent = new Intent();
intent.setAction("com.elnui.callRemoteService");
conn = new MyConn();
// 连接,为了回去IBinder对象
bindService(intent, conn, BIND_AUTO_CREATE);
}
十一、aidl的应用场景
补充
补充1:混合方式开启服务
需求:既想服务在后台长期运行,又想调用服务里面的方法
- 先调用startService()开启服务,能够保证服务在后台长期运行
- 再调用bindService()去获取中间人对象
- 然后调用unbindService()解绑服务
- 最后调用stopService()停止服务