?? 1.安卓中的IPC方式
1.1 Bundle
💡 bundle就不多写了,只需要注意,四大组件除ContentProvider之外,其它组件都支持bundle
1.2使用文件共享
💡 通过共享文件来进行进程间通信,不多说,需要注意,安卓提供的轻量级存储方案SharedPreferences,其也属于文件系统的一种,但由于其有一定的缓存策略,使得在内存里面有一份SharedPreferences的缓存,这会造成在多进程下使用SharedPreferences有一定的几率会丢失数据。
1.3使用Messenger
💡 可以通过Messenger实现进程间的通信,其底层实现是aidl,Messenger对aidl进行了封装,一次只可以处理一次请求,是一种轻量级的IPC方案,同时由于一次只处理一个请求,因此没有必要考虑同步问题,但是不适合在大量的并发请求里面使用。
3.3.1 实现方式
package com.example.artandroidlearn;
public class MyConstants {
public static final int MSG_FROM_CLIENT = 0;
public static final int MSG_FROM_SERVICE = 2;
}
package com.example.artandroidlearn.Service;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.example.artandroidlearn.MyConstants;
public class MessengerService extends Service {
private static final String TAG = "MessengerService";
private static class MessengerHandler extends Handler {
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what) {
case MyConstants.MSG_FROM_CLIENT:
Log.d(TAG, msg.getData().getString("msg"));
Messenger messenger = msg.replyTo;
Message reply = Message.obtain(null, MyConstants.MSG_FROM_SERVICE);
Bundle bundle = new Bundle();
bundle.putString("reply", "service");
reply.setData(bundle);
try {
messenger.send(reply);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
super.handleMessage(msg);
}
}
}
private final Messenger messenger = new Messenger(new MessengerHandler());
@Nullable
@Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();
}
}
package com.example.artandroidlearn.activity;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import com.example.artandroidlearn.Service.MessengerService;
import com.example.artandroidlearn.MyConstants;
import com.example.artandroidlearn.R;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private Messenger messenger;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
messenger = new Messenger(service);
Message message = Message.obtain(null, MyConstants.MSG_FROM_CLIENT);
Bundle data = new Bundle();
data.putString("msg", "hello,this is client");
message.setData(data);
message.replyTo = messenger1;
try {
messenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
private Messenger messenger1 = new Messenger(new MessengerHandler());
private static class MessengerHandler extends Handler {
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what) {
case MyConstants.MSG_FROM_SERVICE:
Log.d(TAG, msg.getData().getString("reply"));
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, MessengerService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
}
@Override
public void addContentView(View view, ViewGroup.LayoutParams params) {
super.addContentView(view, params);
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(serviceConnection);
}
1.3.2Messenger工作流程图
💡 Messenger 进行消息传递,必须将数据放入Message 里面,通过Messenger 来传递Message ,由于Message 支持的数据类型只有what 、arg1 、arg2 、bundle 、replyTo 及object ,但由于object 只能传输实现了Paracelable 接口的数据,因此实际上只有除object 几个。
1.4AIDL
1.4.1 AIDL支持的数据类型
- 基本数据类型(int、long、char、boolean、double等);
- String和CharSequence;
- List,只支持ArrayList且里面的元素也要被AIDL支持;
- Map,只支持HashMap,且里面的元素也要被AIDL支持;
- 所有实现了Paracelable接口的对象;
- AIDL,AIDL接口本身可以在AIDL文件中使用。
💡 1.自定义的Paracelable对象和AIDL对象需要显示的import进AIDL文件,如果需要使用自定义的Paracelable对象,需要创建同名的AIDL文件;
2.在AIDL文件中引入Paracelable对象的对应AIDL文件时,需要使用paracelable进行修饰,除基本类型外,在AIDL文件中其它类型参数都要使用in、out、inout修饰符修饰,其中in代表输入型参数、out代表输出型参数、inout为前两者的结合; 3.需要根据实际情况去指定参数类型,不能一概使用out或inout,否则会带来开销; 4.AIDL接口只支持声明方法,不支持声明静态常量。
1.4.2 实现实例
package com.example.artandroidlearn;
import com.example.artandroidlearn.Book;
import com.example.artandroidlearn.IonNewBookArrivedListener;
interface IBookManager {
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
List<Book> getBookList();
void addBook(in Book book);
void registerListener(IonNewBookArrivedListener listener);
void unregisterListener(IonNewBookArrivedListener listener);
}
package com.example.artandroidlearn;
import com.example.artandroidlearn.Book;
interface IonNewBookArrivedListener{
void onNewBookArrived(in Book newBook);
}
package com.example.artandroidlearn;
parcelable Book;
package com.example.artandroidlearn;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
public class Book implements Parcelable {
public int bookId;
public String bookName;
public Book() {
}
public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(bookId);
dest.writeString(bookName);
}
public static final Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
protected Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}
@NonNull
@Override
public String toString() {
return bookId + ":" + bookName;
}
}
package com.example.artandroidlearn.Service;
import android.app.Service;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
import androidx.annotation.Nullable;
import com.example.artandroidlearn.Book;
import com.example.artandroidlearn.IBookManager;
import com.example.artandroidlearn.IonNewBookArrivedListener;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
public class BookManagerService extends Service {
private static final String TAG = "BookManagerService";
private AtomicBoolean mIsServiceDestroyed = new AtomicBoolean(false);
private RemoteCallbackList<IonNewBookArrivedListener> mListenerRemoteCallbackList = new RemoteCallbackList<>();
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
private Binder mBinder = new IBookManager.Stub() {
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
int check = checkCallingOrSelfPermission("com.example.artandroidlearn.ACCESS_BOOK");
if (check == PackageManager.PERMISSION_DENIED) {
return false;
}
String packageName = null;
String[] packages = getPackageManager().getPackagesForUid(getCallingUid());
if (packages != null && packages.length > 0) {
packageName = packages[0];
}
if (!packageName.startsWith("com.example")) {
return false;
}
return super.onTransact(code, data, reply, flags);
}
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public List<Book> getBookList() throws RemoteException {
return mBookList;
}
@Override
public void addBook(Book book) throws RemoteException {
mBookList.add(book);
}
@Override
public void registerListener(IonNewBookArrivedListener listener) throws RemoteException {
mListenerRemoteCallbackList.register(listener);
}
@Override
public void unregisterListener(IonNewBookArrivedListener listener) throws RemoteException {
mListenerRemoteCallbackList.unregister(listener);
}
};
@Override
public void onCreate() {
super.onCreate();
mBookList.add(new Book(1, "www"));
mBookList.add(new Book(2, "kkk"));
new Thread(new ServiceWork()).start();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
int check = checkCallingOrSelfPermission("com.example.artandroidlearn.ACCESS_BOOK");
if (check == PackageManager.PERMISSION_DENIED) {
return null;
}
return mBinder;
}
@Override
public void onDestroy() {
mIsServiceDestroyed.set(true);
super.onDestroy();
}
private void onNewBookArrived(Book book) throws RemoteException {
mBookList.add(book);
final int n = mListenerRemoteCallbackList.beginBroadcast();
for (int i = 0; i < n; i++) {
IonNewBookArrivedListener newBookArrivedListener = mListenerRemoteCallbackList.getBroadcastItem(i);
if (newBookArrivedListener != null) {
try {
newBookArrivedListener.onNewBookArrived(book);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
mListenerRemoteCallbackList.finishBroadcast();
}
private class ServiceWork implements Runnable {
@Override
public void run() {
while (!mIsServiceDestroyed.get()) {
try {
Thread.sleep(500);
} 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();
}
}
}
}
}
💡 这里面实现了Stub接口并将其作为binder返回,注意使用了CopyOnWriteArrayList ,由于AIDL方法是在binder线程池里面运行,所以需要使用它来来保证线程同步,这里备注一下,由于AIDL里面所支持的是抽象的List,虽然服务端使用的是CopyOnWriteArrayList ,但是在客户端还是返回的ArrayList 。
package com.example.artandroidlearn.activity;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.example.artandroidlearn.Book;
import com.example.artandroidlearn.IonNewBookArrivedListener;
import com.example.artandroidlearn.Service.BookManagerService;
import com.example.artandroidlearn.IBookManager;
import com.example.artandroidlearn.R;
import java.util.List;
public class BookManagerActivity extends Activity {
private static final String TAG = "BookManagerActivity";
private static final int MESSAGE_NEW_BOOK_ARRIVED = 1;
private IBookManager mRemoteBookManager;
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
if (mRemoteBookManager == null) {
return;
}
mRemoteBookManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
mRemoteBookManager = null;
}
};
private static Handler mHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what) {
case MESSAGE_NEW_BOOK_ARRIVED:
Log.d(TAG, "receive new book:" + msg.obj);
break;
default:
super.handleMessage(msg);
}
}
};
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
IBookManager bookManager = IBookManager.Stub.asInterface(service);
try {
mRemoteBookManager = bookManager;
mRemoteBookManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
List<Book> bookList = bookManager.getBookList();
Log.i(TAG, "query book list,list type:" + bookList.getClass().getCanonicalName());
bookManager.addBook(new Book(3, "ks"));
Log.i(TAG, "query book list:" + bookList.toString());
bookManager.addBook(new Book(3, "ks"));
bookList = bookManager.getBookList();
Log.i(TAG, "query book list:" + bookList.toString());
bookManager.registerListener(monNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
private IonNewBookArrivedListener monNewBookArrivedListener = new IonNewBookArrivedListener.Stub() {
@Override
public void onNewBookArrived(Book newBook) throws RemoteException {
mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, newBook).sendToTarget();
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_book_manager);
Intent intent = new Intent(this, BookManagerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
if (mRemoteBookManager != null && mRemoteBookManager.asBinder().isBinderAlive()) {
try {
mRemoteBookManager.unregisterListener(monNewBookArrivedListener);
Log.d(TAG, "unregisterListener:" + monNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
unbindService(mConnection);
super.onDestroy();
}
}
💡 注意:AIDL客户端调服务端,在服务端没有返回数据前,客户端会挂起,因此当服务端比较耗时,需要避免客户端的耗时操作,或将客户端的相应操作放在非UI线程中里面,同样道理,服务端调客户端方法,如此例调listener,若listener耗时,则会导致服务端耗时,因此需要将对应的运行listener操作放在子线程里面去。
1.5 ContentProvider
💡 注意:ContentProvider底层也是使用的binder进行数据通信。
3.5.1 使用示例(接上例)
package com.example.artandroidlearn;
import androidx.annotation.NonNull;
public class User {
public int userId;
public String userName;
public int sex;
public User() {
}
@NonNull
@Override
public String toString() {
return "name=" + this.userName + ",id=" + userId + "sex=" + this.sex;
}
}
package com.example.artandroidlearn.db;
import android.content.Context;
import android.database.DatabaseErrorHandler;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
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 text)";
public DbOpenHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
public DbOpenHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
public DbOpenHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version, @Nullable DatabaseErrorHandler errorHandler) {
super(context, name, factory, version, errorHandler);
}
public DbOpenHelper(@Nullable Context context, @Nullable String name, int version, @NonNull SQLiteDatabase.OpenParams openParams) {
super(context, name, version, openParams);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_BOOK_TABLE);
db.execSQL(CREATE_USER_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
package com.example.artandroidlearn.provider;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.example.artandroidlearn.db.DbOpenHelper;
import java.net.URI;
public class BookProvider extends ContentProvider {
private static String TAG = "BookProvider";
private static final String AUTHORITY = "com.example.artandroidlearn.provider.BookProvider";
public static final Uri BOOK_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/book");
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);
private SQLiteDatabase mDb;
private Context mContext;
static {
sUriMatcher.addURI(AUTHORITY, "book", BOOK_URI_CODE);
sUriMatcher.addURI(AUTHORITY, "user", USER_URI_CODE);
}
@Override
public boolean onCreate() {
Log.d(TAG, "onCreate,current thread:" + Thread.currentThread().getName());
mContext = getContext();
initProviderData();
return false;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
Log.d(TAG, "query,current thread:" + Thread.currentThread().getName());
String tableName = getTableName(uri);
if (tableName == null) {
throw new IllegalArgumentException("Unsupported Uri: " + uri);
}
return mDb.query(tableName, projection, selection, selectionArgs, null, null, sortOrder, null);
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
Log.d(TAG, "getType,current thread:" + Thread.currentThread().getName());
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
Log.d(TAG, "insert,current thread:" + Thread.currentThread().getName());
String tableName = getTableName(uri);
if (tableName == null) {
throw new IllegalArgumentException("Unsupported Uri: " + uri);
}
mDb.insert(tableName, null, values);
mContext.getContentResolver().notifyChange(uri, null);
return uri;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
Log.d(TAG, "delete,current thread:" + Thread.currentThread().getName());
String tableName = getTableName(uri);
if (tableName == null) {
throw new IllegalArgumentException("Unsupported Uri: " + uri);
}
int count = mDb.delete(tableName, selection, selectionArgs);
if (count > 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return count;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
Log.d(TAG, "update,current thread:" + Thread.currentThread().getName());
String tableName = getTableName(uri);
if (tableName == null) {
throw new IllegalArgumentException("Unsupported Uri: " + uri);
}
int row = mDb.update(tableName, 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;
}
private void initProviderData() {
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(2,'android');");
mDb.execSQL("insert into book values(3,'Ios');");
mDb.execSQL("insert into book values(4,'test');");
mDb.execSQL("insert into user values(1,'jake',1);");
mDb.execSQL("insert into user values(2,'jake1',0);");
}
}
💡 注意:provider的注册必须要声明authorities ,如下所示,一般provider的访问都是并发的,因此需要做好线程同步操作。注意除了使用provider的增删改查进行操作,还可以通过ContentResolver 的call 方法来完成自定义操作
<provider
android:name=".provider.BookProvider"
android:authorities="com.example.artandroidlearn.provider.BookProvider"
android:permission="com.example.PROVIDER"
android:process=":provider" />
package com.example.artandroidlearn.activity;
import androidx.appcompat.app.AppCompatActivity;
import com.example.artandroidlearn.Book;
import com.example.artandroidlearn.R;
import com.example.artandroidlearn.User;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
public class ProviderActivity extends AppCompatActivity {
public static final String TAG="ProviderActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_provider);
Uri uri=Uri.parse("content://com.example.artandroidlearn.provider.BookProvider");
Uri bookUri=Uri.parse("content://com.example.artandroidlearn.provider.BookProvider/book");
ContentValues contentValues=new ContentValues();
contentValues.put("_id",2);
contentValues.put("name","testeeee");
getContentResolver().insert(bookUri,contentValues);
Cursor bookCursor=getContentResolver().query(bookUri,new String[]{"_id","name"},
null,null,null);
while (bookCursor.moveToNext()){
Book book=new Book();
book.bookId=bookCursor.getInt(0);
book.bookName=bookCursor.getString(1);
Log.d(TAG,"query book: "+book.toString());
}
bookCursor.close();
Uri userUri=Uri.parse("content://com.example.artandroidlearn.provider.BookProvider/user");
getContentResolver().insert(bookUri,contentValues);
Cursor userCursor=getContentResolver().query(userUri,new String[]{"_id","name","sex"},
null,null,null);
while (userCursor.moveToNext()){
User user=new User();
user.userId=userCursor.getInt(0);
user.userName=userCursor.getString(1);
user.sex=userCursor.getInt(2);
Log.d(TAG,"query user: "+user.toString());
}
userCursor.close();
}
}
1.6 使用Socket
💡 注意需要声明网络权限
1.6.1 代码示例
package com.example.artandroidlearn.Service;
import android.app.Service;
import android.content.Intent;
import android.os.FileUtils;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.Nullable;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Random;
public class TCPServerService extends Service {
private boolean mIsServiceDestroyed = false;
private String[] mDefinedMessages = new String[]{
"hello",
"what is your name",
"have a nice day",
"chat with many people",
"have a joke"
};
@Override
public void onCreate() {
Log.d("whll", "www");
new Thread(new TcpServer()).start();
super.onCreate();
}
@Override
public void onDestroy() {
mIsServiceDestroyed = true;
super.onDestroy();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
private class TcpServer implements Runnable {
@Override
public void run() {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(8688);
} catch (IOException e) {
Log.d("whl1", "ttt");
e.printStackTrace();
return;
}
while (!mIsServiceDestroyed) {
try {
final Socket client = serverSocket.accept();
System.out.println("accept");
new Thread(() -> {
try {
responseClient(client);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private void responseClient(Socket client) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
PrintWriter out = new PrintWriter(new BufferedWriter(new
OutputStreamWriter(client.getOutputStream())), true);
out.println("welcome");
while (!mIsServiceDestroyed) {
String str = in.readLine();
System.out.println("msg from client: " + str);
if (str == null) {
break;
}
int i = new Random().nextInt(mDefinedMessages.length);
out.println(mDefinedMessages[i]);
System.out.println("send to client: " + mDefinedMessages[i]);
}
System.out.println("client quit");
out.close();
in.close();
client.close();
}
}
package com.example.artandroidlearn.activity;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.example.artandroidlearn.R;
import com.example.artandroidlearn.Service.TCPServerService;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TCPClientActivity extends AppCompatActivity 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 mMessageEditView;
private PrintWriter mPrintWriter;
private Socket mClientSocket;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(@NonNull 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:
super.handleMessage(msg);
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);
mMessageEditView = (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) {
Log.d("whl", "test");
if (v == mSendButton) {
final String msg = mMessageEditView.getText().toString();
if (!TextUtils.isEmpty(msg) && mPrintWriter != null) {
new Thread(new Runnable() {
@Override
public void run() {
mPrintWriter.println(msg);
}
}).start();
mMessageEditView.setText("");
String time = formatDateTime(System.currentTimeMillis());
final String showedMsg = "server " + time + ": " + msg + "\n";
mMessageTextView.setText(mMessageTextView.getText() + showedMsg);
}
}
}
private void connectTCPServer() {
Socket socket = null;
while (socket == null) {
try {
Log.d("whl", "test11");
System.out.println("connect server success");
socket = new Socket("localhost", 8688);
mClientSocket = socket;
mPrintWriter = new PrintWriter(new BufferedWriter(new
OutputStreamWriter(socket.getOutputStream())), true);
mHandler.sendEmptyMessage(MESSAGE_SOCKET_CONNECTED);
System.out.println("connect server success");
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
SystemClock.sleep(1000);
e.printStackTrace();
}
}
try {
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while (!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...");
br.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private String formatDateTime(Long time) {
return new SimpleDateFormat("HH:mm:ss").format(new Date(time));
}
}
|