Android跨进程传图片或者大数据(解决TransactionTooLargeException)

news2024/9/21 14:28:32

跨进程传图片方案

  1. 直接intent传bitmap
  2. 使用文件读写
  3. intent传递自定义binder,binder中传递image
  4. 使用网络传输
一、直接intent传bitmap

优势

使用简单


劣势

相关代码可能有侵入性,必须在四大组件中接收。

  1. intent传递数据的总大小是1MB,其中还包括启动四大组件相关的信息。因此使用intent传递的图片不宜超过500KB,甚至应该更小,因为还可能会传递其他数据。
  2. 如果通过此方案传递大图片,必须先压缩后传输。开发者需要自己评估业务场景是否适用,毕竟很多场景不适合让图片质量下降。

如果intent传递的数据超过1MB时,就会报错TransactionTooLargeException。

二、使用文件读写

优势

  1. 使用相对简单
  2. 一定程度上可以避免逻辑耦合的问题,对于单独的模块来说只需要负责“读”或者“写”。

劣势

  1. 需要自己控制读写的时机。
  2. 读写操作相比直接传递效率更低,耗时更长。
三、intent传递自定义binder,binder中传递image

优势

  1. 效率相对最高
  2. 传递图片没有大小限制

劣势

  1. 使用相对麻烦,需要自定义aidl
  2. 相关代码可能有侵入性,必须在四大组件中接收。
四、使用网络传输

这个方案比较特殊,只有特殊场景才会使用。

一般存在两种情况:

  1. 两个进程都与服务端通信,一个进程传输,一个进程接收。如果是图片上传和下载的场景可以使用,但是效率肯定没有直接传输高。
  2. 两个进程一个作为服务端,一个作为客户端。 这个方案的关键在于这个“作为服务端的进程”,需要这个进程本身就是某种图片服务的提供者,且通过网络来对其他进程或模块提供服务度。


intent通过binder传递bitmap的Demo
有兴趣的读者可以自行看下Demo:

github地址
https://github.com/Double2hao/ProcessImageTest

intent通过binder传递bitmap的原理

bitmap在native层传递的时候会有两种方案:

1. 直接将图片写入进程的缓冲区。

缓冲区是进程在初始化的时候就已经申请了的,并且大小是一定的。因此如果写入的大小超过了缓冲区的大小,就会报错。

2. 使用共享内存,将共享内存的fd,也就是文件描述符写入缓冲区。

这样的好处就是传递图片的大小不会受限制。

intent直接传递bitmap对应方案1,intent通过binder传递bitmap对应方案2。

为什么intent传递bitmap不默认使用共享内存?

个人理解,缓冲区的大小是进程创建的时候就申请好的,如果能保证不超出缓冲区大小的情况下使用缓冲区,不需要再另外申请共享内存肯定是最好的。

如果默认就使用共享内存,而缓冲区资源又没人用的话,就造成了资源浪费。

因此如果开发者自己认为需要传递大文件的话,就使用共享内存,默认不使用。
 


Android 基于共享内存跨进程实时传输大量图片或数据

aidl传输文件有大小1M限制,单次传输不适合传递大数据,可以使用aidl传递共享内存引用ParcelFileDescriptor方式传递图片信息,具体实现如下。

一、service端

1.1 aidl文件IIpcService.aidl 定义,这里主要用到pfd参数
interface IIpcService {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
// void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
// double aDouble, String aString);
void register2Server(String packageName,IIpcServiceListener ipcServiceListener);
void unregister2Server(String packageName);
String processClientRequest(String packageName,String clientRequest,inout ParcelFileDescriptor pfd);
}
1.2 service端 处理客户端传递的图片 流 引用ParcelFileDescriptor ,将获取的ParcelFileDescriptor转换成Bitmap 并回调给ui层显示
    public String processClientRequest(String packageName, String clientRequest, ParcelFileDescriptor pfd) {

        Log.i(TAG, "processClientRequest 11 packageName:" + packageName
                + " clientRequest:" + clientRequest + " pfd:" + pfd);
        String ret = clientRequest;
        FileDescriptor fileDescriptor = pfd.getFileDescriptor();
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(fileDescriptor);
            Bitmap rawBitmap = BitmapFactory.decodeStream(fis);
            ret += " process success!";
            Log.i(TAG, "processClientRequest 112 rawBitmap:" + rawBitmap + " mUiShow:" + mUiShow);
            if (null != mUiShow) {
                mUiShow.showBitmap(rawBitmap);
            }
        } catch (Exception e) {
            Log.i(TAG, "processClientRequest 22 error:" + e);
            e.printStackTrace();
        } finally {
            try {
                if (fis != null) {
                    fis.close();
                }
            } catch (IOException e) {
                Log.i(TAG, "processClientRequest 33 error:" + e);
            }
        }

        Log.i(TAG, "processClientRequest 22 end ret:" + ret);
        return ret;
    }
1.3 也可以处理客户端传递的字节数组 数据引用,处理代码如下
    public String processClientRequest(String packageName, String clientRequest, ParcelFileDescriptor pfd) {

        Log.i(TAG, "processClientRequest 11 packageName:" + packageName
                + " clientRequest:" + clientRequest + " pfd:" + pfd);
        String ret = clientRequest;
        FileDescriptor fileDescriptor = pfd.getFileDescriptor();
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(fileDescriptor);

            byte[] content = new byte[5];
            fis.read(content);
            Log.i(TAG, "processClientRequest 111 content:" + content);
            for (int i = 0; i < content.length; i++) {
                Log.i(TAG, "processClientRequest 113 content[" + i + "]=" + content[i]);
            }

        }
    } catch(
    Exception e)

    {
        Log.i(TAG, "processClientRequest 33 error:" + e);
        e.printStackTrace();
    } finally

    {
        try {
            if (fis != null) {
                fis.close();
            }
        } catch (IOException e) {
            Log.i(TAG, "processClientRequest 44 error:" + e);
        }
    }

Log.i(TAG,"processClientRequest 55 end ret:"+ret);
return ret;
}

二客户端

2.1 客户端连接到service后,调用接口 传递图片文件引用 ParcelFileDescriptor
    String path = "/sdcard/lilei/20230207161749238.jpg";
    public ParcelFileDescriptor getPfd() {
        ParcelFileDescriptor pfd = null;
        try {
            pfd = ParcelFileDescriptor.open(new File(path), MODE_READ_WRITE);
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        }
        Log.i(TAG, "getPfd() pfd:" + pfd);
        return pfd;
    }

    public String sendFile(String requestJson) {
        Log.i(TAG, "sendFile() requestJson:" + requestJson);
        if (null != mFtIpcManager) {
            return mFtIpcManager.processClientRequest(requestJson, getPfd());
        }
        return "error";
    }
2.2 客户端也可以传递 byte数组
    public ParcelFileDescriptor getTextPfd() {
        ParcelFileDescriptor pfd = null;
        try {
            MemoryFile memoryFile = new MemoryFile("test", 1024);
            Method method = MemoryFile.class.getDeclaredMethod("getFileDescriptor");
            FileDescriptor des = (FileDescriptor) method.invoke(memoryFile);
            pfd = ParcelFileDescriptor.dup(des);
//向内存中写入字节数组
            memoryFile.getOutputStream().write(new byte[]{1,2,5,4,3});
//关闭流
            memoryFile.getOutputStream().close();
            memoryFile.close();

        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);a
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        Log.i(TAG, "getTextPfd() pfd:" + pfd);
        return pfd;
    }
    public String sendFile(String requestJson) {
        Log.i(TAG, "sendFile() requestJson:" + requestJson);
        if (null != mFtIpcManager) {
            return mFtIpcManager.processClientRequest(requestJson, getTextPfd());
        }
        return "error";
    }
2.3 客户端也可以传递Bitmap数据,需要先将Bitmap转换成 byte数组,service端接收同1.2

public class test {
    public ParcelFileDescriptor getBitmapPfd() {
        ParcelFileDescriptor pfd = null;

        Bitmap bitmap= BitmapFactory.decodeResource(FtClientApp.getAppContext().getResources(), R.drawable.btn_send);
//将Bitmap转成字节数组
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
        byte[] byteArray = stream.toByteArray();

        try {
            MemoryFile memoryFile = new MemoryFile("test", bitmap.getByteCount());
            Method method = MemoryFile.class.getDeclaredMethod("getFileDescriptor");
            FileDescriptor des = (FileDescriptor) method.invoke(memoryFile);
            pfd = ParcelFileDescriptor.dup(des);
//向内存中写入字节数组
            memoryFile.getOutputStream().write(byteArray);
//关闭流
            memoryFile.getOutputStream().close();
            memoryFile.close();

        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        Log.i(TAG, "getPfd() pfd:" + pfd);
        return pfd;
    }
    public String sendFile(String requestJson) {
        Log.i(TAG, "sendFile() requestJson:" + requestJson);
        if (null != mFtIpcManager) {
            return mFtIpcManager.processClientRequest(requestJson, getBitmapPfd());
        }
        return "error";
    }

PS:这里也可以共享内存传递大字符串,只是需要将字符串和字节数组转换一下再传递,转换实现如下。
1.string 字符串转换成 byte[] 数组
String str = "reagan";
byte[] srtbyte = str.getBytes();

2.byte[] 数组转换成 string字符串
String res = new String(srtbyte);
或者
String res = new String(srtbyte,"UTF-8");
System.out.println(res);


Android 跨进程传递大图片

跨进程传大图,有哪些方案?

通过IPC的方式转发图片数据。

  • Binder:性能很好,方便使用,但是有大小限制
  • Socket,管道:存在多次copy问题,性能差,也有大小限制
  • 共享内存:性能好

主要看两个指标

1. 性能,减少copy次数

2. 内存泄露,资源及时关闭

跨进程通信是需要buffer的,发送数据需要buffer,返回数据也需要buffer,buffer只有整个transaction结束时才释放,发送数据占用太多buffer的话,留给返回数据的buffer就很少了。事情buffer是吧就会跑TransactionTooLargeException

进程在启动binder机制时会映射一块内存,大小是1M,也就是说跨进程通信时申请缓冲区大小不大于1M,一个事务用太多的话,其他事务可用空间就变少。甚至事情100K都会跑TransactiionToolargeException。

第三条是官方推荐

binder_alloc_buffer: 分配data_size(parcel)大小的内存空间

Bitmap 是如何传输的

上面代码块,如果使用那个intent启动另外一个进程的Activity,会抛出TransactionTooLargeException, 是因为这个bitmap直接copy到缓冲区了,没有里有ashmem机制,因为allowFd机制没有打开

下面代码块不会抛出TransactionTooLargeException

bitmap超过16K时,使用的是匿名共享内存的方式

setAllowFds(false): 禁用了bundle的fd机制,bundle写入parcel时也会禁用parcel的allowFd机制

这两个底层都用到了共享内存 ,  适合跨进程大数据传输


Android 共享内存实现跨进程大文件传输(设计思路和Demo实现绕过Binder传输限制) 

项目链接  AndroidSharedMemoryDemo

下图是文件详情:13.7M

项目在客户端最终的显示效果:

本人建议可以下载下来直接查看就可以,对照着代码查看.

项目整体分为三个 部分

1.客户端clientapp:负责调用SDK测试

2.SDKjar包:mylibrary:扶着整体的共享内存的开辟以及读取操作.

3.服务端serverapp:当客户端请求数据时,往共享内存里面写数据.

本文不再对如何提供SDK给第三方项目使用的进行讲解,只针对部代码进行详解,如果想看项目的详解可以查看 Android 应用提供SDK Jar包给第三方使用 (设计思路 以及实现步骤) 和本项目的架构类似。

本项目的整体调用时序图如下:

本项目的类关系图:

MemoryFile简介:

MemoryFile是android在最开始就引入的一套框架,其内部实际上是封装了android特有的内存共享机制Ashmem匿名共享内存,简单来说,Ashmem在Android内核中是被注册成一个特殊的字符设备,Ashmem驱动通过在内核的一个自定义slab缓冲区中初始化一段内存区域,然后通过mmap把申请的内存映射到用户的进程空间中(通过tmpfs),这样子就可以在用户进程中使用这里申请的内存了,另外,Ashmem的一个特性就是可以在系统内存不足的时候,回收掉被标记为"unpin"的内存,这个后面会讲到,另外,MemoryFile也可以通过Binder跨进程调用来让两个进程共享一段内存区域。由于整个申请内存的过程并不再Java层上,可以很明显的看出使用MemoryFile申请的内存实际上是并不会占用Java堆内存的。

MemoryFile.java位置在如下,有兴趣的同学可以翻阅源码看一看

frameworks/base/core/java/android/os/MemoryFile.java

mylibrary简介:
本项目中 mylibrary负责整体的内存开辟以及读操作

MemoryFileHelper.java是开辟空间的具体操作类,具体拿到MemoryFIle用的是反射方法,核心方法如下:

    public static MemoryFile openMemoryFile(FileDescriptor fd, int length, int mode) {
        MemoryFile memoryFile = null;
        try {
            memoryFile = new MemoryFile("tem", 1);
            memoryFile.close();
            if (!Utils.isMoreThanAPI27()) {
                Class<?> c = MemoryFile.class;
                Method native_mmap = null;
                Method[] ms = c.getDeclaredMethods();
                for (int i = 0; ms != null && i < ms.length; i++) {
                    if (ms[i].getName().equals("native_mmap")) {
                        native_mmap = ms[i];
                    }
                }
                ReflectUtils.setField("android.os.MemoryFile", memoryFile, "mFD", fd);
                ReflectUtils.setField("android.os.MemoryFile", memoryFile, "mLength", length);
                if (Utils.isMoreThanAPI21()) {
                    long address = (long) ReflectUtils.invokeMethod(null, native_mmap, fd, length, mode);
                    ReflectUtils.setField("android.os.MemoryFile", memoryFile, "mAddress", address);
                } else {
                    int address = (int) ReflectUtils.invokeMethod(null, native_mmap, fd, length, mode);
                    ReflectUtils.setField("android.os.MemoryFile", memoryFile, "mAddress", address);
                }
            } else {
                SharedMemory sharedMemory = SharedMemory.create("tem", 1);
                sharedMemory.close();
                ReflectUtils.setField("android.os.SharedMemory", sharedMemory, "mFileDescriptor", fd);
                ReflectUtils.setField("android.os.SharedMemory", sharedMemory, "mSize", length);
                ReflectUtils.setField("android.os.MemoryFile", memoryFile, "mSharedMemory", sharedMemory);
                ReflectUtils.setField("android.os.MemoryFile", memoryFile, "mMapping", sharedMemory.mapReadWrite());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return memoryFile;
    }

MyControllerImp.java负责开辟共享内存和负责通过Aidl和服务端交互的核心业务类.最核心的方法在链接建立之后,将自己创建的ParcelFileDescriptor对象传递给server这样保证了serverapp拿到的MemoryFile对象是同一个对象

@Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        Log.d("mysdk", " sdk  onServiceConnected  ");
        if (service == null) {
            if (mMyRemoteCtrl != null) {
                try {
                    mMyRemoteCtrl.unlinkToDeath(mFrameDataCallBack.asBinder());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
            mMyRemoteCtrl = null;
        } else {
            mMyRemoteCtrl = IMyRemoteCtrl.Stub.asInterface(service);
            if (mMyRemoteCtrl != null) {
                try {
                    mMyRemoteCtrl.linkToDeath(mFrameDataCallBack.asBinder());
                    Log.d("mysdk", " sdk  onServiceConnected  setBackBufferCallBack ");
                    if (mCallBack != null) {
                        mMyRemoteCtrl.setParcelFileDescriptor(mMemoryFile.getParcelFileDescriptor());
                        mMyRemoteCtrl.registerFrameByteCallBack(mFrameDataCallBack);
                        mMemoryFile.setReadBufferCallBack(mCallBack);
                    } else {
                        mMyRemoteCtrl.unregisterFrameByteCallBack(mFrameDataCallBack);
                        mMemoryFile.release();
                    }
                    Log.d("mysdk", " sdk  onServiceConnected  setBackBufferCallBack  eld ");
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
    }

而客户端注册的IReadBufferCallBack.java的对象也被MyControllerImp.java 设置到了MemoryFileImp.java中当,也就是说MemoryFileImp.java持有客户端注册的数据回调对象

mMemoryFile.setReadBufferCallBack(mCallBack);

serverapp简介:


服务端最核心的类ServerClientService.java中的内部类MyRemoteCtrlImpl.java负责和mylibrary 中的MyControllerImp.java通讯,用于接收传递过来的远端ParcelFileDescriptor对象和callBack.最核心的代码如下,因为没有持续的流可以写,就自己准备了一张在草原天路拍色的图片放在服务端的assets文件夹下 13M 绝对超出了Binder限制.

public class MyRemoteCtrlImpl extends IMyRemoteCtrl.Stub {
...........省略代码.......
        @Override
        public void readFile(String msg) throws RemoteException {
            Log.d("mysdk"," mParcelFileDescriptor  = null ? " + (mParcelFileDescriptor == null));
            if (mParcelFileDescriptor != null) {
                memoryFile = MemoryFileHelper.openMemoryFile(mParcelFileDescriptor, MEMORY_SIZE, 0x3);
            }
            Log.d("mysdk"," memoryFile  = null ? " + (memoryFile == null));
            try {
                InputStream open = getResources().getAssets().open("IMG.JPG");
                byte[] buffer = new byte[open.available()];
                Log.d("mysdk"," 服务端 buffer " + buffer.length );
                open.read(buffer);
                readImage(buffer);
                open.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
//写共享内存方法
    private void readImage(byte[] frame) {
        if (memoryFile != null) {
            try {
                memoryFile.readBytes(isCanRead, 0, 0, 1);
                if (isCanRead[0]== 0) {
                    memoryFile.writeBytes(frame, 0, 1, frame.length);
                    isCanRead[0] = 1;
                    memoryFile.writeBytes(isCanRead, 0, 0, 1);
                }
                Log.d("mysdk"," 服务端 canReadFrameData " );
                mIReadDataCallBack.canReadFileData();
            } catch (Exception e ) {
                Log.d("mysdk"," 服务端 Exception  "  + e.getMessage()  );
                e.printStackTrace();
            }
        }
    }

clientapp简介

集成mylibrary的jar包 不知道如何打jar包的可以看 Android 应用提供SDK Jar包给第三方使用 (设计思路 以及实现步骤) 

核心代码就是读取数据进行显示MainActivity.java中

public class MainActivity extends AppCompatActivity {
    ImageView iv;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
         iv = findViewById(R.id.iv);
        SharedMemoryLibSDK.getInstance().init(this);
        SharedMemoryLibSDK.getInstance().setBackBufferCallBack(new IReadBufferCallBack() {
            @Override
            public void onReadBuffer(final byte[] bytes, int i) {
                Log.d("mysdk"," 客户端 读取到客户写到共享内存的大小为: " + bytes.length);
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Bitmap bitmap = byteToBitmap(bytes);
                        iv.setImageBitmap(bitmap);
                    }
                });
            }
        });
    }
    public static Bitmap byteToBitmap(byte[] imgByte) {  
         InputStream input = null;  
         Bitmap bitmap = null;  
         BitmapFactory.Options options = new BitmapFactory.Options();  
         options.inSampleSize = 8;  
         input = new ByteArrayInputStream(imgByte);
         SoftReference softRef = new SoftReference(BitmapFactory.decodeStream(  
                                  input, null, options));  
         bitmap = (Bitmap) softRef.get();  
         if (imgByte != null) {  
             imgByte = null;  
         }  
         try {
             if (input != null) {  
                  input.close();  
             }  
         } catch (IOException e) {
             // TODO Auto-generated catch block  
             e.printStackTrace();  
         }  
         return bitmap;  
    }
 
    public void  readFIle(View view) {
        Log.d("mysdk"," 客户端  调用服务端的 readFIle  " );
        SharedMemoryLibSDK.getInstance().readFile("我是客户端");
    }
}


点击按钮的最后效果:因为数据太大在用byte生成BitMap的时候容易内存溢出,在客户端读取完成数据之后对生成的BitMap使用了中压缩了处理.

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1238200.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis(哨兵模式)

哨兵模式的定义&#xff1a; 是Redis的一种高可用解决方案&#xff0c;通过运行多个Redis实例来监控主从Redis实例的状态&#xff0c;当主实例出现故障时&#xff0c;哨兵会自动选举一个从实例作为新的主实例&#xff0c;从而保证系统的高可用性。哨兵模式可以监控多个主从Red…

感恩三十载 再创新辉煌——中国音乐著作权协会成立30周年暨著作权集体管理制度实施30周年纪念大会在京召开

感恩三十载 再创新辉煌 2023年11月19日&#xff0c;中国音乐著作权协会成立30周年暨著作权集体管理制度实施30周年纪念大会在北京举行。中宣部副部长张建春&#xff0c;国际作者和作曲者协会联合会&#xff08;CISAC&#xff09;总干事甘迪奥龙&#xff08;Gadi Oron&#xff0…

【追求卓越02】数据结构--链表

引导 今天我们进入链表的学习&#xff0c;我相信大家对链表都很熟悉。链表和数组一样&#xff0c;作为最基础的数据结构。在我们的工作中常常会使用到。但是我们真的了解到数组和链表的区别吗&#xff1f;什么时候使用数组&#xff0c;什么时候使用链表&#xff0c;能够正确的选…

WPF实战项目十五(客户端):RestSharp的使用

1、在WPF项目中添加Nuget包&#xff0c;搜索RestSharp安装 2、新建Service文件夹&#xff0c;新建基础通用请求类BaseRequest.cs public class BaseRequest{public Method Method { get; set; }public string Route { get; set; }public string ContenType { get; set; } &quo…

sd-webui-controlnet代码分析

controlnet前向代码解析_Kun Li的博客-CSDN博客文章浏览阅读1.5k次。要分析下controlnet的yaml文件&#xff0c;在params中分成了4个部分&#xff0c;分别是control_stage_config、unnet_config、first_stage_config、cond_stage_config。其中control_stage_config对应的是13层…

Leo赠书活动-10期 【AIGC重塑教育 AI大模型驱动的教育变革与实践】文末送书

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; 赠书活动专栏 ✨特色专栏&#xff1a;…

好题分享(2023.11.12——2023.11.18)

目录 ​ 前情回顾&#xff1a; 前言&#xff1a; 题目一&#xff1a;《有效括号》 思路&#xff1a; 总结&#xff1a; 题目二&#xff1a;《用队列实现栈》 思路&#xff1a; 总结&#xff1a; 题目三&#xff1a;《用栈实现队列》 思路&#xff1a; 总结 &#x…

验证码 | 可视化一键管控各场景下的风险数据

目录 查看今日验证数据 查看未来趋势数据 验证码作为人机交互界面经常出现的关键要素&#xff0c;是身份核验、防范风险、数据反爬的重要组成部分&#xff0c;广泛应用网站、App上&#xff0c;在注册、登录、交易、交互等各类场景中发挥着巨大作用&#xff0c;具有真人识别、身…

C#winfrom端屏幕截图功能的简单实现(修改了屏幕的缩放比例后,截图功能异常,慎用!!!)

文章目录 1 主要文件1.1 FrmScreenShot.cs1.2 FrmScreenShot.Designer.cs1.1 Utility.cs 在发现有一款播放软件禁止截图功能后&#xff0c;使用了其他的截图工具发现都会被播放软件禁用掉截图功能&#xff0c;想了下试着自己做一个截图工具&#xff0c;也可以方便将截图工具添加…

【追求卓越01】数据结构--数组

引导 这一章节开始&#xff0c;正式进入数据结构与算法的学习过程中。由简到难&#xff0c;先开始学习最基础的数据结构--数组。 我相信对于数组&#xff0c;大家肯定是不陌生&#xff0c;因为数组在大多数的语言中都有&#xff0c;也是大家在编程中常常会接触到的。我不会说数…

文心大模型商业化领跑,百度在自我颠覆中重构生长力

随着科技巨头竞逐AI大模型&#xff0c;人工智能技术成为今年最受瞩目的新技术。但是&#xff0c;AI大模型的创新之路&#xff0c;还缺少一个足够有力的商业化答案。 作为全球最先发布大模型的互联网大厂&#xff0c;百度能否加速大模型的应用落地&#xff0c;以及文心大模型能…

【23真题】劝退211!今年突变3门课!

今天分享的是23年云南大学847&#xff08;原827&#xff09;的考研试题及解析。同时考SSDSP的院校做一个少一个&#xff0c;珍惜&#xff01;同时考三门课的院校&#xff0c;复习压力极大&#xff0c;但是也会帮大家劝退很多人&#xff0c;有利有弊&#xff0c;请自行分析~ 本…

微信小程序开发者工具] ? Enable IDE Service (y/N) ESC[27DESC[27C

在HBuilder运行微信小程序开发者工具报错 如何解决 打开微信小程序开发者工具打开设置--->安全设置--->服务器端口选择打开就可以啦

短时傅里叶变换函数编写

文章目录 傅里叶变换与短时傅里叶变换什么是窗&#xff1f;自己对手实现短时傅里叶变换 傅里叶变换与短时傅里叶变换 在了解短时傅里叶变换之前&#xff0c;首先要知道是什么是傅里叶变换&#xff08; fourier transformation&#xff0c;FT&#xff09;&#xff0c;傅里叶变换…

从 PUGC 到 SGC,普通店员也能用 AI 运营「粉丝群」

同一种文案风格反复使用&#xff0c;商品展示图也单调雷同&#xff0c;要直播时就直接「扔」个链接&#xff0c;社群、朋友圈这些品牌的私域重地有时极易被忽视&#xff0c;而变得千篇一律、简单粗暴。 但是&#xff0c;以内容驱动业务增长&#xff0c;已经成为越来越多品牌在做…

【EI会议征稿】第五届人工智能、网络与信息技术国际学术会议(AINIT 2024)

第五届人工智能、网络与信息技术国际学术会议&#xff08;AINIT 2024&#xff09; 2024 5th International Seminar on Artificial Intelligence, Networking and Information Technology 第五届人工智能、网络与信息技术国际学术会议&#xff08;AINIT 2024&#xff09;将于…

浅析教学型数控车床使用案例

教学型数控车床是一种专为教学和培训设计的机床&#xff0c;它具有小型化、高精度和灵活性的特点&#xff0c;可以作为学校和技术学院的培训机器。下面是一个使用案例&#xff0c;以展示教学型数控车床在教学实训中的应用。 案例背景&#xff1a; 某职业技术学院的机械工程专业…

Java计算时间差,距结束还有几天几小时几分钟

文章目录 1、写法2、备份3、LocalDate、LocalDateTime、Date、String互转 1、写法 //静态方法&#xff0c;传入年月日时分秒 LocalDateTime startTime LocalDateTime.of(2023, 11, 22, 15, 09, 59); LocalDateTime endTime LocalDateTime.of(2023, 11, 30, 0, 0, 0); //计算…

2023.11.22 -数据仓库

目录 https://blog.csdn.net/m0_49956154/article/details/134320307?spm1001.2014.3001.5501 1经典传统数仓架构 2离线大数据数仓架构 3数据仓库三层 数据运营层,源数据层&#xff08;ODS&#xff09;&#xff08;Operational Data Store&#xff09; 数据仓库层&#…

想打造私域流量帝国?先解决这4个难题!

一、谁是你的目标用户 1. 清晰界定目标用户&#xff1a;确定你的产品或服务主要面向的用户群体&#xff0c;如年龄段、性别、职业等特征。 2. 确定最有购买力的用户群体&#xff1a;分析哪个用户群体在购买你的产品或服务时更容易乐于支付&#xff0c;并将其作为重点关注对象。…