Android技能樹 — 多進程相關小結

前言

最近過完年了,打算把本身的Android知識都整理一下。html

Android技能書系列:java

Android基礎知識android

Android技能樹 — 動畫小結git

Android技能樹 — View小結github

Android技能樹 — Activity小結面試

Android技能樹 — View事件體系小結算法

Android技能樹 — Android存儲路徑及IO操做小結數組

Android技能樹 — 多進程相關小結bash

Android技能樹 — Drawable小結數據結構

Android技能樹 — Fragment整體小結

數據結構基礎知識

Android技能樹 — 數組,鏈表,散列表基礎小結

Android技能樹 — 樹基礎知識小結(一)

算法基礎知識

Android技能樹 — 排序算法基礎小結

此次是講Android存儲路徑及IO的基本操做。由於咱們在開發的時候會常常這種方便的需求。這篇文章的內容我寫的可能不多,都沒有細寫。別吐槽。o( ̄︶ ̄)o

其餘很少說,先上腦圖:

多進程小結腦圖下載

多進程

進程與線程

有時候面試別人的時候,我會問說什麼是多進程,怎麼開啓多進程,他們會說new 一個Thread。因此不少人會把多進程和多線程弄錯。我就簡單說明下:通常來講咱們啓動一個APP,就是一個進程,而後這個APP裏面有不少線程,最熟悉的就是咱們日常的主線程(UI)線程。因此進程是包含線程的。

固然我這講的就比較通俗了: 能夠看下其餘相似的文章介紹:Android--進程與線程

開啓多進程

其實開啓多進程很簡單。只須要在AndroidManifest.xml的四大組件中添加android:process便可。這時候就會運行在你定義好的名字的進程中了。

多進程開啓後的問題

簡單來講就是同步會有問題。咱們剛纔說了通常來講啓動一個APP,就建立了一個進程,而後全部的東西都在這個進程裏面。這時候你對某個Activity定義了android:process。他就運行在另一個進程了。這時候Application也會從新建立一次,在這個新的進程中。這個Activity也會在這個新的進程中。並且咱們創建的一些實體類對象也是不一樣進程裏面各自產生本身的副本對象。互不關聯。

因此咱們知道了一些線程同步。單例模式都無效了,由於對象是各個進程中有副本,同步鎖的鎖對象都不是同一個對象。固然線程同步機制就失效了。

其中SharePreferences自己是一個文件,因此不受多進程的影響,可是由於SharePreferences不支持多個進程同時執行寫操做,因此有可能會致使出現數據丟失等問題。甚至是併發讀和寫也可能有問題。可是若是你只是一個進程寫,一個進程讀,並且不是同時,那就問題不大了。

進程間通訊

既然說了多進程,若是咱們如今就是二個進程進行通訊怎麼辦。在講如何通訊以前,咱們能夠先看下相關的基礎,那就是序列化及反序列化。

咱們看序列化有哪些:

咱們能夠看到,序列化通常主要是二個,那就是Serialzable和Parcelable。

具體的時候都很簡單。下面寫大體提下這二個的使用。

Serialzable

User.java (要傳遞的實體類)

public class User implements Serializable {

	private static final long serialVersionUID = 512345678910L;

    public int userId;
    public String userName;
    public boolean isMale;

    public User(int userId, String userName, boolean isMale) {
        this.userId = userId;
        this.userName = userName;
        this.isMale = isMale;
    }
}
複製代碼

咱們只要直接將咱們的類實現Serializable接口便可。很簡單。這裏我提一下serialVersionUID。由於咱們平時寫都不會寫這個。也是正常使用。可是好比我把這個User對象經過ObjectOutputStream序列化後寫到了本地文件,可是這時候咱們把咱們的User對象裏面的屬性改了,好比增長了一項:public boolean haha;而後再經過ObjectInputStream去讀取出來就會拋異常。由於反序列化會和序列化時候的serialVersionUID進行比較,若是不一樣,直接不進行反序列化了,就拋出異常。可是咱們不手動寫這個值,它會根據當前這個類結構去生成的hash值爲值。因此當咱們把這個類結構更改後,再去反序列化就報錯了。

Parcelable

public class User implements Parcelable {

    public int userId;
    public String userName;
    public boolean isMale;


    public User(int userId, String userName, boolean isMale) {
        this.userId = userId;
        this.userName = userName;
        this.isMale = isMale;
    }

    protected User(Parcel in) {

        userId = in.readInt();
        userName = in.readString();
        isMale = in.readInt() == 1;
    }

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

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

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {

        dest.writeInt(userId);
        dest.writeString(userName);
        dest.writeInt(isMale ? 1 : 0);

    }
}

複製代碼

你寫的類實現了Parcelable後AS會自動提示,你就按照他的提示生成相應的代碼便可。這裏咱們只要注意這麼幾個地方:

1.咱們在序列化前,總要先把這個類實例化成對象,而後把相應的內容賦值進去是吧,因此上面代碼中,我寫了個構造函數:

public User(int userId, String userName, boolean isMale) {
        this.userId = userId;
        this.userName = userName;
        this.isMale = isMale;
}
複製代碼

這樣咱們寫代碼的時候就new User(10,"dyp",true)(固然你也能夠寫setXXX方法去設置)

2.咱們要序列化了,咱們把咱們的這個類裏面的屬性值都寫進Parcel中,就比如咱們是拿了個本子,一行行的記下內容,而後等會一行行的取出來。因此咱們看到了。咱們是按照順序先記錄下來,因此等會還原的時候也要按順序取出來相應的值。因此順序很重要。

@Override
public void writeToParcel(Parcel dest, int flags) {
   //記下userId,由於是Int類型,因此用writeInt
   dest.writeInt(userId);
   //記下userName,由於是String類型,因此用writeString
   dest.writeString(userName);
   
   /*記下isMale ,由於是Boolean類型,
     可是沒有writeBoolean,只有writeBooleanArray,
     因此咱們用writeInt()來記錄,1是true,0是false。
     額外說下writeBooleanArray內部其實仍是用writeInt來記錄的。
   */
   dest.writeInt(isMale ? 1 : 0);
}
複製代碼

3.咱們最後傳到了其餘的進程,確定是要從Parcel裏面把咱們的對象給還原出來,確定是先new 一個User對象,而後把各類咱們前面第二步保存好的值給它從新賦值。

public static final Creator<User> CREATOR = new Creator<User>() {
        @Override
        public User createFromParcel(Parcel in) {
			
	    //這裏是否是咱們先進行了new一個對象,同時把Parcel對象傳入。
            return new User(in);
        }

        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };
複製代碼
protected User(Parcel in) {
	
     //而後咱們再生成這個對象的同時,再把這個對象的屬性都賦值好,切記要按照上面寫入的順序來讀取出來賦值。
     userId = in.readInt();
     userName = in.readString();
     isMale = in.readInt() == 1;
}
複製代碼

Binder

序列化的相關的基礎講了。咱們來看Binder ,其實吧,Binder我也不知道怎麼講,直接貼別的大佬的相關文章了。

圖文詳解 Android Binder跨進程通訊的原理

Android Binder之應用層總結與分析

而後這裏特別提一下:

進程間通訊方式

因此咱們能夠一個個具體來看實現進程間通訊方式。

使用Bundle

其實這個咱們平時用的不少,

我寫個Demo你們就知道了。

MainActivity.java

User user = new User(10,"dongyaoping",true);
Intent intent = new Intent(MainActivity.this,MyService.class);
Bundle bundle = new Bundle();
bundle.putSerializable("data",user);
bundle.putInt("int",10);
bundle.putString("string","haha");
intent.putExtras(bundle);
startService(intent);
複製代碼

MyService.java (記得在AndroidManifest.xml中設置android:process屬性,讓它在另一個進程)

public class MyService extends Service{

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        User user = (User) intent.getExtras().getSerializable("data");
        Log.v("dyp","user:"+user.toString());
        return super.onStartCommand(intent, flags, startId);
    }

}
複製代碼

因此咱們看到Bundle能夠put進去不少東西,由於Bundle自己實現了Parcelable

public final class Bundle extends BaseBundle implements Cloneable, Parcelable {}
複製代碼

使用文件共享

其實這個就更簡單了。咱們只須要把一個要傳的數據寫到一個文件,而後在另一個進程中去讀取這個文件就能夠了。

一個進程中取寫入:

User user = new User(100,"dyp",true);
try {
     ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(path));
     out.writeObject(user);
     out.close();
} catch (Exception e) {
     e.printStackTrace();
}
複製代碼

另一個進程中取讀取:

try {
     ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(path));
     User user = (User ) inputStream.readObject();                   
} catch (Exception e) {
     e.printStackTrace();
}
複製代碼

使用Socket

直接貼上大佬的文章:

Android:這是一份很詳細的Socket使用攻略

使用Binder

  1. AIDL
  2. Messenger
  3. ContentProvider

咱們主要講下AIDL,由於Messenger是AIDL的封裝,使用起來也更方便。AIDL會了。Messenger也就會使用。ContentProvider的教程就更多了。說實話平時四大組件中ContentProvider使用的頻率不多不多。因此我也不具體寫了,網上的教程也不少。

貼上大佬的ContentProvider教程:

Android:關於ContentProvider的知識都在這裏了!

貼上另外大佬的 Messenger的教程:

Android 進階10:進程通訊之 Messenger 使用與解析

咱們主要來看AIDL的實現:

具體的細節你們能夠看腦圖,我就不細說了。

咱們能夠看到在客戶端跨進程訪問服務端的時候,咱們分了五步。

第一步:建立AIDL文件。

這裏咱們要注意一點。咱們在AS中建立AIDL,直接就能夠右鍵 --> New --> AIDL便可。這時候會在這個目錄下面。

這時候咱們會看到這樣的界面。

interface IMyAidlInterface {

    String getInfor(String s);
    String getName(char name);

	//傳遞對象。
	String getBook(in Book book);

}

複製代碼

若是咱們要傳遞一個Book對象,這時候這個Book.java應該是在java包裏面,因此咱們同時還要再aidl文件夾中建立一個跟這個對象同名的aidl文件。因此變成了這樣:

不過必定要切記,整個Book.java和Book.aidl的包名要同樣。否則會提示找不到Book這個類。

第二步:聲明一個 IBinder 接口實例(或者基於 AIDL 生成)。

而後咱們build下以後,AS就會根據咱們寫的AIDL自動生成一個IBinder文件。(固然若是你第一步不寫AIDL,徹底本身寫一個IBinder文件也是能夠的。)

第三步:實現 ServiceConnection
final ServiceConnection connection = new ServiceConnection() {
      @Override
      public void onServiceConnected(ComponentName name, IBinder service) {
         
      }

      @Override
      public void onServiceDisconnected(ComponentName name) {
         
      }
};
複製代碼
第四步:調用 Context.bindService(),以傳入您的 ServiceConnection 實現。
Intent intent = new Intent(MainActivity.this, MyService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
複製代碼
第五步:onServiceConnected() 實現中實現相關操做

在你的 onServiceConnected() 實現中,你將收到一個 IBinder 實例(名爲 service)。調用 YourInterfaceName.Stub.asInterface((IBinder)service),以將返回的參數轉換爲 YourInterface 類型。

final ServiceConnection connection = new ServiceConnection() {
      @Override
      public void onServiceConnected(ComponentName name, IBinder service) {
          Log.v("dyp", "已經鏈接上了");
          IMyAidlInterface iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
          try {
              String haha = iMyAidlInterface.getInfor("hello,我是activity");
              Log.v("dyp", "接受到Service發過來的字符串:" + haha);
          } catch (RemoteException e) {
              e.printStackTrace();
          }
      }

      @Override
      public void onServiceDisconnected(ComponentName name) {
           Log.v("dyp","斷開了鏈接");
      }
};
複製代碼
服務端第一步:實例化 YourInterfaceName.Stub對象
private IBinder binder = new IMyAidlInterface.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }

        @Override
        public String getInfor(String s) throws RemoteException {
            Log.v("dyp","接收到Activity的字符串:"+s);
            return "service傳過去的字符串";
        }

        @Override
        public String getBook(Book book) throws RemoteException {
            return null;
        }

        @Override
        public String getName(char name) throws RemoteException {
            return null;
        }
};

複製代碼

服務端第二步:onBind方法中返回上面生成的對象

@Nullable
@Override
public IBinder onBind(Intent intent) {
    return binder;
}
複製代碼

各類通訊方式比較

直接複製別人網上的圖片:

結尾

我仍是不知道說啥,你們輕點噴我就行。。。。。

相關文章
相關標籤/搜索