Android點將臺:你敢摸我貓 [- IPC -]

零、前言:本文源碼:GitHub

1.先在視覺上瞄一下進程和線程的區別


2.再從感性上類比一下進程和線程
若是手機是地球,進程就像一家公司,公司使用着地球的資源,也在地球上扮演一個獨立的個體,實現本身特有的功能與價值。
而線程就像公司裏的人,能夠共享公司的公共資源,處理屬於本身的任務,實現自身的功能與價值。
能夠說進程(公司)是給線程(人)一個運行(工做)的環境。於此同時進程也得到了它的地位。

因此一個進程至少要一個線程來完成任務。進程銷燬後,裏面的線程也就失業拜拜了。
好比某公司的人(線程)集體罷工(崩潰),那公司不管曾經叫什麼,都沒有意義。公司(進程)倒閉了,再多的線程(人)也沒卵用。

多進程就像若干個公司聯盟作一個項目,這時候各個公司的內部資源(靜態成員、單例等)就再也不適用,
就像別的公司人到你公司吃你的零食,敲你鍵盤,摸你貓,你給嗎? 不給,堅定不給。
而後那人非要吃你零食,敲你鍵盤,摸你貓,還搞出個職位叫IPC,說什麼跨進程間通訊。TM說白了就是專門搶你零食,搶你貓,你說氣不氣人。
複製代碼

3.最後走一波概念
IPC(Inter-Process Communication): 進程間通訊或者跨進程通訊
進程:指的一個執行單元,在PC和移動設備上指的是一個程序或者一個應用。
線程:在操做系統中,線程是CPU調度的最小單元,也是一種有限的系統資源。
進程與線程關係:一個進程能夠包含多個線程,所以進程和線程是包含被包含的關係。
複製代碼

2、如何在一個應用建立多個進程

1.三個測試Activity

三個按鈕跳轉三個Activity,佈局就不貼了。只貼一個MainActivity0,其餘兩個MainActivity1,MainActivity2java

class MainActivity0 : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        title="MainActivity0"
        to_one.setOnClickListener {
            startActivity(Intent(this, MainActivity0::class.java))
        }
        to_two.setOnClickListener {
            startActivity(Intent(this, MainActivity1::class.java))
        }
        to_three.setOnClickListener {
            startActivity(Intent(this, MainActivity2::class.java))
        }
    }
}
複製代碼

2.AndroidManifest.xml配置文件

私有進程:有:---------- 全局進程:沒有: 名字能夠隨便取,只要惟一android

<activity android:name=".MainActivity1"
          android:process=":ipc">
</activity>
<activity android:name=".MainActivity2"
          android:process="com.toly1994.ipc.test">
</activity>
複製代碼

3、多進程與單進程的區別

1.打開Activity1時

不加的話,直接經過窗口管理器來顯示Activity1git


加的話,會在孵化一個進程。zygote64的日誌不少,下面只是一小部分。
不清楚Activity啓動和View加載過程的小夥伴,能夠看一下這個日誌,也許會有幫助
好比下面完美呈現了LayoutInflater是怎麼運行的,再跟着源碼走一走,你會有所收穫github


而後發現確實是多了兩個,名字也能對應上數據庫


2.Application的屢次實例化

既然開一個進程會孵化一次,ActivityThread的main方法被觸發,Application天然會被新建
喵了個咪的,建立了三個,一個進程一個。這顯然值得注意,自定義Application初始化第三方庫什麼的bash

public class CatApplication extends Application {
    private static final String TAG = "CatApplication";

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e(TAG, "onCreate: 建立了小貓土土");
    }
}
複製代碼

3.靜態成員變量沒法在不一樣進程獲取
public class CatManager {
    public static Cat cat = new Cat();

    public CatManager() {
        cat = new Cat();
        cat.color = "灰色" + Math.random();
        cat.name = "土土";
    }
}

---->[MainActivity0#oncreate]------------------
CatManager()

---->[MainActivity1#oncreate]------------------
Log.e("CatManager", ": "+CatManager.cat.color);//null 

|--- 說明在MainActivity1裏已經初始化的靜態成員變量沒法在MainActivity2(另外一個進程)使用
|--- 若是將MainActivity2的process去掉能夠打印:灰色0.22701789806635642
|--- 這就尷尬了,個人惟一玩到666的單例腫麼辦?
複製代碼

4.單例模式會怎麼樣?

新建一個Cat(貓)和CatManager(鏟屎官)的類網絡

---->[Cat]------------------------------------
public class Cat {
    public String name;
    public String color;

    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' + ", color='" + color + '\'' + '}'; } } ---->[CatManager]------------------------------------ public class CatManager { private volatile static CatManager sCatManager; private static Cat cat=new Cat(); private CatManager() { } public static CatManager newInstance() { if (sCatManager == null) { synchronized (CatManager.class) { if (sCatManager == null) { sCatManager = new CatManager(); Log.e("CatApplication", "newInstance: "); cat.color = "灰色"+Math.random(); cat.name = "土土"; } } } return sCatManager; } public Cat getCat() { return cat; } } ---->[CatApplication]------------------------------------ public class CatApplication extends Application { private static final String TAG = "CatApplication"; @Override public void onCreate() { super.onCreate(); CatManager manager = CatManager.newInstance(); Log.e("CatApplication", manager.getCat().toString()); } } 複製代碼
2019-05-08 10:18:02.482 25524-25524/? E/CatApplication: newInstance: 
2019-05-08 10:18:02.482 25524-25524/? E/CatApplication: Cat{name='土土', color='灰色0.8695394451026908'}
2019-05-08 10:18:04.761 25561-25561/com.toly1994.ipc:ipc E/CatApplication: newInstance: 
2019-05-08 10:18:04.761 25561-25561/com.toly1994.ipc:ipc E/CatApplication: Cat{name='土土', color='灰色0.9824119267379914'}
2019-05-08 10:18:07.096 25597-25597/com.toly1994.ipc.test E/CatApplication: newInstance: 
2019-05-08 10:18:07.096 25597-25597/com.toly1994.ipc.test E/CatApplication: Cat{name='土土', color='灰色0.18620946012650275'}
複製代碼

可見單例也沒有卵用了,每次開啓進程都會執行到newInstance,致使單例的失調。多線程


5.小結:多進程帶來的問題(老生常談)
Application會屢次建立:開啓一個進程其實就等同於開多一個Application
靜態成員和單例模式徹底失效(處於不一樣的內存塊(進程),擁有各自的副本) 
SharedPreferences的可靠性下降:由於SharedPreferences不支持兩個進程同時去讀寫xml文件 
線程同步機制徹底失效(同一差很少) 
複製代碼

3、IPC的幾種形式

爲了多公司聯盟(多進程)間的和諧,如今決定犧牲貓,讓它能夠被過各公司(進程)共享併發

[1].經過Intent傳遞Bundle對象通訊: 簡單,數據類型侷限,用於組件間傳遞數據
[2].使用共享文件通訊: 簡單,實時性差,不適合高併發
[3].使用Messenger通訊: 支持一對多串行通訊,支持實時通訊,不支持RPC
[4].使用AIDL通訊: 支持一對多併發通訊,適用於,一對多通訊且有RPC需求
[5].使用ContentProvider: 支持一對多併發數據共享
[6].使用Socket: 能夠經過網絡傳輸字節流,支持一對多併發實時通訊
複製代碼

0.如今的CatManager類和Cat類

既然單例不能用,就不用。這裏默認開局一隻貓。Cat實現序列化接口Serializabledom

public class CatManager {
    private static List<Cat> cats = new ArrayList<>();
    public CatManager() {
        Cat tutu = new Cat();
        tutu.color = "灰色" + Math.random();
        tutu.name = "土土";
        add(tutu);
    }
    public void add(Cat cat) {
        cats.add(cat);
    }
    public Cat getCatAt(int index) {
        return cats.get(index);
    }
}

public class Cat implements Serializable {
    public String name;
    public String color;

    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' + ", color='" + color + '\'' + '}'; } } 複製代碼

1.IPC之Intent發送Bundle對象通訊
1-1.Serializable序列化對象實現
---->[MainActivity0#oncreate]------------------
to_two.setOnClickListener {
    val cat = CatManager().getCatAt(0)
    val bundle = Bundle()//建立Bundle對象
    bundle.putSerializable("cat", cat)//把貓裝到Bundle裏,貼個標籤cat
    val intent = Intent(this, MainActivity1::class.java)
    intent.putExtras(bundle)
    startActivity(intent)
}

---->[MainActivity1#oncreate]------------------
val cat = intent.extras?.get("cat") as Cat //把Bundle用打開標籤cat,而後貓到手
Log.e("MainActivity1", ": " + cat.name)//MainActivity1能夠對貓隨心所欲,IPC 通訊完成
複製代碼

注:固然你也能夠直接經過Intent發送序列化(Serializable)對象,源碼瞄一眼,都是經過Bundle的,並沒有本質區別

public @NonNull Intent putExtra(String name, Serializable value) {
    if (mExtras == null) {
        mExtras = new Bundle();
    }
    mExtras.putSerializable(name, value);
    return this;
}
複製代碼

1-2.Parcelable序列化對象實現

Android裏說Serializable,怎麼能少得了同胞兄弟Parcelable呢,二者都是對象序列化的手段
二者的詳細比較這裏就不贅述,詳見:Android點將臺的Intent篇。什麼是序列化和反序列化,我的理解以下:

好比我家有個大的衣櫃(對象),如今要搬家,一會兒搬不走,怎麼辦?
把每塊板貼個標籤,而後拆了,一塊塊擺好,而後就能運走了,這叫序列化。
而後到新家裏,把板再一塊塊地拼起來,而後大衣櫃(對象)就又回來了,這叫反序列化。

上面說的是物質對象的運輸過程,那麼信息/數據對象也能夠這麼類比,思想上是[怎麼好運和拼裝還原]
Serializable和Parcelable不影響序列化的概念,只是手段不一樣,就像是卡車運仍是飛機運同樣
Serializable和Parcelable接口表明這東西可拆,是一種可拆保證。要什麼都亂拆,你家貓拆個試試。
下面直播拆貓:AS自動生成Parcelable相關代碼,能夠省咱們一些事,but,請千萬要了解一下他們是幹嗎用的
複製代碼

---->[MainActivity0#oncreate]------------------
to_two.setOnClickListener {
    val cat = CatManager().getCatAt(0)
    val bundle = Bundle()//建立Bundle對象
    bundle.putParcelable("cat", cat)//把貓裝到Bundle裏,貼個標籤cat
    val intent = Intent(this, MainActivity1::class.java)
    intent.putExtras(bundle)
    startActivity(intent)
}

---->[MainActivity1#oncreate]------------------
val cat = intent.extras?.get("cat") as Cat //把Bundle用打開標籤cat,而後貓到手
Log.e("MainActivity1", ": " + cat.name)//MainActivity1能夠對貓隨心所欲,IPC 通訊完成
複製代碼

2.IPC之文件共享進行通訊

把對象寫入文件,而後經過文件反序列化出對象,給MainActivity2
(文件讀寫不管是效率仍是多線程的不行,因此這裏只是瞭解一下)

---->[MainActivity0#oncreate]------------------
to_three.setOnClickListener {
    val cat = CatManager().getCatAt(0)
    val file = File(cacheDir, "cat.obj")
    val oos = ObjectOutputStream(FileOutputStream(file))
    oos.writeObject(cat)
    oos.close()
    startActivity(Intent(this, MainActivity2::class.java))
}

---->[MainActivity2#oncreate]------------------
val file = File(cacheDir, "cat.obj")
val ois = ObjectInputStream(FileInputStream(file))
val cat = ois.readObject() as Cat//反序列化生成對象
ois.close()
Log.e("MainActivity1", ": " + cat.name)//MainActivity1能夠對貓隨心所欲,IPC 通訊完成
複製代碼

可能到這你以爲IPC不就是傳個對象嗎?好像沒什麼大不了的


3.IPC之Messenger通訊
3-1:Messenger是什麼?

從都構造函數來看,是和Binder有關

private final IMessenger mTarget;

public Messenger(Handler target) {
    mTarget = target.getIMessenger();
}

public Messenger(IBinder target) {
    mTarget = IMessenger.Stub.asInterface(target);
}
複製代碼

3-2:能夠獲取一個Ibinder對象

如此看來mTarget即IMessenger類很像一個AIDL接口

public IBinder getBinder() {
    return mTarget.asBinder();
}
複製代碼

3-3:Messenger的使用

既然是Ibinder對象,能夠用在綁定服務中。既然個公司(摸)的人都要貓,乾脆來個服務端。
誰(客戶端)想來摸一下均可以,核心是Messenger發送消息,Service裏接收消息

---->[CatService]------------------
public class CatService extends Service {
    @Override
    public IBinder onBind(Intent intent) {
        return new Messenger(new Handler(msg -> {
            //接收客戶端數據/信息/對象
            String data = msg.getData().getString("request");
            Log.e("MessengerActivity", "handleMessage: " + data);
            
            //向客戶端發送數據/信息/對象
            Messenger client = msg.replyTo;
            Message message = Message.obtain();
            Cat cat = new CatManager().getCatAt(0);
            Bundle bundle = new Bundle();//建立Bundle對象
            bundle.putParcelable("cat", cat);//把貓裝到Bundle裏,貼個標籤cat
            message.setData(bundle);
            try {
                client.send(message);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            return false;
        })).getBinder();
    }
}

---->[將服務單獨放在一個進程]--------------
<service android:name=".CatService"
         android:process="com.toly1994.ipc.service.cat"/>

---->[MessengerActivity]------------------
public class MessengerActivity extends AppCompatActivity {
    private static final String TAG = "MessengerActivity";
    private ServiceConnection conn;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        conn = new ServiceConnection() {
            private Messenger messenger;
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                messenger = new Messenger(service);
                Message message = Message.obtain();
                Bundle bundle = new Bundle();
                bundle.putString("request", "來自客戶端:給我一隻貓");
                message.setData(bundle);
                message.replyTo = new Messenger(new Handler((msg) -> {//服務端迴應監聽
                    Bundle data = msg.getData();
                    data.setClassLoader(getClass().getClassLoader());
                    Cat cat = (Cat) (data.get("cat"));
                    Log.e(TAG, "來自服務端: "+cat);
                    return false;
                }));
                try {
                    messenger.send(message);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
            @Override
            public void onServiceDisconnected(ComponentName name) {
            }
        };
        bindService(new Intent(this, CatService.class), conn, Context.BIND_AUTO_CREATE);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(conn);
    }
}
複製代碼

注意這裏有一個坑,一開始讀不出來異常以下,看名字感受是類加載器的鍋,貌似找不到Cat類
解決: data.setClassLoader(getClass().getClassLoader());

流程基本以下,並不知道兩個Handler和三個Messenger,還有皮球同樣亂跑的Message有沒有把你繞暈


4.IPC之AIDL通訊

這個不怎麼想說,在Android點將臺:金科玉律[-AIDL-]裏已經講得很詳細了,爲了完整一點,這裏稍微再說一下吧。

4-1:定義接口:ICatService

簡單一點,就定義一個餵養的方法


4-2:自動生成的類

類之間的關係基本以下:

package com.toly1994.ipc;
public interface ICatService extends android.os.IInterface {
    public static abstract class Stub extends android.os.Binder implements com.toly1994.ipc.ICatService {
        private static final java.lang.String DESCRIPTOR = "com.toly1994.ipc.ICatService";
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }
        //經過IBinder獲取ICatService對象,綁定客戶端時使用
        public static com.toly1994.ipc.ICatService asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.toly1994.ipc.ICatService))) {
                return ((com.toly1994.ipc.ICatService) iin);
            }
            return new com.toly1994.ipc.ICatService.Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        @Override//此方法運行在服務端Binder線程池中,客戶端發起跨進程請求時,遠程請求經過系統底層封裝後交由此方法處理
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            java.lang.String descriptor = DESCRIPTOR;
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(descriptor);
                    return true;
                }
                case TRANSACTION_feed: {
                    data.enforceInterface(descriptor);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    this.feed(_arg0);
                    reply.writeNoException();
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }
        //代理類,當客戶端訪問服務端時,客戶端經過代理類生成一個ICatService對象 
        private static class Proxy implements com.toly1994.ipc.ICatService {
            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 feed(java.lang.String aString) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeString(aString);
                    mRemote.transact(Stub.TRANSACTION_feed, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_feed = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

    public void feed(java.lang.String aString) throws android.os.RemoteException;
}

複製代碼

4-3:使用ICatService建立FeedCatService

優點在於客戶端綁定服務是經過:ICatService.Stub.asInterface(service)獲取ICatService對象
就能夠調用ICatService接口方法。這樣只暴露接口,能夠限制客戶端對小貓的操做,客戶端即玩了,又不能隨心所欲。

---->[FeedCatService]--------------------------------------
public class FeedCatService extends Service {
    private static final String TAG = "FeedCatService";
    private Binder binder = new ICatService.Stub() {
        @Override
        public void feed(String aString) throws RemoteException {
            Log.e(TAG, "feed: 你已喂" + aString + "給小貓土土了");
        }
    };

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

---->[將服務單獨放在一個進程]---------------------------------
<service android:name=".FeedCatService"
         android:process="com.toly1994.ipc.service.feed.cat"/>
         
---->[AidlActivity]---------------------------------
public class AidlActivity extends AppCompatActivity {
    private ServiceConnection conn;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        conn = new ServiceConnection() {
            private ICatService catService;
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                catService = ICatService.Stub.asInterface(service);
                try {
                    catService.feed("魚");
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
            @Override
            public void onServiceDisconnected(ComponentName name) {
            }
        };
        bindService(new Intent(this, FeedCatService.class), conn, Context.BIND_AUTO_CREATE);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(conn);
    }
}
複製代碼

5.IPC之使用ContentProvider通訊
5.1:數據庫輔助
private static String DATABASE_NAME = "cat.db";//數據庫名
    private static int DATABASE_VERSION = 1;//數據庫版本
    private volatile static CatDatabaseHelper sInstance;
    //雙檢鎖單例
    public static synchronized CatDatabaseHelper getInstance(Context context) {
        if (sInstance == null) {
            synchronized (CatDatabaseHelper.class) {
                if (sInstance == null) {
                    sInstance = new CatDatabaseHelper(context);
                }
            }
        }
        return sInstance;
    }
    public CatDatabaseHelper(@Nullable Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }
    @Override
    public void onCreate(SQLiteDatabase db) {
        createSwordTable(db);
    }
    /**
     * 建立sword表
     *
     * @param db SQLiteDatabase
     */
    private void createSwordTable(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE cat (" +
                "id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," +
                "name VARCHAR(32) NOT NULL," +
                "color VARCHAR(32) NOT NULL" +
                "); ");
    }
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }
}
複製代碼

5.2:繼承ContentProvider
public class CatContentProvider extends ContentProvider {
    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    private static final int CAT_QUERY = 0;
    private static final int CAT_INSERT = 1;
    private static final int CAT_UPDATE = 2;
    private static final int CAT_DELETE = 3;
    private static final String TABLE_NAME = "cat";
    static {
        //給當前sUriMatcher添加匹配規則
        sUriMatcher.addURI("toly1994.com.cat", "query", CAT_QUERY);
        sUriMatcher.addURI("toly1994.com.cat", "insert", CAT_INSERT);
        sUriMatcher.addURI("toly1994.com.cat", "update", CAT_UPDATE);
        sUriMatcher.addURI("toly1994.com.cat", "delete", CAT_DELETE);
    }
    private SQLiteOpenHelper mOpenHelper;
    @Override
    public boolean onCreate() {
        mOpenHelper = CatDatabaseHelper.getInstance(getContext());
        return true;
    }
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        //進行uri匹配
        int result = sUriMatcher.match(uri);
        if (result == CAT_QUERY) {
            SQLiteDatabase db = mOpenHelper.getReadableDatabase();
            return db.query(TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);
        } else {
            throw new IllegalStateException(" query Uri 錯誤");
        }
    }
    @Override
    public String getType(Uri uri) {
        return null;
    }
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        //進行uri匹配
        int result = sUriMatcher.match(uri);
        if (result == CAT_INSERT) {
            SQLiteDatabase db = mOpenHelper.getWritableDatabase();
            Long insert = db.insert(TABLE_NAME, null, values);
            //uri:數據發送變化,經過uri判斷調用哪一個內容觀察者
            //第二個參數:內容觀察者對象  若是傳null 則註冊了整個uri的內容觀察者皆能夠收到通知
            getContext().getContentResolver().notifyChange(uri, null);
            db.close();
            return Uri.parse(String.valueOf(insert));
        } else {
            throw new IllegalStateException("insert Uri 錯誤");
        }
    }
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        //進行uri匹配
        int result = sUriMatcher.match(uri);
        if (result == CAT_DELETE) {
            SQLiteDatabase db = mOpenHelper.getWritableDatabase();
            int delete = db.delete(TABLE_NAME, selection, selectionArgs);
            db.close();
            return delete;
        } else {
            throw new IllegalStateException("delete Uri 錯誤");
        }
    }
    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        //進行uri匹配
        int result = sUriMatcher.match(uri);
        if (result == CAT_UPDATE) {
            SQLiteDatabase db = mOpenHelper.getWritableDatabase();
            int update = db.update(TABLE_NAME, values, selection, selectionArgs);
            db.close();
            return update;
        } else {
            throw new IllegalStateException("update Uri 錯誤");
        }
    }
}

---->[配置]---------------------------------------------
<provider android:authorities="toly1994.com.cat"
          android:name=".CatContentProvider"
          android:exported="true"
          android:process="com.toly1994.ipc.provider.cat"
/>
複製代碼

5.3:使用

這樣不一樣的進程就能夠經過getContentResolver來操做數據庫了

public class ProviderActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        insert(getContentResolver());
    }
    /**
     * 插入測試
     *
     * @param resolver
     */
    private void insert(ContentResolver resolver) {
        Uri uri = Uri.parse("content://toly1994.com.cat/insert");
        ContentValues values = new ContentValues();
        values.put("name", "土土");
        values.put("color", "灰色");
        resolver.insert(uri, values);
    }
}
複製代碼


6.使用Socket

Socket可讓兩個設備間的通訊,兩個進程天然也不在話下
這裏不深刻,只是客戶端發一句話,服務端接收一下

---->[SocketService]---------------------------------
public class SocketService extends Service {
    private static final String TAG = "SocketService";
    private boolean quit = false;
    
    @Override
    public void onCreate() {
        Log.e(TAG, "onCreate: ");
        FeedServer feedServer = new FeedServer();
        new Thread(feedServer).start();
        super.onCreate();
    }

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

    private class FeedServer implements Runnable {

        @Override
        public void run() {
            ServerSocket serverSocket = null;
            try {
                serverSocket = new ServerSocket(8080);
                Log.e(TAG, "serverSocket onCreate: ");
            } catch (IOException e) {
                e.printStackTrace();
            }

            while (!quit) {
                try {
                    Socket socket = serverSocket.accept();
                    //接收客戶端消息
                    BufferedReader br = new BufferedReader(
                            new InputStreamReader(socket.getInputStream()));
                    String s = br.readLine();
                    Log.e(TAG, "來自客戶端: " + socket.getInetAddress() + "說:" + s);

                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

---->[將服務單獨放在一個進程]---------------------------------
<service android:name=".SocketService"
         android:process="com.toly1994.ipc.service.socket.feed.cat"/>
         
---->[ServerActivity]---------------------------------
public class ServerActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        startService(new Intent(this, SocketService.class));
        findViewById(R.id.to_one).setOnClickListener(v -> {
            new Thread(() -> {
                Socket socket = null;
                try {
                    socket = new Socket("localhost", 8080);
                    //客戶端請求
                    BufferedWriter bw = new BufferedWriter(
                            new OutputStreamWriter(socket.getOutputStream()));
                    bw.write("我要貓");
                    bw.flush();
                    bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();

        });

    }
}
複製代碼

OK,本文就先這樣,《Android開發藝術探索》是本不錯的書,有多瞄幾眼的價值。

相關文章
相關標籤/搜索