Android中的Service初探

Android中的Service初探

一、簡介

1. 官方API的個人翻譯

  • 服務是Android的四大組件之一,所以服務需要在AndroidManifest.xml中用<service>聲明。服務有兩種功能:
    1. 在不影響用戶的情況下,執行長時間的操作;
    2. 提供功能給其他應用使用。
  • 服務可以用Context.startService()Context.bindService()啓動
  • 服務跟其他三大應用組件一樣,是運行在宿主進程的主線程的。這意味着,如果你用服務去做一些CPU密集型(比如播放MP3)或者引起阻塞(比如訪問網絡)的操作,將會產生大量子線程去完成這些任務。IntentService類是Service的標準實現類,它擁有單獨的線程去完成任務。
  • 注意:
    1. 一個Service不是一個單獨的進程,除非特別指定,否則是跟應用的進程是同一的
    2. 一個Service不是一個線程,它不是在主線程上處理事務的工具(爲了避免ANR錯誤)

2. 個人的理解

  • Service :對外提供服務或者執行長時間的操作
  • IBinder:可遠程調用的對象,Activity通過它調用Service的方法,一般使用它的實現類Binder。它的設計符合動態代理模式
  • Intent:意圖,表明將要Service執行的操作
  • ServiceConnection:模擬進程間或者進程內部服務通信的狀態
  • Activity:通過Intent啓動服務和停止服務或綁定和解綁服務
  • 全局服務:通過startService啓動的服務爲全局服務,生命週期不受客戶端影響
  • 綁定服務:通過bindService綁定的服務,生命週期受客戶端影響
  • 本地服務:同一個應用的服務
  • 遠程服務:不同應用的服務

綜合理解:Activity通過Intent啓動/綁定Service,Service再通過回調ServiceConnection中的方法告知Activity與之的通信狀態,並返回一個IBinder對象供Activity使用服務。一個應用就一個進程,一個主線程。Activity和Service都是運行在主線程上的,所以都不能直接運行耗時或耗CPU的操作。

二、創建Service

  1. 寫一個類繼承自Service
  2. 重寫onBind(Intent intent)方法
  3. 在AndroidManifest.xml文件中註冊
//MyService.java
public class MyService extends Service {
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}
<!--AndroidManifest.xml-->
<service android:name="com.example.servicestudy.MyService">
</service>

三、Service的生命週期方法及調用時機

1. 第一種啓動方式

1.1 代碼展示

Intent intent = new Intent(this, MyService.class);
//啓動服務
this.startService(intent);
//停止服務  
this.stopService(intent);

1.2 生命週期方法

**********啓動服務***********
構造器//第一次啓動時調用,如果先前的沒有停止不會新建服務
onCreate()//創建時調用
onStartCommand()//使用startService()啓動服務時調用
**********停止服務***********
onDestroy()//停止服務時調用

2. 第二種啓動方式

2.1 代碼展示

Intent intent = new Intent(this,MyService.class);
/**
* 功能:綁定服務
* 參數intent:服務的意圖
* 參數serviceConnection:服務的連接對象,告知使用服務者通信狀態
* 參數flag:標識,Context.BIND_AUTO_CREATE表示若無則創建
*/
bindService(intent,serviceConnection,flag);
//通過serviceConnection中的IBinder調用服務中的方法
/**
* 功能:解綁服務
*/
unBindService(ServiceConnection);

2.3 ServiceConnection

/**
 * 模擬通信狀態的接口
 */
public interface ServiceConnection {
    /**
     * 功能:服務連接上時由主線程調用,伴隨着一個服務代理對象IBinder
     * 參數name: 連接的服務名稱
     * 參數service :可供調用的服務代理對象
     */
    public void onServiceConnected(ComponentName name, IBinder service);

    /**
     * 功能:當失去連接時調用,比如說服務進程阻塞或被殺死。
     *  注意:ServiceConnection本身並沒有被移除,當服務下一次啓動時會繼續調用onServiceConnected 
     *
     * 參數name: 失去連接的服務名稱
     */
    public void onServiceDisconnected(ComponentName name);
}

2.4 生命週期方法

**********綁定服務***********
構造器//第一次啓動時調用,如果先前的沒有停止不會新建服務
onCreate()//創建時調用
onBind()//綁定服務時調用
**********解綁服務***********
onUnbind()//解綁服務時調用
onDestroy()//銷燬時調用

四、使用Service

1. 本地服務通信

activity_main.xml

//此處省略很多東西,只關注重點
<Button
    android:onClick="startService"
    android:text="綁定服務" />
<Button
    android:onClick="method"
    android:text="調用服務方法" />
<Button
    android:onClick="stopService"
    android:text="解綁服務" />

MyService.java

//此處省略import語句
public class MyService extends Service {
    public class MyBinder extends Binder{
        public void callDosth(){
            dosth();
        }
    }
    @Override
    public IBinder onBind(Intent intent) {
        System.out.println("onBind");
        return new MyBinder();
    }
    public MyService(){
        System.out.println("MyService");
    }
    @Override
    public boolean onUnbind(Intent intent) {
        System.out.println("onUnbind");
        return super.onUnbind(intent);
    }
    private void dosth(){
        System.out.println("do something...");
    }
    @Override
    public void onCreate() {
        System.out.println("onCreate");
        super.onCreate();
    }
    @Override
    public void onDestroy() {
        System.out.println("onDestroy");
        super.onDestroy();
    }
    @Override
    public boolean stopService(Intent name) {
        System.out.println("stopService");
        return super.stopService(name);
    }
}

AndroidMenifest.xml

<application
    ......
    <service android:name="com.example.servicestudy.MyService" />
</application>

MainActivity.java

//此處省略import語句
public class MainActivity extends Activity {
    private MyBinder myBinder;
    private MyServiceConnection conn;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    private class MyServiceConnection implements ServiceConnection {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            System.out.println("name:"+name);
            System.out.println("connected");
            myBinder = (MyBinder) service;
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
            System.out.println("disconnected");
            myBinder=null;
        }
    }
    public void method(View v) {
        if (conn != null && myBinder != null)
            myBinder.callDosth();
    }
    public void startService(View v) {
        if (conn == null) {
            conn = new MyServiceConnection();
            Intent service = new Intent();
            service.setClass(MainActivity.this, MyService.class);
            bindService(service, conn, Context.BIND_AUTO_CREATE);
        }
    }
    public void stopService(View v) {
        if (conn != null) {
            unbindService(conn);
            conn = null;
        }
    }
}

2. 遠程服務通信

2.1 AIDL簡介

AIDL(Android Interface Definition Language)安卓接口定義語言。在Android中,進程內的通信由函數調用、Intent完成,那麼進程間的通信(IPC,interprocess communication)用什麼完成呢?Binder機制,AIDL是Binder機制的一種實現。通過定義AIDL接口文件來定義一個IPC接口,Server端實現IPC接口,Client端調用IPC接口的本地代理。

2.2 AIDL實現IPC的流程

  1. 在遠程服務端定義aidl文件 ,定義好比接口定義,系統工具會自動生成相應的java接口
  2. 在服務端的Service中繼承該接口的Stub類,作爲遠程服務的代理對象
  3. 在客戶端中先拷貝服務端的aidl文件,並且放在與服務端包名一致的包下面
  4. 在客戶端通過ServiceConnetion來獲得遠程服務的代理對象,實現遠程進程通信
  5. 在服務端對Service設置IntentFileter,用於遠程組件的調用
  6. AIDL是線性安全的,由框架來維護其線性安全

注意:這裏的服務端指的是提供服務的另一個應用(處在另一個進程中)而不是Tomcat服務器子類的,不要會錯意!

2.3 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,不能爲其它標記。

3. 案例:使用支付寶支付功能

這個例子模擬的是一個購物應用,且名爲樂購,當點擊支付時調用手機上支付寶的支付服務進行付款。

3.1 支付寶端

PayService.java

package com.example.alipay;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;

public class PayService extends Service{
    @Override
    public IBinder onBind(Intent intent) {
        return new MPayBinder();
    }
    private void pay(String from, String to, double amount) {
        System.out.println(from+"向"+to+"支付了"+amount+"元.");
    }
    public class MPayBinder extends Binder implements IPayBinder{
        @Override
        public void callPay(String from, String to, double amount) {
            pay(from, to, amount);
        }
    }
}

IPayBinder.java

package com.example.alipay;

public interface IPayBinder {
    void callPay(String from,String to,double amount);
}

AndroidMenifest.xml

<service android:name="com.example.alipay.PayService">
    <intent-filter>
        <action android:name="alipay.service.pay"/>
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</service>

————-修改前————-

  1. 在硬盤中找到IPayBinder.java改名爲IPayBinder.adil並修改以符合aidl的規則
  2. 接着在/gen目錄下會自動產生IPayBinder.java,據此修改PayService.java
  3. IPayBinder.java中要關注的點

    這裏寫圖片描述

————-修改後————-

IPayBinder.adil

package com.example.alipay;

interface IPayBinder {
    void callPay(String from,String to,double amount);
}

PayService.java

package com.example.alipay;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

public class PayService extends Service{
    @Override
    public IBinder onBind(Intent intent) {
        return new MPayBinder();
    }
    private void pay(String from, String to, double amount) {
        System.out.println(from+"向"+to+"支付了"+amount+"元.");
    }
    public class MPayBinder extends IPayBinder.Stub{
        @Override
        public void callPay(String from, String to, double amount) {
            pay(from, to, amount);
        }
    }
}

3.2 樂購應用

拷貝支付寶的aidl文件到樂購,注意包名要跟服務端的一致

activity_main.xml

<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">
    <Button
        android:onClick="pay"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:text="支付" />
</RelativeLayout>

MainActivity.java

package com.example.leshop;

import android.app.Activity;
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.os.RemoteException;
import android.view.View;
import com.example.alipay.IPayBinder;

public class MainActivity extends Activity {

    private MServiceConnection conn;
    private IPayBinder mIPayBinder;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //建立獲取連接狀態的對象
        conn = new MServiceConnection();
        //使用隱式意圖綁定服務
        Intent service = new Intent();
        service.setAction("alipay.service.pay");
        bindService(service, conn, Context.BIND_AUTO_CREATE);
    }
    public void pay(View v) {
        //連接不爲空,並且代理在就可以支付了
        if (conn != null && mIPayBinder != null) {
            try {
                mIPayBinder.callPay("Lshare", "樂購", 100);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }
    private class MServiceConnection implements ServiceConnection {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            /*
             * 連接上了就獲取代理對象
             * 注意:如果用mIPayBinder =(IPayBinder) service會導致類型轉換異常
             */
            mIPayBinder = IPayBinder.Stub.asInterface(service);
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
            //失去連接就不要代理了
            mIPayBinder = null;
        }
    }
    @Override
    protected void onDestroy() {
        //應用退出了就解綁服務
        if (conn != null) {
            unbindService(conn);
            conn = null;
        }
    }
}

輸出結果

先運行支付寶,再運行樂購應用,點擊支付後可以在Logcat中看到如下結果:

這裏寫圖片描述

發佈了73 篇原創文章 · 獲贊 15 · 訪問量 23萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章