进程和线程
的区别进程和线程
若是手机是地球,进程就像一家公司,公司使用着地球的资源,也在地球上扮演一个独立的个体,实现本身特有的功能与价值。
而线程就像公司里的人,能够共享公司的公共资源,处理属于本身的任务,实现自身的功能与价值。
能够说进程(公司)是给线程(人)一个运行(工做)的环境。于此同时进程也得到了它的地位。
因此一个进程至少要一个线程来完成任务。进程销毁后,里面的线程也就失业拜拜了。
好比某公司的人(线程)集体罢工(崩溃),那公司不管曾经叫什么,都没有意义。公司(进程)倒闭了,再多的线程(人)也没卵用。
多进程就像若干个公司联盟作一个项目,这时候各个公司的内部资源(静态成员、单例等)就再也不适用,
就像别的公司人到你公司吃你的零食,敲你键盘,摸你猫,你给吗? 不给,坚定不给。
而后那人非要吃你零食,敲你键盘,摸你猫,还搞出个职位叫IPC,说什么跨进程间通讯。TM说白了就是专门抢你零食,抢你猫,你说气不气人。
复制代码
IPC(Inter-Process Communication): 进程间通讯或者跨进程通讯
进程:指的一个执行单元,在PC和移动设备上指的是一个程序或者一个应用。
线程:在操做系统中,线程是CPU调度的最小单元,也是一种有限的系统资源。
进程与线程关系:一个进程能够包含多个线程,所以进程和线程是包含被包含的关系。
复制代码
三个按钮跳转三个Activity,布局就不贴了。只贴一个MainActivity0,其余两个MainActivity1,MainActivity2java
class MainActivity0 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
title="MainActivity0"
to_one.setOnClickListener {
startActivity(Intent(this, MainActivity0::class.java))
}
to_two.setOnClickListener {
startActivity(Intent(this, MainActivity1::class.java))
}
to_three.setOnClickListener {
startActivity(Intent(this, MainActivity2::class.java))
}
}
}
复制代码
AndroidManifest.xml
配置文件私有进程:
有:
---------- 全局进程:没有:
名字能够随便取,只要惟一android
<activity android:name=".MainActivity1"
android:process=":ipc">
</activity>
<activity android:name=".MainActivity2"
android:process="com.toly1994.ipc.test">
</activity>
复制代码
不加的话,直接经过窗口管理器来显示Activity1git
加的话,会在孵化一个进程。zygote64的日志不少,下面只是一小部分。
不清楚Activity启动和View加载过程的小伙伴,能够看一下这个日志,也许会有帮助
好比下面完美呈现了LayoutInflater是怎么运行的,再跟着源码走一走,你会有所收获github
而后发现确实是多了两个,名字也能对应上数据库
既然开一个进程会孵化一次,ActivityThread的main方法被触发,Application天然会被新建
喵了个咪的,建立了三个,一个进程一个。这显然值得注意,自定义Application初始化第三方库什么的bash
public class CatApplication extends Application {
private static final String TAG = "CatApplication";
@Override
public void onCreate() {
super.onCreate();
Log.e(TAG, "onCreate: 建立了小猫土土");
}
}
复制代码
public class CatManager {
public static Cat cat = new Cat();
public CatManager() {
cat = new Cat();
cat.color = "灰色" + Math.random();
cat.name = "土土";
}
}
---->[MainActivity0#oncreate]------------------
CatManager()
---->[MainActivity1#oncreate]------------------
Log.e("CatManager", ": "+CatManager.cat.color);//null
|--- 说明在MainActivity1里已经初始化的静态成员变量没法在MainActivity2(另外一个进程)使用
|--- 若是将MainActivity2的process去掉能够打印:灰色0.22701789806635642
|--- 这就尴尬了,个人惟一玩到666的单例肿么办?
复制代码
新建一个Cat(猫)和CatManager(铲屎官)的类网络
---->[Cat]------------------------------------
public class Cat {
public String name;
public String color;
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' + ", color='" + color + '\'' + '}'; } } ---->[CatManager]------------------------------------ public class CatManager { private volatile static CatManager sCatManager; private static Cat cat=new Cat(); private CatManager() { } public static CatManager newInstance() { if (sCatManager == null) { synchronized (CatManager.class) { if (sCatManager == null) { sCatManager = new CatManager(); Log.e("CatApplication", "newInstance: "); cat.color = "灰色"+Math.random(); cat.name = "土土"; } } } return sCatManager; } public Cat getCat() { return cat; } } ---->[CatApplication]------------------------------------ public class CatApplication extends Application { private static final String TAG = "CatApplication"; @Override public void onCreate() { super.onCreate(); CatManager manager = CatManager.newInstance(); Log.e("CatApplication", manager.getCat().toString()); } } 复制代码
2019-05-08 10:18:02.482 25524-25524/? E/CatApplication: newInstance:
2019-05-08 10:18:02.482 25524-25524/? E/CatApplication: Cat{name='土土', color='灰色0.8695394451026908'}
2019-05-08 10:18:04.761 25561-25561/com.toly1994.ipc:ipc E/CatApplication: newInstance:
2019-05-08 10:18:04.761 25561-25561/com.toly1994.ipc:ipc E/CatApplication: Cat{name='土土', color='灰色0.9824119267379914'}
2019-05-08 10:18:07.096 25597-25597/com.toly1994.ipc.test E/CatApplication: newInstance:
2019-05-08 10:18:07.096 25597-25597/com.toly1994.ipc.test E/CatApplication: Cat{name='土土', color='灰色0.18620946012650275'}
复制代码
可见单例也没有卵用了,每次开启进程都会执行到newInstance,致使单例的失调。多线程
Application会屡次建立:开启一个进程其实就等同于开多一个Application
静态成员和单例模式彻底失效(处于不一样的内存块(进程),拥有各自的副本)
SharedPreferences的可靠性下降:由于SharedPreferences不支持两个进程同时去读写xml文件
线程同步机制彻底失效(同一差很少)
复制代码
为了多公司联盟(多进程)间的和谐,如今决定牺牲猫,让它能够被过各公司(进程)共享并发
[1].经过Intent传递Bundle对象通讯: 简单,数据类型局限,用于组件间传递数据
[2].使用共享文件通讯: 简单,实时性差,不适合高并发
[3].使用Messenger通讯: 支持一对多串行通讯,支持实时通讯,不支持RPC
[4].使用AIDL通讯: 支持一对多并发通讯,适用于,一对多通讯且有RPC需求
[5].使用ContentProvider: 支持一对多并发数据共享
[6].使用Socket: 能够经过网络传输字节流,支持一对多并发实时通讯
复制代码
既然单例不能用,就不用。这里默认开局一只猫。Cat实现序列化接口Serializabledom
public class CatManager {
private static List<Cat> cats = new ArrayList<>();
public CatManager() {
Cat tutu = new Cat();
tutu.color = "灰色" + Math.random();
tutu.name = "土土";
add(tutu);
}
public void add(Cat cat) {
cats.add(cat);
}
public Cat getCatAt(int index) {
return cats.get(index);
}
}
public class Cat implements Serializable {
public String name;
public String color;
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' + ", color='" + color + '\'' + '}'; } } 复制代码
Intent发送Bundle对象通讯
---->[MainActivity0#oncreate]------------------
to_two.setOnClickListener {
val cat = CatManager().getCatAt(0)
val bundle = Bundle()//建立Bundle对象
bundle.putSerializable("cat", cat)//把猫装到Bundle里,贴个标签cat
val intent = Intent(this, MainActivity1::class.java)
intent.putExtras(bundle)
startActivity(intent)
}
---->[MainActivity1#oncreate]------------------
val cat = intent.extras?.get("cat") as Cat //把Bundle用打开标签cat,而后猫到手
Log.e("MainActivity1", ": " + cat.name)//MainActivity1能够对猫随心所欲,IPC 通讯完成
复制代码
注:固然你也能够直接经过Intent发送序列化(Serializable)对象,源码瞄一眼,都是经过Bundle的,并没有本质区别
public @NonNull Intent putExtra(String name, Serializable value) {
if (mExtras == null) {
mExtras = new Bundle();
}
mExtras.putSerializable(name, value);
return this;
}
复制代码
Android里说Serializable,怎么能少得了同胞兄弟
Parcelable
呢,二者都是对象序列化的手段
二者的详细比较这里就不赘述,详见:Android点将台的Intent篇。什么是序列化和反序列化,我的理解以下:
好比我家有个大的衣柜(对象),如今要搬家,一会儿搬不走,怎么办?
把每块板贴个标签,而后拆了,一块块摆好,而后就能运走了,这叫序列化。
而后到新家里,把板再一块块地拼起来,而后大衣柜(对象)就又回来了,这叫反序列化。
上面说的是物质对象的运输过程,那么信息/数据对象也能够这么类比,思想上是[怎么好运和拼装还原]
Serializable和Parcelable不影响序列化的概念,只是手段不一样,就像是卡车运仍是飞机运同样
Serializable和Parcelable接口表明这东西可拆,是一种可拆保证。要什么都乱拆,你家猫拆个试试。
下面直播拆猫:AS自动生成Parcelable相关代码,能够省咱们一些事,but,请千万要了解一下他们是干吗用的
复制代码
---->[MainActivity0#oncreate]------------------
to_two.setOnClickListener {
val cat = CatManager().getCatAt(0)
val bundle = Bundle()//建立Bundle对象
bundle.putParcelable("cat", cat)//把猫装到Bundle里,贴个标签cat
val intent = Intent(this, MainActivity1::class.java)
intent.putExtras(bundle)
startActivity(intent)
}
---->[MainActivity1#oncreate]------------------
val cat = intent.extras?.get("cat") as Cat //把Bundle用打开标签cat,而后猫到手
Log.e("MainActivity1", ": " + cat.name)//MainActivity1能够对猫随心所欲,IPC 通讯完成
复制代码
文件共享进行通讯
把对象写入文件,而后经过文件反序列化出对象,给MainActivity2
(文件读写不管是效率仍是多线程的不行,因此这里只是了解一下)
---->[MainActivity0#oncreate]------------------
to_three.setOnClickListener {
val cat = CatManager().getCatAt(0)
val file = File(cacheDir, "cat.obj")
val oos = ObjectOutputStream(FileOutputStream(file))
oos.writeObject(cat)
oos.close()
startActivity(Intent(this, MainActivity2::class.java))
}
---->[MainActivity2#oncreate]------------------
val file = File(cacheDir, "cat.obj")
val ois = ObjectInputStream(FileInputStream(file))
val cat = ois.readObject() as Cat//反序列化生成对象
ois.close()
Log.e("MainActivity1", ": " + cat.name)//MainActivity1能够对猫随心所欲,IPC 通讯完成
复制代码
可能到这你以为IPC不就是传个对象吗?好像没什么大不了的
Messenger通讯
从都构造函数来看,是和Binder有关
private final IMessenger mTarget;
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
复制代码
如此看来mTarget即IMessenger类很像一个AIDL接口
public IBinder getBinder() {
return mTarget.asBinder();
}
复制代码
既然是Ibinder对象,能够用在绑定服务中。既然个公司(摸)的人都要猫,干脆来个服务端。
谁(客户端)想来摸一下均可以,核心是Messenger发送消息,Service里接收消息
---->[CatService]------------------
public class CatService extends Service {
@Override
public IBinder onBind(Intent intent) {
return new Messenger(new Handler(msg -> {
//接收客户端数据/信息/对象
String data = msg.getData().getString("request");
Log.e("MessengerActivity", "handleMessage: " + data);
//向客户端发送数据/信息/对象
Messenger client = msg.replyTo;
Message message = Message.obtain();
Cat cat = new CatManager().getCatAt(0);
Bundle bundle = new Bundle();//建立Bundle对象
bundle.putParcelable("cat", cat);//把猫装到Bundle里,贴个标签cat
message.setData(bundle);
try {
client.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
return false;
})).getBinder();
}
}
---->[将服务单独放在一个进程]--------------
<service android:name=".CatService"
android:process="com.toly1994.ipc.service.cat"/>
---->[MessengerActivity]------------------
public class MessengerActivity extends AppCompatActivity {
private static final String TAG = "MessengerActivity";
private ServiceConnection conn;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
conn = new ServiceConnection() {
private Messenger messenger;
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
messenger = new Messenger(service);
Message message = Message.obtain();
Bundle bundle = new Bundle();
bundle.putString("request", "来自客户端:给我一只猫");
message.setData(bundle);
message.replyTo = new Messenger(new Handler((msg) -> {//服务端回应监听
Bundle data = msg.getData();
data.setClassLoader(getClass().getClassLoader());
Cat cat = (Cat) (data.get("cat"));
Log.e(TAG, "来自服务端: "+cat);
return false;
}));
try {
messenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
bindService(new Intent(this, CatService.class), conn, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(conn);
}
}
复制代码
注意这里有一个坑,一开始读不出来异常以下,看名字感受是类加载器的锅,貌似找不到Cat类
解决:data.setClassLoader(getClass().getClassLoader());
流程基本以下,并不知道两个Handler和三个Messenger,还有皮球同样乱跑的Message有没有把你绕晕
AIDL通讯
这个不怎么想说,在Android点将台:金科玉律[-AIDL-]里已经讲得很详细了,为了完整一点,这里稍微再说一下吧。
ICatService
简单一点,就定义一个喂养的方法
类之间的关系基本以下:
package com.toly1994.ipc;
public interface ICatService extends android.os.IInterface {
public static abstract class Stub extends android.os.Binder implements com.toly1994.ipc.ICatService {
private static final java.lang.String DESCRIPTOR = "com.toly1994.ipc.ICatService";
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
//经过IBinder获取ICatService对象,绑定客户端时使用
public static com.toly1994.ipc.ICatService asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.toly1994.ipc.ICatService))) {
return ((com.toly1994.ipc.ICatService) iin);
}
return new com.toly1994.ipc.ICatService.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override//此方法运行在服务端Binder线程池中,客户端发起跨进程请求时,远程请求经过系统底层封装后交由此方法处理
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
java.lang.String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_feed: {
data.enforceInterface(descriptor);
java.lang.String _arg0;
_arg0 = data.readString();
this.feed(_arg0);
reply.writeNoException();
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
//代理类,当客户端访问服务端时,客户端经过代理类生成一个ICatService对象
private static class Proxy implements com.toly1994.ipc.ICatService {
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;
}
@Override
public void feed(java.lang.String aString) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(aString);
mRemote.transact(Stub.TRANSACTION_feed, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_feed = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public void feed(java.lang.String aString) throws android.os.RemoteException;
}
复制代码
优点在于客户端绑定服务是经过:
ICatService.Stub.asInterface(service)
获取ICatService对象
就能够调用ICatService接口方法。这样只暴露接口,能够限制客户端对小猫的操做,客户端即玩了,又不能随心所欲。
---->[FeedCatService]--------------------------------------
public class FeedCatService extends Service {
private static final String TAG = "FeedCatService";
private Binder binder = new ICatService.Stub() {
@Override
public void feed(String aString) throws RemoteException {
Log.e(TAG, "feed: 你已喂" + aString + "给小猫土土了");
}
};
@Override
public IBinder onBind(Intent intent) {
return binder;
}
}
---->[将服务单独放在一个进程]---------------------------------
<service android:name=".FeedCatService"
android:process="com.toly1994.ipc.service.feed.cat"/>
---->[AidlActivity]---------------------------------
public class AidlActivity extends AppCompatActivity {
private ServiceConnection conn;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
conn = new ServiceConnection() {
private ICatService catService;
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
catService = ICatService.Stub.asInterface(service);
try {
catService.feed("鱼");
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
bindService(new Intent(this, FeedCatService.class), conn, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(conn);
}
}
复制代码
使用ContentProvider通讯
private static String DATABASE_NAME = "cat.db";//数据库名
private static int DATABASE_VERSION = 1;//数据库版本
private volatile static CatDatabaseHelper sInstance;
//双检锁单例
public static synchronized CatDatabaseHelper getInstance(Context context) {
if (sInstance == null) {
synchronized (CatDatabaseHelper.class) {
if (sInstance == null) {
sInstance = new CatDatabaseHelper(context);
}
}
}
return sInstance;
}
public CatDatabaseHelper(@Nullable Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
createSwordTable(db);
}
/**
* 建立sword表
*
* @param db SQLiteDatabase
*/
private void createSwordTable(SQLiteDatabase db) {
db.execSQL("CREATE TABLE cat (" +
"id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," +
"name VARCHAR(32) NOT NULL," +
"color VARCHAR(32) NOT NULL" +
"); ");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
复制代码
public class CatContentProvider extends ContentProvider {
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
private static final int CAT_QUERY = 0;
private static final int CAT_INSERT = 1;
private static final int CAT_UPDATE = 2;
private static final int CAT_DELETE = 3;
private static final String TABLE_NAME = "cat";
static {
//给当前sUriMatcher添加匹配规则
sUriMatcher.addURI("toly1994.com.cat", "query", CAT_QUERY);
sUriMatcher.addURI("toly1994.com.cat", "insert", CAT_INSERT);
sUriMatcher.addURI("toly1994.com.cat", "update", CAT_UPDATE);
sUriMatcher.addURI("toly1994.com.cat", "delete", CAT_DELETE);
}
private SQLiteOpenHelper mOpenHelper;
@Override
public boolean onCreate() {
mOpenHelper = CatDatabaseHelper.getInstance(getContext());
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
//进行uri匹配
int result = sUriMatcher.match(uri);
if (result == CAT_QUERY) {
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
return db.query(TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);
} else {
throw new IllegalStateException(" query Uri 错误");
}
}
@Override
public String getType(Uri uri) {
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
//进行uri匹配
int result = sUriMatcher.match(uri);
if (result == CAT_INSERT) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
Long insert = db.insert(TABLE_NAME, null, values);
//uri:数据发送变化,经过uri判断调用哪一个内容观察者
//第二个参数:内容观察者对象 若是传null 则注册了整个uri的内容观察者皆能够收到通知
getContext().getContentResolver().notifyChange(uri, null);
db.close();
return Uri.parse(String.valueOf(insert));
} else {
throw new IllegalStateException("insert Uri 错误");
}
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
//进行uri匹配
int result = sUriMatcher.match(uri);
if (result == CAT_DELETE) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int delete = db.delete(TABLE_NAME, selection, selectionArgs);
db.close();
return delete;
} else {
throw new IllegalStateException("delete Uri 错误");
}
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
//进行uri匹配
int result = sUriMatcher.match(uri);
if (result == CAT_UPDATE) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int update = db.update(TABLE_NAME, values, selection, selectionArgs);
db.close();
return update;
} else {
throw new IllegalStateException("update Uri 错误");
}
}
}
---->[配置]---------------------------------------------
<provider android:authorities="toly1994.com.cat"
android:name=".CatContentProvider"
android:exported="true"
android:process="com.toly1994.ipc.provider.cat"
/>
复制代码
这样不一样的进程就能够经过getContentResolver来操做数据库了
public class ProviderActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
insert(getContentResolver());
}
/**
* 插入测试
*
* @param resolver
*/
private void insert(ContentResolver resolver) {
Uri uri = Uri.parse("content://toly1994.com.cat/insert");
ContentValues values = new ContentValues();
values.put("name", "土土");
values.put("color", "灰色");
resolver.insert(uri, values);
}
}
复制代码
Socket可让两个设备间的通讯,两个进程天然也不在话下
这里不深刻,只是客户端发一句话,服务端接收一下
---->[SocketService]---------------------------------
public class SocketService extends Service {
private static final String TAG = "SocketService";
private boolean quit = false;
@Override
public void onCreate() {
Log.e(TAG, "onCreate: ");
FeedServer feedServer = new FeedServer();
new Thread(feedServer).start();
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
private class FeedServer implements Runnable {
@Override
public void run() {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(8080);
Log.e(TAG, "serverSocket onCreate: ");
} catch (IOException e) {
e.printStackTrace();
}
while (!quit) {
try {
Socket socket = serverSocket.accept();
//接收客户端消息
BufferedReader br = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
String s = br.readLine();
Log.e(TAG, "来自客户端: " + socket.getInetAddress() + "说:" + s);
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
---->[将服务单独放在一个进程]---------------------------------
<service android:name=".SocketService"
android:process="com.toly1994.ipc.service.socket.feed.cat"/>
---->[ServerActivity]---------------------------------
public class ServerActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService(new Intent(this, SocketService.class));
findViewById(R.id.to_one).setOnClickListener(v -> {
new Thread(() -> {
Socket socket = null;
try {
socket = new Socket("localhost", 8080);
//客户端请求
BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream()));
bw.write("我要猫");
bw.flush();
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}).start();
});
}
}
复制代码
OK,本文就先这样,
《Android开发艺术探索》
是本不错的书,有多瞄几眼的价值。