Начиная с API 19 (KitKat) почти невозможно напрямую записать содержимое SDcard как простое File
, потому что они смонтированы как ТОЛЬКО ЧТЕНИЕ, по крайней мере, для пользователя по умолчанию (система все еще может писать в него).
DocumentsProvider
API довольно запутанный (даже образец в документах трудно читаемый), поэтому было добавлено DocumentFile
для имитации поведения File
для более легкого доступа.
Предполагая, что пользователь уже предоставил разрешения на чтение / запись для хранения, нам нужно получить URI документа корневого каталога SDcard (в Activity
):
var sdCardUri : Uri? = null
private fun requestSDCardPermissions(){
if(Build.VERSION.SDK_INT < 24){
startActivityForResult(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE), REQ_PICK_DIRECTORY)
return
}
// find removable device using getStorageVolumes
val sm = getSystemService(Context.STORAGE_SERVICE) as StorageManager
val sdCard = sm.storageVolumes.find { it.isRemovable }
if(sdCard != null){
startActivityForResult(sdCard.createAccessIntent(null), REQ_SD_CARD_ACCESS)
}
}
Перед тем, как пользователю API 24 нужно открыть средство выбора документов и вручную выберите SD-карту сами. Это не идеально, но команда Android упустила из виду тот факт, что отсутствует API SD-карт.
В более новой версии getStorageVolumes()
позволяет нам находить SD-карту через код и отображать только явное предупреждение пользователю о том, что к нему будет произведен доступ. Это НЕ тот же диалог, что и у разрешения на чтение / запись.
Теперь для обработки полученного Uri
нам нужно только взять data.data
результата. Это может быть хорошим местом для сохранения его в общих настройках, чтобы не просить пользователя снова и снова:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if(requestCode == REQ_SD_CARD_ACCESS || requestCode == REQ_PICK_DIRECTORY){
if(resultCode == RESULT_OK) {
if(data == null){
Log.e(TAG, "Error obtaining access")
}else{
sdCardUri = data.data
Log.d("StorageAccess", "obtained access to $sdCardUri")
// optionally store uri in preferences as well here { ... }
}
}else
Toast.makeText(this, "access denied", Toast.LENGTH_SHORT).show()
return
}
super.onActivityResult(requestCode, resultCode, data)
}
Я пропущу большинство проверок ошибок / проверок существующих файлов, но теперь вы можете использовать полученные sdCardUri
] вот так (копирование файла «sample.txt» из корня внутреннего хранилища в корень SDcard):
private fun copyToSDCard(){
val sdCardRoot = DocumentFile.fromTreeUri(this, sdCardUri)
val internalFile = File(Environment.getExternalStorageDirectory(), "sample.txt")
// get or create file
val sdCardFile = sdCardRoot.findFile("sample.txt") ?: sdCardRoot.createFile(null, "sample.txt")
val outStream = contentResolver.openOutputStream(sdCardFile.uri)
outStream.write(internalFile.readBytes())
outStream.flush()
outStream.close()
Toast.makeText(this, "copied to SDCard", Toast.LENGTH_SHORT).show()
}
И наоборот (из SDcard во внутреннее хранилище):
private fun copyToInternal(){
val sdCardRoot = DocumentFile.fromTreeUri(this, sdCardUri)
val internalFile = File(Environment.getExternalStorageDirectory(), "sample.txt")
val sdCardFile = sdCardRoot.findFile("sample.txt")
val inStream = contentResolver.openInputStream(sdCardFile.uri)
internalFile.writeBytes(inStream.readBytes())
inStream.close()
Toast.makeText(this, "copied to internal", Toast.LENGTH_SHORT).show()
}
Это не дорогостоящее в C#. С одной стороны, нет никакого “calculation“: запросы длины являются в основном элементарной операцией благодаря встраиванию. И во-вторых, потому что ( по словам его разработчиков ), компилятор распознает этот шаблон доступа и на самом деле оптимизирует любые (избыточные) граничные проверки на доступ на элементах массива.
И между прочим, я полагаю, что что-то подобное верно для современных виртуальных машин JavaScript, и если это уже не будет, то это будет очень скоро, так как это - тривиальная оптимизация.
Все массивы .NET имеют поле, содержащее длину массива, таким образом, длина не вычисляется при использовании, но во время создания.
виртуальная машина .NET очень хороша в устранении граничных проверок, когда это возможно, это - один из тех случаев, куда граничная проверка перемещена вне цикла (в большинстве ситуаций, и если не это - всего 2 инструкции наверху).
Редактирование:
Я верю, используете ли Вы количество Linq () дополнительный метод, затем это может вычислить каждый раз, когда это называют.
Это будет также зависеть от того, делает ли тот метод считывания вычисление или получает доступ к известному значению.
Почти на любом языке ответ будет, "он зависит".
Главным образом, это зависит от того, достаточно ли компилятор умен, чтобы смочь сказать, могла ли длина списка или массива измениться, пока Вы в курсе.
Это вряд ли будет определено спецификацией языка, все же.
Так, вероятно, безопасно предположить, что компиляция не может понимать это. Если Вы действительно действительно полагаете, что длина объекта не изменится, не стесняйтесь вычислять длину сначала и использование, которые в Вашем цикле управляют конструкциями.
, Но остерегаются других потоков...
Если это - что-нибудь как Java, это должен быть O (1) операция.
я нашел следующую ссылку полезной: http://www.devguru.com/Technologies/Ecmascript/Quickref/array.html