
線程是CPU調(diào)度的最小單位,同時(shí)線程也是一種有限的系統(tǒng)資源。 進(jìn)程一般指的是一個(gè)執(zhí)行單元,在PC和移動(dòng)設(shè)備上指一個(gè)程序或者是一個(gè)應(yīng)用。
一個(gè)進(jìn)程可以有很多個(gè)線程,但只有一個(gè)線程的時(shí)候即為主線程,在android里也稱為UI線程。UI線程里才能去操作界面元素。很多時(shí)候,一個(gè)進(jìn)程需要執(zhí)行大量耗時(shí)任務(wù),如果這些任務(wù)放在主線程里,就會(huì)造ANR(應(yīng)用程序無(wú)響應(yīng))。解決這個(gè)問(wèn)題就需要用到線程,需要建立子線程通過(guò)Handle去操作些耗時(shí)操作或更新UI。
想要使用多進(jìn)程,只需在四大組件的xml文件里使用android:PRocess的屬性即可。
<activity android:name=".MainActivity" android:configChanges="orientation|screenSize" android:label="@string/app_name" android:launchMode="standard" > <intent-filter> <action android:name="android.intent.action.MAIN" /> </intent-filter> </activity> <activity android:name=".SecondActivity" android:configChanges="screenLayout" android:label="@string/app_name" android:process=":remote" /> <activity android:name=".ThirdActivity" android:configChanges="screenLayout" android:label="@string/app_name" android:process="com.ryg.chapter_2.remote" />讓我們來(lái)看看這三個(gè)Activity分別對(duì)應(yīng)的進(jìn)程是什么 
可以看到有三個(gè)進(jìn)程,分別表示在xml里聲明的三個(gè)Activity,一次對(duì)應(yīng)MainActivity,SecondActivity,ThirdActivity SecondActivity的進(jìn)程名字是com.ryg.chapter_2:remote ,:的前面部分是當(dāng)前進(jìn)程是在的包名。 ThirdActivity所對(duì)應(yīng)的進(jìn)程com.ryg.chapter_2 .remote ,這個(gè)命名方式是完整的命名方式,不會(huì)附加包名。
以:開頭的進(jìn)程是私有進(jìn)程,其他應(yīng)用組件是不會(huì)和它跑在同一個(gè)進(jìn)程里的,而不是以:開頭的則是全局進(jìn)程其他應(yīng)用可以通過(guò)ShareUID的方式,可以跑在同一個(gè)進(jìn)程里。
注:Android的系統(tǒng)會(huì)給每一個(gè)應(yīng)用分配一個(gè)唯一的UID,具有相同的UID才能共享數(shù)據(jù)。若兩個(gè)相同的UID想要跑在同一個(gè)進(jìn)程里是有要求的,必須要有相同簽名可以。這種情況下,他們可以互相訪問(wèn)私有數(shù)據(jù),還可以共享數(shù)據(jù)。
首先舉個(gè)例子好讓大家理解,在上面的基礎(chǔ)上,我們?cè)偬砑右粋€(gè)類,并且聲明一個(gè)全局變量如下圖 public  class UserManager{ public  int sUserId = 1;}
那么我們需要在MainActivity中去修改sUserId ,把值改為2 并且打印出來(lái),在SecondActivity將sUserId打印出來(lái),結(jié)果 
按照常理來(lái)說(shuō)SecondActivity應(yīng)該輸出2,因?yàn)镸ainActivity已經(jīng)把值改為2了,而事實(shí)卻是相反的,因?yàn)锳ndroid中每一個(gè)進(jìn)程,都會(huì)為其分配一個(gè)DVM(虛擬機(jī)),所以每一個(gè)進(jìn)程都是相對(duì)獨(dú)立的,而MainActivity修改的值,僅對(duì)同一進(jìn)程有效。SecondActivity和MainActivity并不在同一個(gè)進(jìn)程中,所以MainActivity修改的值SecondActivity會(huì)接收不到。
所以這就是多進(jìn)程帶來(lái)的主要影響有以下幾點(diǎn) 1. 靜態(tài)成員和單例模式完全失效 2. 線程同步機(jī)制完全失效 3. SharePreferences的可靠性降低 4. application會(huì)被多次創(chuàng)建
第一個(gè)影響就是上面的例子,第二個(gè)影響也一樣,既讓都不在同一個(gè)虛擬機(jī)中,不再同一個(gè)進(jìn)程中線程又怎么去同步呢。第三個(gè)影響就是Sharepreferences本生不支持兩個(gè)進(jìn)程同事去寫,這樣有一點(diǎn)的幾率會(huì)造成數(shù)據(jù)的丟失。第四個(gè)影響每一個(gè)應(yīng)用對(duì)于一個(gè)進(jìn)程,對(duì)于一個(gè)虛擬機(jī),對(duì)于一個(gè)Application,所以開啟多個(gè)進(jìn)程會(huì)開啟多個(gè)虛擬機(jī)開啟多個(gè)Application 是一樣的道理。
主要包括三個(gè),serializable接口,parcelable接口以及binder
Serializable 是java提供的一個(gè)序列化的接口,就是對(duì)對(duì)象進(jìn)行序列化和反序列化的操作。首先想要實(shí)現(xiàn)序列化的操作需要讓類去實(shí)現(xiàn)Serialezable接口并聲明一個(gè)serialVersionUID即可,事實(shí)上serialVersionUID并不是必須的,不聲明serialVersionUID也可以實(shí)現(xiàn)序列化。
public class User implements Serializable { private static final long serialVersionUID = 519067123721295773L; public int UserId; public String username; public boolean isMale; }就完成對(duì)象的序列化操作,幾乎所有的工作都讓系統(tǒng)去自動(dòng)完成。 如何對(duì)對(duì)象進(jìn)行序列化和反序列化的操作,只需采用ObjectInputStream和ObjectOutputStream
//序列化的過(guò)程 String CHAPTER_2_PATH = Environment.getExternalStorageDirectory().getPath() + "/singwhatiwanna/chapter_2/"; String CACHE_FILE_PATH = CHAPTER_2_PATH + "usercache"; User user = new User(1, "hello world", false); File dir = new File(MyConstants.CHAPTER_2_PATH); if (!dir.exists()) { dir.mkdirs(); } File cachedFile = new File(MyConstants.CACHE_FILE_PATH); ObjectOutputStream objectOutputStream = null; try { objectOutputStream = new ObjectOutputStream( new FileOutputStream(cachedFile)); objectOutputStream.writeObject(user); Log.d(TAG, "persist user:" + user); } catch (IOException e) { e.printStackTrace(); } finally { MyUtils.close(objectOutputStream); }//反序列化過(guò)程 User user = null; File cachedFile = new File(MyConstants.CACHE_FILE_PATH); if (cachedFile.exists()) { ObjectInputStream objectInputStream = null; try { objectInputStream = new ObjectInputStream( new FileInputStream(cachedFile)); user = (User) objectInputStream.readObject(); Log.d(TAG, "recover user:" + user); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { MyUtils.close(objectInputStream); } }這就是一個(gè)簡(jiǎn)單的使用,將對(duì)象輸出到txt中就是序列化,將txt的數(shù)據(jù)讀取出來(lái)就是反序列化。
android中Intent 是可以來(lái)傳遞一個(gè)序列化的對(duì)象 
好了 之前說(shuō)serialVersionUID 沒(méi)有也可以序列化,但到底有什么用呢? 1. serialVersionUID就像是標(biāo)識(shí)符,當(dāng)你序列化的時(shí)候serialVersionUID的值也會(huì)被寫入,在反序列化的時(shí)候 系統(tǒng)會(huì)通過(guò)對(duì)比類里的serialVersionUID與文件中的serialVersionUID 是否一致來(lái)進(jìn)行反序列化,但一致的時(shí)候反序列化成功不一致則不成功。 2. 當(dāng)你沒(méi)聲明serialVersionUID的時(shí)候,在反序列化的時(shí)候 修改了類那么反序列化會(huì)不成功 因?yàn)楫?dāng)前類與序列化時(shí)候的類不一樣導(dǎo)致了反序列化的失敗。(因?yàn)椴宦暶鱯erialVersionUID,系統(tǒng)會(huì)自動(dòng)聲明serialVersionUID,因?yàn)槟氵M(jìn)行類修改所以會(huì)導(dǎo)致 前后兩次serialVersionUID的值不一樣,所以反序列化失敗) 3. 但是如果你手動(dòng)聲明了serialVersionUID,當(dāng)你修改了序列化對(duì)象,對(duì)其進(jìn)行增刪改屬性之后再進(jìn)行反序列化,此時(shí)反序列化會(huì)是成功的,也是無(wú)論修改這樣serialVersionUID的值都是一樣的,系統(tǒng)會(huì)最大程度的去回復(fù)數(shù)據(jù)。
Parcelable也是序列化的一種方式,使用Parcelable只需去實(shí)現(xiàn)Parcelable接口即可
public class User implements Parcelable { public int userId; public String userName; public boolean isMale; public User() { } public User(int userId, String userName, boolean isMale) { this.userId = userId; this.userName = userName; this.isMale = isMale; } public int describeContents() { return 0; } public void writeToParcel(Parcel out, int flags) { out.writeInt(userId); out.writeString(userName); out.writeInt(isMale ? 1 : 0); } public static final Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>() { public User createFromParcel(Parcel in) { return new User(in); } public User[] newArray(int size) { return new User[size]; } }; private User(Parcel in) { userId = in.readInt(); userName = in.readString(); isMale = in.readInt() == 1; }}
 其實(shí)下列方法都會(huì)自動(dòng)生成,不需要手打 
  既讓Serializable 和Parcelable都可以序列化,那就說(shuō)說(shuō)兩者之間的區(qū)別,Serializable 在序列化和反序列化的時(shí)候,需要進(jìn)行大量 I/O操作,很耗時(shí)。Parcelable是android的序列化方式,他很高效,但是使用起來(lái)很麻煩。 Parcelable的性能比Serializable好,在內(nèi)存開銷方面較小,所以在內(nèi)存間數(shù)據(jù)傳輸時(shí)推薦使用Parcelable,如activity間傳輸數(shù)據(jù),而Serializable可將數(shù)據(jù)持久化方便保存,所以在需要保存或網(wǎng)絡(luò)傳輸數(shù)據(jù)時(shí)選擇Serializable,因?yàn)閍ndroid不同版本Parcelable可能不同,所以不推薦使用Parcelable進(jìn)行數(shù)據(jù)持久化
Binder就是android的一個(gè)類,它實(shí)現(xiàn)了IBinder的接口。從IPC角度上來(lái)說(shuō),就是Binder是Android的一種跨進(jìn)程通信的方式。從Android Framework的角度來(lái)分析就是Binder 是ServiceManager用來(lái)連接各種Manager的(ActivityManager WindowManager)橋梁。從Android應(yīng)用層的角度來(lái)分析就是客戶端和服務(wù)端進(jìn)行通信的媒介,當(dāng)bindService的時(shí)候,服務(wù)端會(huì)調(diào)用一個(gè)Binder對(duì)象,通過(guò)這個(gè)對(duì)象,客戶端就可以獲取到一些數(shù)據(jù)。
在Android的開發(fā)中,Binder主要用在Service中,包括AIDL和Messenger。普通的Service中的Binder不涉及到進(jìn)程通信,所以適用AIDL來(lái)分析Binder的工作機(jī)制。
先演示一下AIDL的建立和通過(guò)AIDL生成JAVA文件去看看Binder的機(jī)制



其中Book.java是自定義類,用途就是個(gè)Bean。如果需要使用自定義類 必須要建立一個(gè)同名的aidl文件。 系統(tǒng)可能會(huì)找不到我們創(chuàng)建的類,那么就是要一下聲明
sourceSets { main { manifest.srcFile ='src/main/AndroidManifest.xml' java.srcDirs = ['src/main/java', 'src/main/aidl'] resources.srcDirs = ['src/main/java', 'src/main/aidl'] aidl.srcDirs = ['src/main/aidl'] res.srcDirs = ['src/main/res'] assets.srcDirs = ['src/main/assets'] }}接下來(lái) 我們來(lái)看看生成的JAVA文件 去看看Binder的機(jī)制,先看看生成的java文件,代碼確實(shí)比較多,我就截取一個(gè)方法為例子,我直接在代碼上進(jìn)行解釋
public interface IBookManager extends android.os.IInterface{/** Stub是運(yùn)行在服務(wù)端中的 */public static abstract class Stub extends android.os.Binder implements com.example.gyh.myapplication.IBookManager{//這個(gè)屬性就是Binder的唯一標(biāo)識(shí),也就是包名private static final java.lang.String DESCRIPTOR = "com.example.gyh.myapplication.IBookManager";/** Construct the stub at attach it to the interface. */public Stub(){this.attachInterface(this, DESCRIPTOR);}//該方法就是為了將服務(wù)端的Binder轉(zhuǎn)化為用戶端的AIDL接口類型的對(duì)象,這種轉(zhuǎn)化過(guò)程是區(qū)分進(jìn)程的,若是同一個(gè)進(jìn)程則就返回服務(wù)端本生的Stub對(duì)象,不是則轉(zhuǎn)化成Stub.Proxy對(duì)象。public static com.example.gyh.myapplication.IBookManager asInterface(android.os.IBinder obj){if ((obj==null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin!=null)&&(iin instanceof com.example.gyh.myapplication.IBookManager))) {return ((com.example.gyh.myapplication.IBookManager)iin);}return new com.example.gyh.myapplication.IBookManager.Stub.Proxy(obj);}//該方法就是返回Binder對(duì)象@Override public android.os.IBinder asBinder(){return this;}//該方法是運(yùn)行在服務(wù)端的Binder線程池中的,當(dāng)客戶端發(fā)起跨進(jìn)程請(qǐng)求的時(shí)候,遠(yuǎn)程請(qǐng)求會(huì)通過(guò)系統(tǒng)底層的封裝后交由改方法進(jìn)行處理。現(xiàn)在來(lái)解釋一下里面的參數(shù)代表著什么意思code表示客戶端請(qǐng)求的方式是什么,data保存著方法的參數(shù),reply是要返回的值,flags為false的時(shí)候,客戶端請(qǐng)求失敗,這樣利用這參數(shù)做權(quán)限驗(yàn)證。@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_addBook:{data.enforceInterface(DESCRIPTOR);com.example.gyh.myapplication.Book _arg0;if ((0!=data.readInt())) {_arg0 = com.example.gyh.myapplication.Book.CREATOR.createFromParcel(data);}else {_arg0 = null;}this.addBook(_arg0);reply.writeNoException();return true;}}return super.onTransact(code, data, reply, flags);}//該類是運(yùn)行在客戶端中的private static class Proxy implements com.example.gyh.myapplication.IBookManager{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;}@Override public void addBook(com.example.gyh.myapplication.Book book) throws android.os.RemoteException{//首先要?jiǎng)?chuàng)立三個(gè)對(duì)象,_data,_reply,_result , 然后如果有參數(shù)的話把參數(shù)寫到_data中,接著調(diào)用transact方法去發(fā)起遠(yuǎn)程過(guò)程調(diào)用(RPC),同時(shí)將現(xiàn)場(chǎng)掛起(就是暫停的意思);然后服務(wù)端的ontransact調(diào)用,直到RPC過(guò)程返回后,該線程繼續(xù)。并從_reply中取出返回值,并且賦予_result去返回。android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();try {_data.writeInterfaceToken(DESCRIPTOR);if ((book!=null)) {_data.writeInt(1);book.writeToParcel(_data, 0);}else {_data.writeInt(0);}//transact回去調(diào)用stub中的ontransact方法,找到對(duì)應(yīng)的方法也就是ontransact里的addBook方法,取出返回的_reply,從中取出客戶端需要的返回值mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);_reply.readException();}finally {_reply.recycle();_data.recycle();}}}static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);}public void addBook(com.example.gyh.myapplication.Book book) throws android.os.RemoteException;}通過(guò)上面的分析,我們可以知道客戶端發(fā)起遠(yuǎn)程請(qǐng)求時(shí),當(dāng)前線程會(huì)被掛起直到服務(wù)端進(jìn)程返回結(jié)果,這是一個(gè)耗時(shí)操作,所以不能再UI線程中發(fā)起。其實(shí)服務(wù)端的Binder是運(yùn)行在Binder線程池中的,所以不管Binder是否耗時(shí)都應(yīng)該是同步的。下圖解釋 
 那么我再來(lái)總的概括一下,當(dāng)我們和服務(wù)器建立連接之后,通過(guò)服務(wù)器返回的Binder,將Binder轉(zhuǎn)化為客戶端的AIDL接口對(duì)象,因?yàn)槭强邕M(jìn)程所以返回的是Stub.Proxy對(duì)象,通過(guò)這個(gè)對(duì)象去調(diào)用相應(yīng)的方法如addBook,addBook方法中則會(huì)通過(guò)tranasct方法去調(diào)用Stub中onTranasct方法中對(duì)應(yīng)的addBook,并通過(guò)_reply返回相應(yīng)的數(shù)據(jù)給客戶端。這樣一來(lái)整個(gè)跨進(jìn)程通信就結(jié)束了。
Binder的兩個(gè)很重要的方法linkToDeath和unlinkToDeath,當(dāng)我們服務(wù)端的進(jìn)程由于某種原因斷裂,那么對(duì)導(dǎo)致我們遠(yuǎn)程調(diào)用失敗。但更為重要的是我們不知道Binder是否斷裂,那么客戶端的功能就會(huì)收到影響,所以Binder給我們配置了兩個(gè)方法,通過(guò)linkToDeath我們可以設(shè)置一個(gè)死亡代理,當(dāng)Binder死亡的時(shí)候,可以給客戶端發(fā)來(lái)消息,從而我們可以重新開啟服務(wù)。然后如何去設(shè)置這個(gè)代理呢?我們先來(lái)看看demo
 首先聲明一個(gè)DeathRecipent對(duì)象,DeathRecipent是一個(gè)接口,其內(nèi)部只有一個(gè)方法binderDied,當(dāng)我們實(shí)現(xiàn)該方法的時(shí)候,當(dāng)Binder死亡的時(shí)候系統(tǒng)就會(huì)回調(diào)改方法,然后我們可以移除之前綁帶的Binder去重新開去新的Binder
linkToDeath方法的第二個(gè)參數(shù)是標(biāo)記位,我們直接可以設(shè)置為0,這樣我們就設(shè)置好了死亡代理。另外我們可以通過(guò)Binder的isBinderAlive的方法去判斷Binder是否死亡。
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注