Media Store access using openFile() on Android 10 - Joe Birch

文章推薦指數: 80 %
投票人數:10人

In Android. October 9, 2019. 6 Min Read. M. As of Android 10, things have changed slightly with how we access files that are contained outside of our ... AsofAndroid10,thingshavechangedslightlywithhowweaccessfilesthatarecontainedoutsideofourapplication.Thischangeofbehaviourhascomefromtheconceptofscopedstorage–whichaimstoimprovetheprivacyofuserfiles,addingmorecontroltohowfilesareaccessedwithintheandroidsystem. Evenwiththesechanges,ourapplicationstillhasread/writeaccessbydefaulttothefilesthatithascreated–regardlessofwhetherthosefilesareinsideoroutsideofourapplicationsdirectoryonthedevice.Whenthisisthecase,wedon’trequireanyextrapermissionstoaccessfiles–nochangeisrequiredfromoursidehere. Sowhataboutwhenwewanttoaccessafilethatbelongstoanotherapplication?Forexample,maybeouruserhaseditedaphotousingVSCOorInstagram–thateditedfilewillliveinsideofthecorrespondingappdirectoryonthedevice.Inourapplicationtheusermayhavetheabilitytoaddaphotofromthedevice,forwhateverpurposeourapplicationserves.Previouslywemayhaveaccessthatfileusingapathtothefileonthedevice(suchas/sdcard/DCIM/some_external_file.png)–asofAndroid10,thisapproachviaafilepathwillnolongerwork.Whenthisisattempted,anEACCESS(PermissionDenied)errorwillbethrownaswearetryingtoaccessafileinawaythatisnolongersupportedbythesystem.EvenifwehaverequestedtheREAD_EXTERNAL_STORAGEpermission,thesamefactholdstrue. InthisarticleIwanttojustcovertheaccessoffilesthatresidewithintheMediaStore(MediaStore.Images,MediaStore.VideoorMediaStore.Audio).ProvidedthatthisisthecaseandwehavetheREAD_EXTERNAL_STORAGEpermission,thenwecanaccessthedesiredfilesbymakinguseoftheopenFile()methodwhichhasbeenintroducedinAPIlevel29(Android10).WecanusethisnewmethodtoretrieveareferencetoaParcelFileDescriptor–thisdescriptorwillgiveuseverythingweneedtoaccessandimportthecontentsofthedesiredfile. WhenwecallthismethodweneedtopassintheUrithatwewishtoretrieve,themodewhichwishtousewhenopeningthefile(inthiscase,rforread)andacancellationsignal(whichcanbenullifnotrequired). valfileDescriptor:ParcelFileDescriptor? try{ fileDescriptor=context.contentResolver.openFile( localMedia.mediaUri,"r",null) }catch(e:FileNotFoundException){ //handleerror } AtthispointwehavethisParcelFileDescriptorreference,butwhatwewantisanactualfilesothatwecandosomethingwithitwithinourapplication.Inmostcaseswe’lllikelybeusingtheACTION_GET_CONTENTapproachtopickingmediacontent,sointhiscasewe’regoingtowanttoimportacopyofthedataintothecachedirectoryofourapplication–thismaynotbethecaseineverysituation,buthavingacopyofthisfileinourapplicationcachegivesusplentyofscopetoprocessitscontents.Ifyourapplicationneedsareslightlydifferent,itmaybeworthlookingattheACTION_OPEN_DOCUMENTapproach. We’regoingtostartherebyusingtheFileDescriptorfromourParcelFileDescriptorreferencetobuildaFileInputStream: valinput=FileInputStream(fileDescriptor.fileDescriptor) Nowthatwehavethisinputdata,whatwe’regoingtowanttodoisreadthedataandwriteittoafilewhichisstoredinthetemporarycacheforourapplication.Firstwe’regoingtoneedafunctionthatwilltakeourinputstreamastheinputdataandgiveusbackaByteArrayinstancesothatwecanwritethisdatatoafile.We’llwriteashortfunction(shownbelow)thatdoesexactlythis: privatefunreadBinaryStream( stream:InputStream, byteCount:Int ):ByteArray{ valoutput=ByteArrayOutputStream() try{ valbuffer=ByteArray(if(byteCount>0)byteCountelse4096) varread:Int while(stream.read(buffer).also{read=it}>=0){ output.write(buffer,0,read) } }catch(e:IOException){ e.printStackTrace() }finally{ try{ stream.close() }catch(e:IOException){ e.printStackTrace() } } returnoutput.toByteArray() } Wewon’tdivetoomuchintothishere,butwecanseethatweuseabyteCounttodeterminethesizeofourbytearrayandthenpopulatethiswiththecontentsofourinputstream.Wecanthenusethisfunctionprovidingourfiledescriptor,alongwiththesizeofthefile.ForthisweaccessthegetStatSize()methodofourfiledescriptor–thisisusedtoprovidethesizeofourfile,inbytes,sothisisperfectforcreatingourbytearrayforthesizeofourfile. valinput=FileInputStream(fileDescriptor.fileDescriptor) ... valbyteArray=readBinaryStream(input, fileDescriptor.statSize.toInt()) Nowthatwehaveourbytearray,we’regoingtowanttowritethistoourcachedversionofthisfileforusewithinourapplication.Forthis,wecanbeginbygeneratingafileinthecachedirectoryofourapplication–thiscanbedeclaredusingthecontext.cacheDirreference,alongwithageneratedfilenameofourchoosing. varcachedFile=File(context.cacheDir,someGeneratedFileName) Wecanthenwriteashortfunctionthatwilltakeabytearrayandwritethecontentofittotheprovidedfile.Thisfunctionwilltakeourfilethatisstoredinourapplicationcache,alongwiththebytearraywhosedatawewanttowritetothatfile.Allthisfunctionthendoesisessentiallywritethatdataandthenreturnthesuccessstatusoftheoperation. privatefunwriteFile(cachedFile:File,data:ByteArray):Boolean{ returntry{ varoutput:BufferedOutputStream?=null try{ output=BufferedOutputStream(FileOutputStream(file)) output.write(data) output.flush() true }finally{ output?.close() } }catch(ex:Exception){ false } } Withthatinplace,wecannowtakethe valinput=FileInputStream(fileDescriptor.fileDescriptor) ... valbyteArray=readBinaryStream(input, fileDescriptor.statSize.toInt()) valcachedFile=File(context.cacheDir,someGeneratedFileName) valfileSaved=writeFile(cachedFile,byteArray) Andoncethatfilehasbeensaved,wecanthenaccessourcachedFileusingitsUri.Becausethisfileiswithinthecachedirectoryofourapplication,thereisnorestrictiontousaccessingit–meaningwewillnotrunintothesamepermissionerrorsthatwepreviouslywouldhavedonewhentryingtoaccesstheoriginalfileviaitsUri. WiththisapproachwecannowaccessfilesfromtheMediaStoreusingthenewlyavailableopenFile()methodfromtheAndroidframework.WithapproachesusedinourapplicationspriortoAndroid10wherewewouldhaveseenfileaccesserrors,wecannowsafelyreadfilesfromtheMediaStoreandprocessthemforusewithinourownapplications. HaveyouhadtomakechangeswithhowyouaccessmediawithinAndroid10,orareyoucurrentlyworkingouthowtoapproachtheseproblems?IfthereareanyquestionsontheabovethenpleasereachoutandI’dbehappytohelp! [twitter-followscreen_name=’hitherejoe’show_count=’yes’] FacebookTwitterReddit Abouttheauthor hitherejoe Viewallposts AddComment CancelreplyCommentName* Email* Website Savemyname,email,andwebsiteinthisbrowserforthenexttimeIcomment. Readmore WiththeAndroid12betanowavailable,we’restartingtolearnmoreaboutthenewfeaturesthatthelatestversionofAndroidgivestous.OneofthethingsthatcaughtmyeyehereistheintroductionofaSplashScreenAPI–notonlyprovidingastandardisedwayforappstopresentsplashscreenswithintheirapps,butalsoimprovetheuserexperiencewhenitcomestolaunchingapps... ReadonReadlater TherewasalotofexcitingnewsannouncedatGoogleI/Othisweek–oneofthethingsIhadbeenlookingforwardtowashearingaboutlargescreenexperiences.Whilethesedevicesanddesignprincipleshaveexistedside-by-sideforsometime,it’salwaysfeltlikesupportfordevelopershasneverquitefullybeenaccessible.However,withsomeoftheannouncementsatI/Othisweek... ReadonReadlater AlargeamountofmobileappswillneedsomeformofNavigation,allowinguserstomovebetweendifferentpartsofanapplication.WhenimplementingtheserequirementswithinAndroidApps,applicationshaveeitherrolledtheirownsolutions,reliedontraditionalintentsorthefragmentmanager,orexploredtheoptionofthe NavigationComponent overrecentyears.Throughoutthealpha... ReadonReadlater WiththeannouncementofthefirstAndroid12DeveloperPreviewannouncedyesterday,IstartedtotakeadiveintosomeoftheAPIchangesandthenewthingswe’llbegettingourhandson.OneofthesethingswasthenewunifiedrichcontentAPI,providingasimplifiedwayoftransferringmediabetweenapplications.YoumayhavepreviouslyusedImageKeyboardSupportwhenusingor... ReadonReadlater JoeBirch Posts Latest Android KotlinMultiplatform iOS Speakingtimeline AppearsIn @hitherejoe



請為這篇文章評分?