Android筆記之進(jìn)程通信(簡(jiǎn)易筆記,有待完善)

故書不厭百回讀,熟讀深思子自知——宋.蘇軾《送安驚落第詩(shī)》

小弟初學(xué)安卓,該文算是小弟的學(xué)習(xí)過程,網(wǎng)絡(luò)摘抄的概念,課后筆記與一些自己的思考,希望在自己的自學(xué)路上留下印記,也許有一些自己想的不對(duì)的地方,希望各位前輩斧正。(學(xué)了快兩個(gè)月啦還是感覺自己很菜啊)


一、什么是進(jìn)程

進(jìn)程的概念要是我沒查過資料的話我肯定會(huì)說是“正在進(jìn)行的程序”,結(jié)果這還真的是它的狹義定義,這個(gè)很多人在沒學(xué)習(xí)開發(fā)程序之前知道進(jìn)程這個(gè)詞肯定是在“任務(wù)管理器”中。有時(shí)候在我們發(fā)現(xiàn)運(yùn)行的程序無(wú)響應(yīng)的時(shí)候就會(huì)呼出任務(wù)管理器來(lái)結(jié)束進(jìn)程。

WIN10的任務(wù)管理器
  然而進(jìn)程的概念并沒有我想的那么簡(jiǎn)單啦,下面關(guān)于進(jìn)程的概念摘自網(wǎng)絡(luò): - 廣義定義:進(jìn)程是一個(gè)具有一定獨(dú)立功能的程序關(guān)于某個(gè)數(shù)據(jù)集合的一次運(yùn)行活動(dòng)。它是操作系統(tǒng)動(dòng)態(tài)執(zhí)行的基本單元,在傳統(tǒng)的操作系統(tǒng)中,進(jìn)程既是基本的分配單元,也是基本的執(zhí)行單元。 - 進(jìn)程是一個(gè)實(shí)體,每一個(gè)進(jìn)程都有它自己的地址空間。 - 進(jìn)程是一個(gè)“執(zhí)行中的程序”。>更多資料可查看百度百科谷歌官方開發(fā)文檔 ——“進(jìn)程與線程”   
  一個(gè)程序至少有一個(gè)進(jìn)程,而如果我們使用了多進(jìn)程開發(fā)程序,因?yàn)槊總€(gè)進(jìn)程都有自己的獨(dú)立內(nèi)存空間,當(dāng)一個(gè)進(jìn)程崩潰銷毀時(shí),其它的進(jìn)程依然存在。(話說最近寫了一個(gè)題目好像說安卓的一個(gè)進(jìn)程與LINUX進(jìn)程是同一個(gè)東西,我對(duì)LINUX了解太少,以后還是要補(bǔ))

二、在安卓中使用多進(jìn)程

使用多進(jìn)程的好處那就是由于內(nèi)存空間獨(dú)立能夠讓我們程序更加安全穩(wěn)定啦,但是多開一個(gè)進(jìn)程開銷比開一個(gè)線程要高,但因?yàn)榫€程之間可能共享內(nèi)存會(huì)互相影響,有時(shí)無(wú)法達(dá)到我們想要的效果。    
  想讓我們的安卓程序中的組件成為一個(gè)進(jìn)程步驟倒是很簡(jiǎn)單,只需要在AndroidManifest.xml文件中你希望讓其成為單獨(dú)進(jìn)程的組件里寫下“android:process="xxxxx”就行,注意的是如果你是在雙引號(hào)中以“:”——冒號(hào)開頭的話,則會(huì)成為application的子進(jìn)程,application的進(jìn)程名默認(rèn)為包名,你也可以在其中加入“android:process="xxxxx”改為自定義的進(jìn)程名。在谷歌開發(fā)文檔里發(fā)現(xiàn)不同應(yīng)用的組件還可以在同一進(jìn)程中,但前提是這些應(yīng)用共享相同的 Linux 用戶 ID 并使用相同的證書進(jìn)行簽署(原話)。

<!--使這個(gè)服務(wù)成為單獨(dú)的一個(gè)進(jìn)程--> 
<service android:process="com.qdf.AIDLService" 
android:name=".aidl.AIDLService" android:enabled="true" 
android:exported="true"> 
</service>

與平時(shí)使用安卓手機(jī)相同,當(dāng)手機(jī)內(nèi)存不足時(shí),Android系統(tǒng)可能會(huì)將我們的某些進(jìn)程關(guān)閉(講道理好煩啊,我手機(jī)有時(shí)候就彈出來(lái)看個(gè)短信再回去就又要重新啟動(dòng)了,明明內(nèi)存還很足),但Android系統(tǒng)會(huì)根據(jù)我們的進(jìn)程重要程度來(lái)進(jìn)行裁決,這其中就牽涉到一個(gè)進(jìn)程的優(yōu)先級(jí)的問題,而谷歌開發(fā)文檔也列出了進(jìn)程的重要性層級(jí),共五層:  
1.前臺(tái)進(jìn)程    
  因?yàn)槭亲钪匾上攵狝ndroid系統(tǒng)肯定不會(huì)首先去關(guān)閉它,因?yàn)榍芭_(tái)進(jìn)程一般都是一些正在與用戶進(jìn)行交互的Activity啊,設(shè)置了前臺(tái)運(yùn)行的Service啊,正在執(zhí)行onReceive()方法的BroadcastReceiver啊之類,正執(zhí)行一個(gè)生命周期回調(diào)的 Service(onCreate()、onStart() 或 onDestroy()),還有大部分系統(tǒng)進(jìn)程,基本可以理解,前臺(tái)進(jìn)程都是一些如果讓Android系統(tǒng)去關(guān)閉就會(huì)造成不太良好使用體驗(yàn)的進(jìn)程,所以要將它們列為最高級(jí)。

2.可見進(jìn)程    
  也挺重要,稍遜前臺(tái)進(jìn)程,沒有任何前臺(tái)組件、但仍會(huì)影響用戶在屏幕上所見內(nèi)容的進(jìn)程。谷歌舉得例子就是一個(gè)顯示了Dialog的失去了焦點(diǎn)的Activity(Activity在后面看著你哦~),想想也是,要是支付寶彈出付款確定對(duì)話框,結(jié)果Activity的進(jìn)程就被關(guān)掉了那不是很尷尬? 
   
3.服務(wù)進(jìn)程    
  (官方原話)正在運(yùn)行已使用 startService() 方法啟動(dòng)的服務(wù)且不屬于上述兩個(gè)更高類別進(jìn)程的進(jìn)程。盡管服務(wù)進(jìn)程與用戶所見內(nèi)容沒有直接關(guān)聯(lián),但是它們通常在執(zhí)行一些用戶關(guān)心的操作(例如,在后臺(tái)播放音樂或從網(wǎng)絡(luò)下載數(shù)據(jù))。因此,除非內(nèi)存不足以維持所有前臺(tái)進(jìn)程和可見進(jìn)程同時(shí)運(yùn)行,否則系統(tǒng)會(huì)讓服務(wù)進(jìn)程保持運(yùn)行狀態(tài)。  我的理解就是一個(gè)沒有被設(shè)置為前臺(tái)進(jìn)程但卻正在正在正在!勤勤懇懇的干事的服務(wù),它這個(gè)“齒輪”要是停止工作影響當(dāng)然是很大的。  
  
4.后臺(tái)進(jìn)程    
  處于后臺(tái)不可見進(jìn)程,一般是已經(jīng)做完了自己該做的事了的進(jìn)程,終止后不會(huì)有明顯的影響。(官方原話)通常會(huì)有很多后臺(tái)進(jìn)程在運(yùn)行,因此它們會(huì)保存在 LRU (最近最少使用)列表中,以確保包含用戶最近查看的 Activity 的進(jìn)程最后一個(gè)被終止。如果某個(gè) Activity 正確實(shí)現(xiàn)了生命周期方法,并保存了其當(dāng)前狀態(tài),則終止其進(jìn)程不會(huì)對(duì)用戶體驗(yàn)產(chǎn)生明顯影響,因?yàn)楫?dāng)用戶導(dǎo)航回該 Activity 時(shí),Activity 會(huì)恢復(fù)其所有可見狀態(tài)。 有關(guān)保存和恢復(fù)狀態(tài)的信息,請(qǐng)參閱Activity文檔

5.空進(jìn)程    
  (官方原話)不含任何活動(dòng)應(yīng)用組件的進(jìn)程。保留這種進(jìn)程的的唯一目的是用作緩存,以縮短下次在其中運(yùn)行組件所需的啟動(dòng)時(shí)間。 為使總體系統(tǒng)資源在進(jìn)程緩存和底層內(nèi)核緩存之間保持平衡,系統(tǒng)往往會(huì)終止這些進(jìn)程。  
  此外,一個(gè)進(jìn)程的級(jí)別可能會(huì)因其他進(jìn)程對(duì)它的依賴而有所提高,即服務(wù)于另一進(jìn)程的進(jìn)程其級(jí)別永遠(yuǎn)不會(huì)低于其所服務(wù)的進(jìn)程。 例如,如果進(jìn)程 A 中的內(nèi)容提供程序?yàn)檫M(jìn)程 B 中的客戶端提供服務(wù),或者如果進(jìn)程 A 中的服務(wù)綁定到進(jìn)程 B 中的組件,則進(jìn)程 A 始終被視為至少與進(jìn)程 B 同樣重要。 
 
  (官方原話)由于運(yùn)行服務(wù)的進(jìn)程其級(jí)別高于托管后臺(tái) Activity 的進(jìn)程,因此啟動(dòng)長(zhǎng)時(shí)間運(yùn)行操作的 Activity 最好為該操作啟動(dòng)服務(wù),而不是簡(jiǎn)單地創(chuàng)建工作線程,當(dāng)操作有可能比 Activity 更加持久時(shí)尤要如此。例如,正在將圖片上傳到網(wǎng)站的 Activity 應(yīng)該啟動(dòng)服務(wù)來(lái)執(zhí)行上傳,這樣一來(lái),即使用戶退出 Activity,仍可在后臺(tái)繼續(xù)執(zhí)行上傳操作。使用服務(wù)可以保證,無(wú)論 Activity 發(fā)生什么情況,該操作至少具備“服務(wù)進(jìn)程”優(yōu)先級(jí)。 同理,廣播接收器也應(yīng)使用服務(wù),而不是簡(jiǎn)單地將耗時(shí)冗長(zhǎng)的操作放入線程中。

三、進(jìn)程間通信

Android 利用遠(yuǎn)程過程調(diào)用 (RPC) 提供了一種進(jìn)程間通信 (IPC) 機(jī)制,通過這種機(jī)制,由 Activity 或其他應(yīng)用組件調(diào)用的方法將(在其他進(jìn)程中)遠(yuǎn)程執(zhí)行,而所有結(jié)果將返回給調(diào)用方。這就要求把方法調(diào)用及其數(shù)據(jù)分解至操作系統(tǒng)可以識(shí)別的程度,并將其從本地進(jìn)程和地址空間傳輸至遠(yuǎn)程進(jìn)程和地址空間,然后在遠(yuǎn)程進(jìn)程中重新組裝并執(zhí)行該調(diào)用。 然后,返回值將沿相反方向傳輸回來(lái)。 Android 提供了執(zhí)行這些 IPC 事務(wù)所需的全部代碼,因此您只需集中精力定義和實(shí)現(xiàn) RPC 編程接口即可。要執(zhí)行 IPC,必須使用 bindService() 將應(yīng)用綁定到服務(wù)上。—來(lái)自官方文檔

1.使用Messenger

這是一個(gè)Google比較推薦我們使用的類,使用方法倒也并不難,下面是服務(wù)類的代碼,我已經(jīng)在AndroidManifest.xml文件中給這個(gè)service添加了android:process屬性了,然后實(shí)例化了Messenger需要我們傳入一個(gè)Handler(我這里簡(jiǎn)單創(chuàng)建了一個(gè)繼承Handler的內(nèi)部類,其實(shí)也可以直接寫一個(gè)匿名的Handler對(duì)象,不過都會(huì)有警告,實(shí)際上為了防止內(nèi)存泄漏, 這些寫法都不對(duì),這里就先不這么講究了)

import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.util.Log;


public class MessengerService extends Service {
    //這個(gè)服務(wù)單獨(dú)一個(gè)進(jìn)程
    private Messenger mMessenger = new Messenger(new IncomingHandler());

    class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 0:
                    Bundle bundle = msg.getData();
                    Log.i("MS-", "HELLO MESSENGER    " + bundle.get("str"));
                    break;
            }
        }
    }



    @Override
    public IBinder onBind(Intent intent) {
        return mMessenger.getBinder();
    }
}


可以看到onBind方法中返回的Binder是通過Messenger的getBinder()獲得的。然后是Activity的代碼:

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.Parcel;
import android.os.Parcelable;
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.Button;

import com.qdf.geekband_eighth_week.R;

public class MessengerActivity extends AppCompatActivity {
    //這個(gè)活動(dòng)處于主進(jìn)程
    private Messenger mMessenger;
    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            mMessenger = new Messenger(iBinder);
            Log.i("MA-", "onServiceConnected");

        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            mMessenger = null;
        }
    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_messenger);
        Log.i("MA-", "onCreate");
        Button sendButton = (Button) findViewById(R.id.btn_send);
        sendButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mMessenger!=null) {
                    Log.i("MA-", "not null");
                    Message message = Message.obtain(null, 0, 0, 0);
                    Bundle data=new Bundle();
                    data.putString("str","Hello");
                    message.setData(data);
                    /**
                     * 進(jìn)程間傳遞的Message,Message.obj中存放的必須是實(shí)現(xiàn)了Parcelable接口的對(duì)象,
                     * 否則無(wú)法實(shí)現(xiàn)IPC!!!!!!重要的事情說三遍!!!
                     */
                    try {
                        mMessenger.send(message);
                        Log.i("MA-", "send");
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

    }

    @Override
    protected void onStart() {
        super.onStart();
        bindService(new Intent(this, MessengerService.class), mServiceConnection, Context.BIND_AUTO_CREATE);
        Log.i("MA-", "bindService");


    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(mServiceConnection);
    }
}

在onServiceConnected中我們通過傳過來(lái)的Bindler在Activity中獲得了可以向service發(fā)送message的Messenger了,而我們發(fā)送的message將由之前在service中實(shí)例化的Messenger參數(shù)里傳入的Handler來(lái)處理,這里要說明的是這個(gè)Message使用起來(lái)與我們Handler發(fā)送Message有點(diǎn)不同,主要在這個(gè)message.obj的使用上,我們并不是能隨心所欲的傳我們想傳的對(duì)象了,它要求我們必須使用實(shí)現(xiàn)了Parcelable接口的對(duì)象,小弟折騰了一下,也傳遞了實(shí)現(xiàn)了Parcelable接口的對(duì)象,還是有點(diǎn)小錯(cuò)誤,暫時(shí)還沒摸清門路,但是如果想傳String之類的對(duì)象,可以使用Bundle。

現(xiàn)在Activity能向Service發(fā)送,那Service要怎么向Activity中發(fā)送消息呢?我們假設(shè)把創(chuàng)建于服務(wù)而用來(lái)向服務(wù)發(fā)送消息的Messenger稱為SMessenger,而創(chuàng)建于Activity并用來(lái)向Activity的Messenger稱為AMessenger。Message類中有一個(gè)message.relyto,它允許我們存放Messenger,為了讓Service能發(fā)送信息給Activity,我們?cè)贏ctivity中創(chuàng)建了AMessenger,將其放入message.relyto,我們可以在Activity剛接受到SMessenger時(shí)就馬上發(fā)送消息將這個(gè)message.relyto發(fā)給Service,讓其在SMessenger的Handler中處理使Service新建Messenger來(lái)引用AMessenger,這樣,就達(dá)成了服務(wù)與活動(dòng)的互相通信了。

2.AIDL

由于在服務(wù)中,Messenger將收到的消息放在消息隊(duì)列中,所以它的處理是同步的,而當(dāng)我們想在Service中處理多線程的時(shí)候,就需要用到AIDL了。
  操作步驟:
⑴在Module/src/main中首先創(chuàng)建一個(gè)aidl目錄,并使用NEW->AIDL,會(huì)生成一個(gè)初始的AIDL文件,我們接著要build或者run一下,這樣才能在build/generated/source/aidl/debug/package_name/下生成我們將要用到的java接口。注意,我們不能在這個(gè)接口文件你直接編輯,AS也提醒了我們,當(dāng)我們想要添加方法時(shí),應(yīng)該在aidl文件中添加聲明。void basicTypes的參數(shù)告訴了我們方法返回值可使用的基本類型。

接口
創(chuàng)建AIDL文件
// IMyAidlInterface.aidl
package com.XXXXX;

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

interface IMyAidlInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
    String getName(String nickName);//自己聲明的方法
}


(2)此時(shí)AIDL已構(gòu)建完成,如果我們想加入新方法只需再編輯再build一次就好了,現(xiàn)在,我們要在Service中實(shí)現(xiàn)接口,下面是一個(gè)Activity和一個(gè)Service的簡(jiǎn)單代碼。

AIDLService.java

package com.qdf.geekband_eighth_week.aidl;

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

import com.qdf.geekband_eighth_week.IMyAidlInterface;

public class AIDLService extends Service {

    IMyAidlInterface.Stub myStub = new IMyAidlInterface.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }

        @Override
        public String getName(String nickName) throws RemoteException {
            return "AIDL HAHAHA" + nickName;
        }

        @Override
        public int getInt(int age) throws RemoteException {
            return 0;
        }
    };

    public AIDLService() {
    }

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

可以看到我們實(shí)現(xiàn)接口是實(shí)例化了接口中的Stub內(nèi)部類,該類繼承了Binder(由此可知我們?yōu)槭裁幢仨毷褂胋indService了),并又同時(shí)實(shí)現(xiàn)了IMyAidlInterface(接口文件默認(rèn)名,可更改)。可以看到,當(dāng)我們實(shí)例化后就能看到它會(huì)要求我們實(shí)現(xiàn)我們?cè)赼idl文件中聲明的方法。而在onBind()方法里最后返回的是stub(繼承自Binder嘛)。

AIDLActivity.java

package com.qdf.geekband_eighth_week.aidl;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import com.qdf.geekband_eighth_week.IMyAidlInterface;
import com.qdf.geekband_eighth_week.R;

public class AIDLActivity extends AppCompatActivity {

    private Button mTestButton;
    private IMyAidlInterface mIMyAidlInterface;
    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
             mIMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_aidl);
        mTestButton = (Button) findViewById(R.id.btn_test);
        mTestButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mIMyAidlInterface != null) {
                    try {
                        String name = mIMyAidlInterface.getName("qdf");
                        Toast.makeText(AIDLActivity.this, name + "", Toast.LENGTH_SHORT).show();
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        bindService(new Intent(this, AIDLService.class), mServiceConnection, Context.BIND_AUTO_CREATE);
    }

}

在Activity中,則聲明接口對(duì)象而不是接口.Stub對(duì)象了,在連接服務(wù)后,接收到的IBinder利用Stub的asInrerdace方法將其轉(zhuǎn)為接口對(duì)象。使用上,相比Messenger,AIDL感覺使用起來(lái)更方便,我們只需使用接口對(duì)象調(diào)用之前寫在Service中的Stub方法,而Stub在服務(wù)中又可以輕易調(diào)用我們定義在服務(wù)中的方法,可以很方便的完成進(jìn)程通信。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容