編輯:關於Android編程
什麼是Service?Service是Android系統的四大組件之一,官方文檔是這樣描述Service的:
A Service is an application component that can perform long-running operations in the background and does not provide a user interface. Another application component can start a service and it will continue to run in the background even if the user switches to another application. Additionally, a component can bind to a service to interact with it and even perform interprocess communication (IPC). For example, a service might handle network transactions, play music, perform file I/O, or interact with a content provider, all from the background.
翻譯過來就是:服務是一個應用程序組件,可以在後台執行長時間運行的操作,不提供用戶界面。另一個應用程序組件可以啟動一個服務,它將繼續在後台運行,即使用戶切換到另一個應用程序。此外,一個組件可以綁定到一個服務的互動,甚至執行進程間通信(IPC)。例如,一個服務可以處理網絡交互、播放音樂、執行文件I/O、或與內容提供者進行交互,都來自後台。
本地服務(LocalService):本地服務依附在主線程上而不是一個獨立的進程,這樣節約了內存資源,LocalService在同一進程中,不需要IPC和AIDL。但是LocalService當主進程被kill掉之後,服務也會停止。例如用於音樂播放等
遠程服務(RemoteService):RemoteService是一個獨立的進程,當Activity所在的進程被kill掉之後,還會存在,可以為多個進程服務,不受進程影響。對應進程名格式為所在包名加上你指定的android:process字符串。一般是系統提供的服務,這種服務會常駐內存,占用一定的資源。RemoteService一般非常少見,並且一般都是系統服務。
前台服務:在Notification顯示正在運行圖標,當服務被kill掉的時候,Notification顯示的圖標也會消失,對用戶有一定的通知作用,例如音樂播放服務。
後台服務:默認的服務就是後台服務,不會再通知欄顯示正在運行圖標,當服務被終止的時候,用戶看不到效果。某些不需要運行或者終止提示的服務,例如天氣更新等
後台服務創建運行圖標並且調用startForeground方法,才會使後台服務變成前台服務。
startService 啟動的服務:主要用於啟動一個服務執行後台任務,不進行通信。停止服務使用stopService,啟動之後,關聯的Activity銷毀並不會影響Service。
bindService 啟動的服務:該方法啟動的服務要進行通信。停止服務使用unbindService。當綁定的Activity銷毀的時候,Service也會銷毀。
startService 同時也 bindService 啟動的服務:停止服務應同時使用stepService與unbindService
很多時候,我們都覺得用Thread比用Service簡單的多,但是為什麼還會使用Service呢?
Thread:Thread是比進程更小的單元,Thread 是程序執行的最小單元,它是分配CPU的基本單位。可以用 Thread 來執行一些異步的操作。一個進程裡面可以有多個Thread,Thread必須運行在進程裡面
Service:Service 是Android的一種機制,當它運行的時候如果是LocalService,那麼對應的 Service 是運行在主進程上的。如:onCreate,onStart 這些函數在被系統調用的時候都是在主進程上運行的。如果是RemoteService,那麼對應的 Service 則是運行在獨立進程上。
Thread是獨立於Activity運行的,當一個Activity啟動一個Thread的時候,只要Thread的run方法還沒有執行完或者不是在Activity中停止Thread,Thread會一直執行。這樣就會產生一個問題:當Thread還在運行,但是Activity被Finish掉了,其他的Activity不能持有當前Thread。
舉個例子:如果你的 Thread 需要不停地隔一段時間就要連接服務器做同步的話,該 Thread 需要在 Activity 沒有啟動的時候也在運行。這個時候當你啟動一個新Activity 就沒有辦法在該 Activity 裡面控制之前創建的Thread。因此你便需要創建並啟動一個 Service ,在 Service 裡面創建、運行並控制該 Thread,這樣便解決了該問題(因為任何 Activity 都可以控制同一 Service,而系統也只會創建一個對應 Service 的實例)。
可以把 Service 想象成一種消息服務,我們可以在任何有 Context 的地方調用 Context.startService、Context.stopService、Context.bindService,Context.unbindService,來控制它,你也可以在 Service 裡注冊 BroadcastReceiver,在其他地方通過發送 broadcast 來控制它,當然這些都是 Thread 做不到的。所以我們需要Service。
與Activity一樣,Service也有一系列的生命周期方法,我們可以實現它們來監測service狀態的變化,並且在適當的時候執行適當的工作。如下圖就是Service的生命周期圖
由上圖可以知道,Android使用Service有兩種方式,綁定啟動bindService和startService。還有一種就啟動後之後綁定Service。
Service生命周期方法詳解:<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;">
public class ExampleService extends Service
{
int mStartMode; // indicates how to behave if the service is killed
IBinder mBinder; // interface for clients that bind
boolean mAllowRebind; // indicates whether onRebind should be used
@Override
public void onCreate()
{
// The service is being created
}
@Override
public int onStartCommand(Intent intent, int flags, int startId)
{
// The service is starting, due to a call to startService()
return mStartMode;
}
@Override
public IBinder onBind(Intent intent)
{
// A client is binding to the service with bindService()
return mBinder;
}
@Override
public boolean onUnbind(Intent intent)
{
// All clients have unbound with unbindService()
return mAllowRebind;
}
@Override
public void onRebind(Intent intent)
{
// A client is binding to the service with bindService(),
// after onUnbind() has already been called
}
@Override
public void onDestroy()
{
// The service is no longer used and is being destroyed
}
}
onCreate()方法:當Service第一次被創建後立即回調該方法,該方法在Service整個生命周期中之調用一次
onStartCommand(Intent intent, int flags, int startId)方法:當客戶端調用startService(Intent)方法時會回調,可多次調用StartService方法, 但不會再創建新的Service實例,而是繼續復用前面產生的Service實例,但會繼續回調 onStartCommand()方法!
onBind(Intent intent)方法:此方法是Service都必須實現的方法,該方法會返回一個 IBinder對象,app通過該對象與Service組件進行通信!
onUnbind(Intent intent)方法:當該Service上綁定的所有客戶端都斷開時會回調該方法!
onDestroy()方法:當Service被銷毀的時候會回調該方法,該方法只會回調一次!
onRebind(Intent intent)方法:此方法在app調用onUnbind()方法之後需要重新綁定Service的時候調用。
第一次啟動會創建一個Service實例,依次調用onCreate()(onCreate方法只會調用一次)和onStartCommand()方法,此時Service 進入運行狀態,如果再次調用StartService啟動Service,將不會再創建新的Service對象, 系統會直接復用前面創建的Service對象,調用它的onStartCommand()方法!該Service將會一直在後台運行,而不管對應程序的Activity是否在運行,直到被調用stopService,或自身的stopSelf方法。當然如果系統資源不足,android系統也可能結束服務。
如果一個Service被某個Activity 調用 Context.bindService 方法綁定啟動,此後如果再次使用bindService綁定Service,系統不會創建新的Sevice實例,也不會再調用onBind()方法,只會直接把IBinder對象傳遞給其他後來增加的客戶端!不管調用 bindService 調用幾次,onCreate方法都只會調用一次,同時onStart方法始終不會被調用。當連接建立之後,Service將會一直運行,除非調用Context.unbindService 斷開連接或者之前調用bindService 的 Context 不存在了(如Activity被finish的時候),系統將會自動停止Service,對應onDestroy將被調用。
如果一個Service又被啟動又被綁定,則該Service將會一直在後台運行。並且不管如何調用,onCreate始終只會調用一次,對應startService調用多少次,Service的onStart便會調用多少次。調用unbindService將不會停止Service,而必須調用 stopService 或 Service的 stopSelf 來停止服務。
由於Service和Activity一樣,都是運行在主線程中的,如果直接在Service裡面執行耗時操作會ANR,所以谷歌提供了一個IntentService來處理耗時操作。簡單來說,IntentService是繼承於Service並處理異步請求的一個類,在IntentService內有一個工作線程來處理耗時操作,啟動IntentService的方式和啟動傳統Service一樣,同時,當任務執行完後,IntentService會自動停止,而不需要我們去手動控制。另外,可以啟動IntentService多次,而每一個耗時操作會以worker queue的方式在IntentService的onHandleIntent回調方法中執行,並且,每次只會執行一個worker Thread,執行完第一個再執行第二個,以此類推。而且,所有請求都在一個單線程中,不會阻塞應用程序的主線程(UI Thread),同一時間只處理一個請求。
IntentService在處理事務時,還是采用的Handler方式,創建一個名叫ServiceHandler的內部Handler,並把它直接綁定到HandlerThread所對應的子線程。 ServiceHandler把處理一個intent所對應的事務都封裝到叫做onHandleIntent的虛函數;因此我們直接實現虛函數onHandleIntent,再在裡面根據Intent的不同進行不同的事務處理就可以了。另外,IntentService默認實現了Onbind()方法,返回值為null。
使用IntentService需要兩個步驟:
1、寫構造函數
2、實現虛函數onHandleIntent,並在裡面根據Intent的不同進行不同的事務處理。
這樣做的好處是:處理異步請求的時候可以減少寫代碼的工作量,比較輕松地實現項目的需求
注意:IntentService的構造函數一定是參數為空的構造函數,然後再在其中調用super(“name”)這種形式的構造函數。因為Service的實例化是系統來完成的,而且系統是用參數為空的構造函數來實例化Service的
public class MyIntentService extends IntentService {
final static String TAG="robin";
public MyIntentService() {
super("com.lenovo.robin.test.MyIntentService");
Log.i(TAG,this+" is constructed");
}
@Override
protected void onHandleIntent(Intent arg0) {
Log.i(TAG,"begin onHandleIntent() in "+this);
try {
Thread.sleep(10*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.i(TAG,"end onHandleIntent() in "+this);
}
public void onDestroy()
{
super.onDestroy();
Log.i(TAG,this+" is destroy");
}
}
IntentService源碼
package android.app;
import android.content.Intent;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private String mName;
private boolean mRedelivery;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
public IntentService(String name) {
super();
mName = name;
}
public void setIntentRedelivery(boolean enabled) {
mRedelivery = enabled;
}
@Override
public void onCreate() {
// TODO: It would be nice to have an option to hold a partial wakelock
// during processing, and to have a static startService(Context, Intent)
// method that would launch the service & hand off a wakelock.
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public void onStart(Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
/**
* You should not override this method for your IntentService. Instead,
* override {@link #onHandleIntent}, which the system calls when the IntentService
* receives a start request.
* @see android.app.Service#onStartCommand
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
@Override
public void onDestroy() {
mServiceLooper.quit();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
protected abstract void onHandleIntent(Intent intent);
}
新建一個類繼承Service
public class FirstService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
在清單文件中注冊Service
在Activity中調用startService方法
btn_start_service = (Button) findViewById(R.id.btn_start_service);
btn_start_service.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(getApplicationContext(), FirstService.class);
startService(intent);
}
});
這樣,就完成了一個簡單的Service的創建和啟動,通過startService啟動的Service不會因為Activity的銷毀而銷毀,所以我們必須要調用stopService方法。
SecondService
public class SecondService extends Service {
private final String TAG = "SecondService";
private int count;
private boolean quit;
private MyBinder binder = new MyBinder();
public class MyBinder extends Binder {
public int getCount() {
return count;
}
}
@Override
public IBinder onBind(Intent intent) {
System.out.println("onBind方法被調用!");
return binder;
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public boolean onUnbind(Intent intent) {
System.out.println("onUnbind方法被調用");
return true;
}
@Override
public void onDestroy() {
super.onDestroy();
this.quit = true;
System.out.println("onDestroy方法被調用");
}
@Override
public void onRebind(Intent intent) {
System.out.println("onRebind方法被調用");
super.onRebind(intent);
}
}
清單文件注冊
MainActivity
public class MainActivity extends AppCompatActivity {
Button btn_start_service;
Button btn_bind_service;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_start_service = (Button) findViewById(R.id.btn_start_service);
btn_start_service.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(getApplicationContext(), FirstService.class);
startService(intent);
}
});
btn_bind_service = (Button) findViewById(R.id.btn_bind_service);
btn_bind_service.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(getApplicationContext(), SecondService.class);
getApplicationContext().bindService(intent, mServiceConnection, Service.BIND_AUTO_CREATE);
}
});
}
ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
System.out.println("------->>service connected");
}
@Override
public void onServiceDisconnected(ComponentName name) {
System.out.println("--------------->>service dis connected");
}
};
}
Service
public class ThridService extends IntentService {
public ThridService() {
super("");
System.out.println("intent Service");
}
@Override
protected void onHandleIntent(Intent intent) {
String flag = intent.getExtras().getString("flag");
if (flag.equals("t1")) {
System.out.println("startServiceT1");
} else if (flag.equals("t2")) {
System.out.println("startServiceT2");
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
清單文件
MainActivity
btn_intent_service = (Button) findViewById(R.id.btn_intent_service);
btn_intent_service.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent it1 = new Intent(getApplicationContext(), ThridService.class);
Bundle b1 = new Bundle();
b1.putString("flag", "t1");
it1.putExtras(b1);
Intent it2 = new Intent(getApplicationContext(), ThridService.class);
Bundle b2 = new Bundle();
b2.putString("flag", "t2");
it2.putExtras(b2);
getApplicationContext().startService(it1);
getApplicationContext().startService(it2);
}
});
這裡只是簡單的描述怎麼啟動一個Service,其他復雜的並沒有實現。
我們知道,AndroidOS是基於Linux內核的,但是AndroidOS沒有采用Linux內核提供的各種進程間通信機制(IPC),而是采用Binder機制。Android應用都是運行在獨立的進程中,這樣確保程序之間不會相互影響,但是,很多情況下,我們開發的程序中的Activity需要與系統Service進行通信,它們肯定不是在同一個進程,AndroidOS為我們提供了實現進程之間通信的方式,Binder就是其中的一種。
AndroidOS中的Binder機制,是由client、Server、ServiceManager和Binder驅動程序,其中Client、Server和Service Manager運行在用戶空間,Binder驅動程序運行內核空間。下圖是這四個組件的關系:
客戶端要想訪問Binder的遠程服務,就必須獲取遠程服務的Binder對象在binder驅動層對應的mRemote引用。當獲取到mRemote對象的引用後,就可以調用相應Binder對象的服務了。
一個Binder服務器端就是一個Binder類的對象。當創建一個Binder對象後,內部就會開啟一個線程,這個線程用於接收binder驅動發送的信息,收到消息後,會執行相關的服務代碼。
Service Manager是一個守護進程,用來管理Server,並向Client提供查詢Server接口的能力
當服務端成功創建一個Binder對象後,Binder驅動也會相應創建一個mRemote對象,該對象的類型也是Binder類。客戶就可以借助這個mRemote對象來訪問遠程服務。
使用Android studio實現
在main下新建aidl文件夾,在aidl文件夾中新建和aidl包名相同的包,新建aidl file。
aidl
// IPerson.aidl
package com.example.devin.helloservice.aidl;
// Declare any non-default types here with import statements
interface IPerson {
String findPersion(int num);
}
在build\generated\source\aidl下可以找到編譯生成的java代碼
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: H:\\AndroidStudioIDE\\workspace\\HelloService\\app\\src\\main\\aidl\\com\\example\\devin\\helloservice\\aidl\\IPerson.aidl
*/
package com.example.devin.helloservice.aidl;
// Declare any non-default types here with import statements
public interface IPerson extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.example.devin.helloservice.aidl.IPerson
{
private static final java.lang.String DESCRIPTOR = "com.example.devin.helloservice.aidl.IPerson";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.devin.helloservice.aidl.IPerson interface,
* generating a proxy if needed.
*/
public static com.example.devin.helloservice.aidl.IPerson asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.devin.helloservice.aidl.IPerson))) {
return ((com.example.devin.helloservice.aidl.IPerson)iin);
}
return new com.example.devin.helloservice.aidl.IPerson.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_findPersion:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _result = this.findPersion(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.example.devin.helloservice.aidl.IPerson
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
@Override public java.lang.String findPersion(int num) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(num);
mRemote.transact(Stub.TRANSACTION_findPersion, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_findPersion = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public java.lang.String findPersion(int num) throws android.os.RemoteException;
}
自定義一個Service類,繼承IPerson.Stub類 就是實現了IPerson接口和IBinder接口
package com.example.devin.helloservice.Service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import com.example.devin.helloservice.aidl.IPerson;
/**
* Created by Devin on 2016/6/15.
*/
public class AIDLService extends Service {
private String[] names = {"張三", "李四", "王五", "趙六", "孫七"};
private IBinder mIBinder = new PersionBinder();
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mIBinder;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.out.println("start service");
return super.onStartCommand(intent, flags, startId);
}
private class PersionBinder extends IPerson.Stub {
@Override
public String findPersion(int num) throws RemoteException {
if (num > 0 && num < 6) {
return names[num - 1];
}
return "222";
}
}
}
在清單文件中注冊
在客戶端中使用
package com.example.devin.helloservice;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.example.devin.helloservice.Service.AIDLService;
import com.example.devin.helloservice.aidl.IPerson;
/**
* Created by Devin on 2016/6/15.
*/
public class SecondActivity extends AppCompatActivity implements View.OnClickListener {
EditText et_num;
Button btn_find;
TextView tv_name;
IPerson mIPerson;
PersionConnect mPersionConnect = new PersionConnect();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_aidl);
et_num = (EditText) findViewById(R.id.et_num);
btn_find = (Button) findViewById(R.id.btn_find);
tv_name = (TextView) findViewById(R.id.tv_name);
Intent intent = new Intent(this, AIDLService.class);
startService(intent);
this.bindService(intent, mPersionConnect, Service.BIND_AUTO_CREATE);
btn_find.setOnClickListener(this);
}
@Override
public void onClick(View v) {
String etNum = et_num.getText().toString().trim();
int num = Integer.valueOf(etNum);
try {
tv_name.setText(mIPerson.findPersion(num));
} catch (RemoteException e) {
e.printStackTrace();
}
et_num.setText("");
}
private class PersionConnect implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mIPerson = IPerson.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
mIPerson = null;
}
}
}
這樣可以完成簡單的通信。
Android5.0以後谷歌大力推崇Material Design設計,有意統一之前Android style風格亂象的情況。上一篇博客我們學習了Androi
1、事務2、命令行操作數據庫
本文根據自己的實踐總結而來,參考前人博客之余,也自己總結和開發了一些功能,在這裡給自己備份也分享給大家。不同之處在於:自動打開並搜索藍牙、修改藍牙名字、完整接收藍牙傳輸
2.7導航欄及菜單2.7.1 ActionBarActionBar是Android3.0(API 11)開始增加的新特性,ActionBar出現在活動窗口的頂部,可以顯示