Android 關(guān)于IPC機制的理解(二)

上篇文章介紹了IPC機制的基本概念以及簡單使用逮矛,文章鏈接:Android 關(guān)于IPC機制的理解(一) 這篇文章主要是關(guān)于IPC的方式他挎,也就是用什么方式來進行進程間通信拍屑;
介紹使用前首先我們得了解幾個概念墅茉,就是序列化和反序列以及Binder。
序列化和反序列化
在Java中序列化是指把java對象轉(zhuǎn)換成字節(jié)碼的過程良拼,反序列化是指把字節(jié)碼恢復(fù)為Java對象的過程岸霹,既然涉及進程間的通信那就必須涉及到序列化了和反序列化,在Android中一共有兩個接口可以完成将饺,第一個是Serializable另一個是Parcelable贡避;
Serializable是Java1.1出來的一個特性,它是一個空接口予弧,用它可以為對象提供序列化和反序列化操作刮吧,Serializable使用起來不困難,只要在實體類實現(xiàn)這個接口然后自定義添加一個唯一標(biāo)識就可以了掖蛤,這里就不在舉例了杀捻;
值得說一說的是Parceable,Parcelable是Android為我提供的一個實現(xiàn)序列化方法的接口蚓庭,只要一個對象實現(xiàn)這個接口就可以實現(xiàn)了序列化然后用Intent致讥、Binder來傳遞這個對象了,可能我們會有疑問器赞,既然有了Serializable為什么Android還要提供這個接口來實現(xiàn)序列化呢垢袱?其實是有原因的,Serializable雖然使用簡單但是開銷很大港柜,因為Serializable是通過I/O操作來實現(xiàn)的请契,在序列化過程中用到了大量的反射咳榜,產(chǎn)生了很多臨時變量,Parceable相對來說會更輕便爽锥,性能上要優(yōu)于Serializable涌韩,所以Android也推薦使用Parceable來實現(xiàn)序列化。下面用一個例子實現(xiàn)Parcelable然后講解里面的方法氯夷。

public class ParcelableBean implements Parcelable { String name; int year; /** * 返回當(dāng)前對象的內(nèi)容描述 * @return / @Override public int describeContents() { return 0;//如果有文件掃描符臣樱,返回1,沒有返回0 } /* * 將當(dāng)前對象寫入序列化結(jié)構(gòu)中 * * @param dest 序列化對象 * @param flags 標(biāo)識值 / @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(this.name); dest.writeInt(this.year); } public ParcelableBean() { } /* * 從序列化后的對象中創(chuàng)建原始對象 * @param in 序列化后的對象 / protected ParcelableBean(Parcel in) { this.name = in.readString();//讀取序列化對象中的字符串值 this.year = in.readInt(); } /* * 反序列化功能 / public static final Parcelable.Creator<ParcelableBean> CREATOR = new Parcelable.Creator<ParcelableBean>() { /* * 從序列化后的對象中創(chuàng)建原始對象 * @param source * @return / @Override public ParcelableBean createFromParcel(Parcel source) { return new ParcelableBean(source); } /* * 創(chuàng)建指定長度的原始對象數(shù)組 * @param size * @return */ @Override public ParcelableBean[] newArray(int size) { return new ParcelableBean[size]; } };}
上面的對象就可以通過Intent進行數(shù)據(jù)傳遞了腮考,其實大多數(shù)人不用Parcelable是因為它要寫一些模板代碼擎淤,然后Serializable則不用,所以很多程序員圖省事就直接使用了Serializable秸仙,其實如果你使用Android Studio進行開發(fā)是可以下一個Android Parcelable code generator插件然后就可以自動生成那些模板代碼了。

Binder

Binder要研究起來是可以很深入和很復(fù)雜得桩盲,這里就不在詳述寂纪,如果有興趣可以參考這篇文章<<Android Bander設(shè)計與實現(xiàn) - 設(shè)計篇>>,本文就大概介紹,Binder是Android中的一個實現(xiàn)了IBinder接口的類赌结,也是一種跨進程通信的方式捞蛋,從Android應(yīng)用框架層(Android Framework)來說它是ServiceManager鏈接各種Manager和相應(yīng)ManagerService的橋梁;從Android應(yīng)用層(Android Applications)來說Binder是客戶端和服務(wù)端進行通信的中介柬姚,比如Service中的bindService時服務(wù)端會返回一個包含Binder對象拟杉,同過這個對象就可以獲取服務(wù)端提供的數(shù)據(jù),在Android中Binder主要用在Service中量承,下文講Android中跨進程通信的方式時也會說到Binder搬设。

Android進行跨進程通信的方式
(1).使用Bundle
在Android中,Activity撕捍、Service拿穴、Receiver都可以用Bundle進行數(shù)據(jù)傳遞,這是因為Bundle實現(xiàn)了Parcelable接口忧风,這個我們在日常開發(fā)應(yīng)該是經(jīng)常使用的默色,值得注意的是在使用Bundle附加需要傳輸?shù)臄?shù)據(jù)時要確定傳輸?shù)臄?shù)據(jù)可以被序列化,也就是傳遞的對象必須實現(xiàn)了Parcelable或Serializable接口狮腿。

(2).文件共享
就是兩個進程讀/寫同一個文件來進行數(shù)據(jù)交互但是在使用這種方式進行通信的同時要注意并發(fā)的問題腿宰,避免兩個進程同時并發(fā)寫當(dāng)前文件,這種方式適合在對數(shù)據(jù)同步性要求不高的進程間進行通信缘厢。

(3).使用Messenger
通過在Messenger中存放需要進行交互的數(shù)據(jù)吃度,然后就可以在進程間進行傳遞,下面用一個例子來說明如何使用Messenger在不同進程中進行數(shù)據(jù)傳遞,例子很簡單贴硫,就是Activity中通過Messenger給Service發(fā)送段話规肴,首先是Activity的代碼。

public class MainActivity extends Activity { MyServiceConnection con; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent intent = new Intent(this, MyService.class); con = new MyServiceConnection(); bindService(intent, con, BIND_AUTO_CREATE);//綁定服務(wù) } /** * 服務(wù)連接 / class MyServiceConnection implements ServiceConnection { /* * 已經(jīng)建立連接 * * @param name * @param service / @Override public void onServiceConnected(ComponentName name, IBinder service) { Messenger msgr = new Messenger(service); Message msg = new Message(); msg.what = 1;//標(biāo)示 Bundle data = new Bundle(); data.putString("msg", "哈嘍"); msg.setData(data); try { msgr.send(msg);//發(fā)送消息 } catch (RemoteException e) { e.printStackTrace(); } } /* * 連接失敗 * * @param name */ @Override public void onServiceDisconnected(ComponentName name) { } } @Override protected void onDestroy() { super.onDestroy(); unbindService(con);//解綁服務(wù) }}
接著是Service,這里我已經(jīng)給Service設(shè)置了不同的進程

<service android:name=".MyService" android:process=":messenger" />

Service代碼為

public class MyService extends Service { private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == 1) { Log.d("print", msg.getData().getString("msg")); } } }; private final Messenger msgr = new Messenger(handler); @Nullable @Override public IBinder onBind(Intent intent) { return msgr.getBinder(); }}
Log為:



通過上面的小例子我們就完成了一次使用Messenger在不同進程間通信了拖刃。但是用Messenger是有一定局限性的删壮,因為Messenger是用串行的方式來處理消息,如果數(shù)據(jù)大量的話兑牡,也只能是一個個來發(fā)送和處理央碟,這樣應(yīng)對數(shù)據(jù)并發(fā)顯然不大可能,而且這只是數(shù)據(jù)交互均函,如果想調(diào)用不同進程方法顯然更不可能亿虽。

(4)使用AIDL
AIDL(Android interface definition language)安卓接口定義語言,它的作用就是進行跨進程通信苞也,下面用一個示例來演示兩個進程間通信洛勉, 在Activity中調(diào)用另一個進程中Service的方法,我用的是Android studio如迟,在這個工具中使用AIDL跟Eclipse略有不同收毫,下面我一步來說明怎么實現(xiàn)功能:
首先右鍵新建一個aidl文件,這里我命名為IMyAidlInterface .aidl,這時as會自動幫我生成一個.aidl的文件殷勘,如圖:



然后修改.aidl文件代碼如下

// IMyAidlInterface.aidlpackage com.sjr.ipcdemo;// Declare any non-default types here with import statements/** * 注意AIDL只支持下面幾種類型:基本數(shù)據(jù)類型此再,String、CharSequence玲销、ArrayList输拇、Map、 * 實現(xiàn)了Parcelable的接口對象贤斜、其他AIDL接口, /interface IMyAidlInterface { /* * Demonstrates some basic types that you can use as parameters * and return values in AIDL.\ * */ void show();}

上面的void show()方法是我想要共享的方法策吠,這時gen目錄并沒有生成對應(yīng)的接口文件(跟Eclipse不同),我們在AS中點擊Build->Make Project(快捷鍵Ctrl+F9),這樣gen目錄就生成了對應(yīng)的接口文件了瘩绒,如圖



生成的接口文件為(這里沒興趣的可以跳過奴曙,只是我加了點注釋便于理解Android為我們生成的是什么,有點深入了)

/* * This file is auto-generated. DO NOT MODIFY.(這個文件是自動生成的草讶,不要修改G⒃恪!6檎健坤溃!) * Original file: D:\sjr\lianyiwork\MyApplication\ipcdemo\src\main\aidl\com\sjr\ipcdemo\IMyAidlInterface.aidl /package com.sjr.ipcdemo;// Declare any non-default types here with import statements(聲明非默認(rèn)的類型和導(dǎo)入語句)public interface IMyAidlInterface extends android.os.IInterface { /* * Local-side IPC implementation stub class. / public static abstract class Stub extends android.os.Binder implements com.sjr.ipcdemo.IMyAidlInterface { /* * BInder的唯一標(biāo)識,一般用當(dāng)前Binder的類名來表示 / private static final java.lang.String DESCRIPTOR = "com.sjr.ipcdemo.IMyAidlInterface"; /* * Construct the stub at attach it to the interface. / public Stub() { this.attachInterface(this, DESCRIPTOR); } /* * Cast an IBinder object into an com.sjr.ipcdemo.IMyAidlInterface interface, * generating a proxy if needed. * 將被請求共享進程的Binder對象轉(zhuǎn)換成請求共享進程所需的AIDL接口類型對象 * (區(qū)分進程嘱丢,如果在同一進程返回的是被請求進程的Stub本身薪介,否則返回的是系統(tǒng)封裝之后的Stub.proxy對象) / public static com.sjr.ipcdemo.IMyAidlInterface asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.sjr.ipcdemo.IMyAidlInterface))) { return ((com.sjr.ipcdemo.IMyAidlInterface) iin); } return new com.sjr.ipcdemo.IMyAidlInterface.Stub.Proxy(obj); } /* * 返回當(dāng)前的Binder對象 * @return / @Override public android.os.IBinder asBinder() { return this; } /* * 運行在Service中的Binder線程池中,當(dāng)有跨進程請求時,遠程請求會通過系統(tǒng)底層封裝后交由此方法處理 * @param code 請求碼 * @param data 數(shù)據(jù) * @param reply 寫入返回值 * @param flags 標(biāo)示 * @return 返回false的共享請求會失敗 * @throws android.os.RemoteException / @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true; } case TRANSACTION_show: { data.enforceInterface(DESCRIPTOR); this.show(); reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); } /* * 要請求共享的進程請求調(diào)用時執(zhí)行 / private static class Proxy implements com.sjr.ipcdemo.IMyAidlInterface { 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; } /* * Demonstrates some basic types that you can use as parameters * and return values in AIDL.\ * 方法名跟調(diào)用的方法名一樣 / @Override public void show() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_show, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } } static final int TRANSACTION_show = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); } /* * Demonstrates some basic types that you can use as parameters * and return values in AIDL.\ * 方法名跟調(diào)用的方法名一樣 */ public void show() throws android.os.RemoteException;}
然后是Service的方法越驻,然后更換為其他進程汁政,這里給Service加了個action道偷,是考慮到如果是不同應(yīng)用時Intent跳轉(zhuǎn)不能用this,XXX.class,只能根據(jù)action來跳记劈,單個應(yīng)用可用不用設(shè)置action勺鸦,一樣是可行的。

<service android:name=".TestAIDLService" android:process=":hello"> <intent-filter> <action android:name="com.sjr.ipcdemo.hello" /> </intent-filter> </service>

Service代碼為:

public class TestAIDLService extends Service { @Nullable @Override public IBinder onBind(Intent intent) { return new Intermediary(); } /** * 用一個中介類去繼承自己寫好的接口 / class Intermediary extends IMyAidlInterface.Stub { /* * 然后在這個回調(diào)方法里調(diào)用Service方法 * * @throws RemoteException */ @Override public void show() throws RemoteException { TestAIDLService.this.show(); } } public void show() { Log.d("print", "哈嘍這是Service的方法"); }}
然后就是在MainActivity里調(diào)用了,代碼為:

public class MainActivity extends Activity { MyServiceConnection con; IMyAidlInterface mAidl; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); con = new MyServiceConnection(); //5.0以后Service必須顯示啟動 // 否則異常 java.lang.IllegalArgumentException: Service Intent must be explicit: Intent { act=com.sjr.ipcdemo.hello } Intent intent = new Intent("com.sjr.ipcdemo.hello").setPackage("com.sjr.ipcdemo"); bindService(intent, con, BIND_AUTO_CREATE); Button btn1 = (Button) findViewById(R.id.btn1); btn1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try { mAidl.show(); } catch (RemoteException e) { e.printStackTrace(); } } }); } class MyServiceConnection implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) { mAidl = IMyAidlInterface.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { } } @Override protected void onDestroy() { super.onDestroy(); unbindService(con); }}
當(dāng)我點擊按鈕就調(diào)用Service的方法打出Log目木,Log為:


這樣就完成了一次使用AIDL進行簡單的跨進程通信了换途,有點需要注意的是在5.0以后Service必須是顯示啟動。

(5).使用ContentProvider(內(nèi)容提供者)

ContentProvider是Android四大組件之一刽射,也是Android提供的專門用于不同應(yīng)用數(shù)據(jù)共享的方式军拟,所以ContentProvider也是跨進程通信的方式之一,ContentProvider底層也是Binder誓禁,也同樣是因為它是Android系統(tǒng)提供的共享數(shù)據(jù)方式之一懈息,所以Android對ContentProvider進行了良好的封裝,我們不用關(guān)心底層實現(xiàn)就可以實現(xiàn)跨進程通信摹恰,系統(tǒng)也提供了很多ContentProvider辫继,比如通訊錄、短信等戒祠,要操作這些數(shù)據(jù)使用的是ContentResolver。下面用一個示例來演示ContentProvider實現(xiàn)多進程通信速种,例子比較簡單姜盈,就是利用ContentProvider給一個數(shù)據(jù)庫插入一條語句,然后提供給其他進程的Activity操作
首先是先創(chuàng)建數(shù)據(jù)庫:

public class MyOpenHelper extends SQLiteOpenHelper { public MyOpenHelper(Context context) { super(context, "people.db", null, 1); } /** * 創(chuàng)建數(shù)據(jù)庫表 * * @param db */ @Override public void onCreate(SQLiteDatabase db) { db.execSQL("create table person(_id integer primary key, name char(10), age integer(20))"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { }}
接著是創(chuàng)建ContentProvider類:

public class PersonProvider extends ContentProvider { private MyOpenHelper mHelper; private SQLiteDatabase mDatabase; //創(chuàng)建uri匹配器對象 private static UriMatcher mMatcher = new UriMatcher(UriMatcher.NO_MATCH); //匹配uri static { mMatcher.addURI("com.sjr.ipcdmo.people", "person", 1); } @Override public boolean onCreate() { mHelper = new MyOpenHelper(getContext()); mDatabase = mHelper.getWritableDatabase();//獲取一個可寫的數(shù)據(jù)庫 //開啟新線程插入一條數(shù)據(jù) new Thread(new Runnable() { @Override public void run() { mDatabase.execSQL("delete from person"); mDatabase.execSQL("insert into person values(1,'李子明',2);"); } }).start(); return false; } /** * 查詢 * @param uri * @param projection * @param selection * @param selectionArgs * @param sortOrder * @return / @Nullable @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { Cursor cursor = null; if (mMatcher.match(uri) == 1) cursor = mDatabase.query("person", projection, selection, selectionArgs, null, null, sortOrder, null); else throw new IllegalArgumentException("uri不對"); return cursor; } @Nullable @Override public String getType(Uri uri) { if (mMatcher.match(uri) == 1) return "van.android.cursor.dir/person"; return null; } /* * 其他應(yīng)用調(diào)用配阵,用于忘數(shù)據(jù)庫里插入數(shù)據(jù) * * @param uri 內(nèi)容提供者的地址 * @param values 由其他應(yīng)用傳入馏颂,用于封裝要插入的數(shù)據(jù) * @return / @Nullable @Override public Uri insert(Uri uri, ContentValues values) { if (mMatcher.match(uri) == 1) { mDatabase.insert("person", null, values); //發(fā)送數(shù)據(jù)改變通知,所有注冊在這個uri的內(nèi)容觀察者都可以收到這個通知 getContext().getContentResolver().notifyChange(uri, null); } else throw new IllegalArgumentException("uri不對"); return uri; } /* * 刪除 * @param uri * @param selection * @param selectionArgs * @return / @Override public int delete(Uri uri, String selection, String[] selectionArgs) { return 0; } /* * 更新 * @param uri * @param values * @param selection * @param selectionArgs * @return */ @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { return 0; }}
同時在清單文件注冊Provider和開啟新進程:

<provider android:name=".provider.PersonProvider" android:authorities="com.sjr.ipcdmo.people" android:process=":provider" />
然后在Activity中使用ContentResolver來對數(shù)據(jù)庫操作:

public class ContentProviderActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //在Activity中也插入一條數(shù)據(jù),這時應(yīng)該有兩條了 ContentResolver resolver = getContentResolver(); ContentValues values = new ContentValues(); values.put("_id", 2); values.put("name", "李大哥"); values.put("age", 5); resolver.insert(Uri.parse("content://com.sjr.ipcdmo.people/person"), values); //查詢所有 Cursor query = resolver.query(Uri.parse("content://com.sjr.ipcdmo.people/person"), new String[]{"name", "age"}, null, null, null); while (query.moveToNext()){ Log.d("print","姓名:"+query.getString(0)+"年齡:"+query.getString(1)); } }}
這時運行Log為:


上面就是一個使用ContentProvider進行跨進程通信的一個小示例棋傍;有一個注意的是Provider的query救拉、update、insert瘫拣、delete存在多線程并發(fā)訪問時方法內(nèi)部要做好線程同步亿絮。
(6).使用Socket
Socket也叫"套接字",是網(wǎng)絡(luò)中的概念麸拄,下面用Socket來演示一個跨進程通信的示例:
首先是服務(wù)端:

public class TCPServerService extends Service { private boolean mIsServiceDestoryed = false; private String[] mMessages = new String[]{ "哈嘍", "嗨", "你好", "吃飯了嗎" }; @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { new Thread(new Runnable() { @Override public void run() { ServerSocket socket = null; try { socket = new ServerSocket(8068); } catch (IOException e) { e.printStackTrace(); return; } while (!mIsServiceDestoryed) { try { final Socket client = socket.accept();//接受客戶端請求 Log.d("print","accept"); new Thread(new Runnable() { @Override public void run() { try { responseClient(client); } catch (IOException e) { e.printStackTrace(); } } }).start(); } catch (IOException e) { e.printStackTrace(); } } } }).start(); } @Override public void onDestroy() { super.onDestroy(); mIsServiceDestoryed = true; } private void responseClient(Socket client) throws IOException { // 接收客戶端消息 BufferedReader in = new BufferedReader(new InputStreamReader( client.getInputStream())); // 向客戶端發(fā)送消息 PrintWriter out = new PrintWriter(new BufferedWriter( new OutputStreamWriter(client.getOutputStream())), true); out.println("welcom"); while (!mIsServiceDestoryed) { String str = in.readLine(); Log.d("print","msg from client:" + str); if (str == null) { break; } int i = new Random().nextInt(mMessages.length); String msg = mMessages[i]; out.println(msg); Log.d("print","send :" + msg); } Log.d("print","client quit."); // 關(guān)閉流 if (out != null) out.close(); if (in != null) in.close(); client.close(); }}
當(dāng)然服務(wù)端也要開啟新進程:

<service android:name=".socket.TCPServerService" android:process=":service" />
以及使用Socket是需要如下權(quán)限的:

<pre name="code" class="html"><uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

接著是客戶端Activity:

public class TCPClentActivity extends Activity implements View.OnClickListener { private static final int MESSAGE_RECEIVE_NEW_MSG = 1; private static final int MESSAGE_SOCKET_CONNECTED = 2; private Button mSendButton; private TextView mMessageTextView; private EditText mMessageEditText; private PrintWriter mPrintWriter; private Socket mClientSocket; @SuppressLint("HandlerLeak") private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case MESSAGE_RECEIVE_NEW_MSG: { mMessageTextView.setText(mMessageTextView.getText() + (String) msg.obj); break; } case MESSAGE_SOCKET_CONNECTED: { mSendButton.setEnabled(true); break; } default: break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_tcpclient); mMessageTextView = (TextView) findViewById(R.id.msg_container); mSendButton = (Button) findViewById(R.id.send); mSendButton.setOnClickListener(this); mMessageEditText = (EditText) findViewById(R.id.msg); Intent service = new Intent(this, TCPServerService.class); startService(service); new Thread() { @Override public void run() { connectTCPServer(); } }.start(); } @Override protected void onDestroy() { if (mClientSocket != null) { try { mClientSocket.shutdownInput(); mClientSocket.close(); } catch (IOException e) { e.printStackTrace(); } } super.onDestroy(); } @Override public void onClick(View v) { if (v == mSendButton) { final String msg = mMessageEditText.getText().toString(); if (!TextUtils.isEmpty(msg) && mPrintWriter != null) { mPrintWriter.println(msg); mMessageEditText.setText(""); String time = formatDateTime(System.currentTimeMillis()); final String showedMsg = "self " + time + ":" + msg + "\n"; mMessageTextView.setText(mMessageTextView.getText() + showedMsg); } } } @SuppressLint("SimpleDateFormat") private String formatDateTime(long time) { return new SimpleDateFormat("(HH:mm:ss)").format(new Date(time)); } private void connectTCPServer() { Socket socket = null; while (socket == null) { try { socket = new Socket("localhost", 8068); mClientSocket = socket; mPrintWriter = new PrintWriter(new BufferedWriter( new OutputStreamWriter(socket.getOutputStream())), true); mHandler.sendEmptyMessage(MESSAGE_SOCKET_CONNECTED); System.out.println("connect server success"); } catch (IOException e) { SystemClock.sleep(1000); Log.d("print","connect tcp server failed, retry..."); } } try { // 接收服務(wù)器端的消息 BufferedReader br = new BufferedReader(new InputStreamReader( socket.getInputStream())); while (!TCPClentActivity.this.isFinishing()) { String msg = br.readLine(); System.out.println("receive :" + msg); if (msg != null) { String time = formatDateTime(System.currentTimeMillis()); final String showedMsg = "server " + time + ":" + msg + "\n"; mHandler.obtainMessage(MESSAGE_RECEIVE_NEW_MSG, showedMsg) .sendToTarget(); } } System.out.println("quit..."); if (mPrintWriter!=null) mPrintWriter.close(); if (br!=null) br.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } }}

當(dāng)Activity啟動時會連接服務(wù)端Socket派昧,因為主線程不能聯(lián)網(wǎng),所以新開一個線程去連接拢切,然后為了確定連接成功蒂萎,采用了超時重連策略,重試時間為1秒淮椰,重上面的兩個類就可以完成一次使用Socket進行通信的示例了五慈,效果為:



總結(jié)
跨進程通信就是上述幾個了纳寂,他們的優(yōu)缺點為:


源碼地址:http://download.csdn.net/detail/lxzmmd/9527343

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市泻拦,隨后出現(xiàn)的幾起案子毙芜,更是在濱河造成了極大的恐慌,老刑警劉巖聪轿,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件爷肝,死亡現(xiàn)場離奇詭異,居然都是意外死亡陆错,警方通過查閱死者的電腦和手機灯抛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來音瓷,“玉大人对嚼,你說我怎么就攤上這事∩鳎” “怎么了纵竖?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長杏愤。 經(jīng)常有香客問我靡砌,道長,這世上最難降的妖魔是什么珊楼? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任通殃,我火速辦了婚禮,結(jié)果婚禮上厕宗,老公的妹妹穿的比我還像新娘画舌。我一直安慰自己,他們只是感情好已慢,可當(dāng)我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布曲聂。 她就那樣靜靜地躺著,像睡著了一般佑惠。 火紅的嫁衣襯著肌膚如雪朋腋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天膜楷,我揣著相機與錄音乍丈,去河邊找鬼。 笑死把将,一個胖子當(dāng)著我的面吹牛轻专,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播察蹲,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼请垛,長吁一口氣:“原來是場噩夢啊……” “哼催训!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起宗收,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤漫拭,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后混稽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體采驻,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年匈勋,在試婚紗的時候發(fā)現(xiàn)自己被綠了礼旅。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡洽洁,死狀恐怖痘系,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情饿自,我是刑警寧澤汰翠,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站昭雌,受9級特大地震影響复唤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜烛卧,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一佛纫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧唱星,春花似錦雳旅、人聲如沸跟磨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽抵拘。三九已至哎榴,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間僵蛛,已是汗流浹背尚蝌。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留充尉,地道東北人飘言。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像驼侠,于是被迫代替她去往敵國和親姿鸿。 傳聞我的和親對象是個殘疾皇子谆吴,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容