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

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连接池

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容