关于 Android LocalSocket、LocalServerSocket

可以用于apk之间的模块互斥,比如:

try {
        LocalServerSocket loc = new LocalServerSocket("hello_world");
        Log.e("test", "myView, Socket Start");
                    
    } catch (IOException e) {
        Log.e("test", "myView," + e.toString());
    }

如果在另外一个app中已经创建了这个 "hello_world" 的 LocalServerSocket, 这里再创建会抛异常:java.io.IOException: Address already in use

一 Socket

  Socket最初用在基于TCP/IP网络间进程通信中,以客户端/服务器模式进行通信。

实现异步操作,共享资源集中处理,提高客户端响应能力。

Tcp通信基本流程:

  服务器端                                                                       客户端

  1.创建socket                                                             1.创建socket

  2.bind()                                                                         

  3.listen()

  4.accecp()

  ----等待客户端连接----                                                2.connect()

  5.读数据(recv)                                                       3.写数据(send)

  6.写数据(send)                                                      4.读数据(recv)

  7.关闭socket(closesocket())                               5.关闭socket(closesocket())

 数据流:

    

二 Android LocalSocket

LocalSocket

         在Unix域名空间创建一个套接字(非服务端)。

  是对Linux中Socket进行了封装,采用JNI方式调用,实现进程间通信。

  具体就是Native层Server和Framework层Client之间进行通信,或在各层次中能使用Client/Server模式实现通信。

LocalServerSocket

  创建服务器端Unix域套接字,与LocalSocket对应。

LocalSocketImpl

       Framework层Socket的实现,通过JNI调用系统socket API。

LocalSocketAddress

       Unix域socket的地址以及所处的空间。

JNI访问接口:\frameworks\base\core\jni\android_net_LocalSocketImpl.cpp

  socket_create

  socket_connect_local

  socket_bind_local

  socket_listen

  ……

下面看看这几个类之间的关系:

    

使用Android的LocalSocket建立socket通信,是基于网络socket过程一致的。

三 native与framework之间的通信

以install这个服务为例:

1 增加socket资源

\system\core\rootdir\init.rc中:

  service installd /system/bin/installd

          class main

          socket installd stream 600 system system

在启动install服务时,就会为install分配socket文件系统资源:dev/socket/installd

Install服务的Socket资源和名称installd绑定起来。

这些都是在开机初始化化init进程中启动service时完成:

  service_start

         create_socket

         publish_socket

2 native层

install进程 建立服务端程序

       native 层中作为server:\frameworks\base\cmds\installd\installd.c

int main(const int argc, const char *argv[]) 
{
    //获取已绑定socket
    lsocket = android_get_control_socket(SOCKET_PATH);

    //监听socket
    listen(lsocket, 5);

    for (;;) {
        //等待客户端建立连接
        s = accept(lsocket, &addr, &alen);
        for (;;) {
                 //接收数据 相当于recv
                 readx(s, buf, count);

                 //执行相关的操作
                execute(s, buf);
        }

        //关闭socket
        close(s);
    }
}

3 framework层

       客户端程序:

\frameworks\base\services\java\com\android\server\pm\Installer.java

boolean connect() 
{
       //创建socket
       mSocket = new LocalSocket();

       //设置连接地址
       LocalSocketAddress address = new             LocalSocketAddress("installd",
                    LocalSocketAddress.Namespace.RESERVED);

       //建立连接
       mSocket.connect(address);

       //获取数据输入流 可以读数据
       mIn = mSocket.getInputStream();

       //获取数据输出流 可以写数据
       mOut = mSocket.getOutputStream();
}

因此以native层service与framework建立client/server模式socket通信主要代码:

java层主要代码:

LocalSocket s =null;
    LocalSocketAddress l;

       s = new LocalSocket();
       l = new LocalSocketAddress(SOCKET_NAME,
      LocalSocketAddress.Namespace.RESERVED);
       s.connect(l);

native层主要代码:

     s_fdListen = android_get_control_socket(SOCKET_NAME);
       ret = listen(s_fdListen, n);
       s_fdCommand = accept(s_fdListen, (sockaddr *) &peeraddr, &socklen);

init.rc中加入:

       service myserver /system/bin/myserver
       class main
       socket myserver stream 600 system system
       ……

  当然建立这种client/server模式并不一定要在native层和framework层,仅在framework层也能够进行。

系统提供了LocalSocket作为客户端使用,同时提供了LocalServerSocket作为服务端使用。

Zygote服务使用了LocalServerSocket作为服务端socket通信。

  建立socket通信,也可以在代码执行过程中进行,使用LocalSocket与LocalServerSocket。

在init.rc中为服务端建立的socket资源和初始化时绑定,与在代码中使用LocalServerSocket

建立的服务端socket资源在Linux域空间不同而已,过程是一样的跟一般的socket通信过程一致。

四 LocalSocket与LocalServerSocket建立socket通信

  LocalSocket就是作为客户端建立于服务端的连接,发送数据。

  LocalServerSocket作为服务端使用,建立socket监听客户端请求。通过构造函数看到有两种方式:

在Linux抽象空间 创建一个新的服务端socket:

public LocalServerSocket(String name) throws IOException
{
        //创建socket资源
        impl = new LocalSocketImpl();
        impl.create(true);

        //绑定地址
        localAddress = new LocalSocketAddress(name);
        impl.bind(localAddress);

        //监听
        impl.listen(LISTEN_BACKLOG);
}

用文件描述符创建已经存在并且绑定的服务端socket:

  如在init.rc中指定socket资源 dev/socket/……,zygote使用此方式创建作为服务端的socket

  LocalServerSocket socket = new LocalServerSocket(createFileDescriptor(fileDesc));

public LocalServerSocket(FileDescriptor fd) throws IOException
{
        //已绑定 监听
        impl = new LocalSocketImpl(fd);
        impl.listen(LISTEN_BACKLOG);
        localAddress = impl.getSockAddress();
}

通常使用过程中:

客户端代码:

String message;

//创建socket
LocalSocket sender = new LocalSocket();

//建立对应地址连接
sender.connect(new LocalSocketAddress(SOCKET_ADDRESS));

//发送写入数据
sender.getOutputStream().write(message.getBytes());

//关闭socket
sender.getOutputStream().close();

服务端:

//创建socket并绑定监听 新创建的
LocalServerSocket server = new LocalServerSocket(SOCKET_ADDRESS);
while (true) {
  //等待建立连接
  LocalSocket receiver = server.accept();

  //接收获取数据流
  InputStream input = receiver.getInputStream();    ……
}

package com.skydroid.h16 import android.content.Context import android.content.Intent import android.net.LocalSocket import android.net.LocalSocketAddress import android.os.Bundle import android.util.Log import android.view.View import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import com.skydroid.h16.utils.String2ByteArrayUtils import com.skydroid.h16.R import com.zhouyou.view.seekbar.SignSeekBar import java.io.IOException import java.nio.ByteBuffer /** * 获取摇杆舵量示例 */ class RockerActivity: AppCompatActivity() { protected var PACKAGE_HEADER_BUF = "SKYDROID:".toByteArray() private var localSocket: LocalSocket? = null private var mReadBuffer = ByteBuffer.allocate(4096) private var mReadThread : ReadThread? = null private var mSeekBarList = ArrayList<SignSeekBar?>() private var mTVList = ArrayList<TextView?>() @Volatile var isConnect:Boolean = false override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_rocker) initView() openConnect() } companion object{ fun start(context: Context){ context.startActivity( Intent(context, RockerActivity::class.java) ) } } private fun initView(){ var items = intArrayOf( R.id.item1, R.id.item2, R.id.item3, R.id.item4, R.id.item5, R.id.item6, R.id.item7, R.id.item8, R.id.item9, R.id.item10, R.id.item11, R.id.item12, R.id.item13, R.id.item14, R.id.item15, R.id.item16 ) for (i in items){ var item = findViewById<View>(i) var searchBar = item.findViewById<SignSeekBar>(R.id.search_bar) var tv = item.findViewById<TextView>(R.id.tv)
03-08
### Android Rocker Activity 实现细节 #### 使用 LocalSocket 和 SeekBar 的实现方式 在 Android 应用开发中,LocalSocket 提供了一种进程间通信的方式,而 SeekBar 则用于提供用户交互接口来调整数值。为了展示如何通过这些组件构建一个摇杆控制功能,在 `AndroidRockerActivity` 中可以采用如下方法: 1. **创建并配置 LocalServerSocket** 需要在应用程序启动时初始化服务器端套接字监听连接请求。这通常是在服务类里完成的,以便长时间运行而不受活动生命周期的影响。 2. **建立客户端 Socket 连接到本地服务器** 当用户打开含有摇杆控件(SeekBar)的界面时,应该尝试去连接到之前设置好的 LocalServerSocket 上面。如果成功,则可以通过此通道发送数据;否则提示错误信息给用户[^1]。 3. **处理来自 UI 组件的数据变化事件** 对于 SeekBar 来说,当其进度改变时会触发 OnSeekBarChangeListener 接口中的回调函数 onProgressChanged() 。在这个地方获取当前滑动条的位置百分比作为要传递给其他模块的信息之一。 4. **向远程设备传输指令** 将由 SeekBar 收集到的方向/速度参数打包成字符串形式或者其他自定义协议格式之后再经由已经建立起来的 socket 发送出去。接收方解析收到的消息后执行相应的动作,比如移动虚拟对象或者调节音量大小等操作。 5. **关闭资源释放连接** 不论何时结束使用这套通讯机制——无论是因为正常退出程序还是异常情况发生,都应当记得调用 close 方法断开所有已开启的对象以防止内存泄漏等问题出现。 ```java // Java 示例代码片段 public class AndroidRockerActivity extends AppCompatActivity { private LocalSocket client; private LocalSocketAddress address; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_android_rocker); final SeekBar seekBar = findViewById(R.id.seek_bar); // 设置 SeekBar 变化监听器 seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { String message = "Direction:" + (progress - 50); // 假设中间位置代表静止状态 try { OutputStream outputStream = client.getOutputStream(); PrintWriter writer = new PrintWriter(outputStream); writer.println(message); writer.flush(); } catch (IOException e) { Log.e("AndroidRocker", "Failed to send data via LocalSocket"); } } @Override public void onStartTrackingTouch(SeekBar seekBar) {} @Override public void onStopTrackingTouch(SeekBar seekBar) {} }); connectToLocalServer(); // 初始化与 LocalServerSocket 的连接 } private void connectToLocalServer(){ Thread thread = new Thread(() -> { try{ client = new LocalSocket(); address = new LocalSocketAddress("rocker_service"); // 名称为 'rocker_service'的服务 client.connect(address); InputStream inputStream = client.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); while(true){ String line = reader.readLine(); if(line != null && !line.isEmpty()){ handleReceivedData(line); // 处理从 server 端传来的消息 } } }catch(IOException ex){ Log.e("AndroidRocker","Error connecting or communicating with the service."); } }); thread.start(); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值