美文网首页
Android开发艺术探索(2) --- IPC的点点滴滴

Android开发艺术探索(2) --- IPC的点点滴滴

作者: 官子寒 | 来源:发表于2020-03-01 12:45 被阅读0次

1. 多进程

开启方式

  • android:process = ":remote"
  • android:process="完整报包名"

2. 基本概念

2.1 AIDL

2.1.1 AIDL的基本概念

  • 当客户端发起请求时,当前线程会被挂起,所以如果一个远程方法是很耗时的,那么不能在UI线程中发起此远程请求;
  • 服务端的Binder方法运行在Binder线程池中,所以Binder方法不管是否耗时都应该采用同步的方法去实现
  • 所有能在Binder中传输的类都需要实现IInterface这个类
  • 内部Stub类就是Binder

2.1.2 实现AIDL的基本步骤

步骤1:创建服务端工程IPC_Server

步骤2:创建AIDL文件IMyService.AIDl

AIDL 创建
// IMyService.aidl
package com.example.ipc_server;

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

interface IMyService {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */

    List<Student> getStudent();
    void addStudent(in Student student);
}
  • AIDL支持的数据类型:基本数据类型,String和Charsequence,ArrayList,HashMap,Parcelable,AIDL
  • 注意:如果使用了Parcelable对象,需要创建和对象同名的aidl文件
  • 注意:除了基本数据类型,其他类型需要表明方向:in out inout
  • 注意:aidl接口不支持静态常量,只能写方法

步骤3:Server端实现

package com.example.ipcdemo;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;

import androidx.annotation.NonNull;

import java.util.ArrayList;
import java.util.List;

public class ServerService extends Service {

    private static final String TAG = "ServerService";

    public final static int MSG_FROM_CLIENT = 1;

    private Binder mBinder = new IBookInterface.Stub() {
        @Override
        public List<Book> getBook() throws RemoteException {
            // 具体代码
            return null;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            //具体代码
        }
    };
    public ServerService() {

    }

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


}

步骤4:Client端实现

package com.example.ipcdemo;

import androidx.appcompat.app.AppCompatActivity;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            IBookInterface iBookInterface = IBookInterface.Stub.asInterface(iBinder); //转化成aidl接口 IBookInterface.adil
            try {
                iBookInterface.addBook(new Book("大头儿子与小头爸爸", 10));
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button btn = findViewById(R.id.btn);
        final Intent intent = new Intent(this, ServerService.class);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                bindService(intent, connection, BIND_AUTO_CREATE);
            }
        });
    }
}

2.1.3 推荐阅读

3. Android中的IPC方式

3.1 使用Bundle

  • 四大组件中的ActivityServiceReceiver都支持在Intent中传递Bundle
  • Bundle继承了Parcelable接口,因此可以在不同的进程中传递数据

3.2 使用文件进行传输

  • 利用序列化反序列化操作将对象写入文件并读取
  • 但是反序列化操作只能保证内容上是一样的,本质上还是两个对象
  • 可能会产生并发读写问题

3.3 使用Messenger

  • 使用串行的方式来处理消息,大量消息时效率低
package com.example.ipcdemo;

import androidx.appcompat.app.AppCompatActivity;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    Messenger mMessenger;

    ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            mMessenger = new Messenger(iBinder);
            Message msg = new Message();
            msg.what = ServerService.MSG_FROM_CLIENT;
            Bundle bundle = new Bundle();
            bundle.putString("msg", "Hello!!!");
            msg.setData(bundle);
            try {
                mMessenger.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button btn = findViewById(R.id.btn);
        final Intent intent = new Intent(this, ServerService.class);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                bindService(intent, connection, BIND_AUTO_CREATE);
            }
        });
    }
}

package com.example.ipcdemo;

import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.util.Log;

import androidx.annotation.NonNull;

public class ServerService extends Service {

    private static final String TAG = "ServerService";

    public final static int MSG_FROM_CLIENT = 1;


    static class ServerHandler extends Handler {
        @Override
        public void handleMessage(@NonNull Message msg) {
            switch(msg.what) {
                case MSG_FROM_CLIENT:
                    Log.d(TAG, "handleMessage:  receive message from client!" + msg.getData().getString("msg"));
                    break;
            }
        }
    }

    public ServerService() {

    }

    @Override
    public IBinder onBind(Intent intent) {
        ServerHandler serverHandler = new ServerHandler();
        Messenger messenger = new Messenger(serverHandler);
        return messenger.getBinder();
    }


}

3.3 ContentProvider

  • 抽象类,需要实现onCreate(),query,update,insert,delete,getType
  • 注册
  • 继承SQLiteDatabaseHelper,操作数据库
  • 注意:query,update,insert,delete存在并发访问,因此要注意线程安全和同步
  • 注意:SqliteDatabase对象内部操作数据库有同步处理,但多个SqliteDatabase对象之间不能保证同步

3.4 Socket

实现步骤:

  1. 设置网络权限
    <INTERNET>
    <ACCESS_NETWORK_STATE>

注意:不能在主线程访问网络

  1. 在服务端Server创建ServerSocket监听8688端口
  • BufferedReader 读取客户端数据
  • PrintWriter 给客户端发送数据
  1. 在客户端Client创建Socket连接8688端口
  • BufferedReader 读取服务端数据
  • PrintWriter 给服务端发送数据
package com.example.socketdemo;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;

import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;

public class ClientActivity extends AppCompatActivity {

    private static final String TAG = "ClientActivity";

    Socket socket = null;
    PrintWriter out = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_client);
        Intent intent = new Intent(this, Server.class);
        startService(intent);
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    socket = new Socket("localhost",8688);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                while(true) {
                    try {
                        out = new PrintWriter(socket.getOutputStream());
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    Log.d(TAG, "run: sendMessage ");
                    out.println("123");
                }
            }
        }).start();

    }
}

package com.example.socketdemo;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Random;

public class Server extends Service {
    private static final String TAG = "Server";
    boolean mIsServiceDestroyed = false;

    public Server() {
    }

    @Override
    public void onCreate() {
        super.onCreate();
        new socketThread().start();

    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }
    class socketThread extends Thread {
        @Override
        public void run() {
            ServerSocket serverSocket = null;
            try {
                serverSocket = new ServerSocket(8688);
            } catch (IOException e) {
                e.printStackTrace();
            }
            if(!mIsServiceDestroyed) {
                try {
                    final Socket client = serverSocket.accept();
                    new Thread(new Runnable() {
                        @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 OutputStreamWriter(client.getOutputStream()));

        out.println("Hello");

        while(!mIsServiceDestroyed) {
            String str = in.readLine();
            Log.d(TAG, "responseClient: " +str);
            if(str == null) {
                break;
            }
            out.println("Hello " + new Random().nextInt(3));
        }

        Log.d(TAG, "Client Quit" );

        client.close();
    }

}

参考资料:
https://blog.csdn.net/ding3106/article/details/80714410

我的问题:Socket连接不上

网上的解决方法:
1.设置访问的ip为10.0.2.2

模拟器默认把127.0.0.1localhost当做本身了,在模拟器上可以用10.0.2.2代替127.0.0.1localhost,另外如果是在局域网环境可以用 192.168.0.x或者192.168.1.x(根据具体配置)连接本机,这样应该就不会报错了。

目前这个办法对我没用

2. 网络问题

可正常建立连接的:1,同处于一个校园网(使用状态信息或者wifi信息里的ip连接)2. 服务器开热点(移动网打开的情况下),客户端连状态信息里面的ip

https://blog.csdn.net/qq_37735413/article/details/80012390
https://www.cnblogs.com/1995hxt/p/4469389.html

4. Binder连接池

相关文章

网友评论

      本文标题:Android开发艺术探索(2) --- IPC的点点滴滴

      本文链接:https://www.haomeiwen.com/subject/bosenctx.html