1.IPC
Inter-Process Communication,即進(jìn)程間通信或者跨進(jìn)程通信。
2.進(jìn)程與線程
進(jìn)程與線程是不同的概念,按照操作系統(tǒng)中的描述,線程是CUP調(diào)度的最小單元,而進(jìn)程一般是指一個執(zhí)行單元,對于PC和移動設(shè)備來說,通常指一個程序或者應(yīng)用的一次動態(tài)的過程。一個進(jìn)程可以包含多個線程,即進(jìn)程與線程是包含與被包含的關(guān)系。
3.什么情況下會出現(xiàn)多進(jìn)程
①一個應(yīng)用存在多進(jìn)程;②多個應(yīng)用之間構(gòu)成多進(jìn)程。
在Android中使用多進(jìn)程常規(guī)的方法只有一種,那就是在注冊四大組件時在AndroidManifest.xml中為四大組件聲明屬性 android:process屬性來指定該組件在哪個進(jìn)程中工作。例如本例中創(chuàng)建了3個Activity,它的AndroidManifest.xml文件如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.jdqm.ipcdemo">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".SecondActivity"
android:process=":remote" />
<activity android:name=".ThirdActivity"
android:process="com.jdqm.ipcdemo.remote"/>
</application>
</manifest>
其中為SecondActivity指定在新的進(jìn)程 “包名:remote“中工作,ThirdActivity在 “com.jdqm.ipcdemo.remote”進(jìn)程中工作,啟動應(yīng)用,并依此啟動這個三個Activity,通過菜單Tools-->Android-->Android Devices monitor來查看當(dāng)前設(shè)備的工作進(jìn)程。

8600、8603、8614這三個進(jìn)程就是該應(yīng)用新建的進(jìn)程。除了以上方式外,我們還可以通過命令行的方式去查看進(jìn)程信息: adb shell ps | grep com.jdqm.ipcdemo,其中com.jdqm.ipcdemo 是包名。

注:另外還有一種比較特殊的方式去創(chuàng)建多進(jìn)程,那就是在jni層去fork新的進(jìn)程。
在上面的例子中,android:process=":remote"與android:process="com.jdqm.ipcdemo.remote"有什么不同?
①前者會在冒號前添加當(dāng)前應(yīng)用的包名,即完整進(jìn)程名為:com.jdqm.ipcdeo:remote;
②前者是當(dāng)前應(yīng)用的私有進(jìn)程,其他的應(yīng)用不能訪問,而后者可以通過SharedUID方式與它跑在同一個進(jìn)程中。
4.使用多進(jìn)程會帶來什么問題?
①靜態(tài)成員和單例模式失效(每個進(jìn)程有獨立的虛擬機(jī));
②線程同步機(jī)制失效(既然不是同一塊內(nèi)存,不管是鎖對象還是鎖全局類都無法保證線程的同步);
④SharedPreferences可靠新下降(并發(fā)讀寫都有可能出現(xiàn)問題);
④Application會多次創(chuàng)建(系統(tǒng)創(chuàng)建一個新的進(jìn)程,就會分配一個新的虛擬機(jī),這個過程就是應(yīng)用啟動的過程);
5.進(jìn)程間通信有哪些方式?
Intent傳遞數(shù)據(jù),共享文件和SharedPreferences,基于Binder的Messenger和AIDL以及Socket等。
6.Android實現(xiàn)對象的序列化和反序列化的兩種方式
①實現(xiàn)erializable接口
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("/mnt/sdcard/cache.txt"));
User u = new User("張三", 18);
out.writeObject(u);
out.close();
ObjectInputStream in = new ObjectInputStream(new FileInputStream("/mnt/sdcard/cache.txt"));
User u = (User) in.readObject();
in.close();
通過Serrializable接口實現(xiàn)序列化和反序列化比較簡單,只要該類實現(xiàn)Serializable接口即可,那其中的 “private static final long serialVersionUID = 1L”指定的這個值有什么用?serialVersionUID 的工作機(jī)制是這樣的:在序列化時會把這個serialVersionUID寫入到序列化文件中(也可能是其他中介),當(dāng)反序列化時系統(tǒng)會去檢查這個值與當(dāng)前類的serialVersionUID是否相等,若相等則說明序列化時該類的版本與當(dāng)前的版本是一致的,這個時候是能成功被反序列化的;否則說明當(dāng)前類的結(jié)構(gòu)發(fā)生了某些變換,比如類的成員變量的數(shù)量、類型發(fā)生了改變,這個時候是無法正常被反序列化的。當(dāng)然這個值如果不指定的話,系統(tǒng)會根據(jù)類的結(jié)構(gòu)計算一個Hash值,一旦這個類發(fā)送任何改變都會引起這個值得改變。有時候雖然我們的類發(fā)生了改變,但是我們?nèi)钥梢岳梅葱蛄谢瘉砘謴?fù)原來的數(shù)據(jù),這個時候必須要手動聲明serialVersionUID。例如:假設(shè)序列化的時候類的結(jié)構(gòu)是這樣子的:
public class Student implements Serializable{
private static final long serialVersionUID = 1L;
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
通過序列化寫入文件
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("/mnt/sdcard/student.txt"));
Student stu = new Student("小明", 18);
out.writeObject(stu);
out.close();
這個時候我們?nèi)ジ淖冾惖慕Y(jié)構(gòu),增加一個字段 "private float score;",這個時候類的結(jié)構(gòu)發(fā)生了改變,但是我們指定serialVersionUID 不變,“欺騙”系統(tǒng)。
public class Student implements Serializable{
private static final long serialVersionUID = 1L;
private String name;
private int age;
private float score;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
}
在這種情況下去反序列化
ObjectInputStream in = new ObjectInputStream(new FileInputStream("/mnt/sdcard/student.txt"));
Student stu2 = (Student) in.readObject();
//name='小明', age=18, score=0.0
Log.d(TAG, "onCreate: " + stu2.toString());
in.close();
可以看到原來的兩個字段被正確的反序列化,而增加的字段則是一個默認(rèn)值。假設(shè)序列化時的serialVersionUID與反序列化時的不一致,則會報如下異常:
java.io.InvalidClassException: com.jdqm.ipcdemo.Student; Incompatible class (SUID): com.jdqm.ipcdemo.Student: static final long serialVersionUID =1L; but expected com.jdqm.ipcdeom.Student: static final long serialVersionUID =2L;
②Parcelable
只要實現(xiàn)這個接口的類的對象就可以通過Intent和Binder來傳遞。
public class Person implements Parcelable {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
/**
*返回當(dāng)前對象的內(nèi)容描述
* @return 0或者1,當(dāng)含有文件描述符時返回1,其他情況0,幾乎所有的情況都是返回0
*/
@Override
public int describeContents() {
return 0;
}
/**
*
* @param dest
* @param flags 這個標(biāo)記為可以是0或者1,為1時標(biāo)示當(dāng)前對象需要作為返回值返回,不能立即釋放。幾乎所有情況都是0
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
}
public static Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>(){
@Override
public Person createFromParcel(Parcel source) {
return new Person(source);
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
private Person(Parcel source) {
name = source.readString();
age = source.readInt();
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
7.Serializable與Parcelable有什么區(qū)別?
Serializable是java的序列化接口,使用起來簡單但是開銷大,會伴隨著大量的I/O操作,而Parcelable是Android中的序列化方式,稍顯復(fù)雜但是效率更高。Parcelable主要用于內(nèi)存序列化上,通過Parcelable將對象序列化到存儲設(shè)備上或者序列化后通過網(wǎng)絡(luò)傳輸也是可以的,但是過程會稍顯復(fù)雜,這兩種情況下建議使用Serializable。
8.Binder
Binder是一個類,實現(xiàn)了IBinder接口。從IPC的角度來說,Binder是Android的一種跨進(jìn)程通信方式。Binder還可以理解為一種虛擬的物理設(shè)備,它的設(shè)備驅(qū)動是/dev/binder。
9.Binder很重要的兩個方法linkToDeath和unlinkToDeath
當(dāng)我們發(fā)起RPC的過程中,由于某種原因服務(wù)端由于異常終止了,這個時候客戶端到服務(wù)端的Binder連接就斷了,這樣就導(dǎo)致遠(yuǎn)程調(diào)用失敗,如果我們不知道連接已經(jīng)斷了,那就會影響客戶端。為了解決這個問題,Binder提供一配對的方法linkToDeath和unlinkToDeath,通過linkToDeath我們可以給Binder設(shè)置死亡代理,當(dāng)Binder死亡時,就會收到通知,這個時候就可以選擇重連或者別的操作。那么問題來了,如何給Binder設(shè)置死亡代理?
1)聲明一個DeathRecipient對象
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
if (bookManager == null) {
return;
}
Log.d(TAG, "binderDied: ");
bookManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
bookManager = null;
Intent intent = new Intent(MainActivity.this, BookManagerService.class);
bindService(intent, connection,BIND_AUTO_CREATE);
}
};
2)在客戶端綁定服務(wù)成功后,給返回的Binder設(shè)置死亡代理
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "onServiceConnected: ");
bookManager = IBookManage.Stub.asInterface(service);
try {
bookManager.getBookList();
} catch (RemoteException e) {
e.printStackTrace();
}
try {
service.linkToDeath(mDeathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
}
另外,可以通過binder的isBinderAlive方法查看binder時候還“活著”。