本篇文章,将围绕以下几点来讲解:

1:OTG是什么?
2:Android手机和一些Android系统的TV盒子对OTG的支持情况?
3:如何得知外接储存设备的插入和拔出的广播事件?
4:得到插入广播后,而又如何去读取外部设备的数据?

一: OTG是什么?

OTG是On-The-Go的缩写,是近年发展起来的技术,2001年12月18日由USB Implementers Forum公布,主要应用于各种不同的设备或移动设备间的联接,进行数据交换。

它提出的背景是移动消费类电子产品的迅猛增加,而之前USB协议的主从协议标准让这些电子产品在离开PC电脑时的数据传输变得艰难,OTG技术正是为了解决这一问题的标准。

二: Android手机和一些Android系统的TV盒子对OTG的支持情况?

Android4.0或以上系统的智能手机芯片都是支持USB-OTG的。但是一些android系统的TV盒子可能会不支持。不管是TV盒子还是android手机如果不支持OTG的话,可能有以下几点原因:

  1. 硬件上缺少5V升压器,外接设备没有电压供应。
  2. 硬件设备制造商为了省电考虑,从系统上屏蔽了USB-OTG功能。

解决系统屏蔽OTG问题,网上找到一个处理方案,好不好用未亲测:

ROOT后打开RE管理器,编辑system/etc/vold.fstab文件,
在vold.fstab的末尾添加如下代码# usb otg diskdev_mount usbotg /mnt/usbotg auto /devices/platform/mt_usb /devices/platform/musbfsh_hdrc 最后,修改保存,重启手机

三: 如何得知外接储存设备的插入和拔出的广播事件?

我们可以通过动态或静态的方式注册相应的广播,在广播中我们就能收到U盘的插入和拔出操作。

方式一:

我们动态注册这四个广播:

       //主要是这两个,测试发现,这两个广播的接受比较快,而且很准确UsbManager.ACTION_USB_DEVICE_ATTACHED;//动作USB设备已连接 广播UsbManager.ACTION_USB_DEVICE_DETACHED;//动作USB设备已分离 广播UsbManager.ACTION_USB_ACCESSORY_ATTACHED;//行动USB配件附件 广播UsbManager.ACTION_USB_ACCESSORY_DETACHED;//动作USB附件已分离 广播

方式二:

//主要是这两个,不过测试发现,这两个广播的接受有些缓慢,偶尔还会收不到
Intent.ACTION_MEDIA_MOUNTED //动作媒体安装 广播
Intent.ACTION_MEDIA_REMOVED //行动媒体被删除 广播Intent.ACTION_MEDIA_UNMOUNTED//行动媒体未分配 广播
Intent.ACTION_MEDIA_EJECT//行动媒体EJECT 广播

此时你可能要问了,怎么还有两种方式呢,他们有什么区别呢?其实

通过方式一,我们能准确的得知外接储存设备(例如:U盘)的插入和拔出的操作。

通过方式二,虽然广播的接受有些缓慢,偶尔有时还会收不到,不过在广播的接受中,我们可以从返回的 Intent 中,获取不少有用的信息。例如,U盘的挂载路径。得到U盘的路径,我们就能操作U盘中的文件啦。下面的第四点,会做详情的讲解。不过需要说明一下:要想读取外部设备的数据 ,你的手机或者TV盒子等等,本身要支持USB-OTG功能哦!

##四: 得到插入广播后,而又如何去读取外部设备的数据?

**强调一下:要想读取外部设备的数据 ,你的手机或者TV盒子等等,本身要支持USB-OTG功能!**不然的话,你是无法读取数据的。

###我们首先讲解 第三点中 通过方式一,得知插拔广播后,怎样去读取外部设备数据。

首先,我们项目中用到了一个开源框架,开源地址是:
https://github.com/magnusja/libaums
他是干什么的呢?看看开源作者对他的介绍:

A library to access USB mass storage devices (pen drives, external HDDs, card readers) using the Android USB Host API. Currently it supports the SCSI command set and the FAT32 file system.

大概意思是:

使用Android USB主机API访问USB大容量存储设备(笔驱动器、外部HDDs、读卡器)的库。目前它支持SCSI命令集和FAT32文件系统。

远程依赖为: implementation 'com.github.mjdev:libaums:0.5.5'
github地址:https://github.com/magnusja/libaums

除此之外我们还需要一个依赖库,来帮助实现我们广播的通讯,那就是EventBus3.0
implementation 'org.greenrobot:eventbus:3.0.0'


下面我就开始以贴出代码为主了,代码中也会做出关键性的注释!

布局文件:R.layout.activity_method_one

<?xml version="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_margin="10dp"android:text="Android盒子外接U盘文件读写测试DEMO"/><EditTextandroid:id="@+id/u_disk_edt"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="10dp"android:hint="输入要保存到U盘中的文字内容"/><Buttonandroid:id="@+id/u_disk_write"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="10dp"android:gravity="center"android:text="往U盘中写入数据"/><Buttonandroid:id="@+id/u_disk_read"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="10dp"android:gravity="center"android:text="从U盘中读取数据"/><TextViewandroid:id="@+id/u_disk_show"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_marginLeft="10dp"/>
</LinearLayout>

java类 MethodOneActivity.java:

import android.annotation.SuppressLint;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;import com.example.usbreadwriterdaemon.receiver.UsbStateChangeReceiver;
import com.example.usbreadwriterdaemon.receiver.UsbStatusChangeEvent;
import com.example.usbreadwriterdaemon.utils.FileUtil2;
import com.example.usbreadwriterdaemon.utils.ToastUtil;
import com.github.mjdev.libaums.UsbMassStorageDevice;
import com.github.mjdev.libaums.fs.FileSystem;
import com.github.mjdev.libaums.fs.UsbFile;
import com.github.mjdev.libaums.fs.UsbFileInputStream;
import com.github.mjdev.libaums.partition.Partition;import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;public class MethodOneActivity extends AppCompatActivity {private static final String TAG = "MethodOneActivity";@BindView(R.id.u_disk_edt)EditText mUDiskEdt;@BindView(R.id.u_disk_write)Button mUDiskWrite;@BindView(R.id.u_disk_read)Button mUDiskRead;@BindView(R.id.u_disk_show)TextView mUDiskShow;private UsbMassStorageDevice[] storageDevices;private UsbFile cFolder;//自定义U盘读写权限public static final String ACTION_USB_PERMISSION = "com.example.usbreadwriterdaemon.USB_PERMISSION";private final static String U_DISK_FILE_NAME = "u_disk.txt";@SuppressLint("HandlerLeak")private Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case 100:ToastUtil.showToast("保存成功");break;case 101:String txt = msg.obj.toString();if (!TextUtils.isEmpty(txt))mUDiskShow.setText("读取到的数据是:" + txt);break;}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_method_one);ButterKnife.bind(this);EventBus.getDefault().register(this);//EventBus 注册registerUDiskReceiver();//usb插拔广播 注册}@Subscribe(threadMode = ThreadMode.MAIN)public void onNetworkChangeEvent(UsbStatusChangeEvent event) {if (event.isConnected) {//接收到U盘插入广播,尝试读取U盘设备数据redUDiskDevsList();} else if (event.isGetPermission) {UsbDevice usbDevice = event.usbDevice;//用户已授权,可以进行读取操作Log.i(TAG, "onNetworkChangeEvent: ");ToastUtil.showToast("onReceive: 权限已获取");readDevice(getUsbMass(usbDevice));} else {}}@OnClick({R.id.u_disk_write, R.id.u_disk_read})public void onViewClicked(View view) {switch (view.getId()) {case R.id.u_disk_write:final String content = mUDiskEdt.getText().toString().trim();mHandler.post(new Runnable() {@Overridepublic void run() {saveText2UDisk(content);}});break;case R.id.u_disk_read:readFromUDisk();break;}}/*** @description 保存数据到U盘,目前是保存到根目录的* @author ldm* @time 2017/9/1 17:17*/private void saveText2UDisk(String content) {//项目中也把文件保存在了SD卡,其实可以直接把文本读取到U盘指定文件File file = FileUtil2.getSaveFile(getPackageName() + File.separator + FileUtil2.DEFAULT_BIN_DIR, U_DISK_FILE_NAME);try {FileWriter fw = new FileWriter(file);fw.write(content);fw.close();} catch (IOException e) {e.printStackTrace();}if (null != cFolder) {FileUtil2.saveSDFile2OTG(file, cFolder);mHandler.sendEmptyMessage(100);}}StringBuffer stringBuffer = new StringBuffer();private void readFromUDisk() {UsbFile[] usbFiles = new UsbFile[0];try {usbFiles = cFolder.listFiles();} catch (IOException e) {e.printStackTrace();}if (null != usbFiles && usbFiles.length > 0) {for (UsbFile usbFile : usbFiles) {stringBuffer.append(", " + usbFile.getName());if (usbFile.getName().equals(U_DISK_FILE_NAME)) {readTxtFromUDisk(usbFile);}}//mUDiskShow.setText("文件名:" + stringBuffer.toString());}}/*** @description U盘设备读取* @author ldm* @time 2017/9/1 17:20*/private void redUDiskDevsList() {//设备管理器UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);//获取U盘存储设备storageDevices = UsbMassStorageDevice.getMassStorageDevices(this);PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);//一般手机只有1个OTG插口for (UsbMassStorageDevice device : storageDevices) {//读取设备是否有权限,if (usbManager.hasPermission(device.getUsbDevice())) {ToastUtil.showToast("有权限");readDevice(device);} else {ToastUtil.showToast("没有权限,进行申请");//没有权限,进行申请,此时系统会有个弹框,询问你是否同意,当然我们应该同意啦!usbManager.requestPermission(device.getUsbDevice(), pendingIntent);}}if (storageDevices.length == 0) {ToastUtil.showToast("请插入可用的U盘");}}private UsbMassStorageDevice getUsbMass(UsbDevice usbDevice) {for (UsbMassStorageDevice device : storageDevices) {if (usbDevice.equals(device.getUsbDevice())) {return device;}}return null;}private void readDevice(UsbMassStorageDevice device) {try {device.init();//初始化//设备分区Partition partition = device.getPartitions().get(0);//文件系统FileSystem currentFs = partition.getFileSystem();currentFs.getVolumeLabel();//可以获取到设备的标识//通过FileSystem可以获取当前U盘的一些存储信息,包括剩余空间大小,容量等等Log.e("Capacity: ", currentFs.getCapacity() + "");Log.e("Occupied Space: ", currentFs.getOccupiedSpace() + "");Log.e("Free Space: ", currentFs.getFreeSpace() + "");Log.e("Chunk size: ", currentFs.getChunkSize() + "");ToastUtil.showToast("可用空间:" + currentFs.getFreeSpace());cFolder = currentFs.getRootDirectory();//设置当前文件对象为根目录} catch (Exception e) {e.printStackTrace();}}private void readTxtFromUDisk(UsbFile usbFile) {Log.i(TAG, "readTxtFromUDisk: ");UsbFile descFile = usbFile;//读取文件内容InputStream is = new UsbFileInputStream(descFile);//读取秘钥中的数据进行匹配StringBuilder sb = new StringBuilder();BufferedReader bufferedReader = null;try {bufferedReader = new BufferedReader(new InputStreamReader(is));String read;while ((read = bufferedReader.readLine()) != null) {sb.append(read);}Message msg = mHandler.obtainMessage();msg.what = 101;msg.obj = sb;mHandler.sendMessage(msg);} catch (Exception e) {e.printStackTrace();} finally {try {if (bufferedReader != null) {bufferedReader.close();}} catch (IOException e) {e.printStackTrace();}}}/*** usb插拔广播 注册*/private void registerUDiskReceiver() {IntentFilter usbDeviceStateFilter = new IntentFilter();usbDeviceStateFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);usbDeviceStateFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);usbDeviceStateFilter.addAction(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);usbDeviceStateFilter.addAction(UsbManager.ACTION_USB_ACCESSORY_DETACHED);usbDeviceStateFilter.addAction("android.hardware.usb.action.USB_STATE");usbDeviceStateFilter.addAction(ACTION_USB_PERMISSION); //自定义广播registerReceiver(new UsbStateChangeReceiver(), usbDeviceStateFilter);}@Overrideprotected void onDestroy() {super.onDestroy();if (EventBus.getDefault().isRegistered(this)) {EventBus.getDefault().unregister(this);}}
}

usb插拔广播接受 UsbStateChangeReceiver.java

*** Created by yuanpk on 2018/8/2  14:22* <p>* Description:usb插拔广播接受*/
public class UsbStateChangeReceiver extends BroadcastReceiver {private static final String TAG = "UsbStateChangeReceiver";private boolean isConnected;@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {isConnected = true;ToastUtil.showToast("onReceive: USB设备已连接");UsbDevice device_add = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);if (device_add != null) {EventBus.getDefault().post(new UsbStatusChangeEvent(isConnected));} else {ToastUtil.showToast("onReceive: device is null");}} else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {//Log.i(TAG, "onReceive: USB设备已分离");isConnected = false;ToastUtil.showToast("onReceive: USB设备已拔出");EventBus.getDefault().post(new UsbStatusChangeEvent(isConnected));} else if (action.equals(MethodOneActivity.ACTION_USB_PERMISSION)) {UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);//允许权限申请if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {if (usbDevice != null) {Log.i(TAG, "onReceive: 权限已获取");EventBus.getDefault().post(new UsbStatusChangeEvent(true, usbDevice));} else {ToastUtil.showToast("没有插入U盘");}} else {ToastUtil.showToast("未获取到U盘权限");}} else {//Log.i(TAG, "onReceive: action=" + action);ToastUtil.showToast("action= " + action);}}
}

UsbStatusChangeEvent.java

import android.hardware.usb.UsbDevice;/*** Created by yuanpk on 2018/8/1  9:43* Description:TODO*/
public class UsbStatusChangeEvent {public boolean isConnected = false;public boolean isGetPermission = false;public UsbDevice usbDevice;public String filePath = "";public UsbStatusChangeEvent(boolean isConnected) {this.isConnected = isConnected;}public UsbStatusChangeEvent(String filePath) {this.filePath = filePath;}public UsbStatusChangeEvent(boolean isGetPermission, UsbDevice usbDevice) {this.isGetPermission = isGetPermission;this.usbDevice = usbDevice;}}

FileUtil2.java 工具类

import android.os.Environment;import com.github.mjdev.libaums.fs.UsbFile;
import com.github.mjdev.libaums.fs.UsbFileOutputStream;import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;import static android.os.Environment.getExternalStorageDirectory;/*** 文件操作工具类** @author ldm* @description:* @date 2016-4-28 下午3:17:10*/
public final class FileUtil2 {public static final String DEFAULT_BIN_DIR = "usb";/*** 检测SD卡是否存在*/public static boolean checkSDcard() {return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());}/*** 从指定文件夹获取文件** @return 如果文件不存在则创建, 如果如果无法创建文件或文件名为空则返回null*/public static File getSaveFile(String folderPath, String fileNmae) {File file = new File(getSavePath(folderPath) + File.separator + fileNmae);try {file.createNewFile();} catch (IOException e) {e.printStackTrace();}return file;}/*** 获取SD卡下指定文件夹的绝对路径** @return 返回SD卡下的指定文件夹的绝对路径*/public static String getSavePath(String folderName) {return getSaveFolder(folderName).getAbsolutePath();}/*** 获取文件夹对象** @return 返回SD卡下的指定文件夹对象,若文件夹不存在则创建*/public static File getSaveFolder(String folderName) {File file = new File(getExternalStorageDirectory().getAbsoluteFile()+ File.separator+ folderName+ File.separator);file.mkdirs();return file;}/*** 关闭流*/public static void closeIO(Closeable... closeables) {if (null == closeables || closeables.length <= 0) {return;}for (Closeable cb : closeables) {try {if (null == cb) {continue;}cb.close();} catch (IOException e) {e.printStackTrace();}}}private static void redFileStream(OutputStream os, InputStream is) throws IOException {int bytesRead = 0;byte[] buffer = new byte[1024 * 8];while ((bytesRead = is.read(buffer)) != -1) {os.write(buffer, 0, bytesRead);}os.flush();os.close();is.close();}/*** @description 把本地文件写入到U盘中* @author ldm* @time 2017/8/22 10:22*/public static void saveSDFile2OTG(final File f, final UsbFile usbFile) {UsbFile uFile = null;FileInputStream fis = null;try {//开始写入fis = new FileInputStream(f);//读取选择的文件的if (usbFile.isDirectory()) {//如果选择是个文件夹UsbFile[] usbFiles = usbFile.listFiles();if (usbFiles != null && usbFiles.length > 0) {for (UsbFile file : usbFiles) {if (file.getName().equals(f.getName())) {file.delete();}}}uFile = usbFile.createFile(f.getName());UsbFileOutputStream uos = new UsbFileOutputStream(uFile);try {redFileStream(uos, fis);} catch (IOException e) {e.printStackTrace();}}} catch (final Exception e) {e.printStackTrace();}}
}

到此我们的方式一读取方式就结束啦!虽然代码有点多,但是逻辑还是比较清楚的,首先是我们的布局文件,然后是我们布局文件对应的类,再然后呢是我们的广播接受类,最后是我们的一个工具类。

接着讲解 第三点中 通过方式二,得知插拔广播后,怎样去读取外部设备数据

这里,就不再像方式一,说的那么详细啦,不过也不必担心,最后我会把源码放到github上,供大家参考的。

首先,我们可以通过动态或静态方法,注册广播。这里是采用的静态注册方式,如下:

 <receiver android:name=".receiver.USBReceiver"><intent-filter><action android:name="android.intent.action.MEDIA_REMOVED"/><action android:name="android.intent.action.MEDIA_MOUNTED"/><data android:scheme="file"/></intent-filter></receiver>

广播的意义我们已经在第三点中,说明了哦。

广播接受类, USBReceiver.java

/*** Created by yuanpk on 2018/8/3  11:43* <p>* Description:TODO*/
public class USBReceiver extends BroadcastReceiver {private static final String TAG = USBReceiver.class.getSimpleName();@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {//ACTION_MEDIA_REMOVEDString mountPath = intent.getData().getPath();Log.d(TAG, "mountPath = " + mountPath);if (!TextUtils.isEmpty(mountPath)) {//读取到U盘路径再做其他业务逻辑//ToastUtil.showToast("路径=" + mountPath);EventBus.getDefault().post(new UsbStatusChangeEvent(mountPath));}} else if (action.equals(Intent.ACTION_MEDIA_UNMOUNTED) || action.equals(Intent.ACTION_MEDIA_EJECT)) {Toast.makeText(context, "No services information detected !", Toast.LENGTH_SHORT).show();} else if (action.equals("android.intent.action.BOOT_COMPLETED")) {//如果是开机完成,则需要调用另外的方法获取U盘的路径}}
}

我们通过EventBus,把得到的文件路径,发送的指定Activity中,然后在指定Activity中注册EventBus广播,接受就可以啦:

  @Subscribe(threadMode = ThreadMode.MAIN)public void onNetworkChangeEvent(UsbStatusChangeEvent event) {strFilePath = event.filePath;Toast.makeText(this, "u盘路径:" + strFilePath, Toast.LENGTH_SHORT).show();//这是我测试是U盘中,创建的文件,并且拷贝了一个.mkv格式视频文件。File file = new File(strFilePath + "/wode369tv", "local.mkv");Toast.makeText(this, "getPath=" + file.getPath(), Toast.LENGTH_SHORT).show();//例如:拿到视频文件之后,我们就可以通过播放视频的控件,去播放我们U盘中的视频啦!}

到此,我们的文章,就介绍完毕啦!文章也是写了好几天,着实不易,如果对你有所帮助,点个赞吧!十分谢谢!

文章源码


参考博客:

Android设备与外接U盘实现数据读取操作

Android开发——遍历读写U盘、SD卡等外部存储

Android 读取外接储存设备的数据(如挂载的U盘,SD卡等)相关推荐

  1. Android读取assets目录下文件数据内容

    Android读取assets目录下文件数据内容 Android的体系架构设计中,assets目录下的数据内容(图片.文件等等)将不会被Android系统压缩.二次处理等,assets目录下的文件 ...

  2. 纽曼录音笔转换软件_【数据修复】强烈推荐,SD卡数据免费恢复软件!

    在了解如何恢复SD卡被删除的图片之前,我们首先来了解一下SD卡数据恢复的原理.事实上,SD卡数据恢复的原理与数据写入的原理有关. 简单来说,就是SD卡在存储数据的过程中,会将数据转换成0和1写到数据记 ...

  3. Android 获取外接储存的设备路径(如挂载的U盘)

    前提介绍:项目中遇到这样一种情况,需要在开机和插入U盘的时候去获取U盘的路径,并读取U盘中的一个文件.其中插入U盘的时候,直接监听U盘挂载广播,可以获取U盘的路径,例如说路径A.但是开机的时候去需要找 ...

  4. STM32配置CH375B成HID Host模式读取自定义HID设备的数据 ——STM32端口初始化

    最近产品需要一个USB主机测试治具,所以需要做一个USB HOST去读取HID设备的数据,由于以前也没做过USB方面的项目,对这一块也不是很熟悉,因此遇到了很多困难,所幸的是经过两天半的努力,最终完成 ...

  5. STM32配置CH375B成HID Host模式读取自定义HID设备的数据 ——STM32配置CH375B接口函数

    接着上一篇上传,这个是STM32配置CH375B时用到的接口函数 头文件: #ifndef __BSP_CH375_H__ #define __BSP_CH375_H__#include " ...

  6. 【小陈睡不醒SD卡数据读取以及Altium Designer绘制stm32最小系统原理图及stm32+SD卡绘制】

    一SD卡介绍 1.SD卡 SD存储卡(Secure Digital Memory Card)是一种基于半导体快闪存储器的新一代高速存储设备.SD存储卡的技术是从MMC卡(MultiMedia Card ...

  7. Android存储设备(U盘,SD卡)状态监测(《Android 2.3 SD卡挂载流程浅析1234567)

    我们是以DV6300-T的平台来做测试的,发现有2种方式来检测android中external media(包括SD卡,USB)的状态. 一种是使用StorageListener监听,还有一种是使用广 ...

  8. 【Android 性能优化】应用启动优化 ( 方法追踪代码模板 | 示例项目 | SD 卡访问权限 | 示例代码 | 获取 Trace 文件 | Android Studio 查看文件)

    文章目录 一. 方法追踪代码模板 二. 追踪 Launch 页面的 onCreate 方法执行情况 1. 示例项目 2. SD 卡访问权限问题 ( 动态权限申请 ) 3. MainActivity o ...

  9. 内存卡数据删除了怎么恢复?sd卡数据恢复,3个步骤找回

    sd卡就是常见的内存卡,它作为数据的存储设备之一,通常用来保存照片.音频.视频等数据.但是因为误删除或者格式化原因把里面的数据删除了怎么办?让小编来告诉你sd卡数据恢复的方法,无须再为sd卡数据丢失而 ...

最新文章

  1. [JAVA] java仿windows 字体设置选项卡
  2. RESTful之路由Routers
  3. reduce_sum()中的reduction_indices
  4. 总结:8.9 模拟(枚举搜索)
  5. 多径衰落信道下基带模型的多用户BPSK直接序列扩频系统MATLAB仿真(m序列、Gold序列和正交Gold序列)
  6. Net基础篇_学习笔记_第十二天_面向对象继承(字符串_字符串的不可变性)
  7. Tensorflow Serving Docker compose 部署服务细节(Ubuntu)
  8. 相同MAC地址,相同IP的两天电脑为什么可以同时上网互不影响(转自Nothel的blog)
  9. 语句乎?表达式乎?(Python/C)
  10. mysql修改密码5.7_mysql数据库5.7版修改密码详细(centos7)
  11. Object.prototype.toString方法
  12. 如何为resin的jvm-default.log瘦身
  13. android webview 无法加载插件,webView 测试问题,无法检测到 webView 控件
  14. 深度学习(13):pointnet++论文翻译与学习
  15. Redis 清空数据库
  16. 百度网盘客户端(java)版本
  17. usaco Name That Number
  18. 东方财富 自动止损程序
  19. 感谢爱测未来,零基础的我的实习期是这么过来的
  20. 华为云Debina登录界面输入正确密码却显示认证失败

热门文章

  1. shutil,re,hashlib,subprocess模块及其相关
  2. 【观察】打造智能决策“新引擎”,杉数科技勇闯“无人区”
  3. C-kermit安装方法
  4. 使用OpenCV实现运动背景的重建
  5. 攻防世界RE练习区题目总结(1-10)
  6. 安信证券资管清算重要业务在原生分布式数据库的创新实践
  7. 2018年(第17届)中国软件业务收入前百家企业名单
  8. vue-cli打包后,找不到css、js文件问题的解决
  9. 苹果零日漏洞利用市售800万欧元
  10. 计算机图形学直线算法论文,《计算机图形学》中直线生成算法的教学心得