IPC全稱為Inter-Process Communication,含義為行程間通信,指的是兩個行程之間進行資料交換的程序,
方式一:Bundle實作 用于android四大組件直接的行程間通信
應用一的Activity
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.one_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent=new Intent();
Bundle bundle=new Bundle();
bundle.putString("sendMessage","嘿!我正在給你發訊息!");
ComponentName componentName=new ComponentName("com.example.ipcdemoapplication","com.example.ipcdemoapplication.MainActivity");
intent.setComponent(componentName);
intent.putExtras(bundle);
startActivity(intent);
}
});
}
}
應用二代碼
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textView =(TextView)findViewById(R.id.text);
Bundle bundle=getIntent().getExtras();
if(bundle!=null){
textView.setText(bundle.getString("sendMessage"));
}
}
}
點擊應用一的按鈕之后,跳轉到應用二并輸出“嘿!我正在給你發訊息!”
方式二:使用檔案共享
比如本地目錄下有一個檔案,都可以通過這個檔案來進行資料分享,具體操作(https://www.jianshu.com/p/55eae30d133c)
還有一種方式是通過SharedPreferences 通過共享xml來實作本地檔案共享,但是多行程還是有一些問題的,具體參照(https://www.jianshu.com/p/4984f66f9a4b)
方式三:使用Messenger
Messenger可以在不同行程中傳遞Message物件,在Message中加入我們想要傳遞的資料就可以在行程間進行資料傳遞了,Messenger是一種輕量級的IPC方案并對AIDL進行了封裝,它實作起來比較容易,首先我們先寫服務器(MessengerServce.java),在onBind方法中創建Messenger,關聯接受訊息的Handler呼叫getBinder來獲取Binder物件,在handleMessage方法中接受客戶端發來的資訊,
public class MessengerService extends Service {
private static final String TAG = "WANZIKAIFA";
public static final int MSG_FORMCLIENT = 1000;
private Handler mHandler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what){
case MSG_FORMCLIENT:
Log.e(TAG,"收到客戶端資訊----"+msg.getData().get("msg"));
//得到客戶端傳來的Messenger物件
Messenger mMessenger = msg.replyTo;
Message message = Message.obtain(null,MessengerService.MSG_FORMCLIENT);
Bundle mBundle = new Bundle();
mBundle.putString("rep","這里是服務端,我們收到資訊了");
message.setData(mBundle);
try {
mMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
}
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new Messenger(mHandler).getBinder();
}
}
注冊服務時要另開啟一個行程
<service android:name=".MessengerService" android:process=":remoute"/>
接下來創建客戶端(MainActivity.java),系結另一個行程服務,系結成功后根據服務端回傳的Binder物件創建Messenger,并用Messenger向服務端發送資訊,
public class MainActivity extends AppCompatActivity {
private Messenger mMessenger;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.one_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.d("WANZIKAIFA", "點擊按鈕 ");
Intent intent = new Intent(MainActivity.this,MessengerService.class);
bindService(intent,mServiceConnon, Context.BIND_AUTO_CREATE);
}
});
}
private ServiceConnection mServiceConnon = new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Log.d("WANZIKAIFA", "這里是客戶端,服務端收到了嗎 ");
mMessenger = new Messenger(iBinder);
Message mMessage = Message.obtain(null,MessengerService.MSG_FORMCLIENT);
Bundle mBundle = new Bundle();
mBundle.putString("msg","這里是客戶端,服務端收到了嗎");
mMessage.setData(mBundle);
//將Messenger傳遞給服務端
mMessage.replyTo = new Messenger(mHandler);
try {
mMessenger.send(mMessage);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
private Handler mHandler= new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what){
case MessengerService.MSG_FORMCLIENT:
Log.d("WANZIKAIFA", "收到服務端資訊: "+msg.getData().get("rep"));
break;
}
}
};
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(mServiceConnon);
}
}
在handleMessage回呼中收到客戶端資訊時,呼叫Message.replyTo得到客戶端傳遞過來的Messenger物件,創建訊息并通過Messenger發送給客戶端,然后客戶端需要創建一個Handler來接受服務端的資訊,當系結完服務之后需要關聯定義的Handler,
列印的log
01-01 03:02:51.601 12156 12156 D WANZIKAIFA: 點擊按鈕
01-01 03:02:52.011 12156 12156 D WANZIKAIFA: 這里是客戶端,服務端收到了嗎
01-01 03:02:52.012 12565 12565 E WANZIKAIFA: 收到客戶端資訊----這里是客戶端,服務端收到了嗎
01-01 03:02:52.013 12156 12156 D WANZIKAIFA: 收到服務端資訊: 這里是服務端,我們收到資訊了
方式四:通過AIDL來進行IPC通信
之前發過的一篇AIDL通信(https://blog.csdn.net/qq_27647919/article/details/107204389)
方式五:ContentProvider方式 實作對另一個應用行程開放provider資料的查詢
第一步創建資料庫并創建表名為“game_provider.db” 里面有兩個欄位name,describe
public class DBOpenHelper extends SQLiteOpenHelper {
public static final String DB_NAME = "game_provider.db";
public static final String GAME_TABLE_NAME = "game";
public static final int DB_VERSION = 1;
public String CREATE_GAME_TABLE = "create table if not exists " + GAME_TABLE_NAME
+"(_id integer primary key ,"+"name TEXT ,"+"describe TEXT)";
public DBOpenHelper(@Nullable Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
sqLiteDatabase.execSQL(CREATE_GAME_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
}
}
第二步對資料庫存入一條資料
public class GameProvider extends ContentProvider {
public static final String AUTHORITY = "com.example.myapplication.GameProvider";
public static final Uri GAME_CONTENT_URL = Uri.parse("content://"+AUTHORITY+"/game");
private static final UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
private SQLiteDatabase mDB;
private Context mContext;
private String table;
static {
mUriMatcher.addURI(AUTHORITY,"game",0);
}
@Override
public boolean onCreate() {
table = DBOpenHelper.GAME_TABLE_NAME;
mContext = getContext();
initProvider();
return false;
}
private void initProvider() {
Log.d("WANZIKAIFA", "insert into game: ");
mDB = new DBOpenHelper(mContext).getWritableDatabase();
new Thread(new Runnable() {
@Override
public void run() {
mDB.execSQL("delete from "+DBOpenHelper.GAME_TABLE_NAME);
mDB.execSQL("insert into game values(1,'插入第一條','我是第一條');");
}
}).start();
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
String table = DBOpenHelper.GAME_TABLE_NAME;
Cursor mCursor = mDB.query(table,
projection,
selection,
selectionArgs,
null,
sortOrder,
null);
return mCursor;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) {
mDB.insert(table,null,contentValues);
mContext.getContentResolver().notifyChange(uri,null);
return null;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String s, @Nullable String[] strings) {
return 0;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Nullable String s, @Nullable String[] strings) {
return 0;
}
}
為了測驗是跨行程通信,將provdier寫入另一個行程
<provider
android:authorities="com.example.myapplication.GameProvider"
android:name=".GameProvider"
android:process=":provider"/>
在Activity中插入另一條資料
public class ContentProviderActivity extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.contentprovier_activity);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Uri uri = Uri.parse("content://com.example.myapplication.GameProvider");
ContentValues mContentValues = new ContentValues();
mContentValues.put("_id",2);
mContentValues.put("name","插入第二條");
mContentValues.put("describe","我是第二條");
getContentResolver().insert(uri,mContentValues);
Cursor gameCursor = getContentResolver().query(uri,new String[]{"name","describe"},null,null,null);
while (gameCursor.moveToNext()){
Game mGame = new Game(gameCursor.getString(0),gameCursor.getString(1));
Log.d("WANZIKAIFA",mGame.gameName+"---"+mGame.gameDescribe);
}
}
});
}
}
game.java
public class Game implements Parcelable {
public String gameName;
public String gameDescribe;
public Game(String gameName,String gameDescribe){
this.gameName = gameName;
this.gameDescribe = gameDescribe;
}
protected Game(Parcel in) {
gameName = in.readString();
gameDescribe=in.readString();
}
public static final Creator<Game> CREATOR = new Creator<Game>() {
@Override
public Game createFromParcel(Parcel in) {
return new Game(in);
}
@Override
public Game[] newArray(int size) {
return new Game[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(gameName);
parcel.writeString(gameDescribe);
}
}
點擊按鈕之后,輸出log為
01-01 00:10:46.764 8779 8779 D WANZIKAIFA: insert into game:
01-01 00:10:46.842 8736 8736 D WANZIKAIFA: 插入第一條---我是第一條
01-01 00:10:46.842 8736 8736 D WANZIKAIFA: 插入第二條---我是第二條
由log可以看出行程號pid8779 pid8736 第二條跨行程插入成功
方式六用Socket實作跨行程聊天程式
Socket是位于應用層和傳輸層之間的一個抽象層,它把TCP/IP層復雜的操作抽象為幾個簡單的介面,供應用層呼叫以實作行程在網路中通信,Socket分為流式套接字和資料包套接字,分別對應網路傳輸控制層的TCP協議和UDP協議,TCP協議是一種面向連接的,可靠的,基于位元組流的傳輸層通信協議,它使用三次握手協議建立連接,并且提供了超時重傳機制,具有很高的穩定性,UDP協議則是一種無連接的協議,且不對傳送資料包進行可靠性保證,它適合一次傳輸少量資料,UDP傳輸的可靠性是由應用層負責,在網路質量令人十分不滿意的環境下,UDP協議資料包丟失會比較嚴重,但是由于UDP協議的特性:他不屬于連接型協議,因而具有資源消耗小,處理速度快的優點,所以通常音頻,視頻和普通資料在傳送時使用UDP協議比較多,
第一步添加權限
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
第二步實作服務端
實際流程是在執行緒中創建TCP服務,監聽8688埠,等待客戶端連接,當客戶端連接時就會生成Socket,通過每次創建的Socket就可以和不同的客戶端通信了,當客戶端斷開連接時,服務端也會跟著關閉Socket并結束通話執行緒,服務端首先會向客戶端發送一條訊息:“您好,我是服務端”,并接受客戶端發來的訊息,將受到的訊息進行加工再回傳給客戶端
public class SocketServerService extends Service {
private boolean isServiceDestoryed = false;
@Override
public void onCreate() {
new Thread(new TcpServer()).start();
super.onCreate();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
private class TcpServer implements Runnable {
@Override
public void run() {
ServerSocket serverSocket = null;
try {
//監聽8688視窗
serverSocket = new ServerSocket(8688);
} catch (IOException e) {
e.printStackTrace();
}
while (!isServiceDestoryed){
try {
//接受客戶端請求,并且阻塞直接接受訊息
final Socket client = serverSocket.accept();
new Thread(){
@Override
public void run() {
responseClient(client);
}
}.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private void responseClient(Socket client) {
//用戶接受客戶端訊息
try {
BufferedReader in = new BufferedReader((new InputStreamReader(client.getInputStream())));
//用于向客戶端發送訊息
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream())),true);
out.println("您好呀!我是服務端!");
while (!isServiceDestoryed){
String str = in.readLine();
Log.d("WANZIKAIFA", "收到客戶端的資訊: "+str);
if (TextUtils.isEmpty(str)){
//客戶端斷開了連接
Log.d("WANZIKAIFA","客戶端斷開連接");
break;
}
String message = "收到了客戶端的資訊為"+ str;
//從客戶端收到的訊息加工再發給客戶端
out.println(message);
}
out.close();
in.close();
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onDestroy() {
isServiceDestoryed = true;
super.onDestroy();
}
}
第三步實作客戶端
流程:客戶端會啟動服務,并開啟執行緒連接服務端Socket,
public class SocketClientActivity extends Activity {
private EditText et_receiver;
private Button bt_send;
private TextView tv_message;
private PrintWriter mPrintWriter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_socket);
initView();
Intent intent = new Intent(this,SocketServerService.class);
startService(intent);
new Thread(){
@Override
public void run() {
connectSocketServer();
}
}.start();
}
private void initView() {
et_receiver = (EditText)findViewById(R.id.et_receiver);
bt_send =(Button)findViewById(R.id.bt_send);
tv_message = (TextView)findViewById(R.id.tv_message);
bt_send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
final String msg = et_receiver.getText().toString();
//向服務端發送資訊
if (!TextUtils.isEmpty(msg)&&null!=mPrintWriter){
new Thread(new Runnable() {
@Override
public void run() {
mPrintWriter.println(msg);
}
}).start();
tv_message.setText(tv_message.getText()+"\n"+"客戶端:"+msg);
Log.d("WANZIKAIFA", tv_message.getText()+"\n"+"客戶端:"+msg);
et_receiver.setText("");
}
}
});
}
private void connectSocketServer() {
Socket socket = null;
while (socket==null){
//選擇和服務器相同的埠8688
try {
socket = new Socket("localhost",8688);
mPrintWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())),true);
} catch (IOException e) {
e.printStackTrace();
}
}
//接受服務器端的訊息
try {
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while (!isFinishing()){
final String msg = br.readLine();
if (msg!=null){
runOnUiThread(new Runnable() {
@Override
public void run() {
tv_message.setText(tv_message.getText()+"\n"+"服務端:"+msg);
Log.d("WANZIKAIFA", tv_message.getText()+"\n"+"服務端:"+msg);
}
});
}
}
mPrintWriter.close();
br.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
列印出log為
01-01 02:08:54.459 9771 9771 D WANZIKAIFA: 服務端:您好呀!我是服務端!
01-01 02:09:28.140 9790 9814 D WANZIKAIFA: 收到客戶端的資訊: 收到
01-01 02:09:28.142 9771 9771 D WANZIKAIFA: 客戶端:收到
01-01 02:09:28.190 9771 9771 D WANZIKAIFA: 服務端:收到了客戶端的資訊為收到
方式七廣播
比如開機啟動廣播
public class BootUpReceiver extends BroadcastReceiver {
private static final String cTag = "BootUpReceiver";
@Override
public void onReceive( final Context context, final Intent intent ) {
LogTool.d(cTag, " BootUpReceiver");
}
}
注冊清單:
<receiver android:name=".BootUpReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</receiver>
最后針對這幾種IPC通信方式分析一下優缺點:總結轉自(https://www.jianshu.com/p/71480c680a65)
1.bundle :
簡單易用 但是只能傳輸Bundle支持的物件 常用于四大組件間行程間通信
2.檔案共享:
簡單易用 但不適合在高并發的情況下 并且讀取檔案需要時間 不能即時通信 常用于并發程度不高 并且實時性要求不高的情況
3.AIDL :
功能強大 支持一對多并發通信 支持即時通信 但是使用起來比其他的復雜 需要處理好多執行緒的同步問題 常用于一對多通信 且有RPC 需求的場合(服務端和客戶端通信)
4.Messenger :
功能一般 支持一對多串行通信 支持實時通信 但是不能很好處理高并發情況 只能傳輸Bundle支持的型別 常用于低并發的無RPC需求一對多的場合
5.ContentProvider :
在資料源訪問方面功能強大 支持一對多并發操作 可擴展call方法 可以理解為約束版的AIDL 提供CRUD操作和自定義函式 常用于一對多的資料共享場合
6.Socket :
功能強大 可以通過網路傳輸位元組流 支持一對多并發操作 但是實作起來比較麻煩 不支持直接的RPC 常用于網路資料交換
總結
當僅僅是跨行程的四大組件間的傳遞資料時 使用Bundle就可以 簡單方便
當要共享一個應用程式的內部資料的時候 使用ContentProvider實作比較方便
當并發程度不高 也就是偶爾訪問一次那種 行程間通信 用Messenger就可以
當設計網路資料的共享時 使用socket
當需求比較復雜 高并發 并且還要求實時通信 而且有RPC需求時 就得使用AIDL了
檔案共享的方法用于一些快取共享 之類的功能
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/224288.html
標籤:其他
