Android進(jìn)程通信IPC

IPC也看過很多次,每次看完后記會忘記屈糊,這次自己給它寫下來加深印象

IPC

進(jìn)程間通信(Inter-Process Communication),簡稱IPC,就是指進(jìn)程與進(jìn)程之間進(jìn)行通信

基于Binder的AIDL和Messager的同一個應(yīng)用的通信

步驟一

首先寫個最基本的就是重寫IBinder,增強(qiáng)服務(wù)端的的功能 看代碼:

//IpcService.java文件
public class IpcService extends Service {
    private IBinder mIBinder;

    @Override
    public void onCreate() {
        super.onCreate();
        mIBinder = new MyBinder();
    }

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

    public class MyBinder extends Binder {
        public void print() {
            Log.d("PHN", "服務(wù)Service,執(zhí)行任務(wù)");
        }
    }

}

注意要在清單文件中注冊服務(wù)

public class MainActivity extends AppCompatActivity {

    private IpcService.MyBinder myBinder;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //自動綁定服務(wù)
        Intent intent = new Intent(this, IpcService.class);
        MyServiceConnection serviceConnection = new MyServiceConnection();
        bindService(intent, serviceConnection, BIND_AUTO_CREATE);

        findViewById(R.id.bt).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (null != myBinder) {
                    myBinder.print();
                }
            }
        });
    }

    private class MyServiceConnection implements ServiceConnection {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            myBinder = (IpcService.MyBinder) service;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    }
}
loginfo_01.png

執(zhí)行點(diǎn)擊服務(wù)端會打印信息,說明這個活動和服務(wù)進(jìn)行通信是成功的昧诱!

步驟二

我們用Messager來做剛才的功能
1.首先在Service里面創(chuàng)建一個Hander用來接受消息:

private final static Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
       Log.d("PHN", "服務(wù)Service,執(zhí)行任務(wù)");
    }
};

2.在Service里面創(chuàng)建一個Messager,并把Handler放入其中

private final static Messenger mMessenger = new Messenger(mHandler);

3.重寫onbind方法,返回Messager里面的Binder

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

貼出完整的服務(wù)代碼


public class IpcService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
    }

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


    private final static Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            Log.d("PHN", "服務(wù)Service凶掰,執(zhí)行任務(wù)");
        }
    };
    private final static Messenger mMessenger = new Messenger(mHandler);

}

再貼出活動里的代碼

public class MainActivity extends AppCompatActivity {

    private Messenger messenger;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //自動綁定服務(wù)
        Intent intent = new Intent(this, IpcService.class);
        MyServiceConnection serviceConnection = new MyServiceConnection();
        bindService(intent, serviceConnection, BIND_AUTO_CREATE);

        findViewById(R.id.bt).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (null != messenger) {
                    Message message = Message.obtain();
                    try {
                        messenger.send(message);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }

    private class MyServiceConnection implements ServiceConnection {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            messenger = new Messenger(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    }
}

執(zhí)行看看結(jié)果

loginfo_02.png

果然可以得到同樣的效果懦窘!

步驟三

上面我們可能看到只能是活動讓服務(wù)執(zhí)行任務(wù)稚配,那么怎樣才能讓服務(wù)端讓活動也執(zhí)行任務(wù)呢,我們先對服務(wù)器端的代碼進(jìn)行修改,首先修改Service的Handler
看下面服務(wù)端代碼處理:

public class IpcService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
    }

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


    private final static Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            Log.d("PHN", "服務(wù)Service道川,執(zhí)行任務(wù)");
            //獲取Messager
            Messenger messenger = msg.replyTo;
            //創(chuàng)建消息
            Message msg_reply = Message.obtain();
            try {
                //發(fā)送
                messenger.send(msg_reply);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    };
    private final static Messenger mMessenger = new Messenger(mHandler);

}

接著我們在活動端也增加一個Handler和Messager來處理消息,貼出代碼

public class MainActivity extends AppCompatActivity {

    private Messenger messenger;


    private final static Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            Log.d("PHN", "活動Acitivity冒萄,執(zhí)行任務(wù)");
        }
    };
    private final static Messenger mReplyMessager = new Messenger(mHandler);


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //自動綁定服務(wù)
        Intent intent = new Intent(this, IpcService.class);
        MyServiceConnection serviceConnection = new MyServiceConnection();
        bindService(intent, serviceConnection, BIND_AUTO_CREATE);

        findViewById(R.id.bt).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (null != messenger) {
                    Message message = Message.obtain();
                    //通過msg把客戶端的Messager傳送到服務(wù)器端(關(guān)鍵代碼)
                    message.replyTo =mReplyMessager;
                    try {
                        messenger.send(message);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }

    private class MyServiceConnection implements ServiceConnection {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            messenger = new Messenger(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    }
}

還有一個比較關(guān)鍵的地方,就是要在客戶端發(fā)送消息的時候把客戶端的Messager通過消息傳送到服務(wù)器端
(msg.replyTo =mReplyMessager)

來執(zhí)行看看結(jié)果:

loginfo_03.png

果然可以!扇单!

步驟四

上面的通信是基于本應(yīng)用里的奠旺,下面我們來看看不同的兩個應(yīng)用之間的通信AIDL施流,我是參照任玉剛的開發(fā)藝術(shù)里寫例子鄙信,
1.首先那就是新建AIDL文件瞪醋,很簡單:

screenshot_01.png

那我就直接創(chuàng)建兩個aidl文件Book.aidl,BookManager.aidl

screenshot_02.png

下面直接看文件的具體內(nèi)容银受,我貼上來:

//Book.java
public class Book implements Parcelable{
    protected Book(Parcel in) {
        name = in.readString();
        price = in.readInt();
    }

    public static final Creator<Book> CREATOR = new Creator<Book>() {
        @Override
        public Book createFromParcel(Parcel in) {
            return new Book(in);
        }

        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    private String name;

    private int price;

    public Book() {}

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeInt(price);
    }
    /**
     * 參數(shù)是一個Parcel,用它來存儲與傳輸數(shù)據(jù)
     * @param dest
     */
    public void readFromParcel(Parcel dest) {
        //注意鸦采,此處的讀值順序應(yīng)當(dāng)是和writeToParcel()方法中一致的
        name = dest.readString();
        price = dest.readInt();
    }

    @Override
    public String toString() {
        return "Book{" +
                "name='" + name + '\'' +
                ", price=" + price +
                '}';
    }
}
// Booka.aidl
package com.hanny.aidldemo;

// Declare any non-default types here with import statements
parcelable Book;

// BookManager.aidl
package com.hanny.aidldemo;
import com.hanny.aidldemo.Book;
// Declare any non-default types here with import statements

interface BookManager {

    //所有的返回值前都不需要加任何東西,不管是什么數(shù)據(jù)類型
      List getBooks();

      //傳參時除了Java基本類型以及String顶霞,CharSequence之外的類型
      //都需要在前面加上定向tag锣吼,具體加什么量需而定
      void addBook(in Book book);
}

文件搞好了,先編譯下玄叠,看看會不會錯,應(yīng)該會錯。

loginfo_03.png

這是因?yàn)榘袯ook.java文件放到了aidl下隧膘,所以要在build.gradle 的android{ }的閉包下加入

   sourceSets {
        main {
            java.srcDirs = ['src/main/java', 'src/main/aidl']
        }
    }

不會錯了寺惫,那就來寫AIDL的service文件:

public class AIDLService extends Service {

    //包含Book對象的list
    private List mBooks = new ArrayList<>();
    private BookManager.Stub bookManager = new BookManager.Stub() {
        @Override
        public List getBooks() throws RemoteException {
            if (mBooks != null) {
                return mBooks;
            }
            return new ArrayList();
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            synchronized (this) {
                if (mBooks == null) {
                    mBooks = new ArrayList();
                }
                if (book == null) {
                    book = new Book();
                }
                book.setPrice(23);
                if (!mBooks.contains(book)) {
                    mBooks.add(book);
                }
                //打印mBooks列表,觀察客戶端傳過來的值
                Log.e("PHN", "增加后的圖書 : " + mBooks.toString());
            }
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        Book book = new Book();
        book.setName("Android開發(fā)藝術(shù)探索");
        book.setPrice(28);
        mBooks.add(book);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {

        return bookManager;
    }
}

服務(wù)端寫好了互墓,那就新建一個工程蒋搜,然后把服務(wù)端的aidl下的文件全部拷貝過來~放在main下級目錄篡撵!

screenshot_03.png

接著在活動里用隱式意圖開啟服務(wù)育谬!這樣的話就要在服務(wù)端的清單文件里給服務(wù)配上action

   <service android:name=".AIDLService">
            <intent-filter>
                <action android:name="action.ipc"/>
            </intent-filter>
        </service>

看看客戶端活動里的代碼:

public class MainActivity extends AppCompatActivity {

    //標(biāo)志當(dāng)前與服務(wù)端連接狀況的布爾值帮哈,false為未連接膛檀,true為連接中
    private boolean mBound = false;
    private BookManager bookManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.bt_get).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                addBook();
            }
        });
    }

    private void addBook() {
        if (!mBound) {
            attemptToBindService();
            Toast.makeText(this, "當(dāng)前與服務(wù)端處于未連接狀態(tài)咖刃,正在嘗試重連泳炉,請稍后再試", Toast.LENGTH_SHORT).show();
            return;
        }
        if (bookManager == null) return;

        Book book = new Book();
        book.setName("APP研發(fā)錄In");
        book.setPrice(30);
        try {
            bookManager.addBook(book);
            Log.e(getLocalClassName(), book.toString());
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    //開啟服務(wù)
    private void attemptToBindService() {
        Intent intent = new Intent();
        intent.setAction("action.ipc");
        intent.setPackage("com.hanny.aidldemo");
        bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
    }

    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            bookManager = BookManager.Stub.asInterface(service);
            mBound = true;
            if (bookManager != null) {
                try {
                    List books = bookManager.getBooks();
                    Log.d("PHN", "初始化得到的書: "+books.toString());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mBound = false;
        }
    };


    @Override
    protected void onStop() {
        super.onStop();
        if (mBound) {
            unbindService(mServiceConnection);
            mBound = false;
        }
    }

}

下面先運(yùn)行服務(wù)端apk 花鹅,然后運(yùn)行客服務(wù)apk然后點(diǎn)擊增加圖書枫浙,看看客戶端和服務(wù)端的打印結(jié)果:

Paste_Image.png

反正就是這么簡單,動手實(shí)踐加深印象箩帚,并沒有什么難的地方,

注意地方

AIDL 發(fā)生異常的原因 Android java.lang.SecurityException: Binder invocation to an incorrect interface

解決方法如下:
在使用上請注意盔然,服務(wù)端與客戶端都要有相同的接口(使用到的)焕参,這里的“相同”是指完全相同,包括包名叠纷,也就是說要在不同工程下建立相同的包名潦嘶,這樣一來,問題應(yīng)該迎刃而解了掂僵!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市幔睬,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌麻顶,老刑警劉巖舱卡,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異轮锥,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進(jìn)店門赵辕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來杯活,“玉大人,你說我怎么就攤上這事旁钧。” “怎么了嚎幸?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵寄猩,是天一觀的道長。 經(jīng)常有香客問我田篇,道長,這世上最難降的妖魔是什么泊柬? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮状答,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘惊科。我一直安慰自己亮钦,他們只是感情好馆截,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布孙咪。 她就那樣靜靜地躺著,像睡著了一般翎蹈。 火紅的嫁衣襯著肌膚如雪男公。 梳的紋絲不亂的頭發(fā)上合陵,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天澄阳,我揣著相機(jī)與錄音,去河邊找鬼碎赢。 笑死,一個胖子當(dāng)著我的面吹牛肮塞,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播枕赵,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼拷窜!你這毒婦竟也來了开皿?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤篮昧,失蹤者是張志新(化名)和其女友劉穎赋荆,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體懊昨,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡窄潭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了疚颊。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片狈孔。...
    茶點(diǎn)故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡信认,死狀恐怖材义,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情嫁赏,我是刑警寧澤其掂,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站潦蝇,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏攘乒。R本人自食惡果不足惜贤牛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望则酝。 院中可真熱鬧殉簸,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蝠检,卻和暖如春沐鼠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背叹谁。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工饲梭, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人本慕。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓排拷,卻偏偏與公主長得像,于是被迫代替她去往敵國和親锅尘。 傳聞我的和親對象是個殘疾皇子监氢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評論 2 355

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