零、前言
1.先在視覺上瞄一下程式和執行緒
的區別
2.再從感性上類比一下程式和執行緒
如果手機是地球,程式就像一家公司,公司使用著地球的資源,也在地球上扮演一個獨立的個體,實現自己特有的功能與價值。
而執行緒就像公司裡的人,可以共享公司的公共資源,處理屬於自己的任務,實現自身的功能與價值。
可以說程式(公司)是給執行緒(人)一個執行(工作)的環境。於此同時程式也獲得了它的地位。
所以一個程式至少要一個執行緒來完成任務。執行緒銷燬後,裡面的程式也就失業拜拜了。
比如某公司的人(執行緒)集體罷工(崩潰),那公司無論曾經叫什麼,都沒有意義。公司(程式)倒閉了,再多的執行緒(人)也沒鳥用。
多程式就像若干個公司聯盟做一個專案,這時候各個公司的內部資源(靜態成員、單例等)就不再適用,
就像別的公司人到你公司吃你的零食,敲你鍵盤,摸你貓,你給嗎? 不給,堅決不給。
然後那人非要吃你零食,敲你鍵盤,摸你貓,還搞出個職位叫IPC,說什麼跨程式間通訊。TM說白了就是專門搶你零食,搶你貓,你說氣不氣人。
複製程式碼
3.最後走一波概念
IPC(Inter-Process Communication): 程式間通訊或者跨程式通訊
程式:指的一個執行單元,在PC和移動裝置上指的是一個程式或者一個應用。
執行緒:在作業系統中,執行緒是CPU排程的最小單元,也是一種有限的系統資源。
程式與執行緒關係:一個程式可以包含多個執行緒,因此程式和執行緒是包含被包含的關係。
複製程式碼
二、如何在一個應用建立多個程式
1.三個測試Activity
三個按鈕跳轉三個Activity,佈局就不貼了。只貼一個MainActivity0,其他兩個MainActivity1,MainActivity2
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
配置檔案
私有程式:
有:
---------- 全域性程式:沒有:
名字可以隨便取,只要唯一
<activity android:name=".MainActivity1"
android:process=":ipc">
</activity>
<activity android:name=".MainActivity2"
android:process="com.toly1994.ipc.test">
</activity>
複製程式碼
三、多程式與單程式的區別
1.開啟Activity1時
不加的話,直接通過視窗管理器來顯示Activity1
加的話,會在孵化一個程式。zygote64的日誌很多,下面只是一小部分。
不清楚Activity啟動和View載入過程的小夥伴,可以看一下這個日誌,也許會有幫助
比如下面完美呈現了LayoutInflater是怎麼執行的,再跟著原始碼走一走,你會有所收穫
然後發現確實是多了兩個,名字也能對應上
2.Application的多次例項化
既然開一個程式會孵化一次,ActivityThread的main方法被觸發,Application自然會被新建
喵了個咪的,建立了三個,一個程式一個。這顯然值得注意,自定義Application初始化第三方庫什麼的
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檔案
執行緒同步機制完全失效(同一差不多)
複製程式碼
三、IPC的幾種形式
為了多公司聯盟(多程式)間的和諧,現在決定犧牲貓,讓它可以被過各公司(程式)共享
[1].通過Intent傳遞Bundle物件通訊: 簡單,資料型別侷限,用於元件間傳遞資料
[2].使用共享檔案通訊: 簡單,實時性差,不適合高併發
[3].使用Messenger通訊: 支援一對多序列通訊,支援實時通訊,不支援RPC
[4].使用AIDL通訊: 支援一對多併發通訊,適用於,一對多通訊且有RPC需求
[5].使用ContentProvider: 支援一對多併發資料共享
[6].使用Socket: 可以通過網路傳輸位元組流,支援一對多併發實時通訊
複製程式碼
0.現在的CatManager類和Cat類
既然單例不能用,就不用。這裡預設開局一隻貓。Cat實現序列化介面Serializable
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開發藝術探索》
是本不錯的書,有多瞄幾眼的價值。