Android 新API 之 MediaCodec使用笔记 一

2024-02-05 10:18

本文主要是介绍Android 新API 之 MediaCodec使用笔记 一,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

http://blog.csdn.net/shawnkong/article/details/16337381


Android的视频编解码一直让人有点纠结,SDK竟然不提供硬件编解码的API,如果你想自己做,只能通过JNI借助第三方编解码器,其都是使用的软解码,效率很难保证,这对想做视频通话的是一个不小的打击。

好了,说到google 新提供的SDK中出现的类MediaCodec,这个api限制在API 16后,也就是Android 4.1.2后才可以使用,如果你的系统低于这个版本,是不可以使用这个类的。MediaCodec这家伙能提供给你硬件编解码功能,当然得厂商支持在下层已经,如果厂商没做好,系统会提供给你软件编解码器,反正不用你操心,可以直接就拿来用的。

使用网上有人提供的Demo,可以解码mp4文件,具体情况还没研究,先放出Github链接,直接自己抓出来就可以用,

如果懒得去抓,下面贴出代码,就一个简单的Activity,一切搞定,

“记得修改文件名,SAMPLE变量为你自己的文件名称”,

[java]  view plain copy
在CODE上查看代码片 派生到我的代码片
  1. package io.vec.demo.mediacodec;  
  2.   
  3. import java.nio.ByteBuffer;  
  4.   
  5. import android.app.Activity;  
  6. import android.media.MediaCodec;  
  7. import android.media.MediaCodec.BufferInfo;  
  8. import android.media.MediaExtractor;  
  9. import android.media.MediaFormat;  
  10. import android.os.Bundle;  
  11. import android.os.Environment;  
  12. import android.util.Log;  
  13. import android.view.Surface;  
  14. import android.view.SurfaceHolder;  
  15. import android.view.SurfaceView;  
  16.   
  17. public class DecodeActivity extends Activity implements SurfaceHolder.Callback {  
  18.     private static final String SAMPLE = Environment.getExternalStorageDirectory() + "/video.mp4";  
  19.     private PlayerThread mPlayer = null;  
  20.   
  21.     @Override  
  22.     protected void onCreate(Bundle savedInstanceState) {  
  23.         super.onCreate(savedInstanceState);  
  24.         SurfaceView sv = new SurfaceView(this);  
  25.         sv.getHolder().addCallback(this);  
  26.         setContentView(sv);  
  27.     }  
  28.   
  29.     protected void onDestroy() {  
  30.         super.onDestroy();  
  31.     }  
  32.   
  33.     @Override  
  34.     public void surfaceCreated(SurfaceHolder holder) {  
  35.     }  
  36.   
  37.     @Override  
  38.     public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {  
  39.         if (mPlayer == null) {  
  40.             mPlayer = new PlayerThread(holder.getSurface());  
  41.             mPlayer.start();  
  42.         }  
  43.     }  
  44.   
  45.     @Override  
  46.     public void surfaceDestroyed(SurfaceHolder holder) {  
  47.         if (mPlayer != null) {  
  48.             mPlayer.interrupt();  
  49.         }  
  50.     }  
  51.   
  52.     private class PlayerThread extends Thread {  
  53.         private MediaExtractor extractor;  
  54.         private MediaCodec decoder;  
  55.         private Surface surface;  
  56.   
  57.         public PlayerThread(Surface surface) {  
  58.             this.surface = surface;  
  59.         }  
  60.   
  61.         @Override  
  62.         public void run() {  
  63.             extractor = new MediaExtractor();  
  64.             extractor.setDataSource(SAMPLE);  
  65.   
  66.             for (int i = 0; i < extractor.getTrackCount(); i++) {  
  67.                 MediaFormat format = extractor.getTrackFormat(i);  
  68.                 String mime = format.getString(MediaFormat.KEY_MIME);  
  69.                 if (mime.startsWith("video/")) {  
  70.                     extractor.selectTrack(i);  
  71.                     decoder = MediaCodec.createDecoderByType(mime);  
  72.                     decoder.configure(format, surface, null0);  
  73.                     break;  
  74.                 }  
  75.             }  
  76.   
  77.             if (decoder == null) {  
  78.                 Log.e("DecodeActivity""Can't find video info!");  
  79.                 return;  
  80.             }  
  81.   
  82.             decoder.start();  
  83.   
  84.             ByteBuffer[] inputBuffers = decoder.getInputBuffers();  
  85.             ByteBuffer[] outputBuffers = decoder.getOutputBuffers();  
  86.             BufferInfo info = new BufferInfo();  
  87.             boolean isEOS = false;  
  88.             long startMs = System.currentTimeMillis();  
  89.   
  90.             while (!Thread.interrupted()) {  
  91.                 if (!isEOS) {  
  92.                     int inIndex = decoder.dequeueInputBuffer(10000);  
  93.                     if (inIndex >= 0) {  
  94.                         ByteBuffer buffer = inputBuffers[inIndex];  
  95.                         int sampleSize = extractor.readSampleData(buffer, 0);  
  96.                         if (sampleSize < 0) {  
  97.                             // We shouldn't stop the playback at this point, just pass the EOS  
  98.                             // flag to decoder, we will get it again from the  
  99.                             // dequeueOutputBuffer  
  100.                             Log.d("DecodeActivity""InputBuffer BUFFER_FLAG_END_OF_STREAM");  
  101.                             decoder.queueInputBuffer(inIndex, 000, MediaCodec.BUFFER_FLAG_END_OF_STREAM);  
  102.                             isEOS = true;  
  103.                         } else {  
  104.                             decoder.queueInputBuffer(inIndex, 0, sampleSize, extractor.getSampleTime(), 0);  
  105.                             extractor.advance();  
  106.                         }  
  107.                     }  
  108.                 }  
  109.   
  110.                 int outIndex = decoder.dequeueOutputBuffer(info, 10000);  
  111.                 switch (outIndex) {  
  112.                 case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:  
  113.                     Log.d("DecodeActivity""INFO_OUTPUT_BUFFERS_CHANGED");  
  114.                     outputBuffers = decoder.getOutputBuffers();  
  115.                     break;  
  116.                 case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:  
  117.                     Log.d("DecodeActivity""New format " + decoder.getOutputFormat());  
  118.                     break;  
  119.                 case MediaCodec.INFO_TRY_AGAIN_LATER:  
  120.                     Log.d("DecodeActivity""dequeueOutputBuffer timed out!");  
  121.                     break;  
  122.                 default:  
  123.                     ByteBuffer buffer = outputBuffers[outIndex];  
  124.                     Log.v("DecodeActivity""We can't use this buffer but render it due to the API limit, " + buffer);  
  125.   
  126.                     // We use a very simple clock to keep the video FPS, or the video  
  127.                     // playback will be too fast  
  128.                     while (info.presentationTimeUs / 1000 > System.currentTimeMillis() - startMs) {  
  129.                         try {  
  130.                             sleep(10);  
  131.                         } catch (InterruptedException e) {  
  132.                             e.printStackTrace();  
  133.                             break;  
  134.                         }  
  135.                     }  
  136.                     decoder.releaseOutputBuffer(outIndex, true);  
  137.                     break;  
  138.                 }  
  139.   
  140.                 // All decoded frames have been rendered, we can stop playing now  
  141.                 if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {  
  142.                     Log.d("DecodeActivity""OutputBuffer BUFFER_FLAG_END_OF_STREAM");  
  143.                     break;  
  144.                 }  
  145.             }  
  146.   
  147.             decoder.stop();  
  148.             decoder.release();  
  149.             extractor.release();  
  150.         }  
  151.     }  
  152. }  

最後再奉上我修改過的代碼,增加了選擇文件的功能.

http://download.csdn.net/detail/shawnkong/6555857



最近从事手机iOS/Android直播、视频实时美颜方面的工作。

欢迎加群交流:496010189

4
0
我的同类文章
  • c++中局部变量未初始化引发的离奇惨案~~2014-09-09
  • Ubuntu 14.04 Linux系统安装Subversion结合Eclipse的一些问题2014-04-22
  • Android NDK 开发进阶之 mk文件写法2014-01-13
  • Android 不能生产 R.java2013-11-27
  • Android Studio配置2013-10-30
  • Android NDK开发之配置 adb-bundle、eclipse 支持C/C++ 语言的JNI开发2014-05-27
  • Linphone 杂记2014-02-10
  • 编译x264 出现No working C compiler found.2013-12-23
  • Ubuntu android 开发环境搭建之eclipse篇2013-11-12
  • 关于64位 Ubuntu 13.04 安装Android Studio的一些问题及ADB驱动配置2013-10-30
更多文章

参考知识库

img
iOS知识库

img
Android知识库

img
.NET知识库

img
Java SE知识库

img
Java EE知识库

img
Java 知识库

img
Swift知识库

猜你在找
FFmpeg音视频高级开发实战 iOS&Android;
Android 5.x顶级视频课程
Android SQLite 性能优化——显示使用事务
Android底层技术:HAL驱动开发
Android基础视频教程
查看评论
15楼  IT_Transformers 2016-10-19 18:14发表 [回复]
你好,我解码以后从哪个地方可以得到每一帧的数据呢?
14楼  邪恶的鱼蛋 2016-06-27 09:14发表 [回复]
您好,我想请问一下MediaCodec 如何暂停解码,就是我点暂停,他就停留在这里,点击继续他就继续开始解码
Re:  Shawn4com 2016-07-25 18:15发表 [回复]
回复邪恶的鱼蛋:把解码线程挂起,这样解码就不会继续了
13楼  ai454121 2015-11-10 20:26发表 [回复]
LZ,您好,我发现decoder.configure(format, surface, null, 0); 中如果surface设置为null,那解码速度会比现在慢很多,这边测试差不多慢2倍,这个可能是什么原因引起的,如果不配置surface,有什么办法改进吗?
Re:  Shawn4com 2016-07-25 18:21发表 [回复]
回复ai454121:不设置surface会解码变慢么?你是从哪里看出来解码变慢了么?检查一下sleep那个地方,看是不是调用次数增多了
12楼  he_wen_jian 2015-03-05 13:13发表 [回复]
请问如何播放网络视频?
Re:  Shawn4com 2016-07-25 18:16发表 [回复]
回复he_wen_jian:这个只是简单演示解码video,如果需要播放网络视频,你还需要有接收、音频解码播放功能
11楼  amsh10421 2014-09-15 17:46发表 [回复]
extractor = new MediaExtractor(); 
extractor.setDataSource(SAMPLE); 
for (int i = 0; i < extractor.getTrackCount(); i++) 


extractor.getTrackCount(); 返回值是0 请问这个怎么修改?是我的文件没读进去么?
Re:  Shawn4com 2014-09-16 11:06发表 [回复]
回复amsh10421: 加文件读写权限了么?
10楼  sanbo_xyz 2014-06-19 18:51发表 [回复]
上传的不对。。。
Re:  Shawn4com 2014-06-20 14:54发表 [回复]
回复sanbo_xyz:什么不对?
Re:  sanbo_xyz 2014-06-23 11:21发表 [回复]
回复Shawn4com:不能正常使用。
Re:  Shawn4com 2014-06-23 16:09发表 [回复]
回复sanbo_xyz:有什么问题,能说的具体点么?我这里跑的好好的。。
9楼  newplumage 2014-04-03 15:54发表 [回复]
感谢这个例子,我能正常运行了。有两个问题
1.音频需另外解码,怎么处理?
2.视频和音频怎么同步?
请指教!!
Re:  Shawn4com 2014-04-15 10:18发表 [回复]
回复newplumage:sorry,我暂时只用来解码视频,没有测试音频,因为现在我主要不是用来做播放器。同步的话使用时间戳。
8楼  yqymmzby 2014-04-01 13:45发表 [回复]
请问音频使用MediaPlayer同步解吗?还是什么?
7楼  yubestming_job 2014-03-20 10:51发表 [回复]
你好,我想问下,我做的实时视频通话,c++那边给我传递过来的是一个Byte[]的字节数组,我怎么显示出来,在android端,求指教。QQ512573717
Re:  Shawn4com 2014-03-21 11:01发表 [回复]
回复yubestming_job: 实时流好像不支持的,我试过,没弄出来,要不你试试,如果成功了,回头跟出来?
6楼  xhcw1011 2014-02-19 09:17发表 [回复]
你好,请问我想解实时的h264的流该怎么解,我现在按照网上一些例子做出来了,但是效果很差,很模糊。基本看不到东西。
Re:  chenqingfei 2014-10-11 09:05发表 [回复]
回复xhcw1011:同感,我也是在网上搜集了一些信息,写出来的,效果比较差,
如果是非高清的,流畅度还能保证,如果是高清的,网络根本就支撑不了,而且我还做了分包,如果不分包,高清的一帧数据UDP都发不过去。这个问题真的很棘手啊。不知道微信是怎么做的。
Re:  紫枫wrongs 2014-02-28 14:36发表 [回复]
回复xhcw1011:你好,对于实时的视频,在接收端,如何解析解包呢,是不是需要将包组起来,然后解码, 请问下你解码是使用的啥方式,求助!!!
Re:  紫枫wrongs 2014-02-19 14:16发表 [回复]
回复xhcw1011:你好,请问你对h264如何进行解码的呢,我使用的是.264文件解码,可在extractor.setDataSource(fileString); 一直报异常呢
Re:  xhcw1011 2014-02-19 14:33发表 [回复]
回复紫枫wrongs:你先看下是报什么错。
Re:  紫枫wrongs 2014-02-20 10:35发表 [回复]
回复xhcw1011:播放H264错误是我的文件有问题,现在换了文件解决了,对于实时流的接收端,你是使用RTP 接收的吗 可以给个Demo参考下吗
5楼  紫枫wrongs 2014-02-17 16:48发表 [回复]
你好,下载的demo,怎么在extractor.setDataSource(SAMPLE);
一直报错呢,是不是不能支持.h264吗 ?
Re:  Shawn4com 2014-02-17 22:21发表 [回复]
回复紫枫wrongs:SAMPLE 要制定一个文件。。
Re:  紫枫wrongs 2014-02-18 10:03发表 [回复]
回复Shawn4com:sample 这个路径下的文件,是我自己编码的,使用h264的播放器都可以播放的,
Re:  Shawn4com 2014-04-14 14:43发表 [回复]
回复紫枫wrongs:sdk里面的extractor只支持mp4,avi和mkv格式的,h264的裸码解析不了的
4楼  紫枫wrongs 2014-02-13 16:48发表 [回复]
下载csdn的实例,extractor = new MediaExtractor();
extractor.setDataSource(SAMPLE);
执行到这的时候一直报错,求指点啊,
3楼  zuchgi 2014-02-11 00:46发表 [回复]
String SAMPLE = Environment.getExternalStorageDirectory() + "/encoder.h264";
extractor.setDataSource(SAMPLE);

运行到这里的时候显示找不到extractor,请问楼主要如何处理?
Re:  Shawn4com 2014-02-25 12:14发表 [回复]
回复zuchgi:贴错误信息看下。
2楼  chen7yang 2014-01-08 15:03发表 [回复] [引用] [举报]
求助:
我的代码 执行到这句时
outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo,0);

就会抛 IllegalStateException ,
Re:  Shawn4com 2014-01-09 16:17发表 [回复] [引用] [举报]
回复chen7yang:据说这个api还不稳定,实际使用可能有问题。。
1楼  temofil2009 2013-12-24 11:17发表 [回复]
这样的话送到surface播出来的视频是没有audio的吧?
Re:  Shawn4com 2013-12-25 10:40发表 [回复]
回复temofil2009:是的,这个只是单解视频,音频需另外解码。。























另外一篇文章的代码,也可以看看:

代码片段(1)[全屏查看所有代码]

1. [代码][Java]代码     

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
private final String TAG = "MediaCodeSample" ;
     /** 用来解码 */
     private MediaCodec mMediaCodec;
     /** 用来读取音频文件 */
     private MediaExtractor extractor;
     private MediaFormat format;
     private String mime = null ;
     private int sampleRate = 0 , channels = 0 , bitrate = 0 ;
     private long presentationTimeUs = 0 , duration = 0 ;
     public void decode(String url)
     {
         extractor = new MediaExtractor();
         // 根据路径获取源文件
         try
         {
             extractor.setDataSource(url);
         } catch (Exception e)
         {
             Log.e(TAG, " 设置文件路径错误" + e.getMessage());
         }
         try
         {
             // 音频文件信息
             format = extractor.getTrackFormat( 0 );
             mime = format.getString(MediaFormat.KEY_MIME);
             sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
             // 声道个数:单声道或双声道
             channels = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
             // if duration is 0, we are probably playing a live stream
             duration = format.getLong(MediaFormat.KEY_DURATION);
             // System.out.println("歌曲总时间秒:"+duration/1000000);
             bitrate = format.getInteger(MediaFormat.KEY_BIT_RATE);
         } catch (Exception e)
         {
             Log.e(TAG, "音频文件信息读取出错:" + e.getMessage());
             // 不要退出,下面进行判断
         }
         Log.d(TAG, "Track info: mime:" + mime + " 采样率sampleRate:" + sampleRate + " channels:" + channels + " bitrate:"
                 + bitrate + " duration:" + duration);
         // 检查是否为音频文件
         if (format == null || !mime.startsWith( "audio/" ))
         {
             Log.e(TAG, "不是音频文件 end !" );
             return ;
         }
         // 实例化一个指定类型的解码器,提供数据输出
         // Instantiate an encoder supporting output data of the given mime type
         mMediaCodec = MediaCodec.createDecoderByType(mime);
         if (mMediaCodec == null )
         {
             Log.e(TAG, "创建解码器失败!" );
             return ;
         }
         mMediaCodec.configure(format, null , null , 0 );
         mMediaCodec.start();
         // 用来存放目标文件的数据
         ByteBuffer[] inputBuffers = mMediaCodec.getInputBuffers();
         // 解码后的数据
         ByteBuffer[] outputBuffers = mMediaCodec.getOutputBuffers();
         // 设置声道类型:AudioFormat.CHANNEL_OUT_MONO单声道,AudioFormat.CHANNEL_OUT_STEREO双声道
         int channelConfiguration = channels == 1 ? AudioFormat.CHANNEL_OUT_MONO : AudioFormat.CHANNEL_OUT_STEREO;
         Log.i(TAG, "channelConfiguration=" + channelConfiguration);
         extractor.selectTrack( 0 );
         // ==========开始解码=============
         boolean sawInputEOS = false ;
         boolean sawOutputEOS = false ;
         final long kTimeOutUs = 10 ;
         MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
         while (!sawOutputEOS)
         {
             try
             {
                 if (!sawInputEOS)
                 {
                     int inputBufIndex = mMediaCodec.dequeueInputBuffer(kTimeOutUs);
                     if (inputBufIndex >= 0 )
                     {
                         ByteBuffer dstBuf = inputBuffers[inputBufIndex];
                         int sampleSize = extractor.readSampleData(dstBuf, 0 );
                         if (sampleSize < 0 )
                         {
                             Log.d(TAG, "saw input EOS. Stopping playback" );
                             sawInputEOS = true ;
                             sampleSize = 0 ;
                         } else
                         {
                             presentationTimeUs = extractor.getSampleTime();
                         }
                         mMediaCodec.queueInputBuffer(inputBufIndex, 0 , sampleSize, presentationTimeUs,
                                 sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0 );
                         if (!sawInputEOS)
                         {
                             extractor.advance();
                         }
                     } else
                     {
                         Log.e(TAG, "inputBufIndex " + inputBufIndex);
                     }
                 } // !sawInputEOS
                 // decode to PCM and push it to the AudioTrack player
                 int res = mMediaCodec.dequeueOutputBuffer(info, kTimeOutUs);
                 if (res >= 0 )
                 {
                     int outputBufIndex = res;
                     ByteBuffer buf = outputBuffers[outputBufIndex];
                     final byte [] chunk = new byte [info.size];
                     buf.get(chunk);
                     buf.clear();
                     if (chunk.length > 0 )
                     {
                         // chunk解码后的音频流
                         // TODO:处理...
                     }
                     mMediaCodec.releaseOutputBuffer(outputBufIndex, false );
                     if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0 )
                     {
                         Log.d(TAG, "saw output EOS." );
                         sawOutputEOS = true ;
                     }
                 } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED)
                 {
                     outputBuffers = mMediaCodec.getOutputBuffers();
                     Log.w(TAG, "[AudioDecoder]output buffers have changed." );
                 } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED)
                 {
                     MediaFormat oformat = mMediaCodec.getOutputFormat();
                     Log.w(TAG, "[AudioDecoder]output format has changed to " + oformat);
                 } else
                 {
                     Log.w(TAG, "[AudioDecoder] dequeueOutputBuffer returned " + res);
                 }
             } catch (RuntimeException e)
             {
                 Log.e(TAG, "[decodeMP3] error:" + e.getMessage());
             }
         }
         // =================================================================================
         if (mMediaCodec != null )
         {
             mMediaCodec.stop();
             mMediaCodec.release();
             mMediaCodec = null ;
         }
         if (extractor != null )
         {
             extractor.release();
             extractor = null ;
         }
         // clear source and the other globals
         duration = 0 ;
         mime = null ;
         sampleRate = 0 ;
         channels = 0 ;
         bitrate = 0 ;
         presentationTimeUs = 0 ;
         duration = 0 ;
     }




这篇关于Android 新API 之 MediaCodec使用笔记 一的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/680594

相关文章

vue使用docxtemplater导出word

《vue使用docxtemplater导出word》docxtemplater是一种邮件合并工具,以编程方式使用并处理条件、循环,并且可以扩展以插入任何内容,下面我们来看看如何使用docxtempl... 目录docxtemplatervue使用docxtemplater导出word安装常用语法 封装导出方

Linux换行符的使用方法详解

《Linux换行符的使用方法详解》本文介绍了Linux中常用的换行符LF及其在文件中的表示,展示了如何使用sed命令替换换行符,并列举了与换行符处理相关的Linux命令,通过代码讲解的非常详细,需要的... 目录简介检测文件中的换行符使用 cat -A 查看换行符使用 od -c 检查字符换行符格式转换将

使用Jackson进行JSON生成与解析的新手指南

《使用Jackson进行JSON生成与解析的新手指南》这篇文章主要为大家详细介绍了如何使用Jackson进行JSON生成与解析处理,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 核心依赖2. 基础用法2.1 对象转 jsON(序列化)2.2 JSON 转对象(反序列化)3.

使用Python实现快速搭建本地HTTP服务器

《使用Python实现快速搭建本地HTTP服务器》:本文主要介绍如何使用Python快速搭建本地HTTP服务器,轻松实现一键HTTP文件共享,同时结合二维码技术,让访问更简单,感兴趣的小伙伴可以了... 目录1. 概述2. 快速搭建 HTTP 文件共享服务2.1 核心思路2.2 代码实现2.3 代码解读3.

Elasticsearch 在 Java 中的使用教程

《Elasticsearch在Java中的使用教程》Elasticsearch是一个分布式搜索和分析引擎,基于ApacheLucene构建,能够实现实时数据的存储、搜索、和分析,它广泛应用于全文... 目录1. Elasticsearch 简介2. 环境准备2.1 安装 Elasticsearch2.2 J

使用C#代码在PDF文档中添加、删除和替换图片

《使用C#代码在PDF文档中添加、删除和替换图片》在当今数字化文档处理场景中,动态操作PDF文档中的图像已成为企业级应用开发的核心需求之一,本文将介绍如何在.NET平台使用C#代码在PDF文档中添加、... 目录引言用C#添加图片到PDF文档用C#删除PDF文档中的图片用C#替换PDF文档中的图片引言在当

Java中List的contains()方法的使用小结

《Java中List的contains()方法的使用小结》List的contains()方法用于检查列表中是否包含指定的元素,借助equals()方法进行判断,下面就来介绍Java中List的c... 目录详细展开1. 方法签名2. 工作原理3. 使用示例4. 注意事项总结结论:List 的 contain

C#使用SQLite进行大数据量高效处理的代码示例

《C#使用SQLite进行大数据量高效处理的代码示例》在软件开发中,高效处理大数据量是一个常见且具有挑战性的任务,SQLite因其零配置、嵌入式、跨平台的特性,成为许多开发者的首选数据库,本文将深入探... 目录前言准备工作数据实体核心技术批量插入:从乌龟到猎豹的蜕变分页查询:加载百万数据异步处理:拒绝界面

Android中Dialog的使用详解

《Android中Dialog的使用详解》Dialog(对话框)是Android中常用的UI组件,用于临时显示重要信息或获取用户输入,本文给大家介绍Android中Dialog的使用,感兴趣的朋友一起... 目录android中Dialog的使用详解1. 基本Dialog类型1.1 AlertDialog(

Python使用自带的base64库进行base64编码和解码

《Python使用自带的base64库进行base64编码和解码》在Python中,处理数据的编码和解码是数据传输和存储中非常普遍的需求,其中,Base64是一种常用的编码方案,本文我将详细介绍如何使... 目录引言使用python的base64库进行编码和解码编码函数解码函数Base64编码的应用场景注意