Android开发艺术探索_IPC机制(二)

Android IPC简介

IPC是Inter-Process Communication的缩写,含义为进程间通讯或者跨进程通讯,是指两个进程之间进行数据交换的过程.
理解IPC,首先需要理解进程和线程之间的关系;
线程是CPU调度的最小单元,同时线程是一种有限的系统资源;
进程一般是指一个执行单元,在PC和移动设备上指一个程序或者一个应用;
一个进程可以包含多个线程,android中分为主线程也叫UI线程

Android多进程模式

通过指定四大组建的android:process属性,我们可以轻易的开启多进程模式,但暗藏杀机;
也可以通过JNI在native层去fork一个新的进程
        <activity android:name=".BActivity"
                  android:process=":remote"
            >
在该活动运行的时候,系统会为它创建一个单独的进程,名字为 包名:remote
以:开头的进程属于当前应用的私有进程;反之为全局进程,其他应用通过ShareUID可以和它跑在同一个进程中(要求UID相同且签名相同)

多进程模式带来的问题

用一句话来形容开启多进程后的状况:
当应用开启了多进程以后,各种奇怪的问题都出现了
举个栗子:
在main活动下创建一个全局变量设置为1
在开启第二个进程的2活动下修改该全局变量为0
打印发现还是1,并没有被修改
原因:每个进程都会分配一个独立的虚拟机,那么一个对象在内存分配上有不同的地址,简单的来说就是一个对象会产生多个备份,也就是说无法通过内存来共享数据了

一般来说,多进程会导致:
1.静态成员和单例模式完全失效
2.线程同步机制完全失效
3.sharedPreference的可靠性下降
4.application会多次创建

IPC基础概念介绍

主要分为三个部分:Serializable Parceable Binder

Serializable接口

Serializable是java所提供的一个序列化接口,是一个空接口,作用是为对象提供标准的序列化和反序列化操作
序列化过程:
User user=new User("joker");
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("cache.txt"));
out.writeObject(user);
out.close();

反序列化:
in=new ObjectInputStream(new FileInputStream("cache.tet"));
User user=(User)in.readObject();
in.close()
//ps,反序列化出来的对象和原来的对象不是同一个对象

序列化原理:
实现了Serializable接口的对象中有一个
private static final long serialVersionUID= 123414124L;用来辅助序列化和反序列化的,本质是一个hash值,原则上只有serialVersionUID和类的serialVersionUID相同才能正常的反序列化
ps静态成员变量不属于类,不会参与序列化过程,其次transient关键字标记的成员也不参与序列化过程

Parcelable接口

谷歌提供的一种新的接口,其过程要稍微复杂一点;
只要实现这个接口,一个类就能通过Intent Binder进行传递
DEMO
public class User implements Parcelable{
    public int age;
    public String name;

    protected User(Parcel in) {
        age = in.readInt();
        name = in.readString();
    }
    //实现反序列化功能,其内部表明了如何创建序列化对象和数组,最终调用一系列的read()方法来实现反序列化
    public static final Creator<User> CREATOR = new Creator<User>() {
        @Override
        public User createFromParcel(Parcel in) {
            return new User(in);
        }

        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };

    //几乎所有情况都返回0,只有当对象中存在文件描述符时,才返回1;
    @Override
    public int describeContents() {
        return 0;
    }
    //实现序列化的功能,最终是由一些列的write方法来完成的
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(age);
        dest.writeString(name);
    }
}
常用的Parcelable实现类,intent bundle bitmap ,还有list map也是可以序列化的,前提是它们里面的每个元素都是可序列化的

Binder

直观上来说,binder是android的一个类,实现类IBinder接口;
还可以理解为是一种虚拟的物理设备,他的驱动设备是dev/binder,这在linux中是没有的;

从Android Framgework来说角度来说,Binder是Service连接各种Manager(ActivityManager WindowManager等等)和像一个ManagerService的桥梁;
从Android应用层来说,Binder是客户端和服务端进行通信的媒介,当binderService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于AIDL的服务
AIDLdemo
创建一个IBook.aidl IBookManager.aidl

会自动生成如下代码

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: C:\\OpenSourceProject\\ArtOfAndroidDeveloping\\aidldemo\\src\\main\\aidl\\com\\junx\\aidldemo\\aidl\\Book.aidl
 */
package com.junx.aidldemo.aidl;
// Declare any non-default types here with import statements

public interface Book extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.junx.aidldemo.aidl.Book
{
private static final java.lang.String DESCRIPTOR = "com.junx.aidldemo.aidl.Book";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
 * Cast an IBinder object into an com.junx.aidldemo.aidl.Book interface,
 * generating a proxy if needed.
 */
public static com.junx.aidldemo.aidl.Book asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.junx.aidldemo.aidl.Book))) {
return ((com.junx.aidldemo.aidl.Book)iin);
}
return new com.junx.aidldemo.aidl.Book.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_basicTypes:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
long _arg1;
_arg1 = data.readLong();
boolean _arg2;
_arg2 = (0!=data.readInt());
float _arg3;
_arg3 = data.readFloat();
double _arg4;
_arg4 = data.readDouble();
java.lang.String _arg5;
_arg5 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.junx.aidldemo.aidl.Book
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
/**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
@Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, 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.writeInt(anInt);
_data.writeLong(aLong);
_data.writeInt(((aBoolean)?(1):(0)));
_data.writeFloat(aFloat);
_data.writeDouble(aDouble);
_data.writeString(aString);
mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
/**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
}

解释一下

DESCRIPTOR 
Binder的唯一标识,一般用当前Binder的类名表示,比如本例中的private static final java.lang.String DESCRIPTOR = "com.junx.aidldemo.aidl.Book";

asInterface(android.os.IBinder obj)
用于将服务端的Binder对象转换成客户端所需的ADIL接口类型的对象,着中国转换是区分进程的,如果客户端和服务端位于同一进程,那么返回的就是服务端的Stub本身,否则返回的是系统封装后的Stub.proxy

asBinder
返回当前的Binder对象

onTransact
这个方法运行在服务端的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过这个系统底层封装后交由此方法及你想那个处理,该方法的原型为public Boolean onTransact(int code,android.os.Parcel data,android.os.Parcel reply,int flags).
服务端通过code可以确定请请求的目标方法是什么,接着从data中取出目标方法后所需的参数(如果目标方法有参数的化),然后执行目标的什么方法.当目标方法执行完毕后,就想reply中写入返回值(如果有返回值);需要注意的是,如果该方法返回false,那么客户端的请求会失败,用于做权限验证

Proxy#getBooklist
这个方法运行在客户端,执行过程和getBookList是一样的,addBook没有返回值所有它不需要从_reply中取出返回值;

PS:
当客户端发起远程请求时,由于当前献策好难过会被挂起直至服务端进程返回数据,所以如果一个远程方法是很耗时的,就不能在UI线程中发起远程请求;
PPS:由于服务端的Binder方法运行在Binder线程中,所有Binder方法不管是否耗时都应该采用同步的方法实现,应为它已经运行在一个线程中了;

AIDL的运行流程

客户端向Binder对象发起请求
客户端挂机
Binderdata写入数据
执行Transact(客户端和服务端处于同一线程不会执行)
服务端onTransact(),在线程池等待执行
向reply写入结果到Binder对象
返回数据并唤醒客户端

Binder意外销毁的处理

        new IBinder.DeathRecipient() {
            @Override
            public void binderDied() {
                if (mBookManager == null)
                    return;
                mBookManager.asBinder().unlinkToDeath(deathRecipient,0);
                mBookManager=null;
                //todo 重新绑定
            }
        };

也可以通过Binder.isBinderAlive判断Binder是否死亡;

Android中的IPC方式的使用

Bundle

activity service receiver都是支持在intent中传递Bundle数据的,由于Bundle实现了Parcelable接口,所以它可以方便地在不同的地方进行进程间传输.
当我们在一个进程中启动另一个进程的activity service receiver的时候,我们就可以在bundle中附加我们需要传输给远程信息并通过intent发送出去.

小技巧:a进程需要进行计算,并将计算后的值传递给进程B但这个计算结果不支持放入bundle中;
可以,通过intent启动一个intentservice,让service在后台进行计算仅算完毕后再将进程中真正要启动的目标组件如activityb,由于service是进程b中的所以目标组件就可以直接获取计算结果从而解决跨进程的问题

文件共享

基于linux的android可以做到多进程同时对同一文件操作,window不行
public class Activity1 extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_1);
    }
    public void save(View view) {
        new Thread(new Runnable() {
            @Override
            public void run() {
            //user实现了Serializable,file操作建议实现Serializable
                User user = new User("小黄",18);
                File file = getCacheDir();
                if(!file.exists()){
                    file.mkdir();
                }
                File userfile = new File(file, "user");
                ObjectOutputStream os=null;
                try {
                    os=new ObjectOutputStream(new FileOutputStream(userfile));
                    os.writeObject(user);
                } catch (IOException e) {
                    e.printStackTrace();
                }finally {
                    try {
                        os.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

            }
        }).start();
    }
    public void jump(View view) {
        Intent intent = new Intent(this,Activity2.class);
        startActivity(intent);
    }
}

public class Activity2 extends AppCompatActivity {

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_2);
        }

        public void load(View view) {
            User user=null;
            File file = new File(getCacheDir(),"user");
            Log.v("meee","file"+file.exists());
            if (file.exists()){
                ObjectInputStream objectInputStream = null;
                try {
                    Log.v("meee","1");
                    objectInputStream = new ObjectInputStream(new FileInputStream(file));
                    Log.v("meee","2");
                    user = (User) objectInputStream.readObject();
                    Log.v("meee","3");
                    Toast.makeText(getApplicationContext(),"user"+user.getName(),Toast.LENGTH_SHORT).show();
                } catch (IOException e) {
                    Log.v("meee","IOException");
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                    Log.v("meee","ClassNotFoundException");
                    e.printStackTrace();
                }finally {
                    if (objectInputStream!=null)
                        try {
                            objectInputStream.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                }
            }
        }
    }
sp同理,但由于sp在内存中有缓存,所以不建议使用sp进程间通讯

Messenger

翻译为信使,可以实现不同进程间传递message对象;
是一种轻量级的IPC方案,底层实现是AIDL;
demo:
//服务端
public class MessengerService extends Service {
    private static final String TAG="SAFAS";
    //创建一个静态handler用于处理消息
    private static class MessengerHandler extends Handler{
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case 666:
                    Log.v("meee",""+msg.getData().getString("msg"));
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }
    //创建一个Messenger对象
    private final Messenger mMessenger=new Messenger(new MessengerHandler());
    public MessengerService() {
    }
    //返回Messenger的binder
    @Override
    public IBinder onBind(Intent intent) {
        return  mMessenger.getBinder();
    }
}
//配置到不同进程
        <service
            android:name=".messenger.MessengerService"
            android:enabled="true"
            android:exported="true"
            android:process=":remote">
        </service>

//客户端
public class MessengerActivity extends AppCompatActivity {
    private static final String TAG="sfasf";
    private Messenger mService;

    private ServiceConnection mConn=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
        //从返回的service中创建Messenger对象,也就是服务端返回的ibinder对象
            mService=new Messenger(service);
            Message msg = Message.obtain(null, 666);
            Bundle bundle = new Bundle();
            bundle.putString("msg","这里是客户端");
            msg.setData(bundle);
            try{
            //使用Messenger的send发送给服务端
                mService.send(msg);

            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_messenger);
        //顺手使用下FloatingActionButton和Snackbar,这部分不用看
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "this is floating actionbar and Snackbar demo", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });
        Intent intent = new Intent(this, MessengerService.class);
        bindService(intent,mConn, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        unbindService(mConn);
        super.onDestroy();
    }
}
进阶:
从服务端返回消息的demo
//服务端
public class MessengerService extends Service {
    private static final String TAG="SAFAS";
    private static class MessengerHandler extends Handler{
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case 666:
                    Log.v("meee",""+msg.getData().getString("msg"));
                    //回复客户端,这里是客户端发送过来的msg.replyto
                    Messenger client=msg.replyTo;
                    Message replyMessage = Message.obtain(null, 777);
                    Bundle bundle = new Bundle();
                    bundle.putString("reply","这里是服务端,你的消息已经收到");
                    replyMessage.setData(bundle);
                    try {
                        client.send(replyMessage);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }
    private final Messenger mMessenger=new Messenger(new MessengerHandler());
    public MessengerService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        return  mMessenger.getBinder();
    }
}

//客户端
public class MessengerActivity extends AppCompatActivity {
    private static final String TAG="sfasf";
    private Messenger mService;
    private Messenger mClientMessage;
    private ServiceConnection mConn=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mService=new Messenger(service);
            Message msg = Message.obtain(null, 666);
            Bundle bundle = new Bundle();
            bundle.putString("msg","这里是客户端");
            msg.setData(bundle);
            //注意,如果需要服务端返回消息,需要添加如下这句,这里设置的就是服务端中的Messenger client=msg.replyTo;将服务端需要的客户端mClientMessage发送过去
            msg.replyTo=mClientMessage;
            try{
                mService.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
    //创建客户端Messenger需要的handler
    private static class MessagerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case 777:
                    Log.v("meee",""+msg.getData().getString("reply"));
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_messenger);
        //初始化客户端需要的mClientMessage
        mClientMessage=new Messenger(new MessagerHandler());

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });
        Intent intent = new Intent(this, MessengerService.class);
        bindService(intent,mConn, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        unbindService(mConn);
        super.onDestroy();
    }
}

AIDL的使用

messenger是以串行的发那个是来处理消息的
同时messenger的作用主要是为了传递消息,很多时候我们可能需要跨进程来调用服务端的方法,但这messenger无法做到;
这时候就需要aidl,而messenger本质上也是对aidl的轻量级封装

1.服务端,创建一个service来监听客户端的连接请求,然后创建一个aidl文件,将暴露为客户端的接口在这个aidl文件中声明,最后在service中实现这个aidl接口即可;
2.客户端,绑定服务端的服务,将binder对象转换成aidl接口的属性,调用其方法
3.aidl接口的创建

ps:在aidl中不是所有的类型都可以传递的,可传递的有
1.基本数据类型
2.String charsequence
3.list中只支持arraylist,且内容必须能被aidl支持
4.map只支持hashmap
5.parceable的实现类,必须显示import下来,且必须创建一个同名的aidl文件并在其中声明它为parceable类型
6.aidl文件类型
pps:除基本数据类型以外,所有的参数类型必须表上方法 in out inout;
aidl中只支持方法,不支持静态常量,这一点不同于传统的接口

步骤比较复杂:

1.创建aidl接口
package com.junx.ipcdemo.AIDL;
//用到parceable对象必须显示import进来
import com.junx.ipcdemo.AIDL.Book;
interface IBookManager {
    List<Book> getBooklist();
    void addBook(in Book book);
2.创建一个Book对象实现parceable
public class Book implements Parcelable {
    String name;

3(重要).必须在Book的同一个包下创建一个同名的aidl文件,内容如下
package com.junx.ipcdemo.AIDL;
parcelable Book;    

4.在远程服务中实现
public class AIDLservice extends Service {
    private static final String TAG="tafsaf";
    //这个类支持并发读写,用于处于线程同步
    //aidl是支持arraylist的,但CopyOnWriteArrayList不是arraylist的之类,为什么能用?
    //因为aidl支持的是list接口,binder中会按照list的规范去访问数据并最终高兴成一个arraylist传递给客户端
    CopyOnWriteArrayList<Book> mBookList=new CopyOnWriteArrayList<>();
    //创建binder对象继承于aidl.Stub()
    Binder mBinder=new IBookManager.Stub(){

        @Override
        public List<Book> getBooklist() throws RemoteException {
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            mBookList.add(book);
        }
    };
    public AIDLservice() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}

5.客户端
public class BookManagerActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_book_manager);
        Intent intent = new Intent(this, AIDLservice.class);
        bindService(intent, conn, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        unbindService(conn);
        super.onDestroy();
    }

    ServiceConnection conn = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            IBookManager bookManager = IBookManager.Stub.asInterface(service);
            try {
                List<Book> booklist = bookManager.getBooklist();
                //发现返回的是arraylist对象
                Log.v("meee", "" + booklist.getClass().getCanonicalName());
                Log.v("meee", "" + booklist.toString());
                bookManager.addBook(new Book("安卓开发艺术探索"));
                booklist = bookManager.getBooklist();
                Log.v("meee", "" + booklist.toString());

            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
}

ContentProvider

天生用于不同应用程序件通讯的方式,底层也是通过binder;
        1.创建contentprovider的子类
        2.注册
        <provider
            android:name=".contentprovider.BookProvider"
            //Uri.parse("content://com.junx.ipcdemo.contentprovider.BookProvider");
            android:authorities="com.junx.ipcdemo.contentprovider.BookProvider"
            //自定义权限
            android:permission="com.777"
            android:process=":remote"/>

Socket

也称套接字,是网络通信中的概念,它分为流失套接字和用户数据报套接字两种;分别对应于网络中的传输控制层的TCP和UDP协议;TCP是面向连接的协议,提供稳定的双向通信功能,TCP的连接的建立需要三次握手才能完成;
//服务端
public class TCPService extends Service {
    private boolean mIsServiceDestroyed=false;
    private String[] mDefinedMessages=new String[]{"你好啊","你叫什么名字呀?","今天北京天气不错","你知道吗,我可以和多个人聊天哦"
            ,"我给你讲个笑话吧:据说矮小的人运气不会太差,不知道真假"};
    public TCPService() {
    }

    @Override
    public void onCreate() {
        new Thread(new TcpService()).start();
        super.onCreate();
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public void onDestroy() {
        mIsServiceDestroyed=true;
        super.onDestroy();
    }
    private class TcpService implements Runnable{
        @SuppressWarnings("resource")
        @Override
        public void run() {
            //接口客户端请求
            ServerSocket serverSocket=null;
            try {
                //监听本地8688
                serverSocket=new ServerSocket(8688);
            } catch (IOException e) {
                Log.v("meee","io异常1"+getPackageName());
                return;
            }

            while(!mIsServiceDestroyed){
                try {
                    final Socket client=serverSocket.accept();
                    Log.v("meee","已接收");
                    new Thread(){
                        @Override
                        public void run() {
                            try {
                                responseClient(client);
                            } catch (IOException e) {
                                Log.v("meee","io异常3");
                            }
                        }
                    }.start();
                } catch (IOException e) {
                    Log.v("meee","io异常2"+getPackageName());
                }
            }
        }
    }
    //Socket的使用
    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.print("欢迎来到聊天室");
        while(!mIsServiceDestroyed){
            String str=in.readLine();
            Log.v("meee","client:"+str);
            if (str==null){
                break;
            }
            //获取一个随机数
            int i = new Random().nextInt(mDefinedMessages.length);
            String msg=mDefinedMessages[i];
            out.print(msg);
            Log.v("meee","send:"+msg);
        }
        Log.v("meee","client quit");
        //关闭流
        in.close();
        out.close();
        client.close();
    }
}


//客户端
public class TCPactivity extends AppCompatActivity implements View.OnClickListener {

    private static final int MESSAGE_RECEIVE_NEW_MSG = 1;
    private static final int MESSAGE_SOCKET_CONNETCTED = 2;

    private TextView tv;
    private EditText et;
    private Button send;

    private PrintWriter mPrintWriter;
    private Socket mClientSocket;

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MESSAGE_RECEIVE_NEW_MSG: {
                    Log.v("meee","接收返回指令");
                    tv.setText(tv.getText() + (String) msg.obj);
                    break;
                }
                case MESSAGE_SOCKET_CONNETCTED: {
                    send.setEnabled(true);
                    break;
                }
            }
        }
    };


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tcpactivity);
        initView();

        Intent service = new Intent(this, TCPService.class);
        startService(service);

        new Thread() {
            @Override
            public void run() {
                connectTCPServer();
            }
        }.start();
    }

    @Override
    protected void onDestroy() {
        if (mClientSocket != null) {
            try {
                mClientSocket.shutdownInput();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        super.onDestroy();
    }

    @Override
    public void onClick(View view) {
        if (view==send){
            final String msg=et.getText().toString();
            if (!TextUtils.isEmpty(msg)&&mPrintWriter!=null){
                mPrintWriter.println(msg);
                et.setText("");
                //格式化日期
                String time=formatDataTime(System.currentTimeMillis());
                String showedMsg="self"+time+":"+msg+"\n";
                tv.setText(tv.getText()+showedMsg);
            }
        }
    }

    private String formatDataTime(long l) {
        return new SimpleDateFormat("(HH:mm:ss)").format(new Date(l));
    }

    private void connectTCPServer() {
        Socket socket=null;
        while(socket==null){
            try {
                socket=new Socket("localhost",8688);
                mClientSocket=socket;
                mPrintWriter=new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())),true);
                mHandler.sendEmptyMessage(MESSAGE_SOCKET_CONNETCTED);
                Log.v("meee","连接成功");
            } catch (IOException e) {
                Log.v("meee",""+e.getCause());
                //超时重连
                SystemClock.sleep(1000);
                Log.v("meee","连接失败,正在重连.....");
            }
        }
        //接收服务端的信息
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            Log.v("meee","??"+(!TCPactivity.this.isFinishing()));
            while(!TCPactivity.this.isFinishing()){
                Log.v("meee","11111");
                String msg = br.readLine();
                Log.v("meee","2222222");
                Log.v("meee","msg"+msg);
                if (msg!=null){
                    Log.v("meee","发送");
                    String time = formatDataTime(System.currentTimeMillis());
                    String showedMsg="server"+time+":"+msg+"\n";
                    mHandler.obtainMessage(MESSAGE_RECEIVE_NEW_MSG).sendToTarget();
                }
            }
            Log.v("meee","退出...");
            mPrintWriter.close();
            br.close();
            socket.close();
        } catch (IOException e) {
            Log.v("meee","系统异常");
        }
    }


    private void initView() {
        tv = (TextView) findViewById(R.id.tv);
        et = (EditText) findViewById(R.id.et);
        send = (Button) findViewById(R.id.send);

        send.setOnClickListener(this);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值