一、问题:我的app调用系统拍照功能,然后使用知乎的Matisse框架加载自己定义的文件夹中的图片,发现怎么也不显示。
二、原因:Android给图片创建了一个数据库,我们需要手动去刷新这个数据库,把刚刚拍摄的照片信息放到数据库中,其它的软件才可以获取到图片。否则只有自己找到那个路径去拿图片了。
三、先看实现:
package com.ysl.photo;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.text.TextUtils;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.FileProvider;
import com.tbruyelle.rxpermissions2.RxPermissions;
import com.ysl.myandroidbase.R;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import io.reactivex.disposables.Disposable;
public class TakePhotoActivity extends AppCompatActivity {
private String path;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_takephoto);
Disposable subscribe = new RxPermissions(this).request(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE)
.subscribe(aBoolean -> {
if (aBoolean) {
findViewById(R.id.take_photo).setOnClickListener(v -> takePhoto());
} else {
}
});
}
private void takePhoto() {
Intent openCameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File file = new File(getPictureDirPath().getAbsolutePath(),
System.currentTimeMillis() + ".jpg");
path = file.getAbsolutePath();
Uri imageUri = FileProvider.getUriForFile(getApplicationContext(),
"com.ysl.myandroidbase.fileprovider", file);
System.out.println("-------->"+imageUri);
//-------->content://com.ysl.myandroidbase.fileprovider/m/MyAndroidBase/mybase/1574394370534.jpg
System.out.println("-------->"+path);
//-------->/storage/emulated/0/MyAndroidBase/mybase/1574394370534.jpg
openCameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
openCameraIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
List resInfoList = getApplicationContext().getPackageManager()
.queryIntentActivities(openCameraIntent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
String packageName = resolveInfo.activityInfo.packageName;
getApplicationContext().grantUriPermission(packageName, imageUri,
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
| Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
}
TakePhotoActivity.this.startActivityForResult(openCameraIntent, 0);
}
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case 0:
if (!TextUtils.isEmpty(path) && resultCode == -1) {
Bitmap mBitmap = BitmapFactory.decodeFile(path);
saveBitmapFile(mBitmap);
}
break;
default:
break;
}
}
/**
* 保存图片并发送广播通知数据库刷新,只有这样才能在相册里面看到这张照片
* @param bitmap
*/
public void saveBitmapFile(Bitmap bitmap){
//将要保存图片的路径
File file = new File(path);
try {
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos);
bos.flush();
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
//发送广播给系统,刷新数据库
Uri uri = Uri.fromFile(file);
System.out.println("imageUri------>"+uri);
//imageUri------>file:///storage/emulated/0/MyAndroidBase/mybase/1574394370534.jpg
Intent localIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri);
sendBroadcast(localIntent);
//下面这种扫描的方法也可以刷新数据库
new SingleMediaScanner(this.getApplicationContext(),
getPictureDirPath().getAbsolutePath(),
new SingleMediaScanner.ScanListener() {
@Override public void onScanFinish() {
Log.i("SingleMediaScanner", "scan finish!");
}
});
}
public static File getPictureDirPath() {
File mIVMSFolder = null;
try {
String path = Environment.getExternalStorageDirectory().getAbsolutePath()
+ File.separator + "MyAndroidBase" + File.separator + "mybase";
mIVMSFolder = new File(path);
if (!mIVMSFolder.exists()) {
mIVMSFolder.mkdirs();
}
} catch (Exception e) {
e.printStackTrace();
}
return mIVMSFolder;
}
}
以上就是核心代码:
调用相机时:
Uri imageUri = FileProvider.getUriForFile(getApplicationContext(),
"com.ysl.myandroidbase.fileprovider", file);
使用这种方式来获取uri,因为使用文件的方法在高版本Android下报错;其中的第二个参数怎么来的:
在清单文件中:
android:grantUriPermissions="true">
path="."/>
看看打印的uri:
content://com.ysl.myandroidbase.fileprovider/m/MyAndroidBase/mybase/1574394370534.jpg
方便和后面对比;
在activity跳转回调中:
要保存图片并发送更新系统数据库的广播:
//发送广播给系统,刷新数据库
Uri uri = Uri.fromFile(file);
System.out.println("imageUri------>"+uri);
//imageUri------>file:///storage/emulated/0/MyAndroidBase/mybase/1574394370534.jpg
Intent localIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri);
sendBroadcast(localIntent);
这段代码就可以让相册发现刚刚拍摄的照片了;
注意这里一个坑:看这里获取的uri方法:Uri uri = Uri.fromFile(file); 再看打印的uri,是以file://+路径的方式。
file:///storage/emulated/0/MyAndroidBase/mybase/1574394370534.jpg
我们再看上面的uri:content://com.ysl.myandroidbase.fileprovider/m/MyAndroidBase/mybase/1574394370534.jpg
这两个是不一样的;我之前就是因为发送广播时使用了上面的uri,导致怎么都不起作用;后来才发现,这里已经存好文件了,就使用Uri.fromFile(file)来获取uri。
再往下看:
//下面这种扫描的方法也可以刷新数据库 new SingleMediaScanner(this.getApplicationContext(), getPictureDirPath().getAbsolutePath(), new SingleMediaScanner.ScanListener() { @Override public void onScanFinish() { Log.i("SingleMediaScanner", "scan finish!"); } });
这段代码也能实现刷新数据库的功能,主要是引入一个实现MediaScannerConnection.MediaScannerConnectionClient的类:
package com.ysl.photo;
import android.content.Context;
import android.media.MediaScannerConnection;
import android.net.Uri;
/**
* description:媒体扫描
*/
public class SingleMediaScanner implements MediaScannerConnection.MediaScannerConnectionClient {
private MediaScannerConnection mMsc;
private String mPath;
private ScanListener mListener;
public interface ScanListener {
/**
* scan finish
*/
void onScanFinish();
}
public SingleMediaScanner(Context context, String mPath, ScanListener mListener) {
this.mPath = mPath;
this.mListener = mListener;
this.mMsc = new MediaScannerConnection(context, this);
this.mMsc.connect();
}
@Override public void onMediaScannerConnected() {
mMsc.scanFile(mPath, null);
}
@Override public void onScanCompleted(String mPath, Uri mUri) {
mMsc.disconnect();
if (mListener != null) {
mListener.onScanFinish();
}
}
}
可以看到,他是Android的media库中的一个接口,然后在连接上之后,调用扫描文件的方法,扫描指定的路径下的文件。这样图片也就被扫描出来了。