目录
一、使用 Intent
Activity,Service,Receiver 都支持在 Intent 中传递 Bundle 数据,而 Bundle 实现了 Parcelable 接口,可以在不同的进程间进行传输。
在一个进程中启动了另一个进程的 Activity,Service 和 Receiver ,可以在 Bundle 中附加要传递的数据通过 Intent 发送出去。
二、使用文件共享
Windows 上,一个文件如果被加了排斥锁会导致其他线程无法对其进行访问,包括读和写;而 Android 系统基于 Linux ,使得其并发读取文件没有限制地进行,甚至允许两个线程同时对一个文件进行读写操作,尽管这样可能会出问题。
可以在一个进程中序列化一个对象到文件系统中,在另一个进程中反序列化恢复这个对象(注意:并不是同一个对象,只是内容相同。)。
SharedPreferences 是个特例,系统对它的读 / 写有一定的缓存策略,即内存中会有一份 ShardPreferences 文件的缓存,系统对他的读 / 写就变得不可靠,当面对高并发的读写访问,SharedPreferences 有很多大的几率丢失数据。因此,IPC 不建议采用 SharedPreferences。
三、使用 Messenger
Messenger 是一种轻量级的 IPC 方案,它的底层实现是 AIDL ,可以在不同进程中传递 Message 对象,它一次只处理一个请求,在服务端不需要考虑线程同步的问题,服务端不存在并发执行的情形。
服务端进程:服务端创建一个 Service 来处理客户端请求,同时通过一个 Handler 对象来实例化一个 Messenger 对象,然后在 Service 的 onBind 中返回这个 Messenger 对象底层的 Binder 即可。
publicclassMessengerServiceextendsService {privatestaticfinalStringTAG=MessengerService.class.getSimpleName();privateclassMessengerHandlerextendsHandler {/***@param msg*/@OverridepublicvoidhandleMessage(Messagemsg) {switch (msg.what) {caseConstants.MSG_FROM_CLIENT:Log.d(TAG,"receive msg from client: msg = ["+ msg.getData().getString(Constants.MSG_KEY)+"]");Toast.makeText(MessengerService.this,"receive msg from client: msg = ["+ msg.getData().getString(Constants.MSG_KEY)+"]",Toast.LENGTH_SHORT).show();Messenger client= msg.replyTo;Message replyMsg=Message.obtain(null,Constants.MSG_FROM_SERVICE);Bundle bundle=newBundle(); bundle.putString(Constants.MSG_KEY,"我已经收到你的消息,稍后回复你!"); replyMsg.setData(bundle);try { client.send(replyMsg); }catch (RemoteException e) { e.printStackTrace(); }break;default:super.handleMessage(msg); } } }privateMessenger mMessenger=newMessenger(newMessengerHandler());@Nullable@OverridepublicIBinderonBind(Intentintent) {return mMessenger.getBinder();
}
}
客户端进程:首先绑定服务端 Service ,绑定成功之后用服务端的 IBinder 对象创建一个 Messenger ,通过这个 Messenger 就可以向服务端发送消息了,消息类型是 Message 。如果需要服务端响应,则需要创建一个 Handler 并通过它来创建一个 Messenger(和服务端一样),并通过 Message 的 replyTo 参数传递给服务端。服务端通过 Message 的 replyTo 参数就可以回应客户端了。
publicclassMainActivityextendsAppCompatActivity {privatestaticfinalStringTAG=MainActivity.class.getSimpleName();privateMessenger mGetReplyMessenger=newMessenger(newMessageHandler());privateMessenger mService;privateclassMessageHandlerextendsHandler {@OverridepublicvoidhandleMessage(Messagemsg) {switch (msg.what) {caseConstants.MSG_FROM_SERVICE:Log.d(TAG,"received msg form service: msg = ["+ msg.getData().getString(Constants.MSG_KEY)+"]");Toast.makeText(MainActivity.this,"received msg form service: msg = ["+ msg.getData().getString(Constants.MSG_KEY)+"]",Toast.LENGTH_SHORT).show();break;default:super.handleMessage(msg); } } }@OverrideprotectedvoidonCreate(BundlesavedInstanceState) {super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); }publicvoidbindService(Viewv) {Intent mIntent=newIntent(this,MessengerService.class); bindService(mIntent, mServiceConnection,Context.BIND_AUTO_CREATE); }publicvoidsendMessage(Viewv) {Message msg=Message.obtain(null,Constants.MSG_FROM_CLIENT);Bundle data=newBundle(); data.putString(Constants.MSG_KEY,"Hello! This is client."); msg.setData(data); msg.replyTo= mGetReplyMessenger;try { mService.send(msg); }catch (RemoteException e) { e.printStackTrace(); } }@OverrideprotectedvoidonDestroy() { unbindService(mServiceConnection);super.onDestroy(); }privateServiceConnection mServiceConnection=newServiceConnection() {/***@param name*@param service*/@OverridepublicvoidonServiceConnected(ComponentNamename,IBinderservice) { mService=newMessenger(service);Message msg=Message.obtain(null,Constants.MSG_FROM_CLIENT);Bundle data=newBundle(); data.putString(Constants.MSG_KEY,"Hello! This is client."); msg.setData(data);// msg.replyTo= mGetReplyMessenger;try { mService.send(msg); }catch (RemoteException e) { e.printStackTrace(); } }/***@param name*/@OverridepublicvoidonServiceDisconnected(ComponentNamename) {
}
};
}
**注意:**客户端和服务端是通过拿到对方的 Messenger 来发送 Message 的。只不过客户端通过 bindService onServiceConnected 而服务端通过 message.replyTo 来获得对方的 Messenger 。Messenger 中有一个 Hanlder 以串行的方式处理队列中的消息。不存在并发执行,因此我们不用考虑线程同步的问题。
四、使用 AIDL
Messenger 是以串行的方式处理客户端发来的消息,如果大量消息同时发送到服务端,服务端只能一个一个处理,所以大量并发请求就不适合用 Messenger ,而且 Messenger 只适合传递消息,不能跨进程调用服务端的方法。AIDL 可以解决并发和跨进程调用方法的问题,要知道 Messenger 本质上也是 AIDL ,只不过系统做了封装方便上层的调用而已。
基本数据类型;
String和CharSequence
ArrayList,里面的元素必须能够被 AIDL 支持;
HashMap,里面的元素必须能够被 AIDL 支持;
Parcelable,实现 Parcelable 接口的对象; 注意:如果 AIDL 文件中用到了自定义的 Parcelable 对象,必须新建一个和它同名的 AIDL 文件。
AIDL,AIDL 接口本身也可以在 AIDL 文件中使用。
服务端创建一个 Service 用来监听客户端的连接请求,然后创建一个 AIDL 文件,将暴露给客户端的接口在这个 AIDL 文件中声明,最后在 Service 中实现这个 AIDL 接口即可。
绑定服务端的 Service ,绑定成功后,将服务端返回的 Binder 对象转成 AIDL 接口所属的类型,然后就可以调用 AIDL 中的方法了。客户端调用远程服务的方法,被调用的方法运行在服务端的 Binder 线程池中,同时客户端的线程会被挂起,如果服务端方法执行比较耗时,就会导致客户端线程长时间阻塞,导致 ANR 。客户端的 onServiceConnected 和 onServiceDisconnected 方法都在 UI 线程中。
使用 Permission 验证,在 manifest 中声明
服务端 onBinder 方法中
publicIBinder onBind(Intent intent) {//Permission 权限验证int check= checkCallingOrSelfPermission("com.jc.ipc.ACCESS_BOOK_SERVICE");if (check==PackageManager.PERMISSION_DENIED) {returnnull; }return mBinder;
}
Pid Uid 验证
详细代码:
// Book.aidlpackagecom.jc.ipc.aidl;parcelableBook;
// IBookManager.aidlpackage com.jc.ipc.aidl;import com.jc.ipc.aidl.Book;import com.jc.ipc.aidl.INewBookArrivedListener;// AIDL 接口中只支持方法,不支持静态常量,区别于传统的接口interfaceIBookManager {ListgetBookList();// AIDL 中除了基本数据类型,其他数据类型必须标上方向,in,out 或者 inout// in 表示输入型参数// out 表示输出型参数// inout 表示输入输出型参数voidaddBook(inBookbook);voidregisterListener(INewBookArrivedListenerlistener);voidunregisterListener(INewBookArrivedListenerlistener);
}
// INewBookArrivedListener.aidlpackagecom.jc.ipc.aidl;importcom.jc.ipc.aidl.Book;// 提醒客户端新书到来interfaceINewBookArrivedListener {voidonNewBookArrived(inBooknewBook);
}
publicclassBookManagerActivityextendsAppCompatActivity {privatestaticfinalStringTAG=BookManagerActivity.class.getSimpleName();privatestaticfinalintMSG_NEW_BOOK_ARRIVED=0x10;privateButton getBookListBtn,addBookBtn;privateTextView displayTextView;privateIBookManager bookManager;privateHandler mHandler=newHandler(){@OverridepublicvoidhandleMessage(Messagemsg) {switch (msg.what) {caseMSG_NEW_BOOK_ARRIVED:Log.d(TAG,"handleMessage: new book arrived"+ msg.obj);Toast.makeText(BookManagerActivity.this,"new book arrived"+ msg.obj,Toast.LENGTH_SHORT).show();break;default:super.handleMessage(msg); } } };privateServiceConnection mServiceConn=newServiceConnection() {@OverridepublicvoidonServiceConnected(ComponentNamename,IBinderservice) { bookManager=IBookManager.Stub.asInterface(service);try { bookManager.registerListener(listener); }catch (RemoteException e) { e.printStackTrace(); } }@OverridepublicvoidonServiceDisconnected(ComponentNamename) { } };privateINewBookArrivedListener listener=newINewBookArrivedListener.Stub() {@OverridepublicvoidonNewBookArrived(BooknewBook)throwsRemoteException { mHandler.obtainMessage(MSG_NEW_BOOK_ARRIVED, newBook).sendToTarget(); } };@OverrideprotectedvoidonCreate(@NullableBundlesavedInstanceState) {super.onCreate(savedInstanceState); setContentView(R.layout.book_manager); displayTextView= (TextView) findViewById(R.id.displayTextView);Intent intent=newIntent(this,BookManagerService.class); bindService(intent, mServiceConn,BIND_AUTO_CREATE); }publicvoidgetBookList(Viewview) {try {List list= bookManager.getBookList();Log.d(TAG,"getBookList:"+ list.toString()); displayTextView.setText(list.toString()); }catch (RemoteException e) { e.printStackTrace(); } }publicvoidaddBook(Viewview) {try { bookManager.addBook(newBook(3,"天龙八部")); }catch (RemoteException e) { e.printStackTrace(); } }@OverrideprotectedvoidonDestroy() {if (bookManager!=null&& bookManager.asBinder().isBinderAlive()) {Log.d(TAG,"unregister listener"+ listener);try { bookManager.unregisterListener(listener); }catch (RemoteException e) { e.printStackTrace(); } } unbindService(mServiceConn);super.onDestroy();
}
}
publicclassBookManagerServiceextendsService {privatestaticfinalStringTAG=BookManagerService.class.getSimpleName();// CopyOnWriteArrayList 支持并发读写,实现自动线程同步,他不是继承自 ArrayListprivateCopyOnWriteArrayList mBookList=newCopyOnWriteArrayList();//对象是不能跨进程传输的,对象的跨进程传输本质都是反序列化的过程,Binder 会把客户端传递过来的对象重新转化生成一个新的对象//RemoteCallbackList 是系统专门提供的用于删除系统跨进程 listener 的接口,利用底层的 Binder 对象是同一个//RemoteCallbackList 会在客户端进程终止后,自动溢出客户端注册的 listener ,内部自动实现了线程同步功能。privateRemoteCallbackList mListeners=newRemoteCallbackList<>();privateAtomicBoolean isServiceDestroied=newAtomicBoolean(false);privateBinder mBinder=newIBookManager.Stub() {@OverridepublicListgetBookList()throwsRemoteException {return mBookList; }@OverridepublicvoidaddBook(Bookbook)throwsRemoteException {Log.d(TAG,"addBook:"+ book.toString()); mBookList.add(book); }@OverridepublicvoidregisterListener(INewBookArrivedListenerlistener)throwsRemoteException { mListeners.register(listener); }@OverridepublicvoidunregisterListener(INewBookArrivedListenerlistener)throwsRemoteException { mListeners.unregister(listener); } };@OverridepublicvoidonCreate() {super.onCreate(); mBookList.add(newBook(1,"老人与海")); mBookList.add(newBook(2,"哈姆雷特"));newThread(newServiceWorker()).start(); }privatevoidonNewBookArrived(Bookbook)throwsRemoteException { mBookList.add(book);int count= mListeners.beginBroadcast();for (int i=0; i< count; i++) {INewBookArrivedListener listener= mListeners.getBroadcastItem(i);if (listener!=null) { listener.onNewBookArrived(book); } } mListeners.finishBroadcast(); }privateclassServiceWorkerimplementsRunnable {@Overridepublicvoidrun() {while (!isServiceDestroied.get()) {try {Thread.sleep(5000); }catch (InterruptedException e) { e.printStackTrace(); }int bookId= mBookList.size()+1;Book newBook=newBook(bookId,"new book #"+ bookId);try { onNewBookArrived(newBook); }catch (RemoteException e) { e.printStackTrace(); } } } }@Nullable@OverridepublicIBinderonBind(Intentintent) {//Permission 权限验证int check= checkCallingOrSelfPermission("com.jc.ipc.ACCESS_BOOK_SERVICE");if (check==PackageManager.PERMISSION_DENIED) {returnnull; }return mBinder; }@OverridepublicvoidonDestroy() { isServiceDestroied.set(true);super.onDestroy(); }}
五、使用 ContentProvider
用于不同应用间数据共享,和 Messenger 底层实现同样是 Binder 和 AIDL,系统做了封装,使用简单。 系统预置了许多 ContentProvider ,如通讯录、日程表,需要跨进程访问。 使用方法:继承 ContentProvider 类实现 6 个抽象方法,这六个方法均运行在 ContentProvider 进程中,除 onCreate 运行在主线程里,其他五个方法均由外界回调运行在 Binder 线程池中。
ContentProvider 的底层数据,可以是 SQLite 数据库,可以是文件,也可以是内存中的数据。
详见代码:
publicclassBookProviderextends ContentProvider {
privatestaticfinalString TAG = "BookProvider";
publicstaticfinalString AUTHORITY = "com.jc.ipc.Book.Provider";
publicstaticfinalUri BOOK_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/book");
publicstaticfinalUri USER_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/user");
publicstaticfinalintBOOK_URI_CODE = 0;
publicstaticfinalintUSER_URI_CODE = 1;
privatestaticfinalUriMatcher sUriMatcher =new UriMatcher(UriMatcher.NO_MATCH);
static {
sUriMatcher.addURI(AUTHORITY, "book", BOOK_URI_CODE);
sUriMatcher.addURI(AUTHORITY, "user", USER_URI_CODE);
}
private Context mContext;
private SQLiteDatabase mDB;
@Override
publicboolean onCreate() {
mContext = getContext();
initProviderData();
returntrue;
}
privatevoid initProviderData() {
//不建议在 UI 线程中执行耗时操作mDB =new DBOpenHelper(mContext).getWritableDatabase();
mDB.execSQL("delete from " + DBOpenHelper.BOOK_TABLE_NAME);
mDB.execSQL("delete from " + DBOpenHelper.USER_TABLE_NAME);
mDB.execSQL("insert into book values(3,'Android');");
mDB.execSQL("insert into book values(4,'iOS');");
mDB.execSQL("insert into book values(5,'Html5');");
mDB.execSQL("insert into user values(1,'haohao',1);");
mDB.execSQL("insert into user values(2,'nannan',0);");
}
@Nullable
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
Log.d(TAG, "query, current thread"+ Thread.currentThread());
String table = getTableName(uri);
if(table ==null) {
thrownewIllegalArgumentException("Unsupported URI" + uri);
}
returnmDB.query(table, projection, selection, selectionArgs,null,null, sortOrder,null);
}
@Nullable
@Override
public String getType(Uri uri) {
Log.d(TAG, "getType");
returnnull;
}
@Nullable
@Override
public Uri insert(Uri uri, ContentValues values) {
Log.d(TAG, "insert");
String table = getTableName(uri);
if(table ==null) {
thrownewIllegalArgumentException("Unsupported URI" + uri);
}
mDB.insert(table, null, values);
// 通知外界 ContentProvider 中的数据发生变化mContext.getContentResolver().notifyChange(uri,null);
return uri;
}
@Override
publicint delete(Uri uri, String selection, String[] selectionArgs) {
Log.d(TAG, "delete");
String table = getTableName(uri);
if(table ==null) {
thrownewIllegalArgumentException("Unsupported URI" + uri);
}
intcount = mDB.delete(table, selection, selectionArgs);
if(count > 0) {
mContext.getContentResolver().notifyChange(uri, null);
}
return count;
}
@Override
publicint update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
Log.d(TAG, "update");
String table = getTableName(uri);
if(table ==null) {
thrownewIllegalArgumentException("Unsupported URI" + uri);
}
introw = mDB.update(table, values, selection, selectionArgs);
if(row > 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return row;
}
private String getTableName(Uri uri) {
String tableName =null;
switch (sUriMatcher.match(uri)) {
case BOOK_URI_CODE:
tableName = DBOpenHelper.BOOK_TABLE_NAME;
break;
case USER_URI_CODE:
tableName = DBOpenHelper.USER_TABLE_NAME;
break;
default:
break;
}
return tableName;
}
}
publicclassDBOpenHelperextends SQLiteOpenHelper {
privatestaticfinalString DB_NAME = "book_provider.db";
publicstaticfinalString BOOK_TABLE_NAME = "book";
publicstaticfinalString USER_TABLE_NAME = "user";
privatestaticfinalintDB_VERSION = 1;
privateString CREATE_BOOK_TABLE = "CREATE TABLE IF NOT EXISTS " + BOOK_TABLE_NAME + "(_id INTEGER PRIMARY KEY," + "name TEXT)";
privateString CREATE_USER_TABLE = "CREATE TABLE IF NOT EXISTS " + USER_TABLE_NAME + "(_id INTEGER PRIMARY KEY," + "name TEXT," + "sex INT)";
public DBOpenHelper(Context context) {
super(context, DB_NAME,null, DB_VERSION);
}
@Override
publicvoid onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_BOOK_TABLE);
db.execSQL(CREATE_USER_TABLE);
}
@Override
publicvoidonUpgrade(SQLiteDatabase db,intoldVersion,int newVersion) {
}
}
publicclassProviderActivityextends AppCompatActivity {
privatestaticfinalString TAG = ProviderActivity.class.getSimpleName();
private TextView displayTextView;
private Handler mHandler;
@Override
protectedvoid onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_provider);
displayTextView = (TextView) findViewById(R.id.displayTextView);
mHandler =new Handler();
getContentResolver().registerContentObserver(BookProvider.BOOK_CONTENT_URI, true,new ContentObserver(mHandler) {
@Override
publicboolean deliverSelfNotifications() {
returnsuper.deliverSelfNotifications();
}
@Override
publicvoidonChange(boolean selfChange) {
super.onChange(selfChange);
}
@Override
publicvoidonChange(boolean selfChange, Uri uri) {
Toast.makeText(ProviderActivity.this, uri.toString(), Toast.LENGTH_SHORT).show();
super.onChange(selfChange, uri);
}
});
}
publicvoid insert(View v) {
ContentValues values =new ContentValues();
values.put("_id",1123);
values.put("name", "三国演义");
getContentResolver().insert(BookProvider.BOOK_CONTENT_URI, values);
}
publicvoid delete(View v) {
getContentResolver().delete(BookProvider.BOOK_CONTENT_URI, "_id = 4",null);
}
publicvoid update(View v) {
ContentValues values =new ContentValues();
values.put("_id",1123);
values.put("name", "三国演义新版");
getContentResolver().update(BookProvider.BOOK_CONTENT_URI, values , "_id = 1123",null);
}
publicvoid query(View v) {
Cursor bookCursor = getContentResolver().query(BookProvider.BOOK_CONTENT_URI,newString[]{"_id", "name"},null,null,null);
StringBuilder sb =new StringBuilder();
while (bookCursor.moveToNext()) {
Book book =newBook(bookCursor.getInt(0),bookCursor.getString(1));
sb.append(book.toString()).append("\n");
}
sb.append("--------------------------------").append("\n");
bookCursor.close();
Cursor userCursor = getContentResolver().query(BookProvider.USER_CONTENT_URI,newString[]{"_id", "name", "sex"},null,null,null);
while (userCursor.moveToNext()) {
sb.append(userCursor.getInt(0))
.append(userCursor.getString(1)).append(" ,")
.append(userCursor.getInt(2)).append(" ,")
.append("\n");
}
sb.append("--------------------------------");
userCursor.close();
displayTextView.setText(sb.toString());
}
}
六、使用 Socket
Socket起源于 Unix,而 Unix 基本哲学之一就是“一切皆文件”,都可以用“打开 open –读写 write/read –关闭 close ”模式来操作。Socket 就是该模式的一个实现,网络的 Socket 数据传输是一种特殊的 I/O,Socket 也是一种文件描述符。Socket 也具有一个类似于打开文件的函数调用: Socket(),该函数返回一个整型的Socket 描述符,随后的连接建立、数据传输等操作都是通过该 Socket 实现的。
常用的 Socket 类型有两种:流式 Socket(SOCK_STREAM)和数据报式 Socket(SOCK_DGRAM)。流式是一种面向连接的 Socket,针对于面向连接的 TCP 服务应用;数据报式 Socket 是一种无连接的 Socket ,对应于无连接的 UDP 服务应用。
Socket 本身可以传输任意字节流。
谈到Socket,就必须要说一说 TCP/IP 五层网络模型:
应用层:规定应用程序的数据格式,主要的协议 HTTP,FTP,WebSocket,POP3 等;
传输层:建立“端口到端口” 的通信,主要的协议:TCP,UDP;
网络层:建立”主机到主机”的通信,主要的协议:IP,ARP ,IP 协议的主要作用:一个是为每一台计算机分配 IP 地址,另一个是确定哪些地址在同一子网;
数据链路层:确定电信号的分组方式,主要的协议:以太网协议;
物理层:负责电信号的传输。
Socket 是连接应用层与传输层之间接口(API)。
只实现 TCP Socket 。
Client 端代码:
publicclassTCPClientActivityextendsAppCompatActivityimplements View.OnClickListener{
privatestaticfinalString TAG = "TCPClientActivity";
publicstaticfinalintMSG_RECEIVED = 0x10;
publicstaticfinalintMSG_READY = 0x11;
private EditText editText;
private TextView textView;
private PrintWriter mPrintWriter;
private Socket mClientSocket;
private Button sendBtn;
private StringBuilder stringBuilder;
privateHandler mHandler =new Handler(){
@Override
publicvoid handleMessage(Message msg) {
switch (msg.what) {
case MSG_READY:
sendBtn.setEnabled(true);
break;
case MSG_RECEIVED:
stringBuilder.append(msg.obj).append("\n");
textView.setText(stringBuilder.toString());
break;
default:
super.handleMessage(msg);
}
}
};
@Override
protectedvoid onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.tcp_client_activity);
editText = (EditText) findViewById(R.id.editText);
textView = (TextView) findViewById(R.id.displayTextView);
sendBtn = (Button) findViewById(R.id.sendBtn);
sendBtn.setOnClickListener(this);
sendBtn.setEnabled(false);
stringBuilder =new StringBuilder();
Intent intent =newIntent(TCPClientActivity.this, TCPServerService.class);
startService(intent);
new Thread(){
@Override
publicvoid run() {
connectTcpServer();
}
}.start();
}
privateString formatDateTime(long time) {
returnnewSimpleDateFormat("(HH:mm:ss)").format(new Date(time));
}
privatevoid connectTcpServer() {
Socket socket =null;
while(socket ==null) {
try {
socket =newSocket("localhost", 8888);
mClientSocket = socket;
mPrintWriter =newPrintWriter(new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())
), true);
mHandler.sendEmptyMessage(MSG_READY);
} catch (IOException e) {
e.printStackTrace();
}
}
// receive messageBufferedReader bufferedReader =null;
try {
bufferedReader =newBufferedReader(new InputStreamReader(socket.getInputStream()));
} catch (IOException e) {
e.printStackTrace();
}
while(!isFinishing()) {
try {
String msg = bufferedReader.readLine();
if(msg !=null) {
String time = formatDateTime(System.currentTimeMillis());
String showedMsg = "server " + time + ":" + msg
+ "\n";
mHandler.obtainMessage(MSG_RECEIVED, showedMsg).sendToTarget();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
publicvoid onClick(View v) {
if(mPrintWriter !=null) {
String msg = editText.getText().toString();
mPrintWriter.println(msg);
editText.setText("");
String time = formatDateTime(System.currentTimeMillis());
String showedMsg = "self " + time + ":" + msg + "\n";
stringBuilder.append(showedMsg);
}
}
@Override
protectedvoid onDestroy() {
if(mClientSocket !=null) {
try {
mClientSocket.shutdownInput();
mClientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
super.onDestroy();
}
}
Server端代码:
publicclassTCPServerServiceextends Service {
privatestaticfinalString TAG = "TCPServerService";
privatebooleanisServiceDestroyed =false;
privateString[] mMessages =new String[]{
"Hello! Body!",
"用户不在线!请稍后再联系!",
"请问你叫什么名字呀?",
"厉害了,我的哥!",
"Google 不需要***是真的吗?",
"扎心了,老铁!!!" };
@Override
publicvoid onCreate() {
newThread(new TCPServer()).start();
super.onCreate();
}
@Override
publicvoid onDestroy() {
isServiceDestroyed =true;
super.onDestroy();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
returnnull;
}
privateclassTCPServerimplements Runnable {
@Override
publicvoid run() {
ServerSocket serverSocket =null;
try {
serverSocket =newServerSocket(8888);
} catch (IOException e) {
e.printStackTrace();
return;
}
while(!isServiceDestroyed) {
// receive request from clienttry {
finalSocket client = serverSocket.accept();
Log.d(TAG, "=============== accept ==================");
new Thread(){
@Override
publicvoid run() {
try {
responseClient(client);
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
privatevoidresponseClient(Socket client)throws IOException {
//receive messageBufferedReader in =new BufferedReader(
new InputStreamReader(client.getInputStream()));
//send messagePrintWriter out =new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(
client.getOutputStream())),true);
out.println("欢迎来到聊天室!");
while(!isServiceDestroyed) {
String str = in.readLine();
Log.d(TAG, "message from client: " + str);
if(str ==null) {
return;
}
Random random =new Random();
intindex = random.nextInt(mMessages.length);
String msg = mMessages[index];
out.println(msg);
Log.d(TAG, "send Message: " + msg);
}
out.close();
in.close();
client.close();
}
}