Android IPC的几种方式

IPC全称为Inter-Process Communication,含义为进程间通信,指的是两个进程之间进行数据交换的过程。

方式一:Bundle实现 用于android四大组件直接的进程间通信

应用一的Activity

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.one_button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent=new Intent();
                Bundle bundle=new Bundle();
                bundle.putString("sendMessage","嘿!我正在给你发消息!");
                ComponentName componentName=new ComponentName("com.example.ipcdemoapplication","com.example.ipcdemoapplication.MainActivity");
                intent.setComponent(componentName);
                intent.putExtras(bundle);
                startActivity(intent);
            }
        });

    }
}

应用二代码

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView textView =(TextView)findViewById(R.id.text);
        Bundle bundle=getIntent().getExtras();
        if(bundle!=null){
            textView.setText(bundle.getString("sendMessage"));
        }
    }
}

点击应用一的按钮之后,跳转到应用二并输出“嘿!我正在给你发消息!”

方式二:使用文件共享

比如本地目录下有一个文件,都可以通过这个文件来进行数据分享,具体操作(https://www.jianshu.com/p/55eae30d133c

还有一种方式是通过SharedPreferences 通过共享xml来实现本地文件共享,但是多进程还是有一些问题的,具体参照(https://www.jianshu.com/p/4984f66f9a4b

方式三:使用Messenger

Messenger可以在不同进程中传递Message对象,在Message中加入我们想要传递的数据就可以在进程间进行数据传递了。Messenger是一种轻量级的IPC方案并对AIDL进行了封装,它实现起来比较容易,首先我们先写服务器(MessengerServce.java),在onBind方法中创建Messenger,关联接受消息的Handler调用getBinder来获取Binder对象,在handleMessage方法中接受客户端发来的信息。

public class MessengerService extends Service {

    private static final String TAG = "WANZIKAIFA";
    public static final int MSG_FORMCLIENT = 1000;
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            switch (msg.what){
                case MSG_FORMCLIENT:
                    Log.e(TAG,"收到客户端信息----"+msg.getData().get("msg"));
                   //得到客户端传来的Messenger对象
                   Messenger mMessenger = msg.replyTo;
                   Message message = Message.obtain(null,MessengerService.MSG_FORMCLIENT);
                    Bundle mBundle = new Bundle();
                    mBundle.putString("rep","这里是服务端,我们收到信息了");
                    message.setData(mBundle);
                    try {
                        mMessenger.send(message);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
            }
        }
    };
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new Messenger(mHandler).getBinder();
    }
}

注册服务时要另开启一个进程

 <service android:name=".MessengerService" android:process=":remoute"/>

接下来创建客户端(MainActivity.java),绑定另一个进程服务,绑定成功后根据服务端返回的Binder对象创建Messenger,并用Messenger向服务端发送信息。

public class MainActivity extends AppCompatActivity {
    private Messenger mMessenger;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.one_button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Log.d("WANZIKAIFA", "点击按钮 ");
                Intent intent = new Intent(MainActivity.this,MessengerService.class);
                bindService(intent,mServiceConnon, Context.BIND_AUTO_CREATE);
            }
        });

    }
    private  ServiceConnection mServiceConnon = new ServiceConnection(){

        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            Log.d("WANZIKAIFA", "这里是客户端,服务端收到了吗 ");
            mMessenger = new Messenger(iBinder);
            Message mMessage = Message.obtain(null,MessengerService.MSG_FORMCLIENT);
            Bundle mBundle = new Bundle();
            mBundle.putString("msg","这里是客户端,服务端收到了吗");
            mMessage.setData(mBundle);

            //将Messenger传递给服务端
            mMessage.replyTo = new Messenger(mHandler);
            try {
                mMessenger.send(mMessage);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };

    private Handler mHandler= new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case MessengerService.MSG_FORMCLIENT:
                    Log.d("WANZIKAIFA", "收到服务端信息: "+msg.getData().get("rep"));
                    break;
            }

        }
    };

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

在handleMessage回调中收到客户端信息时,调用Message.replyTo得到客户端传递过来的Messenger对象,创建消息并通过Messenger发送给客户端。然后客户端需要创建一个Handler来接受服务端的信息。当绑定完服务之后需要关联定义的Handler。

打印的log

01-01 03:02:51.601 12156 12156 D WANZIKAIFA: 点击按钮
01-01 03:02:52.011 12156 12156 D WANZIKAIFA: 这里是客户端,服务端收到了吗
01-01 03:02:52.012 12565 12565 E WANZIKAIFA: 收到客户端信息----这里是客户端,服务端收到了吗
01-01 03:02:52.013 12156 12156 D WANZIKAIFA: 收到服务端信息: 这里是服务端,我们收到信息了

方式四:通过AIDL来进行IPC通信

之前发过的一篇AIDL通信(https://blog.csdn.net/qq_27647919/article/details/107204389

方式五:ContentProvider方式 实现对另一个应用进程开放provider数据的查询

第一步创建数据库并创建表名为“game_provider.db” 里面有两个字段name,describe

public class DBOpenHelper extends SQLiteOpenHelper {
    public static final String DB_NAME = "game_provider.db";
    public static final String GAME_TABLE_NAME = "game";
    public static final int DB_VERSION = 1;
    public String CREATE_GAME_TABLE = "create table if not exists " + GAME_TABLE_NAME
            +"(_id integer primary key ,"+"name TEXT ,"+"describe TEXT)";

    public DBOpenHelper(@Nullable Context context) {
        super(context, DB_NAME, null, DB_VERSION);
    }


    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        sqLiteDatabase.execSQL(CREATE_GAME_TABLE);
    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {

    }
}

第二步对数据库存入一条数据

public class GameProvider extends ContentProvider {

    public static final String AUTHORITY = "com.example.myapplication.GameProvider";
    public static final Uri GAME_CONTENT_URL = Uri.parse("content://"+AUTHORITY+"/game");
    private static final UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    private SQLiteDatabase mDB;
    private Context mContext;
    private String table;

    static {
        mUriMatcher.addURI(AUTHORITY,"game",0);
    }
    @Override
    public boolean onCreate() {
        table = DBOpenHelper.GAME_TABLE_NAME;
        mContext = getContext();
        initProvider();
        return false;
    }

    private void initProvider() {
        Log.d("WANZIKAIFA", "insert into game: ");
        mDB = new DBOpenHelper(mContext).getWritableDatabase();
        new Thread(new Runnable() {
            @Override
            public void run() {
                mDB.execSQL("delete from "+DBOpenHelper.GAME_TABLE_NAME);
                mDB.execSQL("insert into game values(1,'插入第一条','我是第一条');");
            }
        }).start();
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        String table = DBOpenHelper.GAME_TABLE_NAME;
        Cursor mCursor = mDB.query(table,
                projection,
                selection,
                selectionArgs,
                null,
                sortOrder,
                null);
        return mCursor;
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        return null;
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) {
        mDB.insert(table,null,contentValues);
        mContext.getContentResolver().notifyChange(uri,null);
        return null;
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String s, @Nullable String[] strings) {
        return 0;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Nullable String s, @Nullable String[] strings) {
        return 0;
    }
}

为了测试是跨进程通信,将provdier写入另一个进程

 <provider
            android:authorities="com.example.myapplication.GameProvider"
            android:name=".GameProvider"
            android:process=":provider"/>

在Activity中插入另一条数据

public class ContentProviderActivity extends Activity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.contentprovier_activity);

        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Uri uri = Uri.parse("content://com.example.myapplication.GameProvider");
                ContentValues mContentValues = new ContentValues();
                mContentValues.put("_id",2);
                mContentValues.put("name","插入第二条");
                mContentValues.put("describe","我是第二条");
                getContentResolver().insert(uri,mContentValues);
                Cursor gameCursor = getContentResolver().query(uri,new String[]{"name","describe"},null,null,null);
                while (gameCursor.moveToNext()){
                    Game mGame = new Game(gameCursor.getString(0),gameCursor.getString(1));
                    Log.d("WANZIKAIFA",mGame.gameName+"---"+mGame.gameDescribe);
                }
            }
        });
    }
}

game.java

public class Game implements Parcelable {
    public String gameName;
    public String gameDescribe;
    public Game(String gameName,String gameDescribe){
        this.gameName = gameName;
        this.gameDescribe = gameDescribe;
    }

    protected Game(Parcel in) {
        gameName = in.readString();
        gameDescribe=in.readString();
    }
    public static final Creator<Game> CREATOR = new Creator<Game>() {
        @Override
        public Game createFromParcel(Parcel in) {
            return new Game(in);
        }

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

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

    @Override
    public void writeToParcel(Parcel parcel, int i) {
        parcel.writeString(gameName);
        parcel.writeString(gameDescribe);
    }
}

点击按钮之后,输出log为

01-01 00:10:46.764  8779  8779 D WANZIKAIFA: insert into game:
01-01 00:10:46.842  8736  8736 D WANZIKAIFA: 插入第一条---我是第一条
01-01 00:10:46.842  8736  8736 D WANZIKAIFA: 插入第二条---我是第二条

由log可以看出进程号pid8779 pid8736 第二条跨进程插入成功

方式六用Socket实现跨进程聊天程序

Socket是位于应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口,供应用层调用以实现进程在网络中通信。Socket分为流式套接字和数据包套接字,分别对应网络传输控制层的TCP协议和UDP协议。TCP协议是一种面向连接的,可靠的,基于字节流的传输层通信协议,它使用三次握手协议建立连接,并且提供了超时重传机制,具有很高的稳定性。UDP协议则是一种无连接的协议,且不对传送数据包进行可靠性保证,它适合一次传输少量数据,UDP传输的可靠性是由应用层负责。在网络质量令人十分不满意的环境下,UDP协议数据包丢失会比较严重。但是由于UDP协议的特性:他不属于连接型协议,因而具有资源消耗小,处理速度快的优点,所以通常音频,视频和普通数据在传送时使用UDP协议比较多。

第一步添加权限

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

第二步实现服务端

实际流程是在线程中创建TCP服务,监听8688端口,等待客户端连接,当客户端连接时就会生成Socket。通过每次创建的Socket就可以和不同的客户端通信了。当客户端断开连接时,服务端也会跟着关闭Socket并结束通话线程。服务端首先会向客户端发送一条消息:“您好,我是服务端”,并接受客户端发来的消息,将受到的消息进行加工再返回给客户端

public class SocketServerService extends Service {
    private boolean isServiceDestoryed = false;

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

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

    private class TcpServer implements Runnable {
        @Override
        public void run() {
            ServerSocket serverSocket = null;
            try {
                //监听8688窗口
                serverSocket = new ServerSocket(8688);
            } catch (IOException e) {
                e.printStackTrace();
            }
            while (!isServiceDestoryed){
                try {
                    //接受客户端请求,并且阻塞直接接受消息
                    final Socket client = serverSocket.accept();
                    new Thread(){
                        @Override
                        public void run() {
                            responseClient(client);
                        }
                    }.start();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void responseClient(Socket client) {
        //用户接受客户端消息
        try {
            BufferedReader in = new BufferedReader((new InputStreamReader(client.getInputStream())));
        //用于向客户端发送消息
            PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream())),true);
            out.println("您好呀!我是服务端!");
            while (!isServiceDestoryed){
                String str = in.readLine();
                Log.d("WANZIKAIFA", "收到客户端的信息: "+str);
                if (TextUtils.isEmpty(str)){
                    //客户端断开了连接
                    Log.d("WANZIKAIFA","客户端断开连接");
                    break;
                }
                String message = "收到了客户端的信息为"+ str;
                //从客户端收到的消息加工再发给客户端
                out.println(message);
            }
            out.close();
            in.close();
            client.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

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

第三步实现客户端

流程:客户端会启动服务,并开启线程连接服务端Socket。

public class SocketClientActivity extends Activity {
    private EditText et_receiver;
    private Button bt_send;
    private TextView tv_message;
    private PrintWriter mPrintWriter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_socket);
        initView();
        Intent intent = new Intent(this,SocketServerService.class);
        startService(intent);
        new Thread(){
            @Override
            public void run() {
               connectSocketServer();
            }
        }.start();
    }

    private void initView() {
        et_receiver = (EditText)findViewById(R.id.et_receiver);
        bt_send =(Button)findViewById(R.id.bt_send);
        tv_message = (TextView)findViewById(R.id.tv_message);

        bt_send.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                final String msg = et_receiver.getText().toString();
                //向服务端发送信息
                if (!TextUtils.isEmpty(msg)&&null!=mPrintWriter){
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            mPrintWriter.println(msg);
                        }
                    }).start();
                    tv_message.setText(tv_message.getText()+"\n"+"客户端:"+msg);
                    Log.d("WANZIKAIFA", tv_message.getText()+"\n"+"客户端:"+msg);
                    et_receiver.setText("");
                }
            }
        });
    }
    private void connectSocketServer() {
        Socket socket = null;
        while (socket==null){
            //选择和服务器相同的端口8688
            try {
                socket = new Socket("localhost",8688);
                mPrintWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())),true);

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

        }
        //接受服务器端的消息
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            while (!isFinishing()){
                final String msg = br.readLine();
                if (msg!=null){
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            tv_message.setText(tv_message.getText()+"\n"+"服务端:"+msg);
                            Log.d("WANZIKAIFA", tv_message.getText()+"\n"+"服务端:"+msg);
                        }
                    });
                }
            }
            mPrintWriter.close();
            br.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

打印出log为

01-01 02:08:54.459  9771  9771 D WANZIKAIFA: 服务端:您好呀!我是服务端!
01-01 02:09:28.140  9790  9814 D WANZIKAIFA: 收到客户端的信息: 收到
01-01 02:09:28.142  9771  9771 D WANZIKAIFA: 客户端:收到
01-01 02:09:28.190  9771  9771 D WANZIKAIFA: 服务端:收到了客户端的信息为收到

方式七广播

比如开机启动广播

public class BootUpReceiver extends BroadcastReceiver {
    private static final String cTag = "BootUpReceiver";

    @Override
    public void onReceive( final Context context, final Intent intent ) {
        LogTool.d(cTag, " BootUpReceiver"); 
    }
}

注册清单:

<receiver android:name=".BootUpReceiver">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
</receiver>

最后针对这几种IPC通信方式分析一下优缺点:总结转自(https://www.jianshu.com/p/71480c680a65

1.bundle :
简单易用 但是只能传输Bundle支持的对象 常用于四大组件间进程间通信
2.文件共享:
简单易用 但不适合在高并发的情况下 并且读取文件需要时间 不能即时通信 常用于并发程度不高 并且实时性要求不高的情况
3.AIDL :
功能强大 支持一对多并发通信 支持即时通信 但是使用起来比其他的复杂 需要处理好多线程的同步问题 常用于一对多通信 且有RPC 需求的场合(服务端和客户端通信)
4.Messenger :
功能一般 支持一对多串行通信 支持实时通信 但是不能很好处理高并发情况 只能传输Bundle支持的类型 常用于低并发的无RPC需求一对多的场合
5.ContentProvider :
在数据源访问方面功能强大 支持一对多并发操作 可扩展call方法 可以理解为约束版的AIDL 提供CRUD操作和自定义函数 常用于一对多的数据共享场合
6.Socket :
功能强大 可以通过网络传输字节流 支持一对多并发操作 但是实现起来比较麻烦 不支持直接的RPC 常用于网络数据交换

总结

当仅仅是跨进程的四大组件间的传递数据时 使用Bundle就可以 简单方便
当要共享一个应用程序的内部数据的时候 使用ContentProvider实现比较方便
当并发程度不高 也就是偶尔访问一次那种 进程间通信 用Messenger就可以
当设计网络数据的共享时 使用socket
当需求比较复杂 高并发 并且还要求实时通信 而且有RPC需求时 就得使用AIDL了
文件共享的方法用于一些缓存共享 之类的功能

猜你喜欢

转载自blog.csdn.net/qq_27647919/article/details/109678145