浅析Android(安卓)M新功能Adoptable Storage Devices(适配的存储设备)
1.前言
从所有的Nexus手机和平板都没有MicroSd卡槽,可以知道谷歌并不喜欢在Android设备上使用外部存储,从软件工程师的角度来讲是可以理解的,但是主流消费者明显有更强烈的需求。在Android M系统中,谷歌观念发生了改变,终于提供了官方的方式让用户将应用移动到外部存储上,其新功能名字叫“Adoptable Storage Devices”。
2.Adoptable Storage Devcies的作用
Adoptable Storage Devices使系统获得了更大的存储容量,并且使用户可以自由移动应用和隐私数据到外部存储设备,不在需要第三方工具。用户使用Android设备时,可以主动的选择把外部存储设备格式化为常规移动存储或者系统内部存储。在格式化时系统给了两种选择:(1)Use as portable storage(for moving photos and other media between divices),仅仅作为外部存储设备;(2)Use as internal storage(for storing anything on this device only,including apps and photos),包裹一个加密层后,能够作为内部存储空间存储应用和隐私数据,经过加密后,该存储设备就不能再被别的设备使用。
3.Adpotable Storage Decices实现过程
本节主要介绍从framework层到vold层的过程。主要涉及类如下:
StorageSettings–>StorageWizardInit–>StorageWizardFormatConfirm–>StorageWizardFormatProgress–>MountService–>NativeDaemonConnector–>commandlistener.cpp
3.1 当需要格式化时,用户进入格式化选择界面(StorageWizardInit类),在StorageSettings:: onPreferenceTreeClick()中DiskInitFragment.show(this, R.string.storage_dialog_unmountable, vol.getDiskId())将触发格式化选择界面。相关代码如下:
public static class DiskInitFragment extends DialogFragment { … @Override public Dialog onCreateDialog(Bundle savedInstanceState) { … builder.setPositiveButton(R.string.storage_menu_set_up, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { final Intent intent = new Intent(context, StorageWizardInit.class); intent.putExtra(DiskInfo.EXTRA_DISK_ID, diskId); startActivity(intent);//当用户点击Set Up,转到格式化选择界面 } }); builder.setNegativeButton(R.string.cancel, null); return builder.create(); } }
3.2 从StorageWizardInit::Oncreate()中代码setIllustrationInternal(true)可以知道,谷歌处理时是先默认为内部存储(internal storage)的,用户选择后再覆盖默认类型。通过Intent传递StorageWizardFormatConfirm.EXTRA_FORMAT_PRIVATE参数,告知格式化为外部或者内部存储。相关代码如下: @Override public void onNavigateNext() { if (mRadioExternal.isChecked()) {//用户选择格式化为外部移动存储 if (mVolume != null && mVolume.getType() == VolumeInfo.TYPE_PUBLIC && mVolume.getState() != VolumeInfo.STATE_UNMOUNTABLE) { // Remember that user made decision mStorage.setVolumeInited(mVolume.getFsUuid(), true); final Intent intent = new Intent(this, StorageWizardReady.class); intent.putExtra(DiskInfo.EXTRA_DISK_ID, mDisk.getId()); startActivity(intent); } else { // Gotta format to get there final Intent intent = new Intent(this, StorageWizardFormatConfirm.class); intent.putExtra(DiskInfo.EXTRA_DISK_ID, mDisk.getId()); intent.putExtra(StorageWizardFormatConfirm.EXTRA_FORMAT_PRIVATE, false);//false,格式化为外部存储 startActivity(intent); } } else if (mRadioInternal.isChecked()) {//用户选择格式化为内部存储 final Intent intent = new Intent(this, StorageWizardFormatConfirm.class); intent.putExtra(DiskInfo.EXTRA_DISK_ID, mDisk.getId()); intent.putExtra(StorageWizardFormatConfirm.EXTRA_FORMAT_PRIVATE, true);//true,格式化为内部存储 startActivity(intent); } }
3.3 StorageWizardFormatConfirm:Oncreate()提取Intent传递的值,判断用户想格式化为外部或者内部存储。代码如下:
mFormatPrivate = getIntent().getBooleanExtra(EXTRA_FORMAT_PRIVATE, false); if (mFormatPrivate) { setHeaderText(R.string.storage_wizard_format_confirm_title); setBodyText(R.string.storage_wizard_format_confirm_body, mDisk.getDescription());//设置内部存储的相关显示String } else { setHeaderText(R.string.storage_wizard_format_confirm_public_title); setBodyText(R.string.storage_wizard_format_confirm_public_body, mDisk.getDescription());设置外部存储的相关显示String }
当用户点击Next时,重写onNavigateNext()方法,跳转到格式化进程显示界面。
public void onNavigateNext() { final Intent intent = new Intent(this, StorageWizardFormatProgress.class); intent.putExtra(DiskInfo.EXTRA_DISK_ID, mDisk.getId()); intent.putExtra(EXTRA_FORMAT_PRIVATE, mFormatPrivate); intent.putExtra(EXTRA_FORGET_UUID, getIntent().getStringExtra(EXTRA_FORGET_UUID)); startActivity(intent);//跳转并传递mFormatPrivate到格式化进程界面 finishAffinity(); }
3.4 Storagewizardformatprogress:Oncreate()中启动一个task(mTask = new PartitionTask()),并分别调用MountService的相应方法,告知MountService准备发送什么指令到Vold层。代码如下:
public static class PartitionTask extends AsyncTask { ... protected Exception doInBackground(Void... params) { final StorageWizardFormatProgress activity = mActivity; final StorageManager storage = mActivity.mStorage; try { if (activity.mFormatPrivate) { storage.partitionPrivate(activity.mDisk.getId());//内部存储 ... } else { storage.partitionPublic(activity.mDisk.getId());//外部存储 } ... } } }
3.5 MountService:partitionPrivate(…)中mConnector.execute(“volume”, “partition”, diskId, “private”)发送格式化为内部存储的意图到NativeDaemonConnector, partitionPublic(…)中mConnector.execute(“volume”, “partition”,diskId, “public”)发送格式化为外部存储的意图到NativeDaemonConnector。
3.6 NativeDaemonConnector通过socket将MountSercvice中的指令发送到下层vold中。
3.7 vold层在CommandListener.cpp中Socket监听到FrameWork层发送的指令后,执行相应的操作,这就是Adoptable Storage Devices功能就从FrameWork层到Vold。代码如下:
int CommandListener::VolumeCmd::runCommand(SocketClient *cli, int argc, char **argv) { ... if (cmd == "reset") { ... } else if (cmd == "partition" && argc > 3) { // partition [diskId] [public|private|mixed] [ratio] std::string id(argv[2]); auto disk = vm->findDisk(id); if (disk == nullptr) { return cli->sendMsg(ResponseCode::CommandSyntaxError,"Unknown disk", false); } std::string type(argv[3]); if (type == "public") { return sendGenericOkFail(cli, disk->partitionPublic());//外部存储 } else if (type == "private") { return sendGenericOkFail(cli, disk->partitionPrivate());//内部存储 } else if (type == "mixed") { if (argc < 4) { return cli->sendMsg(ResponseCode::CommandSyntaxError, nullptr, false); } int frac = atoi(argv[4]); return sendGenericOkFail(cli, disk->partitionMixed(frac)); } else { return cli->sendMsg(ResponseCode::CommandSyntaxError, nullptr, false); } } ... }
更多相关文章
- 简单处理Android(安卓)65536方法越界问题
- Android中AnimationDrawable使用的简单实例
- android 开发规范
- 【Android】说做就做:都市列表+卫星地图
- JIN学习一、Android使用已有C/C++代码、第三方SO库的方法
- Android面试题-横竖屏切换,activity生命周期的执行顺序,基于8.0,9.
- 使用Vitamio打造自己的Android万能播放器(3)——本地播放(主界面、
- 给大家介绍几个常见的Android代码片段
- Android中Action Bar的使用