admin管理员组文章数量:1592952
有同学问,删除文件不就一个File.delete()吗,有什么好探索的?那你就太年轻了,随着Android的版本更迭,权限越来越严格,不是你说删就能删的。
首先,是Android 5.0(L)以前删除文件的方式:
① File.delete()
② ContentResolver.delete()
Android 5.0(L)以后,你想通过单纯的调用File.delete()或着ContentResolver.delete()来删除Sdcard上的文件会删除失败。前者提示没有权限,后者提示删除成功,但仅仅删除数据库文件对应的信息,但物理文件还存在,手机重启后MediaScanner会重新将其信息扫描进数据库。
root?将apk放在system下?这些都太小题大做了,而且还不一定可以成功,因为权限控制太严格了,即使你有系统权限也不一定能File.delete()掉,当然如果有root权限你可以尝试调用命令rm尝试,但目前root设备实在是很不安全。下面开始介绍非root情况下使用平台正规api删除sdcard文件的方法。
在Android平台中,提供了一个名为DocumentsProvider的内容提供者,当然要使用它就必需继承它。它的作用就是帮助开发者构件一棵Documents树,树的根节点及文件目录的根结点。底下的文件或文件夹就是这棵树的枝叶。说了这么多,我们来介绍一下今天的主角:ExternalStorageProvider,他是继承自DocumentsProvider的一个平台类,通过它我们就能间接的删除Sdcard上的文件了。
正规途径①:
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, 42);
首先通过上面的代码打开系统的DocumentsUI界面,并选择Sdcard的根目录,并点击确认,即可选中U盘目录。
接着在onActivityResult中获取sdcard在ExternalStorageProvider中对应的URI
Uri uri = intent.getData();
//content://com.android.externalstorage.documents/tree/0C3D-8650%3A
接下来获取读写权限:
getContentResolver().takePersistableUriPermission(data,Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
删除文件:
// SDCARD_UUID就是上面的0C3D-8650,filePath是相对于根目录的路径,如:根目录下的aa.pvr,直接传aa.pvr即可
Uri uri = DocumentsContract.buildDocumentUriUsingTree(uri , SDCARD_UUID + ":" + filePath);
DocumentsContract.deleteDocument(mContentResolver, uri);
好了,以上似乎一切正常,然后现实总会给你一个大嘴巴子,就在startActivityForResult的时候,提示诸如“ActivityNotFoundException”、“No activity found to handle…”等等,意思就是找不到对应的Activity去处理选择USB的action,WNM…估计又是哪个粗心的底层开发把这个漏掉了,好吧,既然你系统都编出来上市了,在升级系统之前,我们这些上层应用开发只能曲线救国呗,详细的代码来了,注释都有,一目了然,拿去不谢:
public class MainActivity3 extends AppCompatActivity {
private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
private static final int REQUEST_CODE_RW = 3;
private static final int REQUEST_CODE_URI = 4010;
private String mUsbPath;
// USB节点名称,如:2E54-008A
// 参考1:https://en.wikipedia/wiki/Volume_serial_number
// 参考2:https://android.stackexchange/questions/128703/where-is-the-mount-point-folder-for-a-usb-pen-drive-mounted-on-marshmallow
private String mMountVolumeSerialNumber;
// 要删除的文件名
private String mTargetFileName = "aa.pvr";
// USB根目录的URI,有此URI的权限才可以对此URI对于目录下的文件进行修改删除(但并不拥有子目录的文件权限,需要另外申请)
private Uri mUsbRootUri;
private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (TextUtils.isEmpty(action)) {
return;
}
switch (action) {
case ACTION_USB_PERMISSION:
synchronized (this) {
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
Toast.makeText(MainActivity3.this, "授权成功", Toast.LENGTH_SHORT).show();
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
readMtpDevice(usbDevice);
} else {
Toast.makeText(MainActivity3.this, "授权失败", Toast.LENGTH_SHORT).show();
}
}
break;
case UsbManager.ACTION_USB_DEVICE_DETACHED:
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (usbDevice != null) {
//close connection
Toast.makeText(MainActivity3.this, "U盘拔出", Toast.LENGTH_SHORT).show();
}
break;
case UsbManager.ACTION_USB_DEVICE_ATTACHED:
//当设备插入时执行具体操作
Toast.makeText(MainActivity3.this, "U盘插入", Toast.LENGTH_SHORT).show();
initUsbPermission();
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 动态申请读写设备的权限
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE_RW);
}
// 监听广播U盘拔插
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
filter.addAction(ACTION_USB_PERMISSION);
registerReceiver(mUsbReceiver, filter);
// 初始化USB权限
initUsbPermission();
mUsbPath = getUsbPath();
// 获取U盘URI读写权限,通过这个权限最终实现文件读取删除,这里要传将删文件所在的目录,由于例子是删除U盘根目录下的文件,所以这里直接传U盘根目录了:/storage/2E54-008A
getUsbUriPermission(mUsbPath);
initView();
}
private void initView() {
if (TextUtils.isEmpty(mUsbPath)) {
// todo 说明没有root权限或者系统被修改,需要弹出对话框提示用户选择U盘
// 参考:https://wwwblogs/zqlxtt/p/5462383.html
//Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
//startActivityForResult(intent, 42);
}
findViewById(R.id.btn_del).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 这里假设我们要删除的文件为U盘根目录的aa.pvr文件,即:/storage/2E54-008A/aa.pvr
File file = new File(mUsbPath, mTargetFileName);
if (file.exists() && mUsbRootUri != null) {
//content://com.android.externalstorage.documents/tree/2E54-008A%3A
try {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
// mTargetFileName指的是要删除的文件相对于U盘根目录的路径,比如,要删除的文件在U盘根目录,那么mTargetFileName就是"aa.pvr",如果在根目录的Record文件夹下,那么mTargetFileName就是"Record/aa.pvr"
Uri targetFileUri = DocumentsContract.buildDocumentUriUsingTree(mUsbRootUri,
mMountVolumeSerialNumber + ":" + mTargetFileName);
boolean result = DocumentsContract.deleteDocument(getContentResolver(), targetFileUri);
}
} catch (Exception e) {
e.printStackTrace();
}
} else {
Toast.makeText(MainActivity3.this, "无此文件", Toast.LENGTH_SHORT).show();
return;
}
if (file.exists()) {
Toast.makeText(MainActivity3.this, "删除失败", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity3.this, "删除成功", Toast.LENGTH_SHORT).show();
}
}
});
}
/**
* 注意,这里的参数要具体到你将删除的文件所在的目录
*/
public void getUsbUriPermission(String usbRootPath) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
File sdCard = new File(usbRootPath);
StorageManager storageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
StorageVolume storageVolume = storageManager.getStorageVolume(sdCard);
Intent intent = storageVolume.createAccessIntent(null);
try {
startActivityForResult(intent, REQUEST_CODE_URI);
} catch (ActivityNotFoundException e) {
e.printStackTrace();
}
}
}
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_URI && data != null) {
Uri uri = data.getData();
mUsbRootUri = uri;
grantUriPermission(getPackageName(), uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
Intent.FLAG_GRANT_READ_URI_PERMISSION);
final int takeFlags = data.getFlags() & (Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
Intent.FLAG_GRANT_READ_URI_PERMISSION);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getContentResolver().takePersistableUriPermission(uri, takeFlags);
getContentResolver().takePersistableUriPermission(uri,
Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
}
}
/**
* 申请USB权限
*/
private void initUsbPermission() {
final UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
final HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();
for (UsbDevice device : deviceList.values()) {
if (usbManager.hasPermission(device)) {
readMtpDevice(device);
} else {
//该代码执行后,系统弹出一个对话框授予USB权限(和动态读写权限不一样)
PendingIntent pendingIntent = PendingIntent.getBroadcast(MainActivity3.this, 0, new Intent(ACTION_USB_PERMISSION), 0);
usbManager.requestPermission(device, pendingIntent);
}
}
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public Uri getUri() {
List<UriPermission> persistedUriPermissions = getContentResolver().getPersistedUriPermissions();
if (persistedUriPermissions.size() > 0) {
UriPermission uriPermission = persistedUriPermissions.get(0);
return uriPermission.getUri();
}
return null;
}
private void readMtpDevice(UsbDevice device) {
Toast.makeText(MainActivity3.this, "读取...", Toast.LENGTH_SHORT).show();
/*if (device != null) {
mDevice = device;
//读取usbDevice里的内容
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
UsbDeviceConnection connection = manager.openDevice(device);
MtpDevice mtpDevice = new MtpDevice(device);
if (mtpDevice.open(connection)) {
int[] storageIds = mtpDevice.getStorageIds();
if (storageIds == null) {
}
} else {
Toast.makeText(MainActivity.this, "获取失败", Toast.LENGTH_SHORT).show();
}
}*/
}
/**
* 通过查看mount命令的输出结果获取已经插上的U盘路径,是通用的方式,适用于没有提供底层接口的非定制化系统
* @return eg: /storage/2E54-008A
*/
private String getUsbPath() {
try {
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec("mount");
InputStream inputStream = process.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
String line;
BufferedReader br = new BufferedReader(inputStreamReader);
while ((line = br.readLine()) != null) {
if (line.contains("secure"))
continue;
if (line.contains("asec"))
continue;
if (line.contains("fat")) {// TF card
String columns[] = line.split(" ");
if (columns.length > 2) {
String usbFullName = columns[2];
int len = usbFullName.lastIndexOf("/") + 1;
if (len != -1 && usbFullName.length() > len) {
String mountName = usbFullName.substring(len);
mMountVolumeSerialNumber = mountName;
File file = new File("/storage/" + mountName);
if (file.exists()) {
// 这里只返回第一个找到的U盘,而且是Fat格式的
return file.getAbsolutePath();
}
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
对了,别忘了清单文件里的权限声明:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-feature android:name="android.hardware.usb.host" />
注意:如果是删除文件夹,步骤是一样的,教你一个简单的方法:申请的时候,传入的是U盘根目录的uri,那么你就有权限删除U盘根目录下的空文件夹了,如果文件夹不为空那么递归进去把文件删除再删文件夹。我的意思是,你只需要执行一次startActivityForResult,然后在onActivityResult里递归遍历拼接具体文件(空文件夹)的uri并删除即可删除非空文件夹。
参考:
https://wwwblogs/zqlxtt/p/5462383.html
https://blog.csdn/qq_29924041/article/details/80141514
https://stackoverflow/questions/54945401/android-ask-write-to-sd-card-permission-dialog
https://www.jianshu/p/7ec7539737ef
版权声明:本文标题:Android USB(OTG) 删除文件的探索过程 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://www.elefans.com/dianzi/1728151094a1147338.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论