編輯:關於Android編程
上一篇文章Android 中的 Service 全面總結詳解【下】 介紹了Service的一些知識以及本地Service的使用,如果對Service還不太了解的建議先看下上篇文章;本文主要接著上一篇講下遠程服務的使用;
在說到遠程服務的時候,我們需要先了解一些預備的知識:
首先來了解一下AIDL機制:
AIDL的作用
由於每個應用程序都運行在自己的進程空間,並且可以從應用程序UI運行另一個服務進程,而且經常會在不同的進程間傳遞對象。在Android平台,一個進程通常不能訪問另一個進程的內存空間,所以要想對話,需要將對象分解成操作系統可以理解的基本單元,並且有序的通過進程邊界。
通過代碼來實現這個數據傳輸過程是冗長乏味的,Android提供了AIDL工具來處理這項工作。
AIDL (Android Interface Definition Language) 是一種IDL 語言,用於生成可以在Android設備上兩個進程之間進行進程間通信(interprocess communication, IPC)的代碼。如果在一個進程中(例如Activity)要調用另一個進程中(例如Service)對象的操作,就可以使用AIDL生成可序列化的參數。
AIDL IPC機制是面向接口的,像COM或Corba一樣,但是更加輕量級。它是使用代理類在客戶端和實現端傳遞數據。
在Android中, 每個應用程序都有自己的進程,當需要在不同的進程之間傳遞對象時,該如何實現呢? 顯然, Java中是不支持跨進程內存共享的。因此要傳遞對象, 需要把對象解析成操作系統能夠理解的數據格式, 以達到跨界對象訪問的目的。在JavaEE中,采用RMI通過序列化傳遞對象。在Android中, 則采用AIDL(Android Interface Definition Language:接口描述語言)方式實現。
AIDL是一種接口定義語言,用於約束兩個進程間的通訊規則,供編譯器生成代碼,實現Android設備上的兩個進程間通信(IPC)。AIDL的IPC機制和EJB所采用的CORBA很類似,進程之間的通信信息,首先會被轉換成AIDL協議消息,然後發送給對方,對方收到AIDL協議消息後再轉換成相應的對象。由於進程之間的通信信息需要雙向轉換,所以android采用代理類在背後實現了信息的雙向轉換,代理類由android編譯器生成,對開發人員來說是透明的。
選擇AIDL的使用場合
官方文檔特別提醒我們何時使用AIDL是必要的:只有你允許客戶端從不同的應用程序為了進程間的通信而去訪問你的service,以及想在你的service處理多線程。
了解了AIDL之後下面來看一下項目的結構:
我們這個遠程服務是想在將服務端和客戶端分別放到一個應用中,所以這裡要建立兩個Android項目一個是remoteService,一個是remoteClient
首先來看一下Service端的項目結構:
在這裡我們需要定義一個aidl文件,具體步驟很簡單的:
因為AIDL相當於是一個接口,所以它的定義和interface的定義很類似的,使用interface關鍵字,有一點不同的是,AIDL中不能有修飾符(public,protected,private),不然報錯,這個你們可以自己嘗試一下,然後將定義好的AIDL文件的後綴名.java改成.aidl,此時在gen文件夾下面就會多出一個與之對應的java文件,這個是編譯器干的事情。這個AIDL接口中定義的一般都是Client和Service交互的接口。
下面來看一下StudentQuery.aidl文件:
package cn.itcast.aidl;
//注意沒有任何的訪問權限修飾符
interface StudentQuery {
//通過number來訪問學生的name
String queryStudent(int number);
}
代碼結構和接口是大同小異的。
再來看一下服務端的代碼:
package cn.itcast.remote.service;
import cn.itcast.aidl.StudentQuery;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
/**
* 遠程服務端
*/
public class StudentQueryService extends Service {
//姓名名稱
private String[] names = {"張飛", "李靜", "趙薇"};
private IBinder binder = new StudentQueryBinder();
@Override
public IBinder onBind(Intent intent) {
return binder;
}
/**
* 服務中定義的訪問方法
* @param number
* @return
*/
private String query(int number){
if(number > 0 && number < 4){
return names[number - 1];
}
return null;
}
/**
* 定義Binder,這裡需要繼承StudentQuery.Stub
* StudentQuery是我們定義的AIDL
* @author weijiang204321
*
*/
private final class StudentQueryBinder extends StudentQuery.Stub{
public String queryStudent(int number) throws RemoteException {
return query(number);
}
}
}
這個服務端的代碼和我們之前的本地服務代碼差不多,不同的是我們定義的Binder類是繼承了StudentQuery.Stub類,其中StudentQuery是我們定義的AIDL文件,編譯器幫我們生成的StudentQuery.java(在gen文件夾中)這個類,下面來看一下這個類吧:
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: C:\\Users\\weijiang204321\\Desktop\\傳智播客Android視頻教程_源代碼\\remoteService\\src\\cn\\itcast\\aidl\\StudentQuery.aidl
*/
package cn.itcast.aidl;
//注意沒有任何的訪問權限修飾符
public interface StudentQuery extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements cn.itcast.aidl.StudentQuery
{
private static final java.lang.String DESCRIPTOR = "cn.itcast.aidl.StudentQuery";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an cn.itcast.aidl.StudentQuery interface,
* generating a proxy if needed.
*/
public static cn.itcast.aidl.StudentQuery asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof cn.itcast.aidl.StudentQuery))) {
return ((cn.itcast.aidl.StudentQuery)iin);
}
return new cn.itcast.aidl.StudentQuery.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_queryStudent:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _result = this.queryStudent(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements cn.itcast.aidl.StudentQuery
{
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;
}
//通過number來訪問學生的name
@Override public java.lang.String queryStudent(int number) 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(number);
mRemote.transact(Stub.TRANSACTION_queryStudent, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_queryStudent = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
//通過number來訪問學生的name
public java.lang.String queryStudent(int number) throws android.os.RemoteException;
}
感覺好復雜,其實我們沒必要看懂的,這個是Android內部實現的,我們在這裡可以了解一下,看一下那個Stub抽象類,他實現了Binder接口,所以我們需要繼承這個類就可以了,還有一個問題就是我們注意到,就是返回StudentQuery接口對象的問題:
public static cn.itcast.aidl.StudentQuery asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
//如果bindService綁定的是同一進程的service,返回的是服務端Stub對象本身,那麼在客戶端是直接操作Stub對象,並不進行進程通信了
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof cn.itcast.aidl.StudentQuery))) {
return ((cn.itcast.aidl.StudentQuery)iin);
}
//bindService綁定的不是同一進程的service,返回的是代理對象,obj==android.os.BinderProxy對象,被包裝成一個AIDLService.Stub.Proxy代理對象
//不過AIDLService.Stub.Proxy進程間通信通過android.os.BinderProxy實現
return new cn.itcast.aidl.StudentQuery.Stub.Proxy(obj);
}
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.os;
/**
* Base class for Binder interfaces. When defining a new interface,
* you must derive it from IInterface.
*/
public interface IInterface
{
/**
* Retrieve the Binder object associated with this interface.
* You must use this instead of a plain cast, so that proxy objects
* can return the correct result.
*/
public IBinder asBinder();
}
上面的是Interface接口,他只有一個方法asBinder()這個方法就是返回一個IBinder對象,而我們的AIDL接口需要實現這個接口的,所以說這個asBinder()方法的功能就是將AIDL接口轉化成IBinder對象,這個是內部實現的,在asInterface()方法中可以看到:
private static class Proxy implements com.demo.aidl.StudentAIDL
{
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 queryNameByNo(int no) 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(no);
mRemote.transact(Stub.TRANSACTION_queryNameByNo, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
這個是代理生成類,我們可以看到這個類中是返回的mRemote對象就是IBinder的一個引用,同時也返回了一個StudentQuery實現對象。
在StudentQuery.Stub中有一個asInterface方法,這個方法中我們可以看到,如果這個Service和Client是在同一個進程中,則在Client中的StudentConnection類中返回的是IBinder就是實際的對象,如果不是在同一個進程中的話,返回的是IBinder的代理對象。
其他的問題我們暫時不看了,也不去做深入的了解了,
再來看一下AndroidMainfest.xml文件中配置Service:
service android:name=".StudentQueryService">
這裡我們開啟服務的話,需要用到隱式意圖了,而不能直接用Service類了,因為是在不同的應用中,這樣服務端的代碼就差不多了,我們現在來看一下客戶端的代碼結構:
首先來看一下,客戶端肯定要有aidl文件,所以我們將服務端的aidl的文件拷到Client中(包括AIDL所在的包也要拷過來,刷新一下,在gen文件夾中出現了StudentQuery.java類),下面來看一下Client的代碼(MainActivity.java):
package cn.itcast.remoteservice.client;
import android.app.Activity;
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.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import cn.itcast.aidl.StudentQuery;
/**
* 客戶端的測試代碼
* @author weijiang204321
*
*/
public class MainActivity extends Activity {
//定義控件
private EditText numberText;
private TextView resultView;
private StudentQuery studentQuery;
//定義一個連接
private StudentConnection conn = new StudentConnection();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
numberText = (EditText) this.findViewById(R.id.number);
resultView = (TextView) this.findViewById(R.id.resultView);
//這裡開啟一個Service使用隱式意圖action的名稱必須和remoteService中AndroidMainfest.xml中定義的服務的action的name一樣
Intent service = new Intent("cn.itcast.student.query");
bindService(service, conn, BIND_AUTO_CREATE);
}
/**
* 給按鈕定義點擊事件
* @param v
*/
public void queryStudent(View v) {
String number = numberText.getText().toString();
int num = Integer.valueOf(number);
try {
resultView.setText(studentQuery.queryStudent(num));
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
protected void onDestroy() {
unbindService(conn);
super.onDestroy();
}
/**
* 自定義StudentConnection實現了ServiceConnection
* @author weijiang204321
*
*/
private final class StudentConnection implements ServiceConnection {
public void onServiceConnected(ComponentName name, IBinder service) {
//這裡就用到了Stub類中的asInterface方法,將IBinder對象轉化成接口
studentQuery = StudentQuery.Stub.asInterface(service);
}
public void onServiceDisconnected(ComponentName name) {
studentQuery = null;
}
}
}
Client端的代碼結構和我們之前的本地服務的代碼結構差不多,有幾處不同,第一處不同就是那個開啟服務的方式,這裡使用的是隱式的方式開啟服務的,因為是跨應用訪問的,還有一處不同的是返回的StudentQuery接口對象不同,本地服務的話是通過強制轉化的,而遠程服務這裡是用asInterface方法進行轉化的。
客戶端和服務端的代碼結構看完了,下面我們來運行一下,首先安裝remoteService包,然後運行remoteClient包,在文本框中輸入學號,查詢到名稱了,運行成功。在不同的應用中,一定是跨進程的,不行我們可以查看一下系統的進程:
這裡我們使用adb命令查看:
http://blog.csdn.net/jiangwei0910410003/article/details/17114491
在這篇blog中我說到了怎麼使用這條命令
我們可以看到有兩個進程Proc #9和Proc #12
到這裡我們就介紹了本地服務和遠程服務的使用了,下面我們再來看一下額外的知識,就是怎麼在一個應用中進行遠程服務的訪問,我們之前涉及到的是跨應用的訪問,這裡其實很簡單,只需要改動一處就可以:下面是我改過的一個項目,在同一個應用中進行遠程服務的訪問,代碼都是一樣的,這裡就不做介紹了;
這裡需要修改的地方就是要在AndroidManifest.xml中修改一下Service的定義屬性:<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;">
這裡我們添加了一個屬性就是android:process=”:remote”,這個屬性就是將該服務設置成遠程的,就是和Activity不在同一個進程中,具體的Service屬性說明請看我的另外的一篇blog:
http://blog.csdn.net/jiangwei0910410003/article/details/18794945
運行結果是一樣的,為了證明服務是遠程服務,我們在使用上面的命令打印一下當前系統中的進程信息:
系統中的Proc #9和Proc #10兩個進程,這裡就介紹了怎麼在同一個應用中跨進程訪問服務。
下面在來看一下AIDL:
我們上面說到的AIDL是說他怎麼用,而且很是簡單,我現在來具體看一下AIDL文件的定義:
Aidl默認支持的類型包話java基本類型(int、long、boolean等)和(String、List、Map、CharSequence),如果要傳遞自定義的類型該如何實現呢?
要傳遞自定義類型,首先要讓自定義類型支持parcelable協議,實現步驟如下:
1>自定義類型必須實現Parcelable接口,並且實現Parcelable接口的publicvoid writeToParcel(Parcel dest, int flags)方法 。
2>自定義類型中必須含有一個名稱為CREATOR的靜態成員,該成員對象要求實現Parcelable.Creator接口及其方法。
3> 創建一個aidl文件聲明你的自定義類型。
Parcelable接口的作用:實現了Parcelable接口的實例可以將自身的狀態信息(狀態信息通常指的是各成員變量的值)寫入Parcel,也可以從Parcel中恢復其狀態。Parcel用來完成數據的序列化傳遞。
下面來看一下例子:
1> 創建自定義類型,並實現Parcelable接口,使其支持parcelable協議。如:在cn.itcast.domain包下創建Person.java:
package cn.itcast.domain;
import android.os.Parcel;
import android.os.Parcelable;
public class Person implements Parcelable
privateInteger id;
private Stringname;
public Person(){}
publicPerson(Integer id, String name) {
this.id = id;
this.name = name;
}
public IntegergetId() {
return id;
}
public voidsetId(Integer id) {
this.id = id;
}
public StringgetName() {
return name;
}
public voidsetName(String name) {
this.name = name;
}
@Override
public intdescribeContents() {
return 0;
}
@Override
public voidwriteToParcel(Parcel dest, int flags) {//把javanbean中的數據寫到Parcel
dest.writeInt(this.id);
dest.writeString(this.name);
}
//添加一個靜態成員,名為CREATOR,該對象實現了Parcelable.Creator接口
publicstatic final Parcelable.Creator CREATOR = newParcelable.Creator(){
@Override
public PersoncreateFromParcel(Parcel source) {//從Parcel中讀取數據,返回person對象
return newPerson(source.readInt(), source.readString());
}
@Override
public Person[]newArray(int size) {
return newPerson[size];
}
};
}
2> 在自定義類型所在包下創建一個aidl文件對自定義類型進行聲明,文件的名稱與自定義類型同名。
3> 在接口aidl文件中使用自定義類型,需要使用import顯式導入,本例在cn.itcast.aidl包下創建IPersonService.aidl文件,內容如下:
package cn.itcast.aidl;
import cn.itcast.domain.Person;
interface IPersonService {
void save(inPerson person);
}
4> 在實現aidl文件生成的接口(本例是IPersonService),但並非直接實現接口,而是通過繼承接口的Stub來實現(Stub抽象類內部實現了aidl接口),並且實現接口方法的代碼。內容如下:
public class ServiceBinder extends IPersonService.Stub {
@Override
public voidsave(Person person) throws RemoteException {
Log.i("PersonService",person.getId()+"="+ person.getName());
}
}
5> 創建一個Service(服務),在服務的onBind(Intent intent)方法中返回實現了aidl接口的對象(本例是ServiceBinder)。內容如下:
public class PersonService extends Service {
privateServiceBinder serviceBinder = new ServiceBinder();
@Override
public IBinderonBind(Intent intent) {
return serviceBinder;
}
public class ServiceBinder extends IPersonService.Stub {
@Override
public void save(Person person) throwsRemoteException {
Log.i("PersonService",person.getId()+"="+ person.getName());
}
}
}
其他應用可以通過隱式意圖訪問服務,意圖的動作可以自定義,AndroidManifest.xml配置代碼如下:
6> 把應用中的aidl文件和所在package一起拷貝到客戶端應用的src目錄下,eclipse會自動在客戶端應用的gen目錄中為aidl文件同步生成IPersonService.java接口文件,接下來再把自定義類型文件和類型聲明aidl文件及所在package一起拷貝到客戶端應用的src目錄下。
最後就可以在客戶端應用中實現與遠程服務的通信,代碼如下:
public class ClientActivity extends Activity {
private IPersonService personService;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
this.bindService(newIntent("cn.itcast.process.aidl.PersonService"),this.serviceConnection, BIND_AUTO_CREATE);//綁定到服務
}
@Override
protected voidonDestroy() {
super.onDestroy();
this.unbindService(serviceConnection);//解除服務
}
privateServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
personService =IPersonService.Stub.asInterface(service);
try {
personService.save(new Person(56,"liming"));
} catch(RemoteException e) {
Log.e("ClientActivity",e.toString());
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
personService =null;
}
};
}
這樣就可以訪問我們自己定義的類型了。
下面來總結一下定義一個遠程服務的步驟:
假設A應用需要與B應用進行通信,調用B應用中的download(String path)方法,B應用以Service方式向A應用提供服務。需要下面四個步驟:
1> 在B應用中創建*.aidl文件,aidl文件的定義和接口的定義很相類,如:在cn.itcast.aidl包下創建IDownloadService.aidl文件,內容如下:
package cn.itcast.aidl;
interfaceIDownloadService {
void download(String path);
}
當完成aidl文件創建後,eclipse會自動在項目的gen目錄中同步生成IDownloadService.java接口文件。接口文件中生成一個Stub的抽象類,裡面包括aidl定義的方法,還包括一些其它輔助方法。值得關注的是asInterface(IBinder iBinder),它返回接口類型的實例,對於遠程服務調用,遠程服務返回給客戶端的對象為代理對象,客戶端在onServiceConnected(ComponentName name, IBinder service)方法引用該對象時不能直接強轉成接口類型的實例,而應該使用asInterface(IBinder iBinder)進行類型轉換。
編寫Aidl文件時,需要注意下面幾點:
1.接口名和aidl文件名相同。
2.接口和方法前不用加訪問權限修飾符public,private,protected等,也不能用final,static。
3.Aidl默認支持的類型包話java基本類型(int、long、boolean等)和(String、List、Map、CharSequence),使用這些類型時不需要import聲明。對於List和Map中的元素類型必須是Aidl支持的類型。如果使用自定義類型作為參數或返回值,自定義類型必須實現Parcelable接口。
4.自定義類型和AIDL生成的其它接口類型在aidl描述文件中,應該顯式import,即便在該類和定義的包在同一個包中。
5.在aidl文件中所有非Java基本類型參數必須加上in、out、inout標記,以指明參數是輸入參數、輸出參數還是輸入輸出參數。
6.Java原始類型默認的標記為in,不能為其它標記。
2> 在B應用中實現aidl文件生成的接口(本例是IDownloadService),但並非直接實現接口,而是通過繼承接口的Stub來實現(Stub抽象類內部實現了aidl接口),並且實現接口方法的代碼。內容如下:`
public class ServiceBinder extends IDownloadService.Stub {
@Override
public void download(String path) throws RemoteException {
Log.i("DownloadService",path);
}
}
3> 在B應用中創建一個Service(服務),在服務的onBind(Intent intent)方法中返回實現了aidl接口的對象(本例是ServiceBinder)。內容如下:
public class DownloadService extendsService {
private ServiceBinder serviceBinder = new ServiceBinder();
@Override
public IBinder onBind(Intent intent) {
return serviceBinder;
}
public class ServiceBinder extends IDownloadService.Stub {
@Override
public void download(String path) throws RemoteException {
Log.i("DownloadService",path);
}
}
}
其他應用可以通過隱式意圖訪問服務,意圖的動作可以自定義,AndroidManifest.xml配置代碼如下:
4> 把B應用中aidl文件所在package連同aidl文件一起拷貝到客戶端A應用,eclipse會自動在A應用的gen目錄中為aidl文件同步生成IDownloadService.java接口文件,接下來就可以在A應用中實現與B應用通信,代碼如下:
public class ClientActivity extendsActivity {
private IDownloadService downloadService;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
this.bindService(newIntent("cn.itcast.process.aidl.DownloadService"), this.serviceConnection,BIND_AUTO_CREATE);//綁定到服務
}
@Override
protected void onDestroy() {
super.onDestroy();
this.unbindService(serviceConnection);//解除服務
}
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service){
downloadService = IDownloadService.Stub.asInterface(service);
try {
downloadService.download("http://www.itcast.cn");
} catch (RemoteException e) {
Log.e("ClientActivity", e.toString());
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
downloadService = null;
}
};
}
最後看一下Service的生命周期以及和生命周期相關的方法:
與采用Context.startService()方法啟動服務有關的生命周期方法
onCreate()->onStart()->onDestroy()
onCreate()該方法在服務被創建時調用,該方法只會被調用一次,無論調用多少次startService()或bindService()方法,服務也只被創建一次。
onStart()只有采用Context.startService()方法啟動服務時才會回調該方法。該方法在服務開始運行時被調用。多次調用startService()方法盡管不會多次創建服務,但onStart() 方法會被多次調用。
onDestroy()該方法在服務被終止時調用。
l 與采用Context.bindService()方法啟動服務有關的生命周期方法
onCreate()->onBind()->onUnbind()->onDestroy()
onBind()只有采用Context.bindService()方法啟動服務時才會回調該方法。該方法在調用者與服務綁定時被調用,當調用者與服務已經綁定,多次調用Context.bindService()方法並不會導致該方法被多次調用。
onUnbind()只有采用Context.bindService()方法啟動服務時才會回調該方法。該方法在調用者與服務解除綁定時被調用。
如果先采用startService()方法啟動服務,然後調用bindService()方法綁定到服務,再調用unbindService()方法解除綁定,最後調用bindService()方法再次綁定到服務,觸發的生命周期方法如下:
onCreate()->onStart()->onBind()->onUnbind()[重載後的方法需返回true]->onRebind()
總結:
Android中的服務和windows中的服務是類似的東西,服務一般沒有用戶操作界面,它運行於系統中不容易被用戶發覺,可以使用它開發如監控之類的程序。服務的開發比較簡單,如下:
第一步:繼承Service類
public class SMSService extends Service { }
第二步:在AndroidManifest.xml文件中的節點裡對服務進行配置:
服務不能自己運行,需要通過調用Context.startService()或Context.bindService()方法啟動服務。這兩個方法都可以啟動Service,但是它們的使用場合有所不同。使用startService()方法啟用服務,調用者與服務之間沒有關連,即使調用者退出了,服務仍然運行。使用bindService()方法啟用服務,調用者與服務綁定在了一起,調用者一旦退出,服務也就終止,大有“不求同時生,必須同時死”的特點。
采用Context.startService()方法啟動服務,在服務未被創建時,系統會先調用服務的onCreate()方法,接著調用onStart()方法。如果調用startService()方法前服務已經被創建,多次調用startService()方法並不會導致多次創建服務,但會導致多次調用onStart()方法。采用startService()方法啟動的服務,只能調用Context.stopService()方法結束服務,服務結束時會調用onDestroy()方法。
下面兩張圖是Service的生命周期:
圖一
圖二
有段時間沒寫博客了,可能寫博客的時間都用去把妹了吧。說到把妹,偶爾也會在無意間了解一些把妹的技巧,比如雲擒故縱啊什麼的。可能這些技巧前期會有一定的作用,可能會讓妹子對你產
通過這片文章可以回答以下兩個問題1、為啥EventBus的事件處理方法必須申明為Public?但是我用protected也是OK的2、粘性事件的原理,為啥發送多次Even
動態加載資源原理通常我們調用getResources()方法獲取資源文件public Resources getResources() { return mReso
先給大家展示效果圖:package com.example.walkerlogin1; import android.app.Activity; import andro