运行有问题或需要源码请点赞关注收藏后评论区留言~~~

一、在通知栏显示下载进度

利用GET方式读取数据有很多缺点比如1:无法端点续传 一旦中途失败只能重新获取

2:不是真正意义上的下载操作 无法设置参数

3:下载过程中无法在界面上上展示下载状态

因为下载功能比较常用而且业务功能比较单一,所以Android专门提供了下载管理DownloadManager,方便开发者统一管理下载操作

主要步骤可分为以下两步

1:构建下载请求

2:管理下载队列

此外还有两种下载时间 开发者可通过监听对应的广播消息进行对应的处理

1:正在下载之时的通知栏点击事件

2:下载完成事件

下面是利用DownloadManager下载APK文件的实例 效果如下

在下拉框中选择要下载的安装包然后到通知栏中查看即可

代码如下

Java类

package com.example.chapter14;import android.annotation.SuppressLint;
import android.app.DownloadManager;
import android.app.DownloadManager.Request;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.TextView;import androidx.appcompat.app.AppCompatActivity;import com.example.chapter14.constant.ApkConstant;
import com.example.chapter14.util.DateUtil;@SuppressLint({"SetTextI18n","DefaultLocale"})
public class DownloadApkActivity extends AppCompatActivity {private static final String TAG = "DownloadApkActivity";private Spinner sp_apk_url; // 安装包链接的下拉框private TextView tv_apk_result;private boolean isFirstSelect = true; // 是否首次选择private DownloadManager mDownloadManager; // 声明一个下载管理器对象private long mDownloadId = 0; // 下载编号@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_download_apk);tv_apk_result = findViewById(R.id.tv_apk_result);// 从系统服务中获取下载管理器mDownloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);initApkSpinner(); // 初始化安装包链接的下拉框}// 初始化安装包链接的下拉框private void initApkSpinner() {ArrayAdapter<String> apkUrlAdapter = new ArrayAdapter<String>(this,R.layout.item_select, ApkConstant.NAME_ARRAY);sp_apk_url = findViewById(R.id.sp_apk_url);sp_apk_url.setPrompt("请选择要下载的安装包");sp_apk_url.setAdapter(apkUrlAdapter);sp_apk_url.setOnItemSelectedListener(new ApkUrlSelectedListener());sp_apk_url.setSelection(0);}class ApkUrlSelectedListener implements OnItemSelectedListener {public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {if (isFirstSelect) { // 刚打开页面时不需要执行下载动作isFirstSelect = false;return;}startDownload(arg2); // 开始下载指定序号的apk文件}public void onNothingSelected(AdapterView<?> arg0) {}}// 开始下载指定序号的apk文件private void startDownload(int pos) {tv_apk_result.setText("正在下载" + ApkConstant.NAME_ARRAY[pos] +"的安装包,请到通知栏查看下载进度");Uri uri = Uri.parse(ApkConstant.URL_ARRAY[pos]); // 根据下载地址构建一个Uri对象Request down = new Request(uri); // 创建一个下载请求对象,指定从哪里下载文件down.setTitle(ApkConstant.NAME_ARRAY[pos] + "下载信息"); // 设置任务标题down.setDescription(ApkConstant.NAME_ARRAY[pos] + "安装包正在下载"); // 设置任务描述// 设置允许下载的网络类型down.setAllowedNetworkTypes(Request.NETWORK_MOBILE | Request.NETWORK_WIFI);// 设置通知栏在下载进行时与完成后都可见down.setNotificationVisibility(Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);// 设置下载文件在私有目录的保存路径。从Android10开始,只有保存到公共目录的才会在系统下载页面显示,保存到私有目录的不在系统下载页面显示down.setDestinationInExternalFilesDir(this, Environment.DIRECTORY_DOWNLOADS, pos + ".apk");// 设置下载文件在公共目录的保存路径。保存到公共目录需要申请存储卡的读写权限//down.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, pos + ".apk");mDownloadId = mDownloadManager.enqueue(down); // 把下载请求对象加入到下载队列}// 定义一个下载完成的广播接收器。用于接收下载完成事件private class DownloadCompleteReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {if (intent.getAction().equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) { // 下载完毕// 从意图中解包获得下载编号long downId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);Log.d(TAG, "下载完成 id : " + downId + ", mDownloadId=" + mDownloadId);tv_apk_result.setVisibility(View.VISIBLE);String desc = String.format("%s 编号%d的下载任务已完成", DateUtil.getNowTime(), downId);tv_apk_result.setText(desc); // 显示下载任务的完成描述}}}// 定义一个通知栏点击的广播接收器。用于接收下载通知栏的点击事件,在下载过程中有效,下载完成后失效private class NotificationClickReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {if (intent.getAction().equals(DownloadManager.ACTION_NOTIFICATION_CLICKED)) { // 点击了通知栏// 从意图中解包获得被点击通知的下载编号long[] downIds = intent.getLongArrayExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS);for (long downId : downIds) {Log.d(TAG, "点击通知 id : " + downId + ", mDownloadId=" + mDownloadId);if (downId == mDownloadId) { // 找到当前的下载任务String desc = String.format("%s 点击了编号%d的下载通知", DateUtil.getNowTime(), downId);tv_apk_result.setText(desc); // 显示下载任务的点击描述}}}}}private DownloadCompleteReceiver completeReceiver; // 声明一个下载完成的广播接收器private NotificationClickReceiver clickReceiver; // 声明一个通知栏点击的广播接收器@Overridepublic void onStart() {super.onStart();completeReceiver = new DownloadCompleteReceiver(); // 创建一个下载完成的广播接收器// 注册接收器,注册之后才能正常接收广播registerReceiver(completeReceiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));clickReceiver = new NotificationClickReceiver(); // 创建一个通知栏点击的广播接收器// 注册接收器,注册之后才能正常接收广播registerReceiver(clickReceiver, new IntentFilter(DownloadManager.ACTION_NOTIFICATION_CLICKED));}@Overridepublic void onStop() {super.onStop();unregisterReceiver(completeReceiver); // 注销下载完成的广播接收器unregisterReceiver(clickReceiver); // 注销通知栏点击的广播接收器}}

XML文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:padding="5dp" ><LinearLayoutandroid:layout_width="match_parent"android:layout_height="40dp" ><TextViewandroid:layout_width="wrap_content"android:layout_height="match_parent"android:gravity="center"android:text="请选择要下载的安装包:"android:textColor="@color/black"android:textSize="17sp" /><Spinnerandroid:id="@+id/sp_apk_url"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:spinnerMode="dialog" /></LinearLayout><TextViewandroid:id="@+id/tv_apk_result"android:layout_width="match_parent"android:layout_height="wrap_content"android:textColor="@color/black"android:textSize="17sp" /></LinearLayout>

二、主动轮询当前的下载进度

如果APP自己也想了解当前的下载进度,就要调用管理器的query方法, 常用方法如下

setFilterById 根据编号过滤下载任务

setFilterByStatus 根据状态过滤下载任务

oredrBy 结果集安装指定字段排序

一旦把下载任务加入到下载队列中,就能调用下载管理器对象的query方法,获得任务信息结果集的游标对象

效果如下 可以在页面上动态展示网络图片的下载进度,并且自定义了圆形进度圈

此处连接真机测试效果更佳

代码如下

Java类

package com.example.chapter14;import android.annotation.SuppressLint;
import android.app.DownloadManager;
import android.app.DownloadManager.Query;
import android.app.DownloadManager.Request;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.Spinner;
import android.widget.TextView;import androidx.appcompat.app.AppCompatActivity;import com.example.chapter14.widget.TextProgressCircle;import java.util.HashMap;@SuppressLint("DefaultLocale")
public class DownloadImageActivity extends AppCompatActivity {private Spinner sp_image_url; // 图片链接的下拉框private ImageView iv_image_url;private TextProgressCircle tpc_progress; // 定义一个文本进度圈对象private TextView tv_image_result;private boolean isFirstSelect = true; // 是否首次选择private Uri mImageUri; // 图片的路径对象private DownloadManager mDownloadManager; // 声明一个下载管理器对象private long mDownloadId = 0; // 当前任务的下载编号private static HashMap<Integer, String> mStatusMap = new HashMap<Integer, String>(); // 下载状态映射static { // 初始化下载状态映射mStatusMap.put(DownloadManager.STATUS_PENDING, "挂起");mStatusMap.put(DownloadManager.STATUS_RUNNING, "运行中");mStatusMap.put(DownloadManager.STATUS_PAUSED, "暂停");mStatusMap.put(DownloadManager.STATUS_SUCCESSFUL, "成功");mStatusMap.put(DownloadManager.STATUS_FAILED, "失败");}private String[] imageDescArray = {"洱海公园", "丹凤亭", "宛在堂", "满庭芳", "玉带桥", "眺望洱海", "洱海女儿", "海心亭", "洱海岸边", "烟波浩渺"};private String[] imageUrlArray = {"https://b255.photo.store.qq.com/psb?/V11ZojBI0Zz6pV/nYJcslMIrGeDrujE5KZF2xBW8rjXMIVetZfrOAlSamM!/b/dPwxB5iaEQAA","https://b255.photo.store.qq.com/psb?/V11ZojBI0Zz6pV/Adcl9XVS.RBED4D8shjceYHOhhR*6mcNyCcq24kJG2k!/b/dPwxB5iYEQAA","https://b255.photo.store.qq.com/psb?/V11ZojBI0Zz6pV/bg*X6nT03YUReoJ97ked266WlWG3IzLjBdwHpKqkhYY!/b/dOg5CpjZEAAA","https://b255.photo.store.qq.com/psb?/V11ZojBI0Zz6pV/JOPAKl9BO1wragCEIVzXLlHwj83qVhb8uNuHdmVRwP4!/b/dPwxB5iSEQAA","https://b255.photo.store.qq.com/psb?/V11ZojBI0Zz6pV/7hHOgBEOBshH*7YAUx7RP0JzPuxRBD727mblw9TObhc!/b/dG4WB5i2EgAA","https://b255.photo.store.qq.com/psb?/V11ZojBI0Zz6pV/m4Rjx20D9iFL0D5emuYqMMDji*HGQ2w2BWqv0zK*tRk!/b/dGp**5dYEAAA","https://b255.photo.store.qq.com/psb?/V11ZojBI0Zz6pV/swfCMVl7Oefv8xgboV3OqkrahEs33KO7XwwH6hh7bnY!/b/dECE*5e9EgAA","https://b256.photo.store.qq.com/psb?/V11ZojBI0Zz6pV/tpRlB0oozaD9PyBtCmf3pQ5QY0keJJxYGX93I7n5NwQ!/b/dAyVmZiVEQAA","https://b256.photo.store.qq.com/psb?/V11ZojBI0Zz6pV/wMX2*LM6y.mBsFIYu8spAa7xXWUkPD.GHyazd.vMmYA!/b/dGYwoZjREQAA","https://b255.photo.store.qq.com/psb?/V11ZojBI0Zz6pV/2vl1n0KmKTPCv944MVJgLxKAhMiM*sqajIFQ43c*9DM!/b/dPaoCJhuEQAA",};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_download_image);iv_image_url = findViewById(R.id.iv_image_url);// 从布局文件中获取名叫tpc_progress的文本进度圈tpc_progress = findViewById(R.id.tpc_progress);tv_image_result = findViewById(R.id.tv_image_result);// 从系统服务中获取下载管理器mDownloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);initImageSpinner(); // 初始化下载图片的下拉框}// 初始化下载图片的下拉框private void initImageSpinner() {ArrayAdapter<String> imageUrlAdapter = new ArrayAdapter<String>(this,R.layout.item_select, imageDescArray);sp_image_url = findViewById(R.id.sp_image_url);sp_image_url.setPrompt("请选择要下载的图片");sp_image_url.setAdapter(imageUrlAdapter);sp_image_url.setOnItemSelectedListener(new ImageUrlSelectedListener());sp_image_url.setSelection(0);}class ImageUrlSelectedListener implements OnItemSelectedListener {public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {if (isFirstSelect) { // 刚打开页面时不需要执行下载动作isFirstSelect = false;return;}startDownload(arg2); // 开始下载指定序号的图片文件}public void onNothingSelected(AdapterView<?> arg0) {}}// 开始下载指定序号的图片文件private void startDownload(int pos) {iv_image_url.setImageDrawable(null); // 清空图像视图tpc_progress.setProgress(0); // 设置文本进度圈的当前进度为0,最大进度为100tpc_progress.setVisibility(View.VISIBLE); // 显示文本进度圈Uri uri = Uri.parse(imageUrlArray[pos]); // 根据图片的下载地址构建一个路径对象Request down = new Request(uri); // 创建一个下载请求对象,指定从哪里下载文件// 设置允许下载的网络类型down.setAllowedNetworkTypes(Request.NETWORK_MOBILE | Request.NETWORK_WIFI);down.setNotificationVisibility(Request.VISIBILITY_HIDDEN); // 设置不在通知栏显示// 设置下载文件在本地的保存路径down.setDestinationInExternalFilesDir(this, Environment.DIRECTORY_DCIM, pos + ".jpg");mDownloadId = mDownloadManager.enqueue(down); // 把下载请求对象加入到下载队列mHandler.post(mRefresh); // 启动下载进度的刷新任务}private Handler mHandler = new Handler(); // 声明一个处理器对象// 定义一个下载进度的刷新任务private Runnable mRefresh = new Runnable() {@Overridepublic void run() {boolean isFinish = false;Query down_query = new Query(); // 创建一个下载查询对象,按照下载编号过滤down_query.setFilterById(mDownloadId); // 设置下载查询对象的编号过滤器// 向下载管理器查询下载任务,并返回查询结果集的游标Cursor cursor = mDownloadManager.query(down_query);while (cursor.moveToNext()) {int uriIdx = cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI);int mediaIdx = cursor.getColumnIndex(DownloadManager.COLUMN_MEDIA_TYPE);int totalIdx = cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES);int nowIdx = cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR);int statusIdx = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);if (cursor.getString(uriIdx) == null) {break;}// 根据总大小和已下载大小,计算当前的下载进度int progress = (int) (100 * cursor.getLong(nowIdx) / cursor.getLong(totalIdx));tpc_progress.setProgress(progress); // 设置文本进度圈的当前进度if (progress == 100) { // 下载完毕isFinish = true;}// 获得实际的下载状态int status = isFinish ? DownloadManager.STATUS_SUCCESSFUL : cursor.getInt(statusIdx);mImageUri = Uri.parse(cursor.getString(uriIdx));String desc = String.format("文件路径:%s\n媒体类型:%s\n文件总大小:%d字节" +"\n已下载大小:%d字节\n下载进度:%d%%\n下载状态:%s",mImageUri.toString(), cursor.getString(mediaIdx), cursor.getLong(totalIdx),cursor.getLong(nowIdx), progress, mStatusMap.get(status));tv_image_result.setText(desc); // 显示图片下载任务的下载详情}cursor.close(); // 关闭数据库游标if (!isFinish) { // 下载未完成,则继续刷新mHandler.postDelayed(this, 50); // 延迟50毫秒后再次启动刷新任务} else { // 下载已完成,则显示图片tpc_progress.setVisibility(View.INVISIBLE); // 隐藏文本进度圈iv_image_url.setImageURI(mImageUri); // 设置图像视图的图片路径}}};@Overrideprotected void onDestroy() {super.onDestroy();mHandler.removeCallbacks(mRefresh); // 移除刷新任务}
}

XML文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:padding="5dp"><ScrollViewandroid:layout_width="match_parent"android:layout_height="wrap_content"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="40dp"><TextViewandroid:layout_width="wrap_content"android:layout_height="match_parent"android:gravity="center"android:text="请选择要下载的图片:"android:textColor="@color/black"android:textSize="17sp" /><Spinnerandroid:id="@+id/sp_image_url"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:spinnerMode="dialog" /></LinearLayout><FrameLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"><ImageViewandroid:id="@+id/iv_image_url"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@drawable/downloading"android:scaleType="fitCenter" /><com.example.chapter14.widget.TextProgressCircleandroid:id="@+id/tpc_progress"android:layout_width="match_parent"android:layout_height="350dp"android:layout_gravity="center"android:background="#99ffffff"android:visibility="invisible" /></FrameLayout><TextViewandroid:id="@+id/tv_image_result"android:layout_width="match_parent"android:layout_height="wrap_content"android:textColor="@color/black"android:textSize="17sp" /></LinearLayout></ScrollView></LinearLayout>

三、利用POST方式上传文件

对于社交类APP来说,上传文件是必不可少的功能,因此有必要要掌握文件上传的相关技术。

一样按照HTTP访问的POST流程,只是要采取multipart/form-data的方式分段传输,并加入分段传输的边界字符串即可

效果如下 连接真机测试效果更佳~~~

代码如下

Java类

package com.example.chapter14;import android.annotation.SuppressLint;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.TextView;import androidx.appcompat.app.AppCompatActivity;import com.example.chapter14.task.UploadTask;
import com.example.chapter14.task.UploadTask.OnUploadListener;
import com.example.chapter14.constant.UrlConstant;
import com.example.chapter14.util.DateUtil;
import com.example.chapter14.util.FileUtil;@SuppressLint("SetTextI18n")
public class HttpUploadActivity extends AppCompatActivity implements View.OnClickListener, OnUploadListener {private final static String TAG = "HttpUploadActivity";private int CHOOSE_CODE = 3; // 只在相册挑选图片的请求码private TextView tv_file_path;private String mFilePath; // 图片文件的路径@Overrideprotected void onCreate(Bundle selectdInstanceState) {super.onCreate(selectdInstanceState);setContentView(R.layout.activity_http_upload);tv_file_path = findViewById(R.id.tv_file_path);findViewById(R.id.btn_file_select).setOnClickListener(this);}@Overridepublic void onClick(View v) {if (v.getId() == R.id.btn_file_select) {// 创建一个内容获取动作的意图(准备跳到系统相册)Intent albumIntent = new Intent(Intent.ACTION_GET_CONTENT);albumIntent.setType("image/*"); // 类型为图像startActivityForResult(albumIntent, CHOOSE_CODE); // 打开系统相册}}@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent intent) {super.onActivityResult(requestCode, resultCode, intent);if (resultCode == RESULT_OK && requestCode == CHOOSE_CODE) { // 从相册回来if (intent.getData() != null) { // 从相册选择一张照片Uri uri = intent.getData(); // 获得已选择照片的路径对象// 获得图片的临时保存路径mFilePath = String.format("%s/%s.jpg",getExternalFilesDir(Environment.DIRECTORY_PICTURES), "photo_"+ DateUtil.getNowDateTime());FileUtil.saveFileFromUri(this, uri, mFilePath); // 保存为临时文件tv_file_path.setText("上传文件的路径为:" + mFilePath);UploadTask task = new UploadTask(); // 创建文件上传线程task.setOnUploadListener(this); // 设置文件上传监听器task.execute(mFilePath); // 把文件上传线程加入到处理队列}}}// 在文件上传结束后触发public void finishUpload(String result) {// 以下拼接文件上传的结果描述String desc = String.format("上传文件的路径:%s\n上传结果:%s\n预计下载地址:%s%s",mFilePath, (TextUtils.isEmpty(result))?"失败":result,UrlConstant.REQUEST_URL, mFilePath.substring(mFilePath.lastIndexOf("/")));tv_file_path.setText(desc);Log.d(TAG, desc);}}

XML文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical" ><Buttonandroid:id="@+id/btn_file_select"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="选择待上传的图片"android:textColor="@color/black"android:textSize="17sp" /><TextViewandroid:id="@+id/tv_file_path"android:layout_width="match_parent"android:layout_height="wrap_content"android:paddingLeft="5dp"android:textColor="@color/black"android:textSize="17sp" /></LinearLayout>

创作不易 觉得有帮助请点赞关注收藏~~~

Android Studio App开发之下载管理器DownloadManager中显示、轮询下载进度、利用POST上传文件讲解及实战(附源码)相关推荐

  1. Android Studio App开发之循环试图RecyclerView,布局管理器LayoutManager、动态更新循环视图讲解及实战(附源码)

    运行有问题或需要全部源码请点赞关注收藏后评论区留言~~~ 一.循环视图RecyclerView 尽管ListView和GridView分别实现了多行单列和多行多列的列表,使用也很简单,可是它们缺少变化 ...

  2. ANDROID物联网开发从入门到实战附源码

    本书从获取源码和搭建应用开发环境开始讲起,依次讲解了基础知识篇.数据传输篇.信息识别篇.传感器应用篇和技术提高篇这 5大部分内容. 目录 第1篇 基础知识篇 第1章 Android系统介绍 2 1.1 ...

  3. Android Studio App开发之网络通信中使用POST方式调用HTTP接口实现应用更新功能(附源码 超详细必看)

    运行有问题或需要源码请点赞关注收藏后评论区留言~~~ 一.POST方式调用HTTP接口 POST方式把接口地址与请求报文分开,允许使用自定义的报文格式,由此扩大了该方式的应用场景.POST请求与GET ...

  4. Android Studio App开发之使用摄像机录制视频和从视频库中选取视频的讲解及实战(附源码)

    运行有问题或需要源码请点赞关注收藏后评论区留言~~~ 一.使用摄像机录制视频 与音频类似,通过系统摄像机可以很方便的录制视频,只要指定摄像动作为MediaStore.ACTION_VIDEO_CAPT ...

  5. Android Studio App开发中使用录音机、MediaRecorder录制音频和MediaPlayer播放音频讲解及实战(附源码)

    运行有问题或需要源码请点赞关注收藏后评论区留言~~~ 一.使用录音机录制音频 手机有自带的系统相机,也有自带的系统录音机,只要在调用startActivityForResult之前指定该动作,就会自动 ...

  6. Android Studio App开发入门之在活动之间传递消息(附源码 超详细必看)(包括显示和隐式Intent,向上一个和下一个Activity发送数据)

     运行有问题或需要源码请点赞关注收藏后评论区留言~~ 显示Intent和隐式Intent Intent是各个组件之间的信息沟通的桥梁,既能在Activity之间沟通,又能在Activity与Servi ...

  7. Android Studio App开发之利用图像解码器ImageDecoder播放GIF动图、Webp、HEIF图片(附源码 简单实用)

    需要源码和图片集请点赞关注收藏后评论区留言~~~ 一.图像解码器ImageDecoder 早期的Android只支持3种图像格式,分别是JPEG,PNG,GIF 虽然这三类图片都能在ImageView ...

  8. Android Studio App开发中高级控件下拉列表Spinner的讲解及实战(附源码 超详细必看)

    运行有问题或需要源码请点赞关注收藏后评论区留言~~~ 一.下拉框Spinner Spinner是下拉框控件,它用于从一串列表中选择某项,其功能类似于单选按钮的组合,下拉列表的展示方式有两种,一种是在当 ...

  9. Android Studio App开发之通知渠道NotificationChannel及给华为、小米手机桌面应用添加消息数量角标实战(包括消息重要级别的设置 附源码)

    需要全部源码或运行有问题请点赞关注收藏后评论区留言~~~ 一.通知渠道NtoificationChannel 为了分清消息通知的轻重缓急,Android8.0新增了通知渠道,并且必须指定通知渠道才能正 ...

最新文章

  1. 史上最烧脑的学习方法,看完瞬间涨姿势!
  2. Maven -- 未完待续
  3. 自定义协同程序:CustomYieldInstruction
  4. 苹果自带相册打马赛克_哥们被绿?iOS 13惊现漏洞,马赛克去除
  5. 潭州教育-Python学习笔记@基本数据类型:元祖字典
  6. android 微信小程序 唤起app,Android 微信小程序打不开app方案解决
  7. 17-03-21 课堂笔记
  8. mac windows linux 公用磁盘格式,win访问mac分区 linuxt系统磁盘分区知识(2)
  9. 测试报告应该包括哪些内容?
  10. 四次重启共享充电宝业务 美团终结“三电一兽”格局预言会成真吗?
  11. PS中级应用系列-徐鹏-专题视频课程
  12. img标签无图片或者图片url错误时显示默认图片
  13. 监控摄像头的测试方法
  14. echart地图隐藏南海诸岛
  15. CLA与CPU数据交互
  16. html动画翻书特效,css3 做一个类似于翻书特效的3D动画
  17. 可信验证与TPCM技术
  18. 转:builder模式分析
  19. Struts框架之处理流程和类型转换器
  20. python实现AdaBoost算法

热门文章

  1. 虚拟机Linux挂载新硬盘的方法
  2. pyqt股票行情软件性能优化 差点又让python背了锅
  3. [市场产品部]MP部副部长(陈晓慧):餐饮公司LOGO设计与制作
  4. 【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★
  5. Linux PPP 实现源码分析
  6. 【C语言】文件管理<2>
  7. Lind.DDD.Caching分布式数据集缓存介绍
  8. Mysql第一阶段结束语
  9. linux驱动系列学习之OLED(i2c接口)(八)
  10. tftp与ftp的异同