Android之进程通信机制(下)(AIDL,Messenger,Socket)

来源:互联网 发布:飞行员空姐知乎 编辑:程序博客网 时间:2024/04/29 11:51


上篇博文,我们学习了IPC要用到的一些基本概念,接下来我们就要真刀真抢上战场啦。我们说过,Android的进程通信方法主要有Bundle,文件共享,MessengerAIDL,ContentProviderSocket.本篇博文,我们主要讲解Messenger(可翻译为信使),AIDLSocket,因为Bundle,文件共享太简单了,而ContentProvider我们在Android的四大组件中已经学习过了。

一 Messenger

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

(1).服务端进程

首先,我们需要在服务端创建一个Service来处理客户端请求,同时创建一个Handler并通过它来创建一个Messenger对象,然后再ServiceonBind中返回这个Messenger对象底层的Binder即可。

 

(2).客户端进程

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

 

//MessengerActivity

package cn.tyssen.Messenger;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.*;
import android.util.Log;
import cn.tyssen.utils.MyConstants;

/**
 * Created by Tyssen on 2016/1/18.
 */
public class MessengerActivity extends Activity {

    private static final String TAG "MessengerActivity";

    private Messenger mService;
    private Messenger mGetReplyMessenger new Messenger(new MessengerHandler());

    private static class MessengerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MyConstants.MSG_FROM_SERVICE:
                    Log.i(TAG"receive msg from Service:" + msg.getData().getString("reply"));
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    private ServiceConnection mConnection new ServiceConnection() {
        public void onServiceConnected(ComponentName classNameIBinder service) {
            mService new Messenger(service);
            Log.d(TAG"bind 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);
            catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        public void onServiceDisconnected(ComponentName className) {
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_messenger);
        Intent intent = new Intent("com.ryg.MessengerService.launch");
        bindService(intentmConnectionContext.BIND_AUTO_CREATE);
    }

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

 

//MessengerService

package cn.tyssen.Messenger;

import android.app.Service;
import android.content.Intent;
import android.os.*;
import android.util.Log;
import cn.tyssen.utils.MyConstants;

/**
 * Created by Tyssen on 2016/1/18.
 */
public class MessengerService extends Service {

    private static final String TAG "MessengerService";

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

    private final Messenger mMessenger new Messenger(new MessengerHandler());

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

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

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intentflagsstartId);
    }

}

 

 

 

 

二 AIDL

上面我们知道,Messenger是以串行的方式处理客户端发来的消息,如果大量的消息同时发送到服务端,服务端仍然只能一个处理,所以就不太合适用Messenger了。同时Messenger的作用主要为了传递消息,很多时候,我们可能需要跨进程调用服务端方法,这种情形,Messenger就无法做到了但我们可以使用AIDL.

使用AIDL分为服务端和客户端:

(1).服务端。

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

(2).客户端

客户端所要做的事情就稍微简单一些,首先需要绑定服务端的Service,绑定成功后,将服务端返回的Binder对象转成AIDL接口所属的类型,接着就可以调用AIDL中的方法了。

(3).AIDL接口的创建

 

AIDL文件中,并不是所有的数据类型都可以使用的,那么到底AIDL文件支持哪些数据类型呢:

1.基本数据类型

2.String CharSequence

3.List 只支持ArrayList,里面每个元素都必须能够被AIDL支持。

4.Map 只支持HashMap,里面的每个元素都必须被AIDL支持,包括keyvalue.

5.Parcelabe 所有实现了Parcelable的接口对象

6.AIDL 所有的AIDL接口本身也可以在AIDL文件中使用。

 

如果自定义的Parceable对象和AIDL对象必须要显示import进来,不管他们是否和当前的AIDL文件位于同一个包内。另外,如果AIDL文件中用到了自定义的Parcelable对象,那么必须新建一个和它同名的AIDL文件,并在其中声明Parcelable类型

 

(4). 远程服务端Service的实现

(5). 客户端的实现

 

下面写一个AIDL的例子,实现了客户端调用服务端方法添加课本,删除课本,通知课本变化,以及权限验证。

//Book.java

 

package cn.tyssen.android.androidstudy;

import android.os.Parcel;
import android.os.Parcelable;

/**
 * Created by Tyssen on 2016/1/18.
 */
public class Book implements Parcelable {

    public int bookId;
    public String bookName;

    public Book() {

    }

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

    public int describeContents() {
        return 0;
    }

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

    public static final Parcelable.Creator<Book> CREATOR new Parcelable.Creator<Book>() {
        public Book createFromParcel(Parcel in) {
            return new Book(in);
        }

        public Book[] newArray(int size) {
            return new Book[size];
        }
    };

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

    @Override
    public String toString() {
        return String.format("[bookId:%s, bookName:%s]"bookIdbookName);
    }

}

 

//Book.aidl

// Book.aidl
package cn.tyssen.android.androidstudy;

// Declare any non-default types here with import statements
parcelable Book;

 

//BookManagerActivity.java

 

package cn.tyssen.android.androidstudy;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.*;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import java.util.List;

/**
 * Created by Tyssen on 2016/1/18.
 */
public class BookManagerActivity extends Activity {

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

    private IBookManager mRemoteBookManager;

    @SuppressLint("HandlerLeak")
    private Handler mHandler new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MESSAGE_NEW_BOOK_ARRIVED:
                    Log.d(TAG"receive new book :" + msg.obj);
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    };

    private IBinder.DeathRecipient mDeathRecipient new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            Log.d(TAG"binder died. tname:" + Thread.currentThread().getName());
            if (mRemoteBookManager == null)
                return;
            mRemoteBookManager.asBinder().unlinkToDeath(mDeathRecipient0);
            mRemoteBookManager null;
            // TODO:这里重新绑定远程Service
        }
    };

    private ServiceConnection mConnection new ServiceConnection() {
        public void onServiceConnected(ComponentName classNameIBinder service) {
            IBookManager bookManager = IBookManager.Stub.asInterface(service);
            mRemoteBookManager = bookManager;
            try {
                mRemoteBookManager.asBinder().linkToDeath(mDeathRecipient0);
                List<Book> list = bookManager.getBookList();
                Log.i(TAG"query book list, list type:"
                        + list.getClass().getCanonicalName());
                Log.i(TAG"query book list:" + list.toString());
                Book newBook = new Book(3"Android");
                bookManager.addBook(newBook);
                Log.i(TAG"add book:" + newBook);
                List<Book> newList = bookManager.getBookList();
                Log.i(TAG"query book list:" + newList.toString());
                bookManager.registerListener(mOnNewBookArrivedListener);
            catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        public void onServiceDisconnected(ComponentName className) {
            mRemoteBookManager null;
            Log.d(TAG"onServiceDisconnected. tname:" + Thread.currentThread().getName());
        }
    };

    private IOnNewBookArrivedListener mOnNewBookArrivedListener new IOnNewBookArrivedListener.Stub() {

        @Override
        public void onNewBookArrived(Book newBook) throws RemoteException {
            mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVEDnewBook)
                    .sendToTarget();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(this, BookManagerService.class);
        bindService(intentmConnectionContext.BIND_AUTO_CREATE);
    }

    public void onButton1Click(View view) {
        Toast.makeText(this, "click button1"Toast.LENGTH_SHORT).show();
        new Thread(new Runnable() {

            @Override
            public void run() {
                if (mRemoteBookManager != null) {
                    try {
                        List<Book> newList = mRemoteBookManager.getBookList();
                    catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }

    @Override
    protected void onDestroy() {
        if (mRemoteBookManager != null
                && mRemoteBookManager.asBinder().isBinderAlive()) {
            try {
                Log.i(TAG"unregister listener:" mOnNewBookArrivedListener);
                mRemoteBookManager
                        .unregisterListener(mOnNewBookArrivedListener);
            catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        unbindService(mConnection);
        super.onDestroy();
    }

}

 

 

//BookManagerService.java

 

package cn.tyssen.android.androidstudy;

import android.app.Service;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.*;
import android.util.Log;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;


/**
 * Created by Tyssen on 2016/1/18.
 */
public class BookManagerService extends Service {

    private static final String TAG "BMS";

    private AtomicBoolean mIsServiceDestoryed new AtomicBoolean(false);

    private CopyOnWriteArrayList<Book> mBookList new CopyOnWriteArrayList<Book>();
    // private CopyOnWriteArrayList<IOnNewBookArrivedListener> mListenerList =
    // new CopyOnWriteArrayList<IOnNewBookArrivedListener>();
    private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList new RemoteCallbackList<IOnNewBookArrivedListener>();

    private Binder mBinder new IBookManager.Stub() {

        @Override
        public List<Book> getBookList() throws RemoteException {
            SystemClock.sleep(5000);
            return mBookList;
        }

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

        public boolean onTransact(int codeParcel dataParcel reply, int flags)
                throws RemoteException {
            int check = checkCallingOrSelfPermission("cn.tyssen.aidl.permission.ACCESS_BOOK_SERVICE");
            Log.d(TAG"check=" + check);
            if (check == PackageManager.PERMISSION_DENIED) {
                return false;
            }

            String packageName = null;
            String[] packages = getPackageManager().getPackagesForUid(
                    getCallingUid());
            if (packages != null && packages.length 0) {
                packageName = packages[0];
            }
            Log.d(TAG"onTransact: " + packageName);
            if (!packageName.startsWith("cn.tyssen")) {
                return false;
            }

            return super.onTransact(codedatareplyflags);
        }

        @Override
        public void registerListener(IOnNewBookArrivedListener listener)
                throws RemoteException {
            mListenerList.register(listener);

            final int N = mListenerList.beginBroadcast();
            mListenerList.finishBroadcast();
            Log.d(TAG"registerListener, current size:" + N);
        }

        @Override
        public void unregisterListener(IOnNewBookArrivedListener listener)
                throws RemoteException {
            boolean success = mListenerList.unregister(listener);

            if (success) {
                Log.d(TAG"unregister success.");
            else {
                Log.d(TAG"not found, can not unregister.");
            }
            final int N = mListenerList.beginBroadcast();
            mListenerList.finishBroadcast();
            Log.d(TAG"unregisterListener, current size:" + N);
        };

    };

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

    @Override
    public IBinder onBind(Intent intent) {
        int check = checkCallingOrSelfPermission("cn.tyssen.aidl.permission.ACCESS_BOOK_SERVICE");
        Log.d(TAG"onbind check=" + check);
        if (check == PackageManager.PERMISSION_DENIED) {
            return null;
        }
        return mBinder;
    }

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

    private void onNewBookArrived(Book book) throws RemoteException {
        mBookList.add(book);
        final int N = mListenerList.beginBroadcast();
        for (int i = 0i < Ni++) {
            IOnNewBookArrivedListener l = mListenerList.getBroadcastItem(i);
            if (l != null) {
                try {
                    l.onNewBookArrived(book);
                catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
        mListenerList.finishBroadcast();
    }

    private class ServiceWorker implements Runnable {
        @Override
        public void run() {
            // do background processing here.....
            while (!mIsServiceDestoryed.get()) {
                try {
                    Thread.sleep(5000);
                catch (InterruptedException e) {
                    e.printStackTrace();
                }
                int bookId = mBookList.size() + 1;
                Book newBook = new Book(bookId"new book#" + bookId);
                try {
                    onNewBookArrived(newBook);
                catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

 

 

//IBookManager.aidl

// Book.aidl
package cn.tyssen.android.androidstudy;

import cn.tyssen.android.androidstudy.Book;
import cn.tyssen.android.androidstudy.IOnNewBookArrivedListener;
interface IBookManager {
     List<Book> getBookList();
     void addBook(in Book book);
     void registerListener(IOnNewBookArrivedListener listener);
     void unregisterListener(IOnNewBookArrivedListener listener);
}

 

 

//IOnNewBookArrivedListener.aidl

 

// IOnNewBookArrivedListener.aidl.aidl
package cn.tyssen.android.androidstudy;
import cn.tyssen.android.androidstudy.book;


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

 

 

 

代码中要注意的点:

1.使用观察者模式检测书本变化并通知客户端

2.在Binder线程池中执行的方法(例如IOnNewBookArrivedListener)不能操作UI控件,可以使用Handler切换到主线程。在客户端调用服务端方法,被调用的方法运行在服务端的Binder线程池中,同时客户端线程会被挂起,这个时候,如果服务端方法执行比较耗时,就会导致客户端线程长时间的阻塞在这里,而如果这个客户端线程是UI线程的话,就会导致客户端ANR。因此,当如果我们明确知道某个远程方法是耗时的,那么就要避免在客户端UI线程中去访问远程方法。由于客户端的onServiceConnected和 onServiceDisconnected方法都运行在UI线程中,所是以也不可以在他们里面指教调用服务端耗时的方法。除此之外,由于服务端的方法本身就运行在服务端的Binder线程池中,所以服务端方法可以执行大量耗时操作,所以这个时候,不要再服务端再开启异步任务。同理,当远程服务端需要调用客户端的listener中的方法时,被调用的方法也运行在Binder线程池中,只不过是客户端的线程池。所以,我们同样不可以再服务端中调用客户端的耗时方法。比如你值中的IOnNewBookArrivedListener中的onNewBookArrived.

3.使用RemoteCallbackList.RemoteCallbackList是系统专门提供用户删除跨进程listener的接口。RemoteCallbackList是一个泛型,支持任意AIDL接口。它的工作原理很简单,在它的内部有一个Map结构专门用户保存所有的AIDL回掉接口,这个MapkeyIBinder类型,value Callback类型,其中Callback封装了真正的远程的listener,所以虽然多次跨进程传输客户端的同一个对象会在服务端生成不同的对象,但是这些新生成的对象有一个共同点,就是它们的底层的Binder对象是同一个,同时,当客户端进程终止了以后,他能够自动移除客户端所注册的Listener.另外,RemoteCallbackList内部自动实现了线程同步的功能,所以用它来注册和解注册的时候,不需要做额外的线程同步工作。

4.为了程序的健壮性,我们还需要做一件事。Binder是可能意外死亡的,这可能由于服务端进程意外死亡了,这时候。我们需要重新连接服务。有两种方法:

a) 第一种方式是给Binder设置DeathRecipient监听,当Binder死亡时,我们会受到binderDied方法的会调,在BinderDied方法中我们可以重新连接服务,它运行在客户端的Binder线程池。

b) 另一种方法是在onServiceDisconnected中重新连接服务,只是该方法运行在客户端的UI线程。

5.给AIDL添加权限。有两种方法:

a) 在onBind中进行验证,验证不通过就返回Null,这样验证失败的客户端就无法绑定服务。至于验证方式有多种,比如使用permission验证,是用这种方式验证需要先在Manifest文件中添加权限,然后就可以在onBind中调用checkCallingOrSelfPermission(“权限名”). 一个应用

来绑定我们的服务时,会验证这个应用的权限,如果它没有使用这个权限,则无法绑定。

b)  在服务端的onTransact方法中进行权限验证,如果验证失败,则直接返回false,这样服务端就不会终止执行AIDL中的方法,从而达到保护服务端的效果。至于验证方式,可以有很多,也可以采用和第一种方法一样的permission。另外还可以采用UidPid来做验证,通过getCallingUidgetCallingPid可以拿到客户端所属应用的UidPid,通过这两个参数我们可以做很多事情,比如验证包名等。这种方法,我们在例子中也有实现。

 

三 Socket

Socket也称套接字,是网络通信中的概念,它分为流式套接字和数据报套接字,分别对应于网络传输控制层中的TCPUDP协议。

本篇博客我们通过一个聊天程序去学习Socket。首先,在远程Service建立一个TCP服务,然后在主界面中连接TCP服务。连接上了以后,我们就可以发送消息给服务端了。

例子代码:

//服务端

package cn.tyssen.Socket;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import cn.tyssen.utils.MyUtils;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Random;

/**
 * Created by Tyssen on 2016/1/18.
 */
public class TCPServerService extends Service {

    private boolean mIsServiceDestoryed false;
    private String[] mDefinedMessages 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 {

        @SuppressWarnings("resource")
        @Override
        public void run() {
            ServerSocket serverSocket = null;
            try {
                serverSocket = new ServerSocket(8688);
            catch (IOException e) {
                System.err.println("establish tcp server failed, port:8688");
                e.printStackTrace();
                return;
            }

            while (!mIsServiceDestoryed) {
                try {
                    // 接受客户端请求
                    final Socket client = serverSocket.accept();
                    System.out.println("accept");
                    new Thread() {
                        @Override
                        public void run() {
                            try {
                                responseClient(client);
                            catch (IOException e) {
                                e.printStackTrace();
                            }
                        };
                    }.start();

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

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

}

 

 

//客户端

package cn.tyssen.Socket;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import cn.tyssen.utils.MyUtils;

import java.io.*;
import java.net.Socket;
import java.sql.Date;
import java.text.SimpleDateFormat;

/**
 * Created by Tyssen on 2016/1/18.
 */
public class TCPClientActivity extends Activity implements View.OnClickListener {

    private static final int MESSAGE_RECEIVE_NEW_MSG 1;
    private static final int MESSAGE_SOCKET_CONNECTED 2;

    private Button mSendButton;
    private TextView mMessageTextView;
    private EditText mMessageEditText;

    private PrintWriter mPrintWriter;
    private Socket mClientSocket;

    @SuppressLint("HandlerLeak")
    private Handler mHandler new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MESSAGE_RECEIVE_NEW_MSG: {
                    mMessageTextView.setText(mMessageTextView.getText()
                            + (String) msg.obj);
                    break;
                }
                case MESSAGE_SOCKET_CONNECTED: {
                    mSendButton.setEnabled(true);
                    break;
                }
                default:
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tcpclient);
        mMessageTextView = (TextView) findViewById(R.id.msg_container);
        mSendButton = (Button) findViewById(R.id.send);
        mSendButton.setOnClickListener(this);
        mMessageEditText = (EditText) findViewById(R.id.msg);
        Intent service = new Intent(this, TCPServerService.class);
        startService(service);
        new Thread() {
            @Override
            public void run() {
                connectTCPServer();
            }
        }.start();
    }

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

    @Override
    public void onClick(View v) {
        if (v == mSendButton) {
            final String msg = mMessageEditText.getText().toString();
            if (!TextUtils.isEmpty(msg) && mPrintWriter != null) {
                mPrintWriter.println(msg);
                mMessageEditText.setText("");
                String time = formatDateTime(System.currentTimeMillis());
                final String showedMsg = "self " + time + ":" + msg + "\n";
                mMessageTextView.setText(mMessageTextView.getText() + showedMsg);
            }
        }
    }

    @SuppressLint("SimpleDateFormat")
    private String formatDateTime(long time) {
        return new SimpleDateFormat("(HH:mm:ss)").format(new Date(time));
    }

    private void connectTCPServer() {
        Socket socket = null;
        while (socket == null) {
            try {
                socket = new Socket("localhost"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);
                System.out.println("connect tcp server failed, retry...");
            }
        }

        try {
            // 接收服务器端的消息
            BufferedReader br = new BufferedReader(new InputStreamReader(
                    socket.getInputStream()));
            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_MSGshowedMsg)
                            .sendToTarget();
                }
            }
            System.out.println("quit...");
            MyUtils.close(mPrintWriter);
            MyUtils.close(br);
            socket.close();
        catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 

 

 

四 IPC的选择

名称

优点

缺点

适用场景

Bundle

简单

只能传输Bundle类型的数据

四大组件间通信

文件共享

简单

不适合高并发场景,无法做到即使通讯

无并发访问,交换简单实时性要求不高的数据

AIDL

功能强大,支持一对多并发通信,支持实时通信

使用复杂,要处理好线程同步

一对多且有RPC需求

Messenger

功能一般 支持一对多串行通信,支持实时通信

不时候高并发场景,不支持RPC,数据通过Message传输

低并发的一对多即时通信,无RPC要求

ContentProvider

在数据源方面功能强大,支持一对多并发数据共享,可通过Call方法扩展其他操作

可以理解为受约束的AIDL,主要提供数据源的CRUD

一对多的进程间数据共享

Socket

功能强大,可以通过网络传输,支持一对多并发实时通信

细节麻烦些,不支持RPC

网络交换数据

 

终于写完了,好累,我要去吃饭了。。。。明天写View的工作原理,更精彩!

2 0