Android Service入門到崩潰

xiasuhuei321發表於2017-12-13

最近專案搞完了,有點空正好查漏補缺~關於服務這一塊,一直都沒怎麼用過,趁著這個時機學習一下~至於為啥起這名呢……因為本來就想著稍微看一下Service,結果發現要看的東西越來越多……越來越多……所以就有崩了的意思……本文程式碼比較多,請邊看邊敲,碰到不懂去搜一下。

首先梳理一下我想要寫的東西:

  • Service基礎
  • AIDL

什麼是Service

Service是一個可以在後臺執行長時間執行操作而不提供使用者介面的應用元件。服務可由其他應用元件啟動,而且即使使用者切換到其他應用,服務仍將在後臺繼續執行。服務並不是一個單獨的程式,除非有特殊指明,否則Service是作為application的一部分執行在相同的程式中的。服務沒有分離於主執行緒工作,所以當你要在服務中進行耗時操作最好開啟一個新的執行緒來執行這種操作,避免ANR。

啟動Service的兩種方式

有兩種方式啟動service,一種是startService,一種是bindService,暫時從service的生命週期上來看一下這兩種有何異同,先貼程式碼: Service:

package com.xiasuhuei321.client;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;

/**
 * Created by xiasuhuei321 on 2017/1/11.
 * author:luo
 * e-mail:xiasuhuei321@163.com
 */

public class TestService extends Service {
    public static final String TAG = "TestService";

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.e(TAG, "onBind");
        return null;
    }

    @Override
    public void onCreate() {
        Log.e(TAG, "onCreate");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e(TAG, "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onRebind(Intent intent) {
        Log.e(TAG, "onRebind");
    }

    @Override
    public void onDestroy() {
        Log.e(TAG, "onDestroy");
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.e(TAG, "onUnbind");
        return super.onUnbind(intent);
    }

    private static class MyBinder extends Binder{

    }
}
複製程式碼

Activity:

package com.xiasuhuei321.client;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Intent startIntent;
    private Intent bindIntent;

    public static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.bt_bind_service).setOnClickListener(this);
        findViewById(R.id.bt_start_service).setOnClickListener(this);
        findViewById(R.id.bt_stop_service).setOnClickListener(this);
        findViewById(R.id.bt_unbind_service).setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.bt_bind_service:
                bindIntent = new Intent(this, TestService.class);
                bindService(bindIntent, connection, BIND_AUTO_CREATE);
                break;

            case R.id.bt_start_service:
                startIntent = new Intent(this, TestService.class);
                startService(startIntent);
                break;

            case R.id.bt_stop_service:
                if (startIntent != null)
                    stopService(startIntent);
                break;

            case R.id.bt_unbind_service:
                if(bindIntent != null)
                    unbindService(connection);
                break;
        }
    }

    ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            Log.e(TAG, "onServiceConnected");

        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            Log.e(TAG, "onServiceDisconnected");
        }
    };
}
複製程式碼

xml檔案就不貼了,就是四個按鈕,從上到下分別是bindService、startService、unbindService、stopService兩兩對應,樣子如下所示:

Paste_Image.png
最後別忘了在清單檔案中註冊一下,android四大元件都需要註冊的。

使用bindService方法啟動時service的生命週期:

service

點選unbind按鈕時service的生命週期:

service

點選startService

service

點選stopService

service

通過以上的生命週期我們會發現,通過bindService和startService啟動的服務生命週期會有所不同,其實bindService還有另外的東西,不過因為我在onBind()方法裡返回了null所以沒有體現出來。那麼我返回一個他要的試試看,修改的程式碼只有TestService和ServiceConnection,只放修改的程式碼:

package com.xiasuhuei321.client;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;

/**
 * Created by xiasuhuei321 on 2017/1/11.
 * author:luo
 * e-mail:xiasuhuei321@163.com
 */

public class TestService extends Service {
    public static final String TAG = "TestService";

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.e(TAG, "onBind");
        return new MyBinder();
    }

    @Override
    public void onCreate() {
        Log.e(TAG, "onCreate");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e(TAG, "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onRebind(Intent intent) {
        Log.e(TAG, "onRebind");
    }

    @Override
    public void onDestroy() {
        Log.e(TAG, "onDestroy");
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.e(TAG, "onUnbind");
        return super.onUnbind(intent);
    }

    public static class MyBinder extends Binder {
        public void playMusic() {
            Log.e("MyBinder", "play music");
        }
    }
}
    // 在MainActivity中
    ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            Log.e(TAG, "onServiceConnected");
            TestService.MyBinder mb = (TestService.MyBinder) iBinder;
            mb.playMusic();
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            Log.e(TAG, "onServiceDisconnected");
        }
    };
複製程式碼

那麼讓我們點下BIND_SERVICE按鈕看看有啥反應:

service

可以看到,通過ServiceConnection中有兩個回撥方法,其中一個帶有IBinder型別的物件,而我強轉成我自己繼承自Binder的MyBinder也沒有錯。可以猜測這裡的IBinder就是我在onBind()裡返回的MyBinder,而我可以在這個類裡寫一個方法,再通過拿到的IBinder來呼叫這個方法。

小結:

onCreate方法只在首次建立服務時,系統將呼叫此方法來執行一次性設定程式(在呼叫 onStartCommand()或onBind()之前)。如果服務已在執行,則不會呼叫此方法。點選BIND_SERVIC和START_SERVICE(即bindService和startService都呼叫),再點選UNBIND_SERVICE或STOP_SERVICE實際上都不會呼叫onDestroy方法。因為service可以和多個客戶端繫結(不過只在第一次和客戶端繫結的時候呼叫onBind方法,隨後即可將同一IBinder傳遞給其他繫結的客戶端),除非所有的客戶端均取消繫結(如果通過startService也啟動了這個服務,那麼還得stopService才會停止),否則stopService或stopSelf不會實際停止服務。

在閱讀鴻洋大神的一篇AIDL文章時,我嘗試了下他的程式碼,發現使用隱式Intent啟動服務會報錯,後來搜尋發現說是Android 5.0以上只能顯示啟動服務了,解決方案後文和網上都有。

建立繫結服務的三種方式

建立繫結服務時必須提供IBinder,提供客戶端和服務互動的“介面”。這裡的介面並非java語言中的interface,可以理解為提供客戶端呼叫的方法之類的。android提供三種方式:

  • 擴充套件(繼承)Binder類
  • 使用Messenger
  • 使用AIDL

第一種擴充套件Binder類在Android API指南中描述如下:如果服務是供你的自由應用專用,並且在客戶端相同的程式中執行(常見情況),則應通過擴充套件Binder類並從onBind()返回它的一個例項來建立介面。客戶端收到Binder後,可利用它直接訪問Binder實現中乃至Service中可用的公共方法。

如果服務只是您的自有應用的後臺工作執行緒,則優先採用這種方法。不以這種方式建立介面的唯一原因是,您的服務被其他應用或不同的程式佔用。

也就是說如果不是為了跨程式而用其他兩種方式你就是在耍流氓~

耍流氓

AIDL

在正式瞭解AIDL之前,我覺得有必要先弄明白執行緒和程式的概念。首先看一下他們正式的定義:

  • 執行緒(Thread)是程式中的一個實體,是被系統獨立排程和分派的基本單位,執行緒自己不擁有系統資源,只擁有一點兒在執行中必不可少的資源,但它可與同屬一個程式的其它執行緒共享程式所擁有的全部資源。

  • 程式(Process)是計算機中的程式關於某資料集合上的一次執行活動,是系統進行資源分配和排程的基本單位,是作業系統結構的基礎

從我們平時的開發中可以這麼闡述程式與執行緒:程式是程式的一次執行,執行緒是其中一段程式碼的執行。而我們平時寫的Android程式一般都是在一個程式中執行的,程式名即包名。Android系統核心是Linux核心,而每一個應用程式都執行在一個虛擬機器上,每個應用程式都是一個程式。在Android中一個應用程式也是可以使用多程式的,其方式非常簡單,只要在清單檔案中指定對應的元件屬性:

android:process=":remote"
複製程式碼

這樣就可以了,但是本來一個問題你可以用多程式來解決,然後你用了,之後你就會有兩個問題了。說笑,多程式會帶來的問題就是記憶體不共享,因為每個程式都獨享自己的一塊記憶體,沒辦法直接互相訪問記憶體。一個程式中已經存在物件另外一個程式中不能直接使用,不過好在Android提供了很多方式給我們“跨程式”,AIDL便是我們首先要探究的一種方式。

AIDL小例子

我看了幾篇文的例子,《Android開發藝術探索》、鴻洋大神和網上其他人寫的,感覺還是先用鴻洋大神的例子好一點,鴻洋大神的例子是簡單的計算兩個整型數值的相加和相減的結果。我們首先新建一個AIDL檔案:

新建AIDL

AIDL檔案程式碼如下:

// Calc.aidl
package com.xiasuhuei321.forjianshu;

// Declare any non-default types here with import statements

interface CalcManage {
    int plus(int a,int b);
    int min(int a,int b);
}
複製程式碼

然後點選Android Studio的make project

make pro

完成之後會在專案的app/build/generated/source/aidl/debug下生成一個java檔案,這裡和鴻洋大神一樣,具體的解釋放到之後再說,我們先走完這個流程。

接著新建一個CalcService的Java類:

package com.xiasuhuei321.forjianshu;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;

/**
 * Created by xiasuhuei321 on 2017/1/13.
 * author:luo
 * e-mail:xiasuhuei321@163.com
 */

public class CalcService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    CalcManage.Stub binder = new CalcManage.Stub() {
        @Override
        public int plus(int a, int b) throws RemoteException {
            return a + b;
        }

        @Override
        public int min(int a, int b) throws RemoteException {
            return a - b;
        }
    };

}
複製程式碼

可以看到我這裡只是返回了一個binder,不過這個binder是我們通過AIDL生成的java檔案中的一個類,恩,下文再解釋。

接著是主介面的佈局,和MainActivity的程式碼,恩因為能直接用鴻洋大神的。。直接copy過來的。。。各位不要學我。。。

不要學

佈局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.xiasuhuei321.forjianshu.MainActivity">

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:onClick="bindService"
        android:text="BindService" />

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:onClick="unbindService"
        android:text="UnbindService" />

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:onClick="addInvoked"
        android:text="12+12" />

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:onClick="minInvoked"
        android:text="50-12" />
</LinearLayout>
複製程式碼

MainActivity:

package com.xiasuhuei321.forjianshu;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import java.util.List;

public class MainActivity extends AppCompatActivity {

    private CalcManage mCalcAidl;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    /**
     * 客戶端主要通過ServiceConnected與服務端連線
     */
    private ServiceConnection mServiceConn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.e("client", "onServiceConnected");
            mCalcAidl = CalcManage.Stub.asInterface(service);

        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.e("client", "onServiceDisconnected");
            mCalcAidl = null;
        }
    };

    /**
     * 點選BindService按鈕時呼叫
     *
     * @param v
     */
    public void bindService(View v) {
        Intent intent = new Intent();
        intent.setAction("com.xiasuhuei321.forjianshu.calc");
        // 在5.0以上隱式啟動繫結service會拋異常
        final Intent i = new Intent(createExplicitFromImplicitIntent(this,intent));
        bindService(i, mServiceConn, Context.BIND_AUTO_CREATE);
    }

    /**
     * 點選unBindService按鈕時呼叫
     */
    public void unbindService(View v) {
        unbindService(mServiceConn);
    }

    /**
     * 點選12 + 12按鈕時呼叫
     */
    public void addInvoked(View v) throws Exception {
        if (mCalcAidl != null) {
            int addRes = mCalcAidl.plus(12, 12);
            Toast.makeText(this, addRes + "", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(this, "伺服器被異常殺死,請重新繫結服務端", Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * 點選50 - 12按鈕時呼叫
     */
    public void minInvoked(View v) throws RemoteException {
        if (mCalcAidl != null) {
            int addRes = mCalcAidl.min(50, 12);
            Toast.makeText(this, addRes + "", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(this, "服務端未繫結或被異常殺死,請重新繫結服務端", Toast.LENGTH_SHORT).show();
        }
    }

    /***
     * Android L (lollipop, API 21) introduced a new problem when trying to invoke implicit intent,
     * "java.lang.IllegalArgumentException: Service Intent must be explicit"
     *
     * If you are using an implicit intent, and know only 1 target would answer this intent,
     * This method will help you turn the implicit intent into the explicit form.
     *
     * Inspired from SO answer: http://stackoverflow.com/a/26318757/1446466
     * @param context
     * @param implicitIntent - The original implicit intent
     * @return Explicit Intent created from the implicit original intent
     */
    public static Intent createExplicitFromImplicitIntent(Context context, Intent implicitIntent) {
        // Retrieve all services that can match the given intent
        PackageManager pm = context.getPackageManager();
        List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);

        // Make sure only one match was found
        if (resolveInfo == null || resolveInfo.size() != 1) {
            return null;
        }

        // Get component info and create ComponentName
        ResolveInfo serviceInfo = resolveInfo.get(0);
        String packageName = serviceInfo.serviceInfo.packageName;
        String className = serviceInfo.serviceInfo.name;
        ComponentName component = new ComponentName(packageName, className);

        // Create a new intent. Use the old one for extras and such reuse
        Intent explicitIntent = new Intent(implicitIntent);

        // Set the component to be explicit
        explicitIntent.setComponent(component);

        return explicitIntent;
    }
}
複製程式碼

最後不要忘了在清單檔案中註冊這個service:

        <service android:name=".CalcService">
            <intent-filter>
                <action android:name="com.xiasuhuei321.forjianshu.calc" />

                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </service>
複製程式碼

最後來看一下結果如何:

結果

一些值得注意的地方已經在程式碼中註釋了,看結果很明顯是對的,但是我這裡還沒有體現出來AIDL跨程式跨在哪了,接下來就給這個service指定一個程式,看一下還能跑不。

修改一下清單檔案中service的屬性:

        <service
            android:name=".CalcService"
            android:process=":calc">
            <intent-filter>
                <action android:name="com.xiasuhuei321.forjianshu.calc" />

                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </service>
複製程式碼

啟動之後點選第一個按鈕繫結服務,再檢視果然多了:calc程式。

兩個程式

還能跑不?

結果

結果是一樣的,正常執行,這說明了AIDL的確能實現“跨程式”。這種方式也可以用於不同app間的通訊,只不過可能會麻煩一點。接下來就新建一個client module:

新建一個module

現在我們新建的這個module將之稱為client,而之前的專案將之稱為server。現在我們將在server通過AIDL檔案生成的java檔案複製過來

複製
當然了,因為包更改了,肯定一堆問題,改過來就好了,將該檔案內的包名全部修改對了就可以了。然後,恩,把之前的佈局和能用到的都複製過來……佈局不怎麼需要改,MainActivity的程式碼也先不該,複製過來跑起來看看咋樣。

結果肯定是閃退了~別問我咋知道,如果你報的錯是以下這種:

錯誤

那說明CalcMange程式碼裡的DESCRIPTOR錯了,畢竟直接拷貝過來的。。。各種錯不稀奇。。

private static final String DESCRIPTOR = "com.xiasuhuei321.client.CalcManage";
複製程式碼

因為剛全域性修改了包名,所以這裡也改了,但是這不能改,不然就會拋這個異常。

private static final String DESCRIPTOR = "com.xiasuhuei321.forjianshu.CalcManage";
複製程式碼

好了,改成這樣再來一遍,看一下結果:

結果

分析

正確的得出了結果,恩,試也試完了,接下來研究一下AIDL為啥能做到跨程式。前面一直拖著說道後面解釋的玩意,現在再來看看,系統為我們生成的java檔案:

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: /Users/luojun/Desktop/ForJianShu/app/src/main/aidl/com/xiasuhuei321/forjianshu/CalcManage.aidl
 */
package com.xiasuhuei321.forjianshu;

// Declare any non-default types here with import statements
// 繼承了IInterface,同時自己也是也是一個介面,所有可以再Binder中傳輸的介面都需要繼承IIterface
public interface CalcManage extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    // Binder類,當客戶端和服務端都位於同一個程式的時候,方法呼叫不會走跨程式的transact過程,
    // 而當兩者位於不同程式時,方法呼叫需要走transact過程。邏輯由Stub的內部代理類Proxy來完成
    public static abstract class Stub extends android.os.Binder implements com.xiasuhuei321.forjianshu.CalcManage {
        // Binder的唯一標識,一般用當前Binder的類名標識
        private static final java.lang.String DESCRIPTOR = "com.xiasuhuei321.forjianshu.CalcManage";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.xiasuhuei321.forjianshu.CalcManage interface,
         * generating a proxy if needed.
         */
        // 用於將服務端的Binder物件轉換成客戶端所需的AIDL介面型別的物件,這種轉換過程是區分程式的,如果
        // 客戶端和服務端位於同一程式,那麼此方法返回的就是服務端的Stub物件本身,否則返回的就是系統
        // 封裝後的Stub.proxy
        public static com.xiasuhuei321.forjianshu.CalcManage asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.xiasuhuei321.forjianshu.CalcManage))) {
                return ((com.xiasuhuei321.forjianshu.CalcManage) iin);
            }
            return new com.xiasuhuei321.forjianshu.CalcManage.Stub.Proxy(obj);
        }

        // 返回當前Binder物件
        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        // 這個方法執行在客戶端,當客戶端遠端呼叫此方法時,它的內部實現是這樣的:建立該方法所需要的輸入型別
        // Parcel物件_data、輸出型Parcel物件_reply和返回值物件List;然後把該方法的引數資訊寫入_data中
        // (如果有引數的話);接著呼叫transact方法來發起RPC(遠端過程呼叫)請求,同事當前執行緒掛起;然後
        // 服務端的onTransact方法會被呼叫,直到RPC過程返回後,當前執行緒繼續執行,並從_reply中取出RPC過程
        // 的返回結果;返回_reply中的資料。
        @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_plus: {
                    // 與客戶端的writeInterfaceToken對用,標識遠端服務的名稱
                    data.enforceInterface(DESCRIPTOR);
                    // 讀取客戶端傳入的兩個引數
                    int _arg0;
                    _arg0 = data.readInt();
                    int _arg1;
                    _arg1 = data.readInt();
                    // 這裡涉及到java多型,是一個基本的概念,這裡最終呼叫的是我們在
                    // Service中建立的實現類中的方法。
                    int _result = this.plus(_arg0, _arg1);
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
                case TRANSACTION_min: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    int _arg1;
                    _arg1 = data.readInt();
                    int _result = this.min(_arg0, _arg1);
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.xiasuhuei321.forjianshu.CalcManage {
            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;
            }

            // CalcManage.aidl中宣告的方法。這個方法執行在客戶端,當客戶端遠端呼叫此方法時,它的內部
            // 實現是這樣的:建立該方法所需要的輸入型Parcel物件_data、輸出型Parcel物件_reply和返回值
            // 物件
            @Override
            public int plus(int a, int b) throws android.os.RemoteException {
                // 建立輸入物件
                android.os.Parcel _data = android.os.Parcel.obtain();
                // 建立輸出物件
                android.os.Parcel _reply = android.os.Parcel.obtain();
                // 返回值
                int _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    // 將引數寫入_data
                    _data.writeInt(a);
                    _data.writeInt(b);
                    // 發起RPC(遠端郭恆呼叫)請求,當前執行緒掛起。服務端的onTransact方法會被
                    // 呼叫,直到RPC過程返回後,當前執行緒繼續執行,並從_reply中取出RPC過程中的
                    // 返回結果;
                    mRemote.transact(Stub.TRANSACTION_plus, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public int min(int a, int b) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                int _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(a);
                    _data.writeInt(b);
                    mRemote.transact(Stub.TRANSACTION_min, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        // 用於標識plus和min方法
        static final int TRANSACTION_plus = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_min = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    public int plus(int a, int b) throws android.os.RemoteException;

    public int min(int a, int b) throws android.os.RemoteException;
}
複製程式碼

寫到這,我覺得有必要再重新審視一下Binder了,因為通過以上的程式碼和註釋,你一定發現老提到Binder。Binder是Android中的一個類,它實現了IBinder介面。從IPC(Inter-Process Communication,程式間通訊)角度來說,Binder是Android中的一種跨程式通訊方式。從Android應用層來說,Binder是客戶端和服務端呼叫的Binder物件,通過這個Binder物件,客戶端就可以獲取服務端提供的服務或者資料,這裡的服務端包括普通服務和基於AIDL的服務。

本來還想分個服務端,客戶端繼續吹吹牛,但是寫著寫著覺得我註釋夠詳細了。。。不吹了不吹了,各位自己看上面我帶註釋的程式碼吧。

寫到這感覺篇幅有點小長了,關於Binder、Messenger和IPC還是留待下一篇吧~

下一篇

參考資料

相關文章