什麼是service?
service是被“後臺服務”,它的運行不依賴ui界面,我個人還喜歡把它看着一種消息服務,因爲你可以在任何有 Context 的地方調用 Context.startService、Context.stopService、Context.bindService,Context.unbindService,來控制它,你也可以在 Service 裏註冊 BroadcastReceiver。
service分類
a、按運行地點分類
本地服務(Local) 該服務依附在主進程上
遠程服務(Remote)該服務是獨立進程的(少見的,並且一般都是系統服務)
b、按運行類型分類
前臺服務 會在通知一欄顯示 ONGOING 的 Notificatio
後臺服務 默認的服務即爲後臺服務,即不會在通知一欄顯示 ONGOING 的 Notification
c、按使用方式分類(重要¥¥¥¥¥¥¥)
startService 啓動的服務 主要用於啓動一個服務執行後臺任務,不進行通信。停止服務使用stopService
bindService 啓動的服務 該方法啓動的服務要進行通信(即外部可以調用service內部的方法)。停止服務使用unbindService
startService 同時也 bindService 啓動的服務 停止服務應同時使用stepService與unbindService(這是混合服務)
三種開啓服務的生命週期的區別
startService方式開啓的服務,服務會長期在後臺運行,直到用戶手動停止服務或者調用stopService方法,其中onCreate方法只會執行一次,onstart()可能執行多次(對應調用startService的次數),並且系統只會創建Service的一個實例(因此你應該知道只需要一次stopService調用
bindService方式開啓服務會執行服務的onCreate方法 和 onBind()方法 onstart方法不會執行當連接建立後,服務會一直存在,直到調用它的Context對象銷燬(比如activity銷燬)或者調用了Context.unbindService()方法,系統將會自動停止Service,對應onDestroy將被調用。
如果一個Service是混合方式啓動,則該Service將會一直在後臺運行。並且不管如何調用,onCreate始終只會調用一次,對應startService調用多少次,Service的onStart便會調用多少次。調用unbindService將不會停止Service,而必須調用 stopService 或 Service的 stopSelf 來停止服務。這樣可以實現長期後臺運行和實現通信的
Thread線程和Service的區別
Thread:Thread 是程序執行的最小單元,它是分配CPU的基本單位。可以用 Thread 來執行一些異步的操作
Service:Service 是android的一種機制,當它運行的時候如果是Local Service,那麼對應的 Service 是運行在主進程的 main 線程上的。如:onCreate,onStart 這些函數在被系統調用的時候都是在主進程的 main 線程上運行的。如果是Remote Service,那麼對應的 Service 則是運行在獨立進程的 main 線程上。因此請不要把 Service 理解成線程。
startService方式開啓服務(電話竊聽器)
重要:四大組件都需要配置,因此在創建服務後需要在xml文件中配置
AndroidManifest.xml文件
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.alleged.phoneListener"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="21" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" >
</uses-permission>
<!-- 對外部文件的寫入和刪除權限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" >
</uses-permission>
<!-- 音頻刻錄權限 -->
<uses-permission android:name="android.permission.RECORD_AUDIO" >
</uses-permission>
<!-- 接收手機完全開啓狀態權限 -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" >
</uses-permission>
<!-- 讀取電話狀態權限 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" >
</uses-permission>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".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>
<service android:name=".phonelistener"></service>
<receiver android:name=".BootBrodcastReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"></action>
</intent-filter>
</receiver>
</application>
</manifest>
BootBrodcastReceiver設置開機啓動服務的廣播接受者
package com.alleged.phoneListener;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class BootBrodcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 接受到相應的廣播後,調用onReceive()方法
//這裏調用要開啓的服務
//創建出相應的意圖的對象 第一個參數是上下文 第二參數你要開啓的類
System.out.println("廣播開啓");
Intent listenerIntent = new Intent(context,phonelistener.class);
context.startService(listenerIntent);
}
}
service phoneListenerService.java
package com.alleged.phoneListener;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.media.MediaRecorder;
import android.os.Environment;
import android.os.IBinder;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;
public class phonelistener extends Service {
private String TAG = "PhoneListenerService";// 這裏設置一個Log標誌,方便於調試
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
/* 複寫onCreate()方法,當這個服務被創建的時候就實現監聽 */
@Override
public void onStart(Intent intent, int startId) {
};
@Override
public void onCreate() {
System.out.println("oncreate is working");
/* 取得本機的電話服務 //創建電話管理者 */
TelephonyManager telmanager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
// 註冊一個電話監聽器 通過管理類的listen方法監聽 這裏要實現一個phoneStateListener對象,監聽狀態改變
// 實現PhoneStateListener對象
PhoneStateListener calllistener = new PhoneStateListener() {
private String callNum;// 定義一個監聽電話號碼
private boolean isRecord = false;// 定義一個當前是否正在複製的標誌
private MediaRecorder recorder;// 媒體複製類
@Override
public void onCallStateChanged(int state, String incomingNumber) {
// 判斷一下電話屬於什麼狀態
switch (state) {
// 無任何狀態
case TelephonyManager.CALL_STATE_IDLE:
// 把來電號碼設置爲空
callNum = null;
// 判斷錄音是否爲空和錄音開關的狀態
if (recorder != null && isRecord) {
Log.i(TAG, "錄音完成");// 設定一個錄音完成的標誌,方便調試
recorder.stop();// 錄音完成,
recorder.reset();// 通過reset()方法回到Initialized狀態
recorder.release();// 釋放所有和MediaRecorder對象綁定的資源
isRecord = false;// 錄音完成,改變狀態標誌
}
break;
// 代表響鈴狀態
case TelephonyManager.CALL_STATE_RINGING:
// 獲取來電的電話號碼
this.callNum = incomingNumber;
break;
// 代表接起電話
case TelephonyManager.CALL_STATE_OFFHOOK:
// 判斷來電號碼,是否是要監聽的電話號碼
if (callNum.equals("1234567")) {
Log.i(TAG, "開始錄音");
// 是要監聽的電話,就開始錄音
recorder = new MediaRecorder();
// 設置資源
// 定義聲音來自於麥克風
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
// 設置輸出格式爲3gp格式
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
// 定我編碼
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
// 此處定義一個format類,方便對錄音文件進行命名
SimpleDateFormat format = new SimpleDateFormat("yyMMddHHmmss");
String fileName = this.callNum + "_" + format.format(new Date());
System.out.println(
"/mnt/sdcard/luyin.3gp");
recorder.setOutputFile("/mnt/sdcard/luyin.3gp");
// 準備資源
try {
recorder.prepare();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
recorder.start(); // 開始刻錄
isRecord = true;// 標誌錄音成功
System.out.println("開始錄製");
} else {
System.out.println("不是想要錄音的電話號碼");
Log.e(TAG, "不是想要錄音的電話號碼");
}
}
super.onCallStateChanged(state, incomingNumber);
}
};
// 正式監聽
telmanager.listen(calllistener, PhoneStateListener.LISTEN_CALL_STATE);
super.onCreate();
}
}
(android的4.0後這個種服務安裝啓動的時候需要一個界面,爲了安全)
package com.alleged.phoneListener;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
public class MainActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
System.out.println("ma de zhi zhang");
//監聽器的實現步驟
//第一步:註冊一個廣播接受者
//設置爲開機啓動的廣播
//開啓服務
//在onstart 定一個電話監聽類
//在監聽類中設置管理者
//註冊一個監聽類
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
混合開啓服務的小例子
mainActivity.java
package com.alleged.hybrid;
import android.support.v7.app.ActionBarActivity;
import com.alleged.musicService.Ibinder;
import com.alleged.musicService.music_Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
public class MainActivity extends ActionBarActivity {
private Context context;
private Ibinder binder;
private MyConn conn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context = this;
//在頁面啓動就開啓服務
Intent intent = new Intent(context, music_Service.class);
context.startService(intent);
}
public void startService(View v) {
//開啓服務
}
public void bindService(View v) {
System.out.println("bindservice");
Intent intent = new Intent(context, music_Service.class);
conn = new MyConn();
if(conn!=null){
bindService(intent, conn, BIND_AUTO_CREATE);
}
}
public void unbindService(View v) {
conn = new MyConn();
context.unbindService(conn);
}
public void stopService(View v) {
Intent intent = new Intent(context, music_Service.class);
context.stopService(intent);
}
public void play_music(View v){
binder.playMusic();
}
public void stop_music(View v){
binder.sleepmusic();
}
public void nextmusic(View v){
binder.nextOne();
}
public void prevmusic(View v){
binder.prev();
}
/* public class myconn implements ServiceConnection{
//連接成功
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
System.out.println("連接成功 ");
binder = (Ibinder) service;
System.out.println("連接成功 ");
}
//連接失敗
@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
}
}*/
private class MyConn implements ServiceConnection {
// 當連接成功時候調用
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 獲取我們定義的中間人對象
binder = (Ibinder) service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
}
music_service.java
package com.alleged.musicService;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.widget.Toast;
public class music_Service extends Service {
//IBinder類返回,提供接口調用方法內部的方法
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return new myIBinder();
}
public void play_Music(){
System.out.println("音樂播放");
}
public void sleep_music(){
System.out.println("音樂暫停");
}
public void next_One(){
System.out.println("下一首");
}
public void prev_one(){
System.out.println("上一首");
}
public class myIBinder extends Binder implements Ibinder{
@Override
public void playMusic() {
play_Music();
}
@Override
public void sleepmusic() {
sleep_music();
}
@Override
public void nextOne() {
next_One();
}
@Override
public void prev() {
prev_one();
}
}
}
佈局文件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.alleged.hybrid.MainActivity" >
<Button
android:id="@+id/button1"
android:text="startService"
android:onClick="startService"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true" />
<Button
android:id="@+id/button2"
android:onClick="bindService"
android:text="bindService"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/button3"
android:layout_alignParentLeft="true"
android:layout_marginBottom="58dp" />
<Button
android:id="@+id/button3"
android:onClick="unbindService"
android:text="unbindService"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/button2"
android:layout_below="@+id/button1"
android:layout_marginTop="151dp" />
<Button
android:id="@+id/buttton4"
android:onClick="stopService"
android:text="stopService"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/button2"
android:layout_alignParentLeft="true"
android:layout_marginBottom="30dp" />
<Button
android:id="@+id/stop_music"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/next"
android:layout_below="@+id/next"
android:onClick="stop_music"
android:text="暫停" />
<Button
android:id="@+id/prev"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignRight="@+id/buttton4"
android:layout_below="@+id/button2"
android:layout_marginTop="42dp"
android:onClick="prevmusic"
android:text="上一首" />
<Button
android:id="@+id/next"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/prev"
android:layout_alignBottom="@+id/prev"
android:layout_marginLeft="31dp"
android:layout_toRightOf="@+id/prev"
android:onClick="nextmusic"
android:text="下一首" />
<Button
android:id="@+id/play_music"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/stop_music"
android:layout_alignBottom="@+id/stop_music"
android:layout_toLeftOf="@+id/next"
android:onClick="play_music"
android:text="播放" />
</RelativeLayout>
配置文件
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.alleged.hybrid"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="21" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".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>
<service android:name="com.alleged.musicService.music_Service">
</service>
</application>
</manifest>