/* Ċ Ġ* ĠCopyright Ġ 2 0 1 6 - 2 0 2 0 ĠJetBrains Ġs . r . o . ĠUse Ġof Ġthis Ġsource Ġcode Ġis Ġgoverned Ġby Ġthe ĠApache Ġ 2 . 0 Ġlicense . Ċ Ġ*/ Ċ Ċ package Ġkotlinx . coroutines . internal Ċ Ċ import Ġjava . io .* Ċ import Ġjava . net .* Ċ import Ġjava . util .* Ċ import Ġjava . util . jar .* Ċ import Ġjava . util . zip .* Ċ import Ġkotlin . collections . ArrayList Ċ Ċ /** Ċ Ġ* ĠDon 't Ġuse ĠJ vm Field Ġhere Ġto Ġenable ĠR 8 Ġoptimizations Ġvia Ġ" ass umen os ide effects " Ċ Ġ*/ Ċ internal Ġval ĠAND ROID _ DETECT ED Ġ= Ġrun Catch ing Ġ{ ĠClass . forName (" android . os . Build ") Ġ}. is Success Ċ Ċ /** Ċ Ġ* ĠA Ġsimplified Ġversion Ġof Ġ[ Service Loader ]. Ċ Ġ* ĠFast Service Loader Ġloc ates Ġand Ġinstanti ates Ġall Ġservice Ġproviders Ġnamed Ġin Ġconfiguration Ċ Ġ* Ġfiles Ġplaced Ġin Ġthe Ġresource Ġdirectory Ġ< tt > META - INF / services </ tt >. Ċ Ġ* Ċ Ġ* ĠThe Ġmain Ġdifference Ġbetween Ġthis Ġclass Ġand Ġclassic Ġservice Ġloader Ġis Ġin Ġskipping Ċ Ġ* Ġverification ĠJ AR s . ĠA Ġverification Ġrequires Ġreading Ġthe Ġwhole ĠJ AR Ġ( and Ġit Ġcauses Ġproblems Ġand ĠAN Rs Ġon ĠAndroid Ġdevices ) Ċ Ġ* Ġand Ġprevents Ġonly Ġtrivial Ġchecksum Ġissues . ĠSee Ġ# 8 7 8 . Ċ Ġ* Ċ Ġ* ĠIf Ġany Ġerror Ġoccurs Ġduring Ġloading , Ġit Ġfallback s Ġto Ġ[ Service Loader ], Ġmostly Ġto Ġprevent ĠR 8 Ġissues . Ċ Ġ*/ Ċ internal Ġobject ĠFast Service Loader Ġ{ ĊĠĠĠ Ġprivate Ġconst Ġval ĠPREFIX : ĠString Ġ= Ġ" META - INF / services /" ĊĊĠĠĠ Ġ/** ĊĠĠĠĠ Ġ* ĠThis Ġmethod Ġattempts Ġto Ġload Ġ[ Main Dispatcher Factory ] Ġin ĠAndroid - friendly Ġway . ĊĠĠĠĠ Ġ* ĊĠĠĠĠ Ġ* ĠIf Ġwe Ġare Ġnot Ġon ĠAndroid , Ġthis Ġmethod Ġfallback s Ġto Ġa Ġregular Ġservice Ġloading , ĊĠĠĠĠ Ġ* Ġelse Ġwe Ġattempt Ġto Ġdo Ġ` Class . forName ` Ġlookup Ġfor ĊĠĠĠĠ Ġ* Ġ` Android Dispatcher Factory ` Ġand Ġ` Test Main Dispatcher Factory `. ĊĠĠĠĠ Ġ* ĠIf Ġlook ups Ġare Ġsuccessful , Ġwe Ġreturn Ġresult in Ag Ġinstances Ġbecause Ġwe Ġknow Ġthat ĊĠĠĠĠ Ġ* Ġ` Main Dispatcher Factory ` ĠAPI Ġis Ġinternal Ġand Ġthis Ġis Ġthe Ġonly Ġpossible Ġclasses Ġof Ġ` Main Dispatcher Factory ` ĠService Ġon ĠAndroid . ĊĠĠĠĠ Ġ* ĊĠĠĠĠ Ġ* ĠSuch Ġin tric ate Ġd ance Ġis Ġrequired Ġto Ġavoid Ġcalls Ġto Ġ` Service Loader . load ` Ġfor Ġmultiple Ġreasons : ĊĠĠĠĠ Ġ* Ġ 1 ) ĠIt Ġelim inates Ġdisk Ġlookup Ġon Ġpotentially Ġslow Ġdevices Ġon Ġthe ĠMain Ġthread . ĊĠĠĠĠ Ġ* Ġ 2 ) ĠVar ious ĠAndroid Ġtoolchain Ġversions Ġby Ġvarious Ġv endors Ġdon 't Ġtend Ġto Ġhandle ĠService Loader Ġcalls Ġproperly . ĊĠĠĠĠ Ġ* ĠĠĠ ĠSometimes ĠMETA - INF Ġis Ġremoved Ġfrom Ġthe Ġresulting ĠA PK , Ġsometimes Ġclass Ġnames Ġare Ġm angled , Ġetc . ĊĠĠĠĠ Ġ* ĠĠĠ ĠWhile Ġit Ġis Ġnot Ġthe Ġproblem Ġof Ġ` kotlin x . coroutines `, Ġit Ġsignificantly Ġw ors ens Ġuser Ġexperience , Ġthus Ġwe Ġare Ġworkaround ing Ġit . ĊĠĠĠĠ Ġ* ĠĠĠ ĠExamples Ġof Ġsuch Ġissues Ġare Ġ# 9 3 2 , Ġ# 1 0 7 2 , Ġ# 1 5 5 7 , Ġ# 1 5 6 7 ĊĠĠĠĠ Ġ* ĊĠĠĠĠ Ġ* ĠWe Ġalso Ġuse ĠSL Ġfor Ġ[ Coroutine ExceptionHandler ], Ġbut Ġwe Ġdo Ġnot Ġexperience Ġthe Ġsame Ġproblems Ġand ĠCE H Ġis Ġa Ġpublic ĠAPI ĊĠĠĠĠ Ġ* Ġthat Ġmay Ġalready Ġbe Ġinjected Ġvis ĠSL , Ġso Ġwe Ġare Ġnot Ġusing Ġthe Ġsame Ġtechnique Ġfor Ġit . ĊĠĠĠĠ Ġ*/ ĊĠĠĠ Ġinternal Ġfun Ġload Main Dispatcher Factory (): ĠList < Main Dispatcher Factory > Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġcl z Ġ= ĠMain Dispatcher Factory :: class . java ĊĠĠĠĠĠĠĠ Ġif Ġ(! ANDROID _ DETECT ED ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreturn Ġload ( cl z , Ġcl z . class Loader ) ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠ Ġreturn Ġtry Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġresult Ġ= ĠArrayList < Main Dispatcher Factory >( 2 ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġcreate InstanceOf ( cl z , Ġ" kotlin x . coroutines . android . Android Dispatcher Factory ") ?. apply Ġ{ Ġresult . add ( this ) Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġcreate InstanceOf ( cl z , Ġ" kotlin x . coroutines . test . internal . Test Main Dispatcher Factory ") ?. apply Ġ{ Ġresult . add ( this ) Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresult ĊĠĠĠĠĠĠĠ Ġ} Ġcatch Ġ( e : ĠThrowable ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ// ĠF allback Ġto Ġthe Ġregular ĠSL Ġin Ġcase Ġof Ġany Ġunexpected Ġexception ĊĠĠĠĠĠĠĠĠĠĠĠ Ġload ( cl z , Ġcl z . class Loader ) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ/* ĊĠĠĠĠ Ġ* ĠThis Ġmethod Ġis Ġinline Ġto Ġhave Ġa Ġdirect ĠClass . forName (" string Ġliteral ") Ġin Ġthe Ġbyte Ġcode Ġto Ġavoid Ġweird Ġinteractions Ġwith ĠPro Guard / R 8 . ĊĠĠĠĠ Ġ*/ ĊĠĠĠ Ġ@ Suppress (" NOTH ING _ TO _ INLINE ") ĊĠĠĠ Ġprivate Ġinline Ġfun Ġcreate InstanceOf ( ĊĠĠĠĠĠĠĠ Ġbase Class : ĠClass < Main Dispatcher Factory >, ĊĠĠĠĠĠĠĠ Ġservice Class : ĠString ĊĠĠĠ Ġ): ĠMain Dispatcher Factory ? Ġ{ ĊĠĠĠĠĠĠĠ Ġreturn Ġtry Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġcl z Ġ= ĠClass . forName ( service Class , Ġtrue , Ġbase Class . class Loader ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġbase Class . cast ( cl z . getDeclared Constructor (). newInstance ()) ĊĠĠĠĠĠĠĠ Ġ} Ġcatch Ġ( e : ĠClass NotFoundException ) Ġ{ Ġ// ĠDo Ġnot Ġfail Ġif ĠTest Main Dispatcher Factory Ġis Ġnot Ġfound ĊĠĠĠĠĠĠĠĠĠĠĠ Ġnull ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġ< S > Ġload ( service : ĠClass < S >, Ġloader : ĠClassLoader ): ĠList < S > Ġ{ ĊĠĠĠĠĠĠĠ Ġreturn Ġtry Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġload Providers ( service , Ġloader ) ĊĠĠĠĠĠĠĠ Ġ} Ġcatch Ġ( e : ĠThrowable ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ// ĠF allback Ġto Ġdefault Ġservice Ġloader ĊĠĠĠĠĠĠĠĠĠĠĠ ĠService Loader . load ( service , Ġloader ). toList () ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ// ĠVis ible Ġfor Ġtests ĊĠĠĠ Ġinternal Ġfun Ġ< S > Ġload Providers ( service : ĠClass < S >, Ġloader : ĠClassLoader ): ĠList < S > Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġfull ServiceName Ġ= ĠPREFIX Ġ+ Ġservice . name ĊĠĠĠĠĠĠĠ Ġ// ĠFilter Ġout Ġsituations Ġwhen Ġboth ĠJ AR Ġand Ġregular Ġfiles Ġare Ġin Ġthe Ġclasspath Ġ( e . g . ĠIDEA ) ĊĠĠĠĠĠĠĠ Ġval Ġurls Ġ= Ġloader . getResources ( full ServiceName ) ĊĠĠĠĠĠĠĠ Ġval Ġproviders Ġ= Ġurls . toList (). flatMap Ġ{ Ġparse ( it ) Ġ}. toSet () ĊĠĠĠĠĠĠĠ Ġrequire ( providers . isNotEmpty ()) Ġ{ Ġ" No Ġproviders Ġwere Ġloaded Ġwith ĠFast Service Loader " Ġ} ĊĠĠĠĠĠĠĠ Ġreturn Ġproviders . map Ġ{ Ġget Provider Instance ( it , Ġloader , Ġservice ) Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġ< S > Ġget Provider Instance ( name : ĠString , Ġloader : ĠClassLoader , Ġservice : ĠClass < S >): ĠS Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġclazz Ġ= ĠClass . forName ( name , Ġfalse , Ġloader ) ĊĠĠĠĠĠĠĠ Ġrequire ( service . isAssignableFrom ( clazz )) Ġ{ Ġ" Expected Ġservice Ġof Ġclass Ġ$ service , Ġbut Ġfound Ġ$ clazz " Ġ} ĊĠĠĠĠĠĠĠ Ġreturn Ġservice . cast ( clazz . getDeclared Constructor (). newInstance ()) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġparse ( url : ĠURL ): ĠList < String > Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġpath Ġ= Ġurl . toString () ĊĠĠĠĠĠĠĠ Ġ// ĠFast - path Ġfor ĠJ AR s ĊĠĠĠĠĠĠĠ Ġif Ġ( path . startsWith (" jar ")) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġpath To Jar Ġ= Ġpath . substring After (" jar : file : "). substring Before (' !') ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġentry Ġ= Ġpath . substring After (" !/ ") ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ// Ġmind Ġthe Ġverify Ġ= Ġfalse Ġflag ! ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ( Jar File ( path To Jar , Ġfalse )). use Ġ{ Ġfile Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠBufferedReader ( InputStream Reader ( file . getInputStream ( Zip Entry ( entry )), Ġ" UTF - 8 ")). use Ġ{ Ġr Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġreturn Ġparse File ( r ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ// ĠRegular Ġpath Ġfor Ġeverything Ġelse ĊĠĠĠĠĠĠĠ Ġreturn ĠBufferedReader ( InputStream Reader ( url . open Stream ())). use Ġ{ Ġreader Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠ Ġparse File ( reader ) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ// ĠJar File Ġdoes Ġno Ġimplement ĠCloses able Ġon ĠJava Ġ 1 . 6 ĊĠĠĠ Ġprivate Ġinline Ġfun Ġ< R > ĠJar File . use ( block : Ġ( Jar File ) Ġ-> ĠR ): ĠR Ġ{ ĊĠĠĠĠĠĠĠ Ġvar Ġcause : ĠThrowable ? Ġ= Ġnull ĊĠĠĠĠĠĠĠ Ġtry Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreturn Ġblock ( this ) ĊĠĠĠĠĠĠĠ Ġ} Ġcatch Ġ( e : ĠThrowable ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġcause Ġ= Ġe ĊĠĠĠĠĠĠĠĠĠĠĠ Ġthrow Ġe ĊĠĠĠĠĠĠĠ Ġ} Ġfinally Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġtry Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġclose () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} Ġcatch Ġ( close Exception : ĠThrowable ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġif Ġ( cause Ġ=== Ġnull ) Ġthrow Ġclose Exception ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġcause . add Sup pressed ( close Exception ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġthrow Ġcause ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġparse File ( r : ĠBufferedReader ): ĠList < String > Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġnames Ġ= Ġmutable Set Of < String >() ĊĠĠĠĠĠĠĠ Ġwhile Ġ( true ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġline Ġ= Ġr . readLine () Ġ?: Ġbreak ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval ĠserviceName Ġ= Ġline . substring Before ("# "). trim () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġrequire ( serviceName . all Ġ{ Ġit Ġ== Ġ'.' Ġ|| ĠCharacter . is Java Identifier Part ( it ) Ġ}) Ġ{ Ġ" Illegal Ġservice Ġprovider Ġclass Ġname : Ġ$ serviceName " Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġif Ġ( serviceName . isNotEmpty ()) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġnames . add ( serviceName ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġreturn Ġnames . toList () ĊĠĠĠ Ġ} Ċ } Ċ