Android9上,调用系统相机拍照后,相册中不显示,第三方软件也读取不到

一、问题:我的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库中的一个接口,然后在连接上之后,调用扫描文件的方法,扫描指定的路径下的文件。这样图片也就被扫描出来了。

 

 

你可能感兴趣的:(android,图片相关)