该原创文章首发于微信公众号“字节流动”

Android 中的 IPC 方式

使用 Intent

  1. Activity,Service,Receiver 都支持在 Intent 中传递 Bundle 数据,而 Bundle 实现了 Parcelable 接口,可以在不同的进程间进行传输。
  2. 在一个进程中启动了另一个进程的 Acyivity,Service 和 Receiver ,可以在 Bundle 中附加要传递的数据通过 Intent 发送出去。

使用文件共享

  1. Windows 上,一个文件如果被加了排斥锁会导致其他线程无法对其进行访问,包括读和写;而 Android 系统基于 Linux ,使得其并发读取文件没有限制地进行,甚至允许两个线程同时对一个文件进行读写操作,尽管这样可能会出问题。
  2. 可以在一个进程中序列化一个对象到文件系统中,在另一个进程中反序列化恢复这个对象(注意:并不是同一个对象,只是内容相同。)。
  3. SharedPreferences 是个特例,系统对它的读 / 写有一定的缓存策略,即内存中会有一份 ShardPreferences 文件的缓存,系统对他的读 / 写就变得不可靠,当面对高并发的读写访问,SharedPreferences 有很多大的几率丢失数据。因此,IPC 不建议采用 SharedPreferences。

使用 Messenger

Messenger 是一种轻量级的 IPC 方案,它的底层实现是 AIDL ,可以在不同进程中传递 Message 对象,它一次只处理一个请求,在服务端不需要考虑线程同步的问题,服务端不存在并发执行的情形。

  • 服务端进程:服务端创建一个 Service 来处理客户端请求,同时通过一个 Handler 对象来实例化一个 Messenger 对象,然后在 Service 的 onBind 中返回这个 Messenger 对象底层的 Binder 即可。
public class MessengerService extends Service {private static final String TAG = MessengerService.class.getSimpleName();private class MessengerHandler extends Handler {/*** @param msg*/@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case Constants.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 = new Bundle();bundle.putString(Constants.MSG_KEY, "我已经收到你的消息,稍后回复你!");replyMsg.setData(bundle);try {client.send(replyMsg);} catch (RemoteException e) {e.printStackTrace();}break;default:super.handleMessage(msg);}}}private Messenger mMessenger = new Messenger(new MessengerHandler());@Nullable@Overridepublic IBinder onBind(Intent intent) {return mMessenger.getBinder();}
}
  • 客户端进程:首先绑定服务端 Service ,绑定成功之后用服务端的 IBinder 对象创建一个 Messenger ,通过这个 Messenger 就可以向服务端发送消息了,消息类型是 Message 。如果需要服务端响应,则需要创建一个 Handler 并通过它来创建一个 Messenger(和服务端一样),并通过 Message 的 replyTo 参数传递给服务端。服务端通过 Message 的 replyTo 参数就可以回应客户端了。
public class MainActivity extends AppCompatActivity {private static final String TAG = MainActivity.class.getSimpleName();private Messenger mGetReplyMessenger = new Messenger(new MessageHandler());private Messenger mService;private class MessageHandler extends Handler {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case Constants.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);}}}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}public void bindService(View v) {Intent mIntent = new Intent(this, MessengerService.class);bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);}public void sendMessage(View v) {Message msg = Message.obtain(null,Constants.MSG_FROM_CLIENT);Bundle data = new Bundle();data.putString(Constants.MSG_KEY, "Hello! This is client.");msg.setData(data);msg.replyTo = mGetReplyMessenger;try {mService.send(msg);} catch (RemoteException e) {e.printStackTrace();}}@Overrideprotected void onDestroy() {unbindService(mServiceConnection);super.onDestroy();}private ServiceConnection mServiceConnection = new ServiceConnection() {/*** @param name* @param service*/@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {mService = new Messenger(service);Message msg = Message.obtain(null,Constants.MSG_FROM_CLIENT);Bundle data = new Bundle();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*/@Overridepublic void onServiceDisconnected(ComponentName name) {}};
}

**注意:**客户端和服务端是通过拿到对方的 Messenger 来发送 Message 的。只不过客户端通过 bindService onServiceConnected 而服务端通过 message.replyTo 来获得对方的 Messenger 。Messenger 中有一个 Hanlder 以串行的方式处理队列中的消息。不存在并发执行,因此我们不用考虑线程同步的问题。

使用 AIDL

Messenger 是以串行的方式处理客户端发来的消息,如果大量消息同时发送到服务端,服务端只能一个一个处理,所以大量并发请求就不适合用 Messenger ,而且 Messenger 只适合传递消息,不能跨进程调用服务端的方法。AIDL 可以解决并发和跨进程调用方法的问题,要知道 Messenger 本质上也是 AIDL ,只不过系统做了封装方便上层的调用而已。

AIDL 文件支持的数据类型

  • 基本数据类型

  • StringCharSequence

  • 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 中声明
    <permission android:name="com.jc.ipc.ACCESS_BOOK_SERVICE"android:protectionLevel="normal"/><uses-permission android:name="com.jc.ipc.ACCESS_BOOK_SERVICE"/>

服务端 onBinder 方法中

    public IBinder onBind(Intent intent) {//Permission 权限验证int check = checkCallingOrSelfPermission("com.jc.ipc.ACCESS_BOOK_SERVICE");if (check == PackageManager.PERMISSION_DENIED) {return null;}return mBinder;}
  • Pid Uid 验证

详细代码:

// Book.aidl
package com.jc.ipc.aidl;parcelable Book;
// IBookManager.aidl
package com.jc.ipc.aidl;import com.jc.ipc.aidl.Book;
import com.jc.ipc.aidl.INewBookArrivedListener;// AIDL 接口中只支持方法,不支持静态常量,区别于传统的接口
interface IBookManager {List<Book> getBookList();// AIDL 中除了基本数据类型,其他数据类型必须标上方向,in,out 或者 inout// in 表示输入型参数// out 表示输出型参数// inout 表示输入输出型参数void addBook(in Book book);void registerListener(INewBookArrivedListener listener);void unregisterListener(INewBookArrivedListener listener);}
// INewBookArrivedListener.aidl
package com.jc.ipc.aidl;
import com.jc.ipc.aidl.Book;// 提醒客户端新书到来interface INewBookArrivedListener {void onNewBookArrived(in Book newBook);
}
public class BookManagerActivity extends AppCompatActivity {private static final String TAG = BookManagerActivity.class.getSimpleName();private static final int MSG_NEW_BOOK_ARRIVED = 0x10;private Button getBookListBtn,addBookBtn;private TextView displayTextView;private IBookManager bookManager;private Handler mHandler = new Handler(){@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case MSG_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);}}};private ServiceConnection mServiceConn = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {bookManager = IBookManager.Stub.asInterface(service);try {bookManager.registerListener(listener);} catch (RemoteException e) {e.printStackTrace();}}@Overridepublic void onServiceDisconnected(ComponentName name) {}};private INewBookArrivedListener listener = new INewBookArrivedListener.Stub() {@Overridepublic void onNewBookArrived(Book newBook) throws RemoteException {mHandler.obtainMessage(MSG_NEW_BOOK_ARRIVED, newBook).sendToTarget();}};@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.book_manager);displayTextView = (TextView) findViewById(R.id.displayTextView);Intent intent = new Intent(this, BookManagerService.class);bindService(intent, mServiceConn, BIND_AUTO_CREATE);}public void getBookList(View view) {try {List<Book> list = bookManager.getBookList();Log.d(TAG, "getBookList: " + list.toString());displayTextView.setText(list.toString());} catch (RemoteException e) {e.printStackTrace();}}public void addBook(View view) {try {bookManager.addBook(new Book(3, "天龙八部"));} catch (RemoteException e) {e.printStackTrace();}}@Overrideprotected void onDestroy() {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();}
}
public class BookManagerService extends Service {private static final String TAG = BookManagerService.class.getSimpleName();// CopyOnWriteArrayList 支持并发读写,实现自动线程同步,他不是继承自 ArrayListprivate CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();//对象是不能跨进程传输的,对象的跨进程传输本质都是反序列化的过程,Binder 会把客户端传递过来的对象重新转化生成一个新的对象//RemoteCallbackList 是系统专门提供的用于删除系统跨进程 listener 的接口,利用底层的 Binder 对象是同一个//RemoteCallbackList 会在客户端进程终止后,自动溢出客户端注册的 listener ,内部自动实现了线程同步功能。private RemoteCallbackList<INewBookArrivedListener> mListeners = new RemoteCallbackList<>();private AtomicBoolean isServiceDestroied = new AtomicBoolean(false);private Binder mBinder = new IBookManager.Stub() {@Overridepublic List<Book> getBookList() throws RemoteException {return mBookList;}@Overridepublic void addBook(Book book) throws RemoteException {Log.d(TAG, "addBook: " + book.toString());mBookList.add(book);}@Overridepublic void registerListener(INewBookArrivedListener listener) throws RemoteException {mListeners.register(listener);}@Overridepublic void unregisterListener(INewBookArrivedListener listener) throws RemoteException {mListeners.unregister(listener);}};@Overridepublic void onCreate() {super.onCreate();mBookList.add(new Book(1, "老人与海"));mBookList.add(new Book(2, "哈姆雷特"));new Thread(new ServiceWorker()).start();}private void onNewBookArrived(Book book) throws RemoteException {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();}private class ServiceWorker implements Runnable {@Overridepublic void run() {while (!isServiceDestroied.get()) {try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}int bookId = mBookList.size() +1;Book newBook = new Book(bookId, "new book # " + bookId);try {onNewBookArrived(newBook);} catch (RemoteException e) {e.printStackTrace();}}}}@Nullable@Overridepublic IBinder onBind(Intent intent) {//Permission 权限验证int check = checkCallingOrSelfPermission("com.jc.ipc.ACCESS_BOOK_SERVICE");if (check == PackageManager.PERMISSION_DENIED) {return null;}return mBinder;}@Overridepublic void onDestroy() {isServiceDestroied.set(true);super.onDestroy();}
}

使用 ContentProvider

用于不同应用间数据共享,和 Messenger 底层实现同样是 Binder 和 AIDL,系统做了封装,使用简单。
系统预置了许多 ContentProvider ,如通讯录、日程表,需要跨进程访问。
使用方法:继承 ContentProvider 类实现 6 个抽象方法,这六个方法均运行在 ContentProvider 进程中,除 onCreate 运行在主线程里,其他五个方法均由外界回调运行在 Binder 线程池中。

ContentProvider 的底层数据,可以是 SQLite 数据库,可以是文件,也可以是内存中的数据。

详见代码:

public class BookProvider extends ContentProvider {private static final String TAG = "BookProvider";public static final String AUTHORITY = "com.jc.ipc.Book.Provider";public static final Uri BOOK_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/book");public static final Uri USER_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/user");public static final int BOOK_URI_CODE = 0;public static final int USER_URI_CODE = 1;private static final UriMatcher 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;@Overridepublic boolean onCreate() {mContext = getContext();initProviderData();return true;}private void 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@Overridepublic 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) {throw new IllegalArgumentException("Unsupported URI" + uri);}return mDB.query(table, projection, selection, selectionArgs, null, null, sortOrder, null);}@Nullable@Overridepublic String getType(Uri uri) {Log.d(TAG, "getType");return null;}@Nullable@Overridepublic Uri insert(Uri uri, ContentValues values) {Log.d(TAG, "insert");String table = getTableName(uri);if (table == null) {throw new IllegalArgumentException("Unsupported URI" + uri);}mDB.insert(table, null, values);// 通知外界 ContentProvider 中的数据发生变化mContext.getContentResolver().notifyChange(uri, null);return uri;}@Overridepublic int delete(Uri uri, String selection, String[] selectionArgs) {Log.d(TAG, "delete");String table = getTableName(uri);if (table == null) {throw new IllegalArgumentException("Unsupported URI" + uri);}int count = mDB.delete(table, selection, selectionArgs);if (count > 0) {mContext.getContentResolver().notifyChange(uri, null);}return count;}@Overridepublic int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {Log.d(TAG, "update");String table = getTableName(uri);if (table == null) {throw new IllegalArgumentException("Unsupported URI" + uri);}int row = 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;}
}
public class DBOpenHelper extends SQLiteOpenHelper {private static final String DB_NAME = "book_provider.db";public static final String BOOK_TABLE_NAME = "book";public static final String USER_TABLE_NAME = "user";private static final int DB_VERSION = 1;private String CREATE_BOOK_TABLE = "CREATE TABLE IF NOT EXISTS "+ BOOK_TABLE_NAME + "(_id INTEGER PRIMARY KEY," + "name TEXT)";private String 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);}@Overridepublic void onCreate(SQLiteDatabase db) {db.execSQL(CREATE_BOOK_TABLE);db.execSQL(CREATE_USER_TABLE);}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}
}
public class ProviderActivity extends AppCompatActivity {private static final String TAG = ProviderActivity.class.getSimpleName();private TextView displayTextView;private Handler mHandler;@Overrideprotected void 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) {@Overridepublic boolean deliverSelfNotifications() {return super.deliverSelfNotifications();}@Overridepublic void onChange(boolean selfChange) {super.onChange(selfChange);}@Overridepublic void onChange(boolean selfChange, Uri uri) {Toast.makeText(ProviderActivity.this, uri.toString(), Toast.LENGTH_SHORT).show();super.onChange(selfChange, uri);}});}public void insert(View v) {ContentValues values = new ContentValues();values.put("_id",1123);values.put("name", "三国演义");getContentResolver().insert(BookProvider.BOOK_CONTENT_URI, values);}public void delete(View v) {getContentResolver().delete(BookProvider.BOOK_CONTENT_URI, "_id = 4", null);}public void update(View v) {ContentValues values = new ContentValues();values.put("_id",1123);values.put("name", "三国演义新版");getContentResolver().update(BookProvider.BOOK_CONTENT_URI, values , "_id = 1123", null);}public void query(View v) {Cursor bookCursor = getContentResolver().query(BookProvider.BOOK_CONTENT_URI, new String[]{"_id", "name"}, null, null, null);StringBuilder sb = new StringBuilder();while (bookCursor.moveToNext()) {Book book = new Book(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, new String[]{"_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 端代码:


public class TCPClientActivity extends AppCompatActivity implements View.OnClickListener{private static final String TAG = "TCPClientActivity";public static final int MSG_RECEIVED = 0x10;public static final int MSG_READY = 0x11;private EditText editText;private TextView textView;private PrintWriter mPrintWriter;private Socket mClientSocket;private Button sendBtn;private StringBuilder stringBuilder;private Handler mHandler = new Handler(){@Overridepublic void 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);}}};@Overrideprotected void 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 = new Intent(TCPClientActivity.this, TCPServerService.class);startService(intent);new Thread(){@Overridepublic void run() {connectTcpServer();}}.start();}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", 8888);mClientSocket = socket;mPrintWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);mHandler.sendEmptyMessage(MSG_READY);} catch (IOException e) {e.printStackTrace();}}// receive messageBufferedReader bufferedReader = null;try {bufferedReader = new BufferedReader(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();}}}@Overridepublic void 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);}}@Overrideprotected void onDestroy() {if (mClientSocket != null) {try {mClientSocket.shutdownInput();mClientSocket.close();} catch (IOException e) {e.printStackTrace();}}super.onDestroy();}
}

Server 端代码:

public class TCPServerService extends Service {private static final String TAG = "TCPServerService";private boolean isServiceDestroyed = false;private String[] mMessages = new String[]{"Hello! Body!","用户不在线!请稍后再联系!","请问你叫什么名字呀?","厉害了,我的哥!","Google 不需要科学上网是真的吗?","扎心了,老铁!!!"};@Overridepublic void onCreate() {new Thread(new TCPServer()).start();super.onCreate();}@Overridepublic void onDestroy() {isServiceDestroyed = true;super.onDestroy();}@Nullable@Overridepublic IBinder onBind(Intent intent) {return null;}private class TCPServer implements Runnable {@Overridepublic void run() {ServerSocket serverSocket = null;try {serverSocket = new ServerSocket(8888);} catch (IOException e) {e.printStackTrace();return;}while (!isServiceDestroyed) {// receive request from clienttry {final Socket client = serverSocket.accept();Log.d(TAG, "=============== accept ==================");new Thread(){@Overridepublic void run() {try {responseClient(client);} catch (IOException e) {e.printStackTrace();}}}.start();} catch (IOException e) {e.printStackTrace();}}}}private void responseClient(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();int index = random.nextInt(mMessages.length);String msg = mMessages[index];out.println(msg);Log.d(TAG, "send Message: " + msg);}out.close();in.close();client.close();}
}

UDP Socket 可以自己尝试着实现。

联系与交流

微信公众号

个人微信

Android 进程间通信方式相关推荐

  1. Android——进程间通信方式

    0. 前置知识 其实android中追根溯源只有两种进程间通信方式,其他的方式都是通过封装这两种方式而得到的: Binder与Socket Android--Binder机制. Android中Soc ...

  2. Android 进程间通信方式和线程间通信方式

    1.进程和线程 进程:是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位. 线程:是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独 ...

  3. Android进程间通信 - 几种方式的对比总结

    文章目录 什么是RPC 什么是IPC 参考 什么是RPC RPC(Remote Procedure Call)即远程过程调用,它是一种通过网络从远程计算机程序上请求服务,在不需要了解底层网络技术的协议 ...

  4. Android进程间通信的几种方式

    首先,介绍一下,什么是RPC RPC(Remote Procedure Call)即远程过程调用,它是一种通过网络从远程计算机程序上请求服务,在不需要了解底层网络技术的协议下,即可获取计算机进程中的数 ...

  5. 浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路(1)

    上一篇文章Android进程间通信(IPC)机制Binder简要介绍和学习计划简要介绍了Android系统进程间通信机制Binder的总体架构,它由Client.Server.Service Mana ...

  6. 浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路

    原文地址: http://blog.csdn.net/luoshengyang/article/details/6621566 上一篇文章Android进程间通信(IPC)机制Binder简要介绍和学 ...

  7. Android进程间通信(IPC)机制Binder简要介绍和学习计划

    在Android系统中,每一个应用程序都是由一些Activity和Service组成的,这些Activity和Service有可能运行在同一个进程中,也有可能运行在不同的进程中.那么,不在同一个进程的 ...

  8. Android 进程间通信——AIDL

    代码地址如下: http://www.demodashi.com/demo/12321.html 原文地址:http://blog.csdn.net/vnanyesheshou/article/det ...

  9. dat关闭某进程_超详细解析!工程师必会的Linux进程间通信方式和原理

    ▍进程的概念 · 进程是操作系统的概念,每当我们执行一个程序时,对于操作系统来讲就创建了一个进程,在这个过程中,伴随着资源的分配和释放.可以认为进程是一个程序的一次执行过程. ▍进程通信的概念 · 进 ...

最新文章

  1. Linux压缩/解压缩
  2. RHEL7 下双网卡绑定做主备(冗余)
  3. 每日一皮:一个项目开发的真实写照...
  4. 利用openCV中的cvCanny函数检测人脸的边缘
  5. ASP.NET的命名空间
  6. linux——DHCP的配置问题
  7. chips of wow
  8. 架设mediawiki服务器续--安装VisualEditor编辑器
  9. JPA规范的主要内容
  10. pycharm运行模型时怎么设置权重?_使用AMP和Tensor Cores得到更快速,更节省内存的PyTorch模型...
  11. 《Django实战系列》
  12. 手撕前端面试之经典排序算法
  13. 剑指offer面试题57 - II. 和为s的连续正数序列(双指针)(滑动窗口)
  14. canvas绘制圆环
  15. 利用Scrapy编写“1024网站种子吞噬爬虫”,送福利
  16. Laravel打印操作的sql 阿星小栈
  17. 生鲜配送系统源码开发流程教程
  18. 《博弈论》异或 博弈x^x第二手只能一定赢吗?
  19. R语言使用cph函数和rcs函数构建限制性立方样条cox回归模型、使用ggcoxzph函数可视化进行Schoenfeld残差图检验模型是否满足等比例风险
  20. 【Visual C++】游戏开发笔记四十 浅墨DirectX教程之八 绘制真实质感的三维世界 光照与材质专场

热门文章

  1. 教程 | 阿克曼结构移动机器人的gazebo仿真(五)
  2. apache ajax 跨域访问,AJAX跨域访问(从Tomcat8到Apache/Nginx)
  3. 一个女程序员的心酸和无奈
  4. AltiumDesigner下载安装
  5. css内联样式_如何覆盖内联CSS样式
  6. 红帽linux安装docker,在CentOS7.6、红帽7.6系统中安装Docker:只需3条命令
  7. 计算机服务器排名,2019服务器CPU天梯图 多路CPU性能排名
  8. 内网网站发布到外网-五种方法
  9. JAVA、IDEA超级实用快捷键(公司开发项目中常用快捷键)
  10. Excel表格的密码设置与取消