最近在做图片和视频保存功能,之前做的是保存到sd卡目录下,现在是保存到系统相册中.
遇到问题:
1.Android 9.0网络请求适配,在Manifest中设置android:usesCleartextTraffic="true"即可解决AndroidP加载网络问题.
2.下载完成后进度不更新,在Manifest中配置下载权限.
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
<uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER" />
3.保存到本地相册不刷新问题.解决方法:先动态请求读写权限,然后调用系统数据库用广播通知界面刷新.
4.MainActivity完整代码:
public class MainActivity extends AppCompatActivity {
private TextView tvSaveImage, tvSaveVideo;
private boolean flag = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 请求权限
loadPermission();
initView();
initListener();
}
/**
* 动态请求权限
*/
private void loadPermission() {
PermissionsUtil.requestCamera(this, aBoolean -> {
}
);
}
private void initView() {
tvSaveImage = findViewById(R.id.tv_save_image);
tvSaveVideo = findViewById(R.id.tv_save_video);
}
private void initListener() {
tvSaveImage.setOnClickListener(v -> {
if (false) {
Toast.makeText(MainActivity.this, "图片开始下载", Toast.LENGTH_SHORT).show();
flag = false;
} else {
Toast.makeText(MainActivity.this, "图片正在下载", Toast.LENGTH_SHORT).show();
}
//String url = "";
//String url = "http://ww3.sinaimg.cn/bmiddle/6e91531djw1e8l3c7wo7xj20f00qo755.jpg";
//String url = "https://ae01.alicdn.com/kf/U29ad1424fd024374bf8ba95a61a60d8ai.jpg";
String url = "https://ae01.alicdn.com/kf/Ua227945b506241af975a9b0a16d6df3bA.jpg";
//保存图片
downLoadImage(url);
});
tvSaveVideo.setOnClickListener(v -> {
if (false) {
Toast.makeText(MainActivity.this, "视频下载开始", Toast.LENGTH_SHORT).show();
flag = false;
} else {
Toast.makeText(MainActivity.this, "视频正在下载", Toast.LENGTH_SHORT).show();
}
downLoadVideo("https://www.w3school.com.cn/example/html5/mov_bbb.mp4");
});
}
/**
* 保存图片到相册
* @param path
*/
private void downLoadImage(String path) {
new Thread(() -> new AndroidDownloadManager(MainActivity.this, path)
.setListener(new AndroidDownloadManagerListener() {
@Override
public void onPrepare() {
Log.d("downloadVideo", "onPrepare");
}
@Override
public void onSuccess(String path) {
Toast.makeText(MainActivity.this, "图片已保存到相册", Toast.LENGTH_SHORT).show();
FileUtils.saveImage(MainActivity.this,new File(path));
flag = true;
Log.d("downloadVideo", "onSuccess >>>>" + path);
}
@Override
public void onFailed(Throwable throwable) {
Toast.makeText(MainActivity.this, "图片下载失败,请重新下载!", Toast.LENGTH_SHORT).show();
Log.e("downloadVideo", "onFailed", throwable);
flag = true;
}
}).download()).start();
}
/**
* 保存视频到相册
* @param path
*/
private void downLoadVideo(String path) {
new Thread(() -> new AndroidDownloadManager(MainActivity.this, path)
.setListener(new AndroidDownloadManagerListener() {
@Override
public void onPrepare() {
Log.d("downloadVideo", "onPrepare");
}
@Override
public void onSuccess(String path) {
Toast.makeText(MainActivity.this, "视频已保存到相册", Toast.LENGTH_SHORT).show();
FileUtils.saveVideo(MainActivity.this,new File(path));
flag = true;
Log.d("downloadVideo", "onSuccess >>>>" + path);
}
@Override
public void onFailed(Throwable throwable) {
Toast.makeText(MainActivity.this, "视频下载失败,请重新下载!", Toast.LENGTH_SHORT).show();
Log.e("downloadVideo", "onFailed", throwable);
flag = true;
}
}).download()).start();
}
}
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_save_image"
android:layout_width="0dp"
android:layout_height="50dp"
android:text="保存图片到相册"
android:textSize="16sp"
android:textColor="#ffffff"
android:gravity="center"
android:background="@color/colorPrimary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/tv_save_video"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_save_video"
android:layout_width="0dp"
android:layout_height="50dp"
android:text="保存视频"
android:textSize="16sp"
android:textColor="#ffffff"
android:gravity="center"
android:layout_marginStart="20dp"
app:layout_constraintLeft_toRightOf="@+id/tv_save_image"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="parent"
app:layout_constraintBottom_toTopOf="parent"
android:background="@color/colorPrimary"/>
<androidx.constraintlayout.widget.Group
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="visible"
app:constraint_referenced_ids="tv_save_image,tv_save_video"/>
</androidx.constraintlayout.widget.ConstraintLayout>
5.FileUtils:
/**
* @anthor:njb
* @date: 2020-04-21 04:36
* @desc: 文件保存工具类
**/
public class FileUtils {
/**
* 保存图片
* @param context
* @param file
*/
public static void saveImage(Context context, File file) {
ContentResolver localContentResolver = context.getContentResolver();
ContentValues localContentValues = getImageContentValues(context, file, System.currentTimeMillis());
localContentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, localContentValues);
Intent localIntent = new Intent("android.intent.action.MEDIA_SCANNER_SCAN_FILE");
final Uri localUri = Uri.fromFile(file);
localIntent.setData(localUri);
context.sendBroadcast(localIntent);
}
public static ContentValues getImageContentValues(Context paramContext, File paramFile, long paramLong) {
ContentValues localContentValues = new ContentValues();
localContentValues.put("title", paramFile.getName());
localContentValues.put("_display_name", paramFile.getName());
localContentValues.put("mime_type", "image/jpeg");
localContentValues.put("datetaken", Long.valueOf(paramLong));
localContentValues.put("date_modified", Long.valueOf(paramLong));
localContentValues.put("date_added", Long.valueOf(paramLong));
localContentValues.put("orientation", Integer.valueOf(0));
localContentValues.put("_data", paramFile.getAbsolutePath());
localContentValues.put("_size", Long.valueOf(paramFile.length()));
return localContentValues;
}
/**
* 保存视频
* @param context
* @param file
*/
public static void saveVideo(Context context, File file) {
//是否添加到相册
ContentResolver localContentResolver = context.getContentResolver();
ContentValues localContentValues = getVideoContentValues(context, file, System.currentTimeMillis());
Uri localUri = localContentResolver.insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, localContentValues);
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, localUri));
}
public static ContentValues getVideoContentValues(Context paramContext, File paramFile, long paramLong) {
ContentValues localContentValues = new ContentValues();
localContentValues.put("title", paramFile.getName());
localContentValues.put("_display_name", paramFile.getName());
localContentValues.put("mime_type", "video/mp4");
localContentValues.put("datetaken", Long.valueOf(paramLong));
localContentValues.put("date_modified", Long.valueOf(paramLong));
localContentValues.put("date_added", Long.valueOf(paramLong));
localContentValues.put("_data", paramFile.getAbsolutePath());
localContentValues.put("_size", Long.valueOf(paramFile.length()));
return localContentValues;
}
}
6.文件下载工具类:
/**
* @anthor:njb
* @date: 2020-04-21 04:36
* @desc: 文件下载工具类
**/
public class AndroidDownloadManager {
private DownloadManager downloadManager;
private Context context;
private long downloadId;
private String url;
private String name;
private String path;
private AndroidDownloadManagerListener listener;
public AndroidDownloadManager(Context context, String url) {
this(context, url, getFileNameByUrl(url));
}
public AndroidDownloadManager(Context context, String url, String name) {
this.context = context;
this.url = url;
this.name = name;
}
public AndroidDownloadManager setListener(AndroidDownloadManagerListener listener) {
this.listener = listener;
return this;
}
/**
* 开始下载
*/
public void download() {
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
//移动网络情况下是否允许漫游
request.setAllowedOverRoaming(false);
//在通知栏中显示,默认就是显示的
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE);
request.setTitle(name);
request.setDescription("文件正在下载中......");
request.setVisibleInDownloadsUi(true);
//设置下载的路径
File file = new File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), name);
request.setDestinationUri(Uri.fromFile(file));
path = file.getAbsolutePath();
//获取DownloadManager
if (downloadManager == null) {
downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
}
//将下载请求加入下载队列,加入下载队列后会给该任务返回一个long型的id,通过该id可以取消任务,重启任务、获取下载的文件等等
if (downloadManager != null) {
if (listener != null) {
listener.onPrepare();
}
downloadId = downloadManager.enqueue(request);
}
//注册广播接收者,监听下载状态
context.registerReceiver(receiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
}
//广播监听下载的各个状态
private BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
DownloadManager.Query query = new DownloadManager.Query();
//通过下载的id查找
query.setFilterById(downloadId);
Cursor cursor = downloadManager.query(query);
if (cursor.moveToFirst()) {
int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
switch (status) {
//下载暂停
case DownloadManager.STATUS_PAUSED:
break;
//下载延迟
case DownloadManager.STATUS_PENDING:
break;
//正在下载
case DownloadManager.STATUS_RUNNING:
break;
//下载完成
case DownloadManager.STATUS_SUCCESSFUL:
if (listener != null) {
listener.onSuccess(path);
}
cursor.close();
context.unregisterReceiver(receiver);
break;
//下载失败
case DownloadManager.STATUS_FAILED:
if (listener != null) {
listener.onFailed(new Exception("下载失败"));
}
cursor.close();
context.unregisterReceiver(receiver);
break;
}
}
}
};
// ——————————————————————私有方法———————————————————————
/**
* 通过URL获取文件名
*
* @param url
* @return
*/
private static final String getFileNameByUrl(String url) {
String filename = url.substring(url.lastIndexOf("/") + 1);
filename = filename.substring(0, filename.indexOf("?") == -1 ? filename.length() : filename.indexOf("?"));
return filename;
}
}
public interface AndroidDownloadManagerListener {
void onPrepare();
void onSuccess(String path);
void onFailed(Throwable throwable);
}
7.适配了7.0文件共享权限:
8.最后放一个完整的效果图:可以看到图片和视频到保存到系统,并且图片和视频都可以打开.
9.目前只是测试了华为、小米、魅族、联想等几种机型,可能在某些机型出现问题,小伙伴们可以自行测试,AndroidQ也没有适配,如果有兴趣可以加上.后面我也会加上.