《Android艺术开发探索》学习笔记之IPC

538 阅读25分钟

IPC:Inter-Process-Communication,即进程间通信或者跨进程通信,是指两个进程之间进行数据交换的过程。任何一个操作系统都有相应的IPC,Android是一种基于Linux内核的移动操作系统,它的进程间通信方式并不能完全继承自Linux,相反,它有自己的进程间通信方式,在Android中最有特色的进程间通信方式就是Binder了。

线程:CPU调度的最小单元,同时线程是一种有限的系统资源。

进程:指一个执行单元,在PC和移动设备上指一个程序或者一个应用。

Android中的多进程模式

开启多进程:

1、通过给四大组件(Activity、Service、Receiver、ContentProder)在AndroidMenifest中指定android:process属性,开启多进程模式。

2、通过JNI在native层fork一个新的进程(非常规)。

<service
            android:name="com.lwj.mytestpro.aidl.BookManagerService"
            android:exported="true"
            android:process=":remote">
            <intent-filter>
                <category android:name="android.intent.category.DEFAULT"/>

                <action android:name="com.lwj.ipc.aidl.BookManagerService"/>
            </intent-filter>
</service>

IPC.png

android:process=":remote"系统为此创建一个单独的名为包名:remote的进程。

多进程造成的问题

一般来说,使用多进程会造成如下几个方面的问题: (1)静态成员和单例模式完全失效。 (2)线程同步机制完全失效。 (3)SharedPreferences的可靠性下降。 (4)Application会多次创建。

关于(1)(2)问题本质上相似:Android为每个应用分配一个独立的虚拟机,或者说为每个进程都分配一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,这就导致不同的虚拟机中访问同一个类的对象会产生多分副本。

关于问题(3):因为SharedPreferences不支持两个进程同时执行写操作,否则会导致一定的几率的数据丢失。这是一位SharedPreferences底层是通过读/写XML文件来实现的,并发写显然是可能出问题的。

关于问题(4):当一个组件跑在一个新的进程中的时候,由于系统在创建新的进程时分配独立的虚拟机,所以这个过程其实就是一个启动应用的过程,因此相当于系统又把这个应用重新启动了一遍,既然重启了,那么自然会创建新的Application。可以理解为:运行在同一个进程的组件是属于同一个虚拟机和同一个Application的,同理,运行在不同进程中的组件是属于两个不同的虚拟机和Application的。

IPC的基础概念介绍

Serializable接口

Serializable是Java所提供的一个序列化接口,也是一个空接口,为对象提供标准的序列化和反序列化操作。使用Serializable来实现序列化相当简单,只需要在类的声明指定一个类似下面的标识即可自动实现默认的序列化过程。

public class User implements Serializable {

    private static final long serialVersionUID = 1230212L;

    public int userId;
    public String userName;
    public boolean isMale;

}

通过Serializable方式实现对象的序列化,实现起来很简单,几乎所有的工作都被系统自动完成,如何进行对象的序列化和反序列化也非常简单,只需要采用ObjectOutputStream和ObjectInputStream即可轻松完成。

IPC.png

上述代码演示了采用Serializable方式序列化对象的典型过程。只需要把实现的Serializable接口的对象写到文件中就可以快速恢复了,恢复后的对象newUser和user的内容完全一样,但是两者并不是同一个对象。

serialVersionUID意义:指定serialVersionUID的值,编译器自动生成hash值,这样序列化和反序列化的serialVersionUID值相同,可以正常进行反序列化。如果不指定,反序列化时当前类有所改变(增减成员变量)那么系统会重现计算当前类的hash值并把它复制给serialVersionUID,这时候序列化和反序列化的serialVersionUID不一致,于是反序列化失败,程序出现crash。

Parcelable接口

Parcelable:接口,类实现此接口,其对象就可以实现序列化并可以通过Intent和Binder传递。

public class Book implements Parcelable {
    public int bookId;
    public String bookName;

    public Book() {

    }

    public int getBookId() {
        return bookId;
    }

    public String getBookName() {
        return bookName;
    }

    public Book(int bookId, String bookName) {
        this.bookId = bookId;
        this.bookName = bookName;
    }

    protected Book(Parcel in) {
        bookId = in.readInt();
        bookName = in.readString();
    }

    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];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(bookId);
        dest.writeString(bookName);
    }
}

序列化过程需要实现的功能有序列化、反序列化和内容描述。

序列化:由writeToParcel方法完成,最终通过Parcel的一系列write方法完成。

反序列化:由CREATOR 来完成,其内部表明了如何创建序列化对象和数组,并通过Parcel的一系列read方法完成反序列化过程。

内容描述功能:由describeContents方法来完成,几乎所有情况下这个方法都应该返回0,当且仅当对象中存在文件描述符时,此方法返回1.

Serializable、Parcelable比较:Serializable是Java中的序列化接口,开销很大,序列化和反序列化需要大量的I/O操作,而Parcelable是Android中的序列化方式,这是Android推荐的序列化方式,使用稍微麻烦,但是效率高,因此首选Parcelable。Parcelable主要用在内存序列化,通过Parcelable将对象序列化到存储设备中或者对象序列化后通过网络传输都是可以的,但是过程稍微复杂,因此两者对比下,建议使用Serializable。

Binder

直观来说,Binder是Android中的一个类,实现了IBinder接口。

从IPC来说,Binder是Android中的一种跨进程通信方式,Binder还可以理解为虚拟的物理设备,它的存储驱动是/dev/binder,该通信方式在Linux是没有的。

从Android Framework来说,Binder是ServiceManager连接各种Manager(ActivityManager、WindowManager等等)和ManagerService的桥梁。

从Android应用层来说,Binder是客户端和服务端进行通信的媒介,当bindService的时候,服务端会返回一个包含服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务端的服务或数据,这里的服务包括普通服务和基于AIDL的服务。

Android开发中,Binder 主要用在Service中,包括AIDL和Messenger,其中Service总的Binder不涉及进程间通信,而Messenger的底层其实就是AIDL。

Binder的工作机制

IPC.png

Android中的IPC方式

Android中实现IPC方式有: (1)AIDL (2)Messenger (3)文件共享 (4)Bundle (5)ContentProvider (6)Socket

使用AIDL

1、服务端 服务端首先要创建一个Service来监听客户端的连接请求,然后创建AIDL文件,将暴露给客户端的接口在这个AIDL文件中声明,最后在Service中实现AIDL接口即可。

2、客户端 绑定服务端的Service,绑定成功后,将服务端返回的Binder对象转换成AIDL接口所属的类型,接着可以调用AIDL中的方法了。

AIDL支持的数据类型:

IPC.png

自定义的Parcelable对象和AIDL对象必须显式的import进来,不管是否和当前的AIDL文件在同一个包下。

AndroidStudio新建一个AIDL文件

项目右键——>new——>AIDL

IPC.png

1)创建一个Book.java 文件实现Parcelable接口

public class Book implements Parcelable {
    public int bookId;
    public String bookName;

    public Book() {

    }

    public int getBookId() {
        return bookId;
    }

    public String getBookName() {
        return bookName;
    }

    public Book(int bookId, String bookName) {
        this.bookId = bookId;
        this.bookName = bookName;
    }

    protected Book(Parcel in) {
        bookId = in.readInt();
        bookName = in.readString();
    }

    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];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(bookId);
        dest.writeString(bookName);
    }
}

2)Book.java数据对象需要创建相对应的Book.aidl文件

IPC.png

3)AIDL接口的创建

创建一个后缀为aidl的文件,如下是IOnNewBookArrivedListener.aidl文件

// IOnNewBookArrivedListener.aidl
package com.lwj.ipc.aidl;

// Declare any non-default types here with import statements
import com.lwj.ipc.aidl.Book;

interface IOnNewBookArrivedListener {
    void onNewBookArrived(in Book newBook);
}

这里需要用到Book,需要显式的import进来import com.lwj.ipc.aidl.Book;

再创建一个AIDL接口、用于注册和解注册IOnNewBookArrivedListener接口的方法,和addBook方法添书籍。

IBookManager.aidl

// IBookManager.aidl
package com.lwj.ipc.aidl;
import com.lwj.ipc.aidl.Book;
import com.lwj.ipc.aidl.IOnNewBookArrivedListener;
interface IBookManager {
 List<Book>getBookList();
 void addBook(in Book book);
 void registerListener(IOnNewBookArrivedListener listener);
 void unregisterListener(IOnNewBookArrivedListener listener);
}

同样BookIOnNewBookArrivedListener需要显式import进来。

以上的文件最好放到同一个包下面,也就是aidl包的下面。

4)创建Service,实现AIDL接口

创建Service,并且再AndroidManifest.xml中注册

<service
            android:name="com.lwj.mytestpro.aidl.BookManagerService"
            android:exported="true"
            android:process=":remote">
            <intent-filter>
                <category android:name="android.intent.category.DEFAULT"/>

                <action android:name="com.lwj.ipc.aidl.BookManagerService"/>
            </intent-filter>
        </service>

注册Service的同时,android:process=":remote"开启了一个进程,也就是让服务端跟客户端不同的进程,来演示跨进程通信。

BookManagerService文件实现AIDL接口

public class BookManagerService extends Service {
    private static final String TAG = "BookManagerService";

    private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false);
    //存放书本的集合
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
    //存放监听的集合,必须使用RemoteCallbackList,否则无法解注册
    private RemoteCallbackList<IOnNewBookArrivedListener> mListeners = new RemoteCallbackList<>();

    //实现AIDL接口
    private Binder binder = new IBookManager.Stub() {

        @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 {
            mListeners.register(listener);
        }

        @Override
        public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
           mListeners.unregister(listener);
        }
    };


    @Override
    public void onCreate() {
        super.onCreate();
        mBookList.add(new Book(1, "Android"));
        mBookList.add(new Book(2, "Ios"));
        new Thread(new ServiceWorker()).start();
    }

    /**
     * 把新加的书本添加到集合中
     * 并且通知有新的书本增加
     * @param book
     * @throws RemoteException
     */
    private void onNewBookArrived(Book book)throws RemoteException{
        mBookList.add(book);
        final int N = mListeners.beginBroadcast();
        for (int i = 0; i < N; i++) {
            IOnNewBookArrivedListener listener = mListeners.getBroadcastItem(i);
            if (listener != null){
                //通知
                listener.onNewBookArrived(book);
            }
        }
        mListeners.finishBroadcast();
    }

    /**
     * 演示,每个一秒中增加一本书
     */
    private class ServiceWorker implements Runnable{

        @Override
        public void run() {
            while (!mIsServiceDestoryed.get()){
                try {
                    Thread.sleep(1000);
                } 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
    @Override
    public IBinder onBind(Intent intent) {
        //权限验证
        int check = checkCallingOrSelfPermission("com.lwj.aidl.permission.ACCESS_BOOK_SERVICE");
        if (check == PackageManager.PERMISSION_DENIED){
            return null;
        }
        return binder;
    }

    @Override
    public void onDestroy() {
        mIsServiceDestoryed.set(true);
        super.onDestroy();
    }
}

这里添加了一个权限验证

IPC.png

BookManagerActivity 和Service的跨进程通信

BookManagerActivity客户端实现:

public class BookManagerActivity extends AppCompatActivity {

    private static final String TAG = "BookManagerActivity";
    private static final int MESSAGE_NEW_BOOK_ARRIVED = 1;

    private IBookManager mRemoterBookManager;

    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case MESSAGE_NEW_BOOK_ARRIVED:
                    Log.i("lwjtag","receive new book:"+msg.obj);
                    break;
                default:
                    super.handleMessage(msg);
                    break;
            }
        }
    };
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            IBookManager bookManager = IBookManager.Stub.asInterface(service);
            try {
                mRemoterBookManager = bookManager;
                Book mBook = new Book(3,"Android 艺术开发探索");
                bookManager.addBook(mBook);
                List<Book> list = bookManager.getBookList();
                Log.i("lwjtag","query book list type:"+list.getClass().getCanonicalName());
                if (list != null && list.size() > 0){
                    for (Book book : list) {
                        Log.i("lwjtag","query book list:"+book.getBookName());
                    }
                }
                bookManager.registerListener(mListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
    private IOnNewBookArrivedListener mListener = new IOnNewBookArrivedListener() {
        @Override
        public void onNewBookArrived(Book newBook) throws RemoteException {
            mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED,newBook).sendToTarget();
        }

        @Override
        public IBinder asBinder() {
            return mRemoterBookManager.asBinder();
        }
    };
    @Override
    protected void onCreate(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 (mRemoterBookManager != null && mRemoterBookManager.asBinder().isBinderAlive()){
            try {
                mRemoterBookManager.unregisterListener(mListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        unbindService(mConnection);
        super.onDestroy();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_book_manager, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

binder连接池

如果有10、100个不同的业务模块都需要AIDL来进行进程间通信,那该怎么处理?这种情况不可能创建10或者100个Service来处理,这就需要减少Service数量,将所有的AIDL 文件放到同一个Service中去管理。

整个工作机制:每个业务模块创建自己的AIDL接口并实现此接口,这个时候不同的业务模块之间是不能有耦合的,所有的细节我们单独开来,然后向服务端提供自己的唯一标识和其对应的Binder对象;对于服务端来说,只需要一个Service就可以了,服务端只是提供一个queryBinder接口,这个接口能够根据业务模块的特征来返回相应的Binder对象给他们,不同的业务模块拿到所需的Binder对象后就可以进行远程方法调用了,由此可见,Binder连接池的主要作用就是将每个业务模块的Binder请求统一转发到远程的Service中执行,从而避免了重复创建Service的过程,工作原理如下图所示:

IPC.png

实例:提供两个AIDL接口(ISecurityCenter.aidl和Icompute.aidl)来模拟上面多业务模块都使用AIDL的情况。

1) Icompute.aidl:

// Icompute.aidl
package com.lwj.ipc.aidl;

// Declare any non-default types here with import statements

interface Icompute {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     * binder线程池中的
     */
   int add(int a,int b);
}

2) ISecurityCenter.aidl:

// ISecurityCenter.aidl
package com.lwj.ipc.aidl;

// Declare any non-default types here with import statements

interface ISecurityCenter {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     * binder线程池中的
     */
     //加密
     String encrypt(String content);
     //解密
     String decrypt(String password);
}

3) IBinderPool.aidl:提供查找Binder的接口

// IBinderPool.aidl
package com.lwj.ipc.aidl;

// Declare any non-default types here with import statements

interface IBinderPool {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     * binder线程池接口
     */
     IBinder queryBinder(int binderCoder);
}

4)IcomputeImpl:实现Icompute接口

import android.os.RemoteException;

import com.lwj.ipc.aidl.Icompute;

/**
 * Created by Administrator on 2017/5/20 0020.
 */
public class IcomputeImpl extends Icompute.Stub {
    @Override
    public int add(int a, int b) throws RemoteException {
        return a+b;
    }
}

5)ISecurityCenterImpl:实现ISecurityCenter接口

import android.os.RemoteException;

import com.lwj.ipc.aidl.ISecurityCenter;

/**
 * Created by Administrator on 2017/5/20 0020.
 */
public class ISecurityCenterImpl extends ISecurityCenter.Stub {
    private static final char  SECRET_CODE = '^';
    @Override
    public String encrypt(String content) throws RemoteException {
        char[] chars = content.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            chars[i] ^= SECRET_CODE;
        }
        return new String(chars);
    }

    @Override
    public String decrypt(String password) throws RemoteException {
        return encrypt(password);
    }
}

6)BinderPool实现查询binderpool接口的方法和逻辑

public class BinderPool {
    private static final String TAG = "BinderPool";
    public static final int BINDER_NONE = -1;
    public static final int BINDER_COMPUTE = 0;
    public static final int BINDER_SECURITY_CENTER = 1;

    private Context mContext;
    private IBinderPool mBinderPool;
    private static volatile BinderPool sInstance;
    private CountDownLatch mConnectBinderPoolCountDownLatch;

    private BinderPool(Context context) {
        //获取上下文
        this.mContext = context.getApplicationContext();
        connectBinderPoolService();
    }

    /**
     * 连接BinderPool
     * 启动Service
     */
    private void connectBinderPoolService() {
        mConnectBinderPoolCountDownLatch = new CountDownLatch(1);
        Intent service = new Intent(mContext, BinerPoolService.class);
        mContext.bindService(service, mBinderPoolConnection, Context.BIND_AUTO_CREATE);
        try {
            mConnectBinderPoolCountDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 单例模式实现
     * @param context
     * @return
     */
    public static BinderPool getInstance(Context context) {
        if (sInstance == null) {
            synchronized (BinderPool.class) {
                if (sInstance == null) {
                    sInstance = new BinderPool(context);
                }
            }
        }
        return sInstance;
    }

    /**
     * 根据code查询binder并且返回Binder对象
     * @param binderCode
     * @return
     */
    public IBinder queryBinder(int binderCode) {
        IBinder binder = null;

        try {
            if (mBinderPool != null) {
                binder = mBinderPool.queryBinder(binderCode);
            }
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        return binder;
    }

    /**
     * 连接Service,拿到binderPool对象
     */
    private ServiceConnection mBinderPoolConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mBinderPool = IBinderPool.Stub.asInterface(service);
            try {
                //死亡监听
                mBinderPool.asBinder().linkToDeath(mBinderPoolDeathRecipient, 0);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            mConnectBinderPoolCountDownLatch.countDown();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
    /**
     * 死亡重连
     */
    private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            Log.i(TAG, "binder died.");
            mBinderPool.asBinder().unlinkToDeath(mBinderPoolDeathRecipient, 0);
            mBinderPool = null;
            connectBinderPoolService();
        }
    };

    /**
     * 实现binderpool接口方法
     */
    public static class BinderPoolImpl extends IBinderPool.Stub {
        public BinderPoolImpl() {
            super();
        }

        @Override
        public IBinder queryBinder(int binderCoder) throws RemoteException {
            IBinder binder = null;
            switch (binderCoder) {
                case BINDER_SECURITY_CENTER:
                    binder = new ISecurityCenterImpl();
                    break;
                case BINDER_COMPUTE:
                    binder = new IcomputeImpl();
                    break;
                default:
                    break;
            }
            return binder;
        }
    }
}

7)创建Service,另开进程。

IPC.png

public class BinerPoolService extends Service {
    private static final String TAG = "BinerPoolService";
    private Binder mBinderPool = new BinderPool.BinderPoolImpl();

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

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

8)BinderPoolActivity实现进程间通信

public class BinderPoolActivity extends AppCompatActivity {

    private ISecurityCenter sercurityCenter = null;
    private Icompute mCompute;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_binder_pool);
        new Thread(new Runnable() {
            @Override
            public void run() {
                dowork();
            }
        }).start();
    }
    private void dowork(){
        //获取binderPool实例
        BinderPool binderPool = BinderPool.getInstance(BinderPoolActivity.this);
        //查询isecurityBinder
        IBinder securityBinder = binderPool.queryBinder(BinderPool.BINDER_SECURITY_CENTER);
        //得到ISecurityCenter实例
        sercurityCenter = (ISecurityCenter)ISecurityCenterImpl.asInterface(securityBinder);
        Log.i("lwjtag", "visit ISecurityCenter");
        String msg = "helloworld-安卓";
        System.out.println("content:"+msg);
        try {
            String password = sercurityCenter.encrypt(msg);
            System.out.println("encrypt:"+password);
            System.out.println("decrypt:"+sercurityCenter.decrypt(password));
        } catch (RemoteException e) {
            e.printStackTrace();
        }

        Log.i("lwjtag","visit ICompute");
        IBinder computeBinder = binderPool.queryBinder(BinderPool.BINDER_COMPUTE);
        mCompute = (Icompute) IcomputeImpl.asInterface(computeBinder);
        try {
            System.out.println("3+5="+mCompute.add(3,5));
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_binder_pool, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

结果:

IPC.png

这里需要额外说明一下,为什么要在线程中去执行呢?这是因为在Binder连接池的实现中,我们通过CountDownLath将binderService这一异步操作转换成了同步操作,这就意味着,它有可能是耗时的,然后就是Binder方法的调用过程也可能是耗时的,因此不建议放在主线程去执行。注意到BinderPool是一个单例实现,因此在同一个进程中只会初始化一次,所以如果我们提前初始化BinderPool,那么可以优化程序体验,比如我们可以放到Application中提前对BinderPool进行初始化,虽然不能保证我们调用BinderPool时它一定是初始化好的,但在绝大多数情况下,这种初始化(绑定远程服务)的时间开销(如果BinderPool没有提前初始化完成的话)是可以接受的。另外,BinderPool中有短线重连的机制,当远程服务意外终止时,BinderPool会重新建立连接,这个时候如果业务模块中的Binder调用出现了异常,也需要手动去重新获取最新的Binder对象,这个是需要注意的。

BinderPool大大方便了日常的开发,极大的提高了AIDL的开发效率,并且避免大量的Service创建,建议在开发工作中引入BinderPool机制。

注意:

AIDL:解决5.0以上找不到类的方法,需要再app下的build.gradle加入下的android加入以下语句即可。

    sourceSets {
        main {
//            Manifest.srcFile ['src/main/AndroidManifest.xml']
            java.srcDirs = ['src/main/java', 'src/main/aidl']
            resources.srcDirs = ['src/main/java']
            aidl.srcDirs = ['src/main/aidl']
            res.srcDirs = ['src/main/res']
            assets.srcDirs = ['src/main/assets']
        }
    }

使用Bundle

三组件(Activity、Service、Receiver)都支持在Intent中传递Bundle数据的,由于Bundle实现了Parcelable接口,所以可以方便地在不同的进程间通信。

传送的数据必须能够被序列化,如:基本类型、实现Parcelable和Serializable接口,以及Android支持的特殊对象,具体内容可以看Bundle这个类,就可以看到其支持的类型。

特殊使用场景:如A进程正在进行一个计算,计算完成后它需要启动B进程的一个组件并把计算结果传送给B进程,可是遗憾的是计算结果不支持放入Bundle中,因此无法通过Intent传输,这个时候使用AIDL略显复杂。可以考虑以下方式:我们可以通过Intent启动B进程的一个Service组件(如IntentService),让Service在后台进行计算,计算完毕后再启动B进程中真正要启动的目标组件,由于Service也运行在B进程中,所以目标组件就可以直接获取计算结果,这样一来就轻松解决了跨进程的问题。这种方式的核心思想是在于将原来需要在A进程的计算任务转移到B进程的后台Service中执行,这样就成功地避免了进程间通信问题,而且只用了很小的代价。

使用文件共享

文件共享也是一种不错的进程间通信方式,两个进程通过读写同一个文件来交换数据,比如A进程把数据写入文件,B进程通过读取这个文件来获取数据。windows上,一个文件如果被加入了排斥锁将会导致其他线程无法对其进行访问,包括读写,而由于Android系统基于Linux,使得其并发读/写文件可以没有限制的进行,甚至两个线程同时对同一个文件进行读写操作都是允许的,尽管这可能出问题,通过文件交换数据很好使用,除了交换一些文本信息外,还可以序列化一个对象到文件系统中的同时从另一个进程中恢复这个对象。如下采用的便是这一方式。

public class User implements Serializable {
    private static final long serialVersionUID = 519067123721295773L;//一般需要手动指定此值,避免反序列化失败
    public int userId;
    public String userName;
    public boolean isMale;

    public User(int userId, String userName, boolean isMale) {
        this.userId = userId;
        this.userName = userName;
        this.isMale = isMale;
    }

    public User() {

    }

    public int getUserId() {
        return userId;
    }

    public String getUserName() {
        return userName;
    }

    public boolean isMale() {
        return isMale;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public void setIsMale(boolean isMale) {
        this.isMale = isMale;
    }
}

//序列化

    private void persistToFile(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                User user = new User(1,"hello world",false);
                File dir = new File(MyConstants.FILE_PAHT);
                if (!dir.exists()){
                    dir.mkdirs();
                }
                File cachedFile = new File(MyConstants.FILE_PAHT);
                ObjectOutputStream out = null;
                try {
                    out = new ObjectOutputStream(new FileOutputStream(cachedFile));
                    out.writeObject(user);
                } catch (IOException e) {
                    e.printStackTrace();
                }finally {
                    MyUtils.close(out);
                }
            }
        }).start();
    }

//反序列化

    private void recoverFromFile(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                User user = null;
                File cacheFile = new File(MyConstants.FILE_PAHT);
                if (cacheFile.exists()){
                    ObjectInputStream in = null;
                    try {
                        in = new ObjectInputStream(new FileInputStream(cacheFile));
                        user = (User)in.readObject();
                    } catch (IOException e) {
                        e.printStackTrace();
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }finally {
                        MyUtils.close(in);
                    }
                }
            }
        }).start();
    }

关闭输出和输入流

public class MyUtils {

    public static void close(Closeable stream){
        try {
            stream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


}

通过文件共享这种方式共享数据对文件格式没有具体要求,可以是文本文件,也可以是XML文件,只要读/写双方约定数据格式即可。

使用Messenger

Messenger翻译为信使,通过它可以在不同的进程间传递Message对象,在Message中放入我们要传递的数据,就可以轻松的实现数据在进程间传递。Messenger是一种轻量级的IPC方案,底层使用AIDL实现。Messenger对AIDL做了封装,使得我们可以更加简便的进行进程间通信。同时,由于它一次处理一个请求,因此在服务端不用考虑线程同步的问题,这是因为服务端中不存在并发执行的情形。

步骤: 1)服务端进程 创建Service处理客户端请求,同时创建一个Handler并通过它创建一个Messenger对象,然后在Service的onBind中返回这个Messenger对象底层的Binder即可。

2)客户端进程 绑定服务端的Service,绑定成功后用服务端返回的IBinder对象创建一个Messenger,通过Messenger就可以向服务端发送消息了,发消息类型为message对象。如果需要服务端能够回应客户端,就和服务端一样,我们还需要创建一个Handler并创建一个新的Messenger,并把这个Messenger对象通过Message的replyTo参数传递服务端,服务端通过replyTo参数可以回应客户端。

MessengerService

/**
 * 使用Messenger实现进程间通讯
 */
public class MessengerService extends Service {
    private static final String TAG = "MessengerService";
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        //返回messenger底层binder
        return mMessenger.getBinder();
    }

    //创建Handler
    private static class MessengerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case MyConstants.MSG_FROM_CLIENT:
                    Log.i("lwjtag","receive msg from Client:"+msg.getData().getString("msg"));
                    Messenger client = msg.replyTo;
                    Message replyMessgae = Message.obtain(null,MyConstants.MSG_FROM_SERVICE);
                    Bundle bundle = new Bundle();
                    bundle.putString("reply","嗯,你的消息我已经收到,稍后会回复你。");
                    replyMessgae.setData(bundle);
                    try {
                        client.send(replyMessgae);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    };
    //创建一个Messenger对象
    private final Messenger mMessenger = new Messenger(new MessengerHandler());
}

MessengerActivity

public class MessengerActivity extends AppCompatActivity {
    private static final String TAG = "MessengerActivity";
    private Messenger mService;
    //创建Messenger对象
    private Messenger mGetReplyMessenger = new Messenger(new MessengerHandler());
    //创建handler对象
    private static class MessengerHandler extends Handler{
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case MyConstants.MSG_FROM_SERVICE:
                    Log.i("lwjtag","receive msg from Service:"+msg.getData().getString("reply"));
                    break;
                default:
                    super.handleMessage(msg);
            }

        }
    }


    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mService = new Messenger(service);
            Message msg = Message.obtain(null, MyConstants.MSG_FROM_CLIENT);
            Bundle data = new Bundle();
            data.putString("msg","hello,this is client");
            msg.setData(data);
            msg.replyTo = mGetReplyMessenger;
            try {
                mService.send(msg);//发送message
            } 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);
        Intent intent = new Intent(this,MessengerService.class);
        //绑定Service
        bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        //解绑
        unbindService(mConnection);
        super.onDestroy();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_messenger, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

IPC.png

IPC.png

在Messenger中进行数据传递就必须将数据放入Message中,而Messenger和Message都实现了Parcelable接口,因此可以跨进程传输。简单来说,Message所支持的数据类型就是Messenger所支持的传输类型,实际上,通过Messenger来传输Message,Message中能使用的载体只有what、arg1、arg2、Bundle和replyTo。Message中的另一个字段不支持跨进程传输,即便是2.2以后,也仅仅是系统提供的实现了Parcelable接口的对象才能通过它来传输,这就以为这我们自定义的Parcelable对象是无法通过object字段来传输的。

IPC.png

使用ContentProvider

Contentprovider是Android中提供的专门用于不同应用间的数据共享的方式,从这一点来看,其天生就适合进程间通信,其底层实现是Binder。

系统预置了许多ContentProvider,比如通讯录信息、日程表信息等,要跨进程访问这些信息,只需要通过ContentResolver的query、insert和delete方法即可。

自定义ContentProvider,演示如何在其他的应用中获取Contentprovider中的数据从而实现进程间通信的目的。创建BookProvider,继承Contentprovider并且实现六个抽象方法即可:onCreate、query、update、insert、delete和getType。

BookProvider

public class BookProvider extends ContentProvider {
    private static final String TAG = "BookProvider";
    public static final String AUTHORITY = "com.lwj.ipc.provider";//contentProvider唯一
    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_CONDE = 1;
    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    private Context mContext;
    private SQLiteDatabase mDb;

    static {
        sUriMatcher.addURI(AUTHORITY, "book", BOOK_URI_CODE);
        sUriMatcher.addURI(AUTHORITY, "user", USER_URI_CONDE);
    }

    /**
     * 获取表名
     * @param uri
     * @return
     */
    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_CONDE:
                tableName = DbOpenHelper.USER_TABLE_NAME;
                break;
            default:
                break;
        }
        return tableName;
    }

    @Override
    public boolean onCreate() {
        Log.i(TAG, "onCreate,current thread:" + Thread.currentThread().getName());
        mContext = getContext();
        //ContentProvider 创建时,初始化数据库,注意:这里仅仅是为了演示,事件使用中不推荐在主线程中进行耗时的数据库操作
        initProviderData();
        return true;
    }

    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(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,'jake',1);");
        mDb.execSQL("insert into user values(2,'jasmine',0);");

    }

    /**
     * 查询
     * @param uri
     * @param projection
     * @param selection
     * @param selectionArgs
     * @param sortOrder
     * @return
     */
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        Log.i(TAG, "query,current thread:" + Thread.currentThread().getName());
        String tableName = getTableName(uri);
        if (tableName == null) {
            try {
                throw new IllegalAccessException("Unsupported uri:" + uri);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return mDb.query(tableName, projection, selection, selectionArgs, null, null, sortOrder, null);
    }

    /**
     * 返回一个Uri请求所对应的MIME类型(媒体类型)
     * MIME类型:图片,视频等。
     * @param uri
     * @return
     */
    @Override
    public String getType(Uri uri) {
        Log.i(TAG, "getType");
        return null;
    }

    /**
     * 插入数据
     * @param uri
     * @param values
     * @return
     */
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        Log.i(TAG, "insert");
        String table = getTableName(uri);
        if (table == null) {
            try {
                throw new IllegalAccessException("Unsupported uri:" + uri);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        mDb.insert(table, null, values);
        mContext.getContentResolver().notifyChange(uri, null);
        return uri;
    }

    /**
     * 删除操作
     * @param uri
     * @param selection
     * @param selectionArgs
     * @return
     */
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        Log.i(TAG, "delete");
        String table = getTableName(uri);
        if (table == null) {
            try {
                throw new IllegalAccessException("Unsupported uri:" + uri);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        int count = mDb.delete(table, selection, selectionArgs);
        if (count > 0) {
            getContext().getContentResolver().notifyChange(uri, null);
        }
        return count;
    }

    /**
     * 更新
     * @param uri
     * @param values
     * @param selection
     * @param selectionArgs
     * @return
     */
    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        Log.i(TAG, "delete");
        String table = getTableName(uri);
        if (table == null) {
            try {
                throw new IllegalAccessException("Unsupported uri:" + uri);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        int row = mDb.update(table, values, selection, selectionArgs);
        if (row > 0) {
            getContext().getContentResolver().notifyChange(uri, null);
        }
        return row;
    }
}

AndroidManifest.xml注册

<provider
            android:name="com.lwj.ipc.contentprovider.BookProvider"
            android:authorities="com.lwj.ipc.provider"
            android:permission="com.wlj.PROVIDER"
            android:process=":provider"/>

DbOpenHelper

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);
    }

    @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) {
        //TODO
    }
}

使用contentprovider ProviderActivity

public class ProviderActivity extends AppCompatActivity {

    private static final String TAG = "BookProvider";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_provider);
        Uri uri = Uri.parse("content://com.lwj.ipc.provider");
//        getContentResolver().query(uri,null,null,null,null);
//        getContentResolver().query(uri,null,null,null,null);
//        getContentResolver().query(uri,null,null,null,null);

        Uri bookUri = Uri.parse("content://com.lwj.ipc.provider/book");

        ContentValues values = new ContentValues();
        values.put("_id", 6);
        values.put("name", "程序设计的艺术");
        getContentResolver().insert(bookUri, values);
        Cursor cursor = getContentResolver().query(bookUri,new String[]{"_id","name"},null,null,null);
        while (cursor.moveToNext()){
            Book book = new Book();
            book.bookId = cursor.getInt(0);
            book.bookName = cursor.getString(1);
            Log.i(TAG,"query book id:"+book.getBookId()+ "----bookName:"+book.getBookName());
        }
        cursor.close();

        Uri userUri = Uri.parse("content://com.lwj.ipc.provider/user");
        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.isMale = userCursor.getInt(2) == 1;
            Log.i(TAG,"query usre id:"+user.getUserId()+"----userName:"+user.getUserName());
        }
        userCursor.close();

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

IPC.png

注意:query、update、insert、delete四大方法是存在多线程并发的访问的,因此方法内部要做好线程同步。在本例中,由于采用的是SQLite并且只有一个SQLitedatabase的连接,所以可以正确应对多线程的情况。具体原因是SQLiteDatabase内部对数据库的操作是有同步处理的。但是如果通过多个SQLitedatabase对象之间无法进行线程同步。

使用Socket

Socket:套接字,是网络通信中的概念。分为流式套接字和用户数据包套接字,分别对应网络的传输控制层中的TCP和UDP。

TCP:面向连接的协议,提供稳定的双向通信功能。TCP连接的建立需要经过"三次握手"才能完成,为了提供稳定的数据传输功能,其本身提供了超时重传机制,因此具有超高的稳定性。

UDP: 无连接的,提供不稳定的单向通信功能,当然UDP也可以实现双向通信功能。在性能上,UDP具有更好的效率,其确定就是不能保证数据一定能正确传输,尤其在网络拥塞的情况下。

TCPServerService

/**
 * sockect实现进程间通讯
 */
public class TCPServerService extends Service {
    private boolean mIsServiceDestoryed = false;
    private String[] mDefinderMessages = new String[]{
            "你好啊,哈哈",
            "请问你叫什么名字呀?",
            "今天北京天气不错呀,shy",
            "你知道吗,我可是可以和多个人同时聊天的哦",
            "给你讲个笑话吧,据说爱笑的人运气不会太差,不知道真假"
    };


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

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

    @Override
    public void onDestroy() {
        mIsServiceDestoryed = true;
        super.onDestroy();
    }

    private class TcpServer implements Runnable{

        @Override
        public void run() {
            ServerSocket serverSocket = null;
            try {
                serverSocket = new ServerSocket();
            } catch (IOException e) {
                System.err.println("establish tcp server failed,port:868");
                e.printStackTrace();
                return;
            }
            while (!mIsServiceDestoryed){
                //接收客户端的请求
                try {
                    final Socket client = serverSocket.accept();
                    System.out.println("accept");
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                respondseClient(client);
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }).start();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void respondseClient(Socket client) throws IOException{
        //用于接收客户端消息
        BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
        //用户向客户端发送消息
        PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream())));
        out.println("欢迎来到聊天室");
        while (!mIsServiceDestoryed){
            String str = in.readLine();
            System.out.println("msg form client:"+str);
            if (str == null){
                //客户端断开连接
                return;
            }
            int i = new Random().nextInt(mDefinderMessages.length);
            String msg = mDefinderMessages[i];
            out.println(msg);
            System.out.println("send :"+str);
        }
        System.out.println("client quit");
        MyUtils.close(out);
        MyUtils.close(in);
    }
}

TCPClientActivity

public class TCPClientActivity extends AppCompatActivity implements View.OnClickListener {

    private TextView text;
    private EditText edit;
    private Button send;
    private static final int MESSAGE_RECEIVE_NEW_MSG = 1;
    private static final int MESSAGE_SOCKET_CONNECTED = 2;

    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:
                    text.setText(text.getText() + (String) (msg.obj));
                    break;
                case MESSAGE_SOCKET_CONNECTED:
                    send.setEnabled(true);
                    break;
                default:
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tcpclient);
        text = (TextView) this.findViewById(R.id.text);
        edit = (EditText) this.findViewById(R.id.edit);
        send = (Button) this.findViewById(R.id.send);
        send.setOnClickListener(this);
        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) {
        if (v == send) {
            final String msg = edit.getText().toString();
            if (!TextUtils.isEmpty(msg) && mPrintWriter != null) {
                mPrintWriter.println(msg);
                edit.setText("");
                String time = formatDateTime(System.currentTimeMillis());
                final String showedMsg = "self " + time + ":" + msg + "\n";
                text.setText(text.getText() + showedMsg);
            }
        }

    }

    /**
     * 格式化时间
     *
     * @param l
     * @return
     */
    private String formatDateTime(long l) {
        return new SimpleDateFormat("(HH:mm:ss)").format(new Date(l));
    }

    /**
     * 连接Server
     */
    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_CONNECTED);
                System.out.println("connect server success");
            } catch (IOException e) {
                SystemClock.sleep(1000);//1000毫秒重连
                e.printStackTrace();
            }
            try {
                //接收服务器端的消息
//                if (socket == null || socket.getInputStream() == null)return;
                BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//                if (br == null)return;
                while (!TCPClientActivity.this.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.....");
                MyUtils.close(mPrintWriter);
                MyUtils.close(br);
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

选择合适的IPC方式

根据下图选择合适的IPC方式:

IPC.png