Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android Framework框架之IBinder進程間通信

Android Framework框架之IBinder進程間通信

編輯:關於Android編程

前面一篇介紹了Android中一個進程中有一個VM,一個主線程,一個Looper和一個MessageQueue,這一篇重點講一下利用IBinder實現進程間通信。首先進程間通信肯定至少要有兩個進程嘛。我們就模擬下這個場景,寫一個Demo,聲明這個Demo要用到兩個進程。然後進程A放一個MainActivity,放幾個按鈕,用來控制播放音樂,另一個進程B用來實現播放音樂。通過這個Demo穿插這講進程間通信IPC到底是個什麼東西。

首先看MainActivity的代碼。

public class MainActivity extends AppCompatActivity {
    private IBinder ib = null;
    private Button bt_1;
    private Button bt_2;
    private Button bt_3;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bt_1 = (Button) findViewById(R.id.bt_1);
        bt_2 = (Button) findViewById(R.id.bt_2);
        bt_3 = (Button) findViewById(R.id.bt_3);
        bt_1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Parcel parcel = Parcel.obtain();
                Parcel parcelReply = Parcel.obtain();
                parcel.writeString("play");
                try {
                    ib.transact(1, parcel, parcelReply, 0);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
        bt_2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Parcel parcel = Parcel.obtain();
                Parcel parcelReply = Parcel.obtain();
                parcel.writeString("stop");
                try {
                    ib.transact(2, parcel, parcelReply, 0);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
        bt_3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
        startService(new Intent(this, myService.class));
        bindService(new Intent(this, myService.class), conn, BIND_AUTO_CREATE);

    }

    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            ib = service;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

這裡面很簡單,只有三個按鈕,每個按鈕有一個點擊事件,分別去做不同的事。(先不要管關於進程通信的內容,接下來會仔細的說明)

點擊按鈕bt_1讓另一個進程的Service播放音樂,點擊bt_2,讓另一個進程的Service停止播放音樂,單擊bt_3,finish掉Activity。然後根據上一篇講的關於點擊圖標後怎麼執行到MainActivity的onCreate()方法的問題,在此在重復一遍。App要啟動,zygote孵化器就創建一個新的進程來執行這個App,進程裡包含一個主線程,一個VM,Looper,MessageQueue。然後主線程在while循環裡繞圈圈,框架把MainActivity對象new出來,發Message給主線程,讓主線程執行MainActivity.onCreate();這個時候我們來看MainActivity.onCreate()最後兩行,我們分別寫了啟動一個Service,綁定這個Service。然後根據前幾篇講的,App開發者是沒有權利new一個Service對象的,App開發者只有在Manifest文件裡聲明一個Service,在我們通過Intent告知框架啟動這個Service,然後框架根據我們在Manifest裡寫的Service的配置才知道要new一個什麼樣的Service。下面是我們對這個Service的配置

 




    
        
            
                
                
            
        
        
            
                
            
        
    



上面就是我們對Service的配置了,名字叫myService,而後面的
android:process=":remote"

這個則是告訴框架,我們想要啟動的Service是在一個新的進程中的。所以框架便按照我們的要求,孵化出一個新進程,然後new一個myService放進去。然後這個進程的主線程在while循環裡打轉轉,過一會框架給這個主線程一個Message,告訴他執行Service的onCreate方法。我們我們在在myService的寫一些播放音樂的准備的代碼,和前面的MainActivity.onCreate()一個性質。下面是我們的myService的代碼。(先不要關注具體的業務邏輯後面會講)

public class myService extends Service {
    private IBinder iBinder = null;
    private static ContrlSong contrlSong;

    @Override
    public void onCreate() {
        iBinder = new myBinder();
        contrlSong = new MySongContrler(getApplicationContext());
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return iBinder;
    }

    public static void play() {
        contrlSong.play();
    }

    public static void stop() {
        contrlSong.stop();
    }
}

現在要注意一個問題,就是框架已經為我們啟動兩個進程,兩個進程各自有一個自己的主線程,一個主線程執行完MainActivity.onCreate()之後在它的while循環裡轉圈圈,另一個主線程在執行完myService.onCreate()之後在它自己的while循環裡轉圈圈。

順著剛剛的MainActivity的主線程往下分析,剛剛是執行到startService這一步了,執行完後兩個進程也有了,Service也有了。然後MainActivity想在點擊按鈕後讓myService播放音樂。(為了方便下面用MA代表MainActivity,MS代表myService)如果MA和MS在一個進程中,我們只需要在myService裡寫一個靜態方法play()我們直接調myService.play()就可以了。即便是為了不引起ANR,我們也可以在MS裡面開一個子線程讓它去播放音樂。但是對於MA來說,我不管你怎麼去實現,我只要調一個方法就完事了。但是現在問題是MA和MS不在同一個進程裡。MA是不能直接調用MS的方法的。為什麼不能直接調用呢?關於這個,在這裡在補充一些進程的知識。

進程是一個獨立的執行空間以及它所擁有的資源巴拉巴拉。。。總歸記住一點就可以了,每個進程的空間都是獨立的,這個很重要。此時的MA和MS就好像是在一個是FaceBook用戶,一個是QQ用戶,它們要想交流,是沒辦法直接加好友的。但是問題又來了,我是一個APP的兩個進程,你TM不讓我交流,我還玩個卵子。所以這時候就要通過一個統一接口來把它們兩個連起來了,比如QQ郵箱,和FaceBook郵箱(不知道FaceBook有沒有郵箱,假設有哈)。Android就為我們准備了一個叫IBinder,用來讓兩個進程通信。

現在明白了IBinder的用途,讓我們來看看IBinder怎麼用。接著前面的狀態開始執行,前面說到兩個進程有了,MS也啟動了,然後要通信了,先把兩個連接起來。這時候MS的那個主線程剛執行完startService,然後開始執行bindService,我們來看MainActivity.onCreate()的最後一行代碼

bindService(new Intent(this, myService.class), conn, BIND_AUTO_CREATE);
第一個參數Intent,不奇怪,說明這個操作是要交個框架來完成的。第二個conn
private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            ib = service;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }

在MyActivity中,是類的屬性,類加載後的時候就已經有這個對象了。這個ServiceConnection和OnClickListener是一個性質的東西。就是MA和MS綁定好了以後觸發它執行它的綁定後的方法。前面說的bind動作是交由框架執行的,MA的主線程發出這個向框架的請求後就去轉圈圈了。框架把bind這件事做好後再發一個Message告訴MA的主線程,我bindService已經bind好了,你執行bind後的動作把,然後MA的主線程就來執行conn.onServiceConnected了,注意這個時候Message裡面還帶的有一個IBinder的對象。這個IBinder的對象就是兩個進程通信的工具conn.onServiceConnected()裡面的一行 ib = service;就是把這個對象的引用存下來。也就是bind成功後MA就擁有了回傳的跟MS通信的工具了。看下MS裡面的代碼:

 @Override
    public void onCreate() {
        iBinder = new myBinder();
        contrlSong = new MySongContrler(getApplicationContext());
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return iBinder;
    }
在MS創建的時候我們new了一個Binder對象。然後當MS被綁定onBind()的時候(這個就是在MA中發出bindService操作後框架在這邊調用onBind()),我們把這個iBinder對象return給框架,框架交給交給MA,所以MA和MS就擁有了"同一個"對象,然後如果這個對象裡有一個寫方法,一個讀方法,我寫你讀,或者你寫我讀,這樣不就可以通信了麼。這裡我們為什麼給"同一個"加上引號呢?實際上它們並不是同一個對象,而是利用一個是Binder,一個是BinderProxy。也就是一個是return回去的那個對象,一個是那個對象的分身。因為框架在進程間通信是通過底層Linux來完成的,MS這邊return回去iBinder對象,框架就"new"出一個分身給MA,使他們看起來像是一個對象,操作的時候也像操作同一個對象一樣,由框架和底層來保證分身和本尊的同步問題,對上面是透明的。既然我們MA和MS拿到了"同一個"對象,就可以利用這個對象進行通信了,一個寫一個讀。

懂了這個大概的原理我們再來看IBinder這個接口,是Android框架為我們寫好的用來進程間通信的接口。同時Android也寫了這個接口兩個實現類類Binder和BinderProxy。其實叫什麼名字,一點都不重要,不管它叫IBinder還是叫狗蛋,目的只有一個嘛,就是有一個對象,讓MA和MS同時持有,然後這個對象有一個寫數據的方法,一個讀數據的方法,就可以完成通信了。MA持有的對象是BinderProxy類型的,MA持有的對象是Binder類型的。但是這些是框架做的事對我們是透明的,現在我們認為MA和MS拿到了同一個對象。在這裡我們可以認為MainActivity.ib和myService.iBinder兩個引用指向同一個對象。然後我們來看MA裡的代碼:

bt_1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Parcel parcel = Parcel.obtain();
                Parcel parcelReply = Parcel.obtain();
                parcel.writeString("play");
                try {
                    ib.transact(1, parcel, parcelReply, 0);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
我們前面講的,點擊bt_1要讓MS播放音樂。我們讓它播放音樂的信號就是:
ib.transact(1, parcel, parcelReply, 0);

 

這裡的Parcel就是一個序列化的工具類。序列化又是各什麼東東呢?剛剛我們說的MA和MS擁有"同一個"對象的引用,MA裡面ib.transact()就相當於寫的動作,MS裡面的iBinder.onTransact()就相當於讀的動作。當MA寫了以後由框架通知MS去讀。(iBinder.onTransact()的代碼寫在MyBinder裡):

public class myBinder extends Binder {

    @Override
    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        if (code == 1)
            myService.play();
        if (code == 2)
            myService.stop();
        return true;
    }

}
這裡我們相當於在myBinder裡解析了傳過來的內容。實際有用的就是1和2,MA傳1就讓MS唱歌,MA傳2就讓MS停止。現在來講什麼是序列化,序列化就是把要傳的數據轉成一個大家約定的格式,反正都是二進制0和1表示的,我只要規定String怎麼表示,類怎麼表示等等,就可以做一次轉換,到接收方再反著翻譯回來就行了。為什麼要這麼搞呢,我直接傳數據不就行了?不行,因為前面已經說了,我們並不在操作統一對象,而真正的IPC也並不在Java層,底層代碼鬼知道用什麼語言寫的,有什麼樣的數據結構定義,底層面對一堆亂碼oo**xx怎麼搞,而且以類或者對象什麼的存儲可能占的空間也比較大,不利於快速的讀寫啊。所以就搞出序列化這麼各東東。把要傳的數據轉一種規定的格式,到那邊再翻譯過來就ok了。

到這裡差不多就扯完了。整個執行邏輯就是這樣咯,最後貼上播放音樂部分的業務邏輯。

播放音樂接口:

public interface ContrlSong {
    public void play();
    public void stop();
}

實現類。

public class MySongContrler implements ContrlSong {
    private MediaPlayer mediaPlayer = null;
    private Context context;

    MySongContrler(Context context) {
        this.context = context;
    }

    @Override
    public void play() {
        if (mediaPlayer != null) return;
        mediaPlayer = MediaPlayer.create(context, R.raw.song);
        mediaPlayer.start();
    }

    @Override
    public void stop() {
        if (mediaPlayer != null) {
            mediaPlayer.stop();
            mediaPlayer.release();
            mediaPlayer = null;
        }
    }

}
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved