1、程式和執行緒的概念:
在我的理解中,程式是一段被作業系統執行的指令集,作業系統在對資源>進行分配和排程時,程式是基本單位,程式其實就是一個程式。而執行緒是作業系統排程的最小單元。程式可以包含多個執行緒,多個執行緒可共享程式中的資源,另外執行緒的建立代價比執行緒要小
2、多程式的應用場景:
通常需要用到多程式的是,有常駐後臺的應用,例如音樂播放器的後臺播放服務,健身跑步的計算步數和跑步路徑模組等,這些模組需要脫離介面執行,而且需要長時間的在系統中執行,所以放在獨立的程式中執行是比較好的選擇。另外一種就是,如果一些應用很大,需要拆分模組,為了增加應用的記憶體用量,就需要開啟多個程式來擴大應用的記憶體用量,同時,將應用模組化,也更有利於解耦
3、實現多程式的方式:
在android中實現多程式有兩種方式,一種是在AndroidManifest檔案中給元件設定android:process屬性,這樣,這個元件就執行在設定的程式中了,另外一種就是利用jni在native層fork一個新的程式出來
4、android:process屬性的設定方式:
設定方式有兩種,一種是以“:xxx”的方式設定,這種方式代表該程式是應用的私有程式,其他應用的元件不可在該程式執行,另外一種就是以“xxx.xx.xx”類似包名的方式來設定,這種方式設定的程式是全域性的程式,其他應用的元件也可以在該程式上面執行,但是shareUID必須一樣才可以
5、在多程式開發中會遇到的問題:
1、靜態成員和單例模式完全失效:因為系統會為每個程式分配獨立的虛擬機器,不同的虛擬機器會有不同的記憶體空間,這會導致不同的虛擬機器訪問同一個類的物件會產生多個副本,我們修改其中一個副本只會影響當前程式,因此,靜態成員和單例模式就會完全失效
2、執行緒同步機制會完全失效:既然是不同的記憶體空間,那鎖的就不是同一個物件,因此無法保證執行緒同步
3、Sharedpreferences的可靠性下降:因為SharedPreferences不支援兩個程式同時執行寫操作
3、Application會被多次建立:不同的程式有不同的虛擬機器,當啟動一個新的程式時,相當於一個應用的啟動過程,那麼當然,就會建立一個新的Application
6、使用Serializable時需要注意的問題:
在實現Serializable的過程中,雖然serialVersionUID不寫是可以,但是這樣可能會造成在反序列化的過程失敗,因為這個serialVersionUID在序列化的過程中會被儲存進去,在反序列化的時候會先比較物件中的serialVersionUID是否與類中的serialVersionUID相同,如果在類中沒有設定這個值,那麼系統會自動計算hash值,而如果這個類的結構發生了改變,那麼這個自動計算的hash值就會重新計算,就會造成反序列化的失敗。因此,設定這個值的意義在於儘量讓反序列化可以成功,就算有時候這個類已經發生了部分改變。另外一個就是靜態成員不參與序列化,使用transient關鍵字標記的成員不參與序列化
7、Serializable與Parcelable的區別:
Parcelable是為了Android平臺而定製的,它的操作稍微複雜,但是效率很高,因此在記憶體的序列化上推薦使用Parcelable,而Serializable因為在序列化時需要進行大量的IO操作,因此雖然操作簡單,但是開銷很大,因此,在將物件序列化到儲存裝置中或者在網路中傳輸就推薦使用Serializable
8、實現多程式間通訊的方式
實現多程式間的通訊有很多種方式,1、利用Bundle進行資料的傳遞,它簡單易用,但是隻支援Bundle支援的資料型別,推薦在四大元件的程式通訊可以使用它。2、檔案共享,這種方式也比較簡單易用,但是這種方式不適合多併發的場景,而且無法進行程式間的即時通訊。3、Messenger。這種方式的底層是利用AIDL實現的,支援一對多的序列通訊,支援低併發的即時通訊。3、ContentProvider,ContentProvider的底層利用Binder實現,天生支援跨程式通訊,但是它只適合用來作為資料共享。4、Socket,這種方式通過網路傳輸位元組流也可以實現跨程式通訊,但實現稍微負責,適合網路間的資料交換。5、AIDL,最後一種,也是最常用的一種方式,功能強大,下面會詳細介紹
9、AIDL的一些理解
AIDL(Android Interface Difinition Language)是一種Android種獨有的定義語言,它的主要作用是為了簡化跨程式通訊程式碼的編寫,在編譯階段,我們編寫的AIDL檔案會生成相應的跨程式程式碼,簡單來說,它就是一種簡化的工具,沒有AIDL,我們一樣可以編寫出跨程式程式碼,只是會稍微繁瑣一點
AIDL只會接受基本的資料型別(通過實驗short還不行),String和CharSequence型別,然後如果是自定義的物件,需要實現Parcelable介面,並且需要在AIDL檔案中宣告出來,這樣才可以使用,當然也可以用集合(ArrayList,HashMap)作為引數,但是集合中的元素也必須是AIDL支援的型別。另外需要注意的就是AIDL檔案中的方法引數,是有一個資料流向的,通過in,out,inout三個標識來確定,如果引數設定為in,那麼資料的流向就是客戶端->伺服器(意思就是客戶端傳遞給伺服器的資訊,伺服器可以收到,但是伺服器如果改動了這個傳遞的物件,客戶端中的原物件是不會跟著發生改變的),如果引數設定為out,那麼資料的流向就是服務端->客戶端(意思就是客戶端傳遞給服務端的引數,服務端是無法收到的,但是服務端改動了這個物件的內容,在客戶端的原物件是會跟著改變的),如果引數設定為inout,那就代表著雙向流動,但是一般情況不提倡這樣設定,因為會增加開銷
10、使用AIDL需要注意的地方:
在使用AIDL的過程中,會有一種情形,就是當服務端處理了一些業務,需要主動通知客戶端,而不是等待客戶端發起請求才去響應,那麼這個時候就需要觀察者模式了,這個時候我們需要註冊監聽服務端的狀態,這個時候服務端需要儲存客戶端的監聽,因此,可能會有併發的情形,因此推薦使用RemoteCallbackList進行儲存。另外就是有時候服務端程式被kill掉,這時候會造成連結失敗,因此我們需要通過linkToDeath來繫結服務端,如果服務端程式殺死,客戶端可以收到響應,可以進行重連操作。
11、AIDL的實際操作:
在實際開發過程中,我們如果有很多模組都需要用到AIDL的話,那麼不可能每個模組開啟一個服務程式來處理,這樣造成的系統資源損耗是巨大的,因此我們需要利用Binder連線池來作為中間媒介,來連結各個AIDL的處理,下面的例子來演示相關實現
11.1 第一步
首先,建立兩個AIDL檔案IPlayMedia.aidl和IMonitorDevice.aidl,程式碼如下:
interface IPlayMedia {
void play(in String path);
void puase();
void stop();
String getCurrentMusicName();
}
interface IMonitorDevice {
void monitor(int a);
}
複製程式碼
11.2 第二步
實現AIDL介面,程式碼如下:
// IPlayMedia介面實現
public class PlayMediaImpl extends IPlayMedia.Stub{
@Override
public void play(String path) throws RemoteException {
Log.e("TAG","播放音樂:"+path);
}
@Override
public void puase() throws RemoteException {
Log.e("TAG","暫停播放");
}
@Override
public void stop() throws RemoteException {
Log.e("TAG","停止播放");
}
@Override
public String getCurrentMusicName() throws RemoteException {
Log.e("TAG","獲取當前歌曲名稱");
return "七里香";
}
}
// IMonitorDevice的實現
public class MonitorDeviceImpl extends IMonitorDevice.Stub {
@Override
public void monitor(int a) throws RemoteException {
Log.e("TAG","監控方法實現");
}
}
複製程式碼
11.3 第三步
接下來需要建立一個BinderPool連線池AIDL介面,這個介面是直接與服務端程式的服務互動的
interface BinderPool {
IBinder queryBinder(int binderCode);
}
複製程式碼
11.4 第四步
接下來建立一個服務AIDLService,設定android:process屬性,讓其可以在獨立的程式執行,先不做任何的實現,接著,建立BinderPoolUtils工具類,程式碼如下:
public class BinderPoolUtils {
public static final int BINDER_PLAY_MEDIA = 1;
public static final int BINDER_MONITOR_DEVICE = 2;
public static volatile BinderPoolUtils mInstance = null;
private Context mContext;
private test.com.testpoj.BinderPool mBinderPool;
private CountDownLatch mConnectBinderPoolDownLatch;
/**
* 單例模式
*/
public static BinderPoolUtils getInstance(Context context) {
if (mInstance == null) {
synchronized (BinderPoolUtils.class) {
if (mInstance == null) {
mInstance = new BinderPoolUtils(context);
}
}
}
return mInstance;
}
/**
* 查詢對應的Binder物件
*/
public IBinder queryBinder(int binderCode) {
IBinder binder = null;
try {
if (mBinderPool != null) {
binder = mBinderPool.queryBinder(binderCode);
}
} catch (RemoteException e) {
e.printStackTrace();
}
return binder;
}
/**
* 構造方法私有化
*/
private BinderPoolUtils(Context context) {
mContext = context;
connectBinderPoolService();
}
/**
* 繫結服務
*/
private synchronized void connectBinderPoolService() {
mConnectBinderPoolDownLatch = new CountDownLatch(1);
Intent intent = new Intent(mContext, AIDLService.class);
mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
try {
mConnectBinderPoolDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mBinderPool = test.com.testpoj.BinderPool.Stub.asInterface(service);
try {
mBinderPool.asBinder().linkToDeath(mBinderPoolDeathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
mConnectBinderPoolDownLatch.countDown();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
mBinderPool.asBinder().unlinkToDeath(mBinderPoolDeathRecipient, 0);
mBinderPool = null;
connectBinderPoolService();
}
};
/**
* BinderPool連結池的實現
*/
public static class BinderPoolImpl extends test.com.testpoj.BinderPool.Stub {
@Override
public IBinder queryBinder(int binderCode) throws RemoteException {
IBinder binder = null;
switch (binderCode) {
case BINDER_PLAY_MEDIA:
binder = new PlayMediaImpl();
break;
case BINDER_MONITOR_DEVICE:
binder = new MonitorDeviceImpl();
break;
default:
break;
}
return binder;
}
}
}
複製程式碼
有了上面的工具類,我們就可以在AIDLService中的onBinder方法中返回BinderPoolImpl的實現,然後我們呼叫的時候就可以這樣來呼叫:
BinderPoolUtils utils = BinderPoolUtils.getInstance(this);
IBinder playBinder = utils.queryBinder(BinderPoolUtils.BINDER_PLAY_MEDIA)
mPlayManager = IPlayMedia.Stub.asInterface(playBinder);
複製程式碼
這樣我們就可以呼叫IPlayMedia介面中的方法了