Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
门心叼龙
VirtualAPK
提交
0ae4d69f
V
VirtualAPK
项目概览
门心叼龙
/
VirtualAPK
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
V
VirtualAPK
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
0ae4d69f
编写于
5月 07, 2018
作者:
S
superq_sky
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Optimized process of creating resources.
上级
7857a51c
变更
14
隐藏空白更改
内联
并排
Showing
14 changed file
with
454 addition
and
75 deletion
+454
-75
AndroidStub/src/main/java/android/app/ResourcesManager.java
AndroidStub/src/main/java/android/app/ResourcesManager.java
+5
-0
AndroidStub/src/main/java/android/content/res/CompatibilityInfo.java
.../src/main/java/android/content/res/CompatibilityInfo.java
+41
-0
AndroidStub/src/main/java/android/content/res/Resources.java
AndroidStub/src/main/java/android/content/res/Resources.java
+67
-0
AndroidStub/src/main/java/android/content/res/ResourcesImpl.java
...Stub/src/main/java/android/content/res/ResourcesImpl.java
+8
-0
AndroidStub/src/main/java/android/content/res/ResourcesKey.java
...dStub/src/main/java/android/content/res/ResourcesKey.java
+41
-0
CoreLibrary/src/main/java/com/didi/virtualapk/PluginManager.java
...rary/src/main/java/com/didi/virtualapk/PluginManager.java
+2
-2
CoreLibrary/src/main/java/com/didi/virtualapk/internal/LoadedPlugin.java
.../main/java/com/didi/virtualapk/internal/LoadedPlugin.java
+12
-11
CoreLibrary/src/main/java/com/didi/virtualapk/internal/PluginContext.java
...main/java/com/didi/virtualapk/internal/PluginContext.java
+5
-0
CoreLibrary/src/main/java/com/didi/virtualapk/internal/ResourcesManager.java
...n/java/com/didi/virtualapk/internal/ResourcesManager.java
+208
-36
CoreLibrary/src/main/java/com/didi/virtualapk/internal/VAInstrumentation.java
.../java/com/didi/virtualapk/internal/VAInstrumentation.java
+28
-5
app/src/main/java/com/didi/virtualapk/MainActivity.java
app/src/main/java/com/didi/virtualapk/MainActivity.java
+18
-7
app/src/main/res/layout/activity_main.xml
app/src/main/res/layout/activity_main.xml
+17
-14
app/src/main/res/values-en/strings.xml
app/src/main/res/values-en/strings.xml
+1
-0
app/src/main/res/values/strings.xml
app/src/main/res/values/strings.xml
+1
-0
未找到文件。
AndroidStub/src/main/java/android/app/ResourcesManager.java
浏览文件 @
0ae4d69f
...
...
@@ -8,4 +8,9 @@ public class ResourcesManager {
public
static
ResourcesManager
getInstance
()
{
throw
new
RuntimeException
(
"Stub!"
);
}
public
void
appendLibAssetForMainAssetPath
(
String
assetPath
,
String
libAsset
)
{
throw
new
RuntimeException
(
"Stub!"
);
}
}
\ No newline at end of file
AndroidStub/src/main/java/android/content/res/CompatibilityInfo.java
0 → 100644
浏览文件 @
0ae4d69f
package
android.content.res
;
import
android.content.pm.ApplicationInfo
;
import
android.os.Parcel
;
import
android.os.Parcelable
;
/**
* Created by qiaopu on 2018/5/3.
*/
public
class
CompatibilityInfo
implements
Parcelable
{
public
CompatibilityInfo
(
ApplicationInfo
appInfo
,
int
screenLayout
,
int
sw
,
boolean
forceCompat
)
{
throw
new
RuntimeException
(
"Stub!"
);
}
@Override
public
int
describeContents
()
{
throw
new
RuntimeException
(
"Stub!"
);
}
@Override
public
void
writeToParcel
(
Parcel
dest
,
int
flags
)
{
throw
new
RuntimeException
(
"Stub!"
);
}
public
static
final
Parcelable
.
Creator
<
CompatibilityInfo
>
CREATOR
=
new
Parcelable
.
Creator
<
CompatibilityInfo
>()
{
@Override
public
CompatibilityInfo
createFromParcel
(
Parcel
source
)
{
throw
new
RuntimeException
(
"Stub!"
);
}
@Override
public
CompatibilityInfo
[]
newArray
(
int
size
)
{
throw
new
RuntimeException
(
"Stub!"
);
}
};
}
AndroidStub/src/main/java/android/content/res/Resources.java
0 → 100644
浏览文件 @
0ae4d69f
package
android.content.res
;
import
android.graphics.drawable.Drawable
;
import
android.util.DisplayMetrics
;
/**
* Created by qiaopu on 2018/5/18.
*/
public
class
Resources
{
public
Resources
(
AssetManager
assets
,
DisplayMetrics
metrics
,
Configuration
config
)
{
throw
new
RuntimeException
(
"Stub!"
);
}
public
final
AssetManager
getAssets
()
{
throw
new
RuntimeException
(
"Stub!"
);
}
public
Configuration
getConfiguration
()
{
throw
new
RuntimeException
(
"Stub!"
);
}
public
DisplayMetrics
getDisplayMetrics
()
{
throw
new
RuntimeException
(
"Stub!"
);
}
public
Drawable
getDrawable
(
int
id
)
throws
NotFoundException
{
throw
new
RuntimeException
(
"Stub!"
);
}
public
CharSequence
getText
(
int
id
)
throws
NotFoundException
{
throw
new
RuntimeException
(
"Stub!"
);
}
public
XmlResourceParser
getXml
(
int
id
)
throws
NotFoundException
{
throw
new
RuntimeException
(
"Stub!"
);
}
public
ResourcesImpl
getImpl
()
{
throw
new
RuntimeException
(
"Stub!"
);
}
public
final
Theme
newTheme
()
{
throw
new
RuntimeException
(
"Stub!"
);
}
public
final
class
Theme
{
public
void
applyStyle
(
int
resId
,
boolean
force
)
{
throw
new
RuntimeException
(
"Stub!"
);
}
public
TypedArray
obtainStyledAttributes
(
int
[]
attrs
)
{
throw
new
RuntimeException
(
"Stub!"
);
}
public
void
setTo
(
Theme
other
)
{
throw
new
RuntimeException
(
"Stub!"
);
}
}
public
static
class
NotFoundException
extends
RuntimeException
{
}
}
AndroidStub/src/main/java/android/content/res/ResourcesImpl.java
0 → 100644
浏览文件 @
0ae4d69f
package
android.content.res
;
/**
* Created by qiaopu on 2018/5/18.
*/
public
class
ResourcesImpl
{
}
AndroidStub/src/main/java/android/content/res/ResourcesKey.java
0 → 100644
浏览文件 @
0ae4d69f
package
android.content.res
;
import
android.support.annotation.NonNull
;
import
android.support.annotation.Nullable
;
/**
* Created by qiaopu on 2018/5/3.
*/
public
final
class
ResourcesKey
{
@Nullable
public
final
String
mResDir
;
@Nullable
public
final
String
[]
mSplitResDirs
;
@Nullable
public
final
String
[]
mOverlayDirs
;
@Nullable
public
final
String
[]
mLibDirs
;
public
final
int
mDisplayId
;
@NonNull
public
final
Configuration
mOverrideConfiguration
;
@NonNull
public
final
CompatibilityInfo
mCompatInfo
;
public
ResourcesKey
(
@Nullable
String
resDir
,
@Nullable
String
[]
splitResDirs
,
@Nullable
String
[]
overlayDirs
,
@Nullable
String
[]
libDirs
,
int
displayId
,
@Nullable
Configuration
overrideConfig
,
@Nullable
CompatibilityInfo
compatInfo
)
{
throw
new
RuntimeException
(
"Stub!"
);
}
}
CoreLibrary/src/main/java/com/didi/virtualapk/PluginManager.java
浏览文件 @
0ae4d69f
...
...
@@ -69,7 +69,7 @@ public class PluginManager {
private
Map
<
String
,
LoadedPlugin
>
mPlugins
=
new
ConcurrentHashMap
<>();
private
final
List
<
Callback
>
mCallbacks
=
new
ArrayList
<>();
private
Instrumentation
mInstrumentation
;
// Hooked instrumentation
private
VA
Instrumentation
mInstrumentation
;
// Hooked instrumentation
private
IActivityManager
mActivityManager
;
// Hooked IActivityManager binder
private
IContentProvider
mIContentProvider
;
// Hooked IContentProvider binder
...
...
@@ -277,7 +277,7 @@ public class PluginManager {
return
this
.
mContext
;
}
public
Instrumentation
getInstrumentation
()
{
public
VA
Instrumentation
getInstrumentation
()
{
return
this
.
mInstrumentation
;
}
...
...
CoreLibrary/src/main/java/com/didi/virtualapk/internal/LoadedPlugin.java
浏览文件 @
0ae4d69f
...
...
@@ -47,11 +47,12 @@ import android.content.res.XmlResourceParser;
import
android.graphics.Rect
;
import
android.graphics.drawable.Drawable
;
import
android.os.Build
;
import
android.os.Looper
;
import
android.os.Process
;
import
android.os.UserHandle
;
import
android.support.annotation.NonNull
;
import
android.support.annotation.Nullable
;
import
android.support.annotation.
Worker
Thread
;
import
android.support.annotation.
Ui
Thread
;
import
com.didi.virtualapk.PluginManager
;
import
com.didi.virtualapk.utils.DexUtil
;
...
...
@@ -108,12 +109,9 @@ public final class LoadedPlugin {
}
}
@WorkerThread
private
static
Resources
createResources
(
Context
context
,
File
apk
)
{
private
static
Resources
createResources
(
Context
context
,
String
packageName
,
File
apk
)
throws
Exception
{
if
(
Constants
.
COMBINE_RESOURCES
)
{
Resources
resources
=
ResourcesManager
.
createResources
(
context
,
apk
.
getAbsolutePath
());
ResourcesManager
.
hookResources
(
context
,
resources
);
return
resources
;
return
ResourcesManager
.
createResources
(
context
,
packageName
,
apk
);
}
else
{
Resources
hostResources
=
context
.
getResources
();
AssetManager
assetManager
=
createAssetManager
(
context
,
apk
);
...
...
@@ -145,7 +143,11 @@ public final class LoadedPlugin {
private
Application
mApplication
;
@UiThread
LoadedPlugin
(
PluginManager
pluginManager
,
Context
context
,
File
apk
)
throws
Exception
{
if
(
Thread
.
currentThread
()
!=
Looper
.
getMainLooper
().
getThread
())
{
throw
new
RuntimeException
(
"plugin mast be created by UI thread."
);
}
this
.
mPluginManager
=
pluginManager
;
this
.
mHostContext
=
context
;
this
.
mLocation
=
apk
.
getAbsolutePath
();
...
...
@@ -177,7 +179,7 @@ public final class LoadedPlugin {
this
.
mPackageManager
=
new
PluginPackageManager
();
this
.
mPluginContext
=
new
PluginContext
(
this
);
this
.
mNativeLibDir
=
context
.
getDir
(
Constants
.
NATIVE_DIR
,
Context
.
MODE_PRIVATE
);
this
.
mResources
=
createResources
(
context
,
apk
);
this
.
mResources
=
createResources
(
context
,
getPackageName
(),
apk
);
this
.
mClassLoader
=
createClassLoader
(
context
,
apk
,
this
.
mNativeLibDir
,
context
.
getClassLoader
());
tryToCopyNativeLib
(
apk
);
...
...
@@ -280,14 +282,13 @@ public final class LoadedPlugin {
}
public
void
invokeApplication
()
{
if
(
mApplication
!=
null
)
{
return
;
}
// make sure application's callback is run on ui thread.
RunUtil
.
runOnUiThread
(
new
Runnable
()
{
@Override
public
void
run
()
{
if
(
mApplication
!=
null
)
{
return
;
}
mApplication
=
makeApplication
(
false
,
mPluginManager
.
getInstrumentation
());
}
},
true
);
...
...
CoreLibrary/src/main/java/com/didi/virtualapk/internal/PluginContext.java
浏览文件 @
0ae4d69f
...
...
@@ -36,6 +36,11 @@ class PluginContext extends ContextWrapper {
super
(
plugin
.
getPluginManager
().
getHostContext
());
this
.
mPlugin
=
plugin
;
}
public
PluginContext
(
LoadedPlugin
plugin
,
Context
base
)
{
super
(
base
);
this
.
mPlugin
=
plugin
;
}
@Override
public
Context
getApplicationContext
()
{
...
...
CoreLibrary/src/main/java/com/didi/virtualapk/internal/ResourcesManager.java
浏览文件 @
0ae4d69f
...
...
@@ -16,70 +16,93 @@
package
com.didi.virtualapk.internal
;
import
android.annotation.TargetApi
;
import
android.app.Activity
;
import
android.app.ActivityThread
;
import
android.app.LoadedApk
;
import
android.content.Context
;
import
android.content.pm.ApplicationInfo
;
import
android.content.res.AssetManager
;
import
android.content.res.Configuration
;
import
android.content.res.Resources
;
import
android.content.res.ResourcesImpl
;
import
android.content.res.ResourcesKey
;
import
android.os.Build
;
import
android.util.ArrayMap
;
import
android.util.DisplayMetrics
;
import
android.util.Log
;
import
com.didi.virtualapk.PluginManager
;
import
com.didi.virtualapk.utils.Reflector
;
import
java.io.File
;
import
java.lang.ref.WeakReference
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Objects
;
/**
* Created by renyugang on 16/8/9.
*/
class
ResourcesManager
{
public
static
final
String
TAG
=
"LoadedPlugin"
;
public
static
synchronized
Resources
createResources
(
Context
hostContext
,
String
apk
)
{
private
static
Configuration
mDefaultConfiguration
;
public
static
synchronized
Resources
createResources
(
Context
hostContext
,
String
packageName
,
File
apk
)
throws
Exception
{
if
(
Build
.
VERSION
.
SDK_INT
>=
Build
.
VERSION_CODES
.
N
)
{
return
createResourcesForN
(
hostContext
,
packageName
,
apk
);
}
Resources
resources
=
ResourcesManager
.
createResourcesSimple
(
hostContext
,
apk
.
getAbsolutePath
());
ResourcesManager
.
hookResources
(
hostContext
,
resources
);
return
resources
;
}
private
static
synchronized
Resources
createResourcesSimple
(
Context
hostContext
,
String
apk
)
throws
Exception
{
Resources
hostResources
=
hostContext
.
getResources
();
Resources
newResources
=
null
;
AssetManager
assetManager
;
try
{
Reflector
reflector
=
Reflector
.
on
(
AssetManager
.
class
).
method
(
"addAssetPath"
,
String
.
class
);
if
(
Build
.
VERSION
.
SDK_INT
<
Build
.
VERSION_CODES
.
LOLLIPOP
)
{
assetManager
=
AssetManager
.
class
.
newInstance
();
reflector
.
bind
(
assetManager
);
reflector
.
call
(
hostContext
.
getApplicationInfo
().
sourceDir
);
}
else
{
assetManager
=
hostResources
.
getAssets
();
reflector
.
bind
(
assetManager
);
}
reflector
.
call
(
apk
);
List
<
LoadedPlugin
>
pluginList
=
PluginManager
.
getInstance
(
hostContext
).
getAllLoadedPlugins
();
for
(
LoadedPlugin
plugin
:
pluginList
)
{
reflector
.
call
(
plugin
.
getLocation
());
}
if
(
isMiUi
(
hostResources
))
{
newResources
=
MiUiResourcesCompat
.
createResources
(
hostResources
,
assetManager
);
}
else
if
(
isVivo
(
hostResources
))
{
newResources
=
VivoResourcesCompat
.
createResources
(
hostContext
,
hostResources
,
assetManager
);
}
else
if
(
isNubia
(
hostResources
))
{
newResources
=
NubiaResourcesCompat
.
createResources
(
hostResources
,
assetManager
);
}
else
if
(
isNotRawResources
(
hostResources
))
{
newResources
=
AdaptationResourcesCompat
.
createResources
(
hostResources
,
assetManager
);
}
else
{
// is raw android resources
newResources
=
new
Resources
(
assetManager
,
hostResources
.
getDisplayMetrics
(),
hostResources
.
getConfiguration
());
}
// lastly, sync all LoadedPlugin to newResources
for
(
LoadedPlugin
plugin
:
pluginList
)
{
plugin
.
updateResources
(
newResources
);
}
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
Reflector
reflector
=
Reflector
.
on
(
AssetManager
.
class
).
method
(
"addAssetPath"
,
String
.
class
);
if
(
Build
.
VERSION
.
SDK_INT
<
Build
.
VERSION_CODES
.
LOLLIPOP
)
{
assetManager
=
AssetManager
.
class
.
newInstance
();
reflector
.
bind
(
assetManager
);
reflector
.
call
(
hostContext
.
getApplicationInfo
().
sourceDir
);
}
else
{
assetManager
=
hostResources
.
getAssets
();
reflector
.
bind
(
assetManager
);
}
reflector
.
call
(
apk
);
List
<
LoadedPlugin
>
pluginList
=
PluginManager
.
getInstance
(
hostContext
).
getAllLoadedPlugins
();
for
(
LoadedPlugin
plugin
:
pluginList
)
{
reflector
.
call
(
plugin
.
getLocation
());
}
if
(
isMiUi
(
hostResources
))
{
newResources
=
MiUiResourcesCompat
.
createResources
(
hostResources
,
assetManager
);
}
else
if
(
isVivo
(
hostResources
))
{
newResources
=
VivoResourcesCompat
.
createResources
(
hostContext
,
hostResources
,
assetManager
);
}
else
if
(
isNubia
(
hostResources
))
{
newResources
=
NubiaResourcesCompat
.
createResources
(
hostResources
,
assetManager
);
}
else
if
(
isNotRawResources
(
hostResources
))
{
newResources
=
AdaptationResourcesCompat
.
createResources
(
hostResources
,
assetManager
);
}
else
{
// is raw android resources
newResources
=
new
Resources
(
assetManager
,
hostResources
.
getDisplayMetrics
(),
hostResources
.
getConfiguration
());
}
// lastly, sync all LoadedPlugin to newResources
for
(
LoadedPlugin
plugin
:
pluginList
)
{
plugin
.
updateResources
(
newResources
);
}
return
newResources
;
}
public
static
void
hookResources
(
Context
base
,
Resources
resources
)
{
if
(
Build
.
VERSION
.
SDK_INT
>=
Build
.
VERSION_CODES
.
N
)
{
return
;
}
try
{
Reflector
reflector
=
Reflector
.
with
(
base
);
reflector
.
field
(
"mResources"
).
set
(
resources
);
...
...
@@ -108,6 +131,90 @@ class ResourcesManager {
e
.
printStackTrace
();
}
}
/**
* Use System Apis to update all existing resources.
* <br/>
* 1. Update ApplicationInfo.splitSourceDirs and LoadedApk.mSplitResDirs
* <br/>
* 2. Replace all keys of ResourcesManager.mResourceImpls to new ResourcesKey
* <br/>
* 3. Use ResourcesManager.appendLibAssetForMainAssetPath(appInfo.publicSourceDir, "${packageName}.vastub") to update all existing resources.
* <br/>
*
* see android.webkit.WebViewDelegate.addWebViewAssetPath(Context)
*/
@TargetApi
(
Build
.
VERSION_CODES
.
N
)
private
static
Resources
createResourcesForN
(
Context
context
,
String
packageName
,
File
apk
)
throws
Exception
{
long
startTime
=
System
.
currentTimeMillis
();
String
newAssetPath
=
apk
.
getAbsolutePath
();
ApplicationInfo
info
=
context
.
getApplicationInfo
();
String
baseResDir
=
info
.
publicSourceDir
;
info
.
splitSourceDirs
=
append
(
info
.
splitSourceDirs
,
newAssetPath
);
LoadedApk
loadedApk
=
Reflector
.
with
(
context
).
field
(
"mPackageInfo"
).
get
();
Reflector
rLoadedApk
=
Reflector
.
with
(
loadedApk
).
field
(
"mSplitResDirs"
);
String
[]
splitResDirs
=
rLoadedApk
.
get
();
rLoadedApk
.
set
(
append
(
splitResDirs
,
newAssetPath
));
final
android
.
app
.
ResourcesManager
resourcesManager
=
android
.
app
.
ResourcesManager
.
getInstance
();
ArrayMap
<
ResourcesKey
,
WeakReference
<
ResourcesImpl
>>
originalMap
=
Reflector
.
with
(
resourcesManager
).
field
(
"mResourceImpls"
).
get
();
synchronized
(
resourcesManager
)
{
HashMap
<
ResourcesKey
,
WeakReference
<
ResourcesImpl
>>
resolvedMap
=
new
HashMap
<>();
if
(
Build
.
VERSION
.
SDK_INT
>=
28
||
(
Build
.
VERSION
.
SDK_INT
==
27
&&
Build
.
VERSION
.
PREVIEW_SDK_INT
!=
0
))
{
// P Preview
ResourcesManagerCompatForP
.
resolveResourcesImplMap
(
originalMap
,
resolvedMap
,
context
,
loadedApk
);
}
else
{
ResourcesManagerCompatForN
.
resolveResourcesImplMap
(
originalMap
,
resolvedMap
,
baseResDir
,
newAssetPath
);
}
originalMap
.
clear
();
originalMap
.
putAll
(
resolvedMap
);
}
android
.
app
.
ResourcesManager
.
getInstance
().
appendLibAssetForMainAssetPath
(
baseResDir
,
packageName
+
".vastub"
);
Resources
newResources
=
context
.
getResources
();
// lastly, sync all LoadedPlugin to newResources
for
(
LoadedPlugin
plugin
:
PluginManager
.
getInstance
(
context
).
getAllLoadedPlugins
())
{
plugin
.
updateResources
(
newResources
);
}
Log
.
d
(
TAG
,
"createResourcesForN cost time: +"
+
(
System
.
currentTimeMillis
()
-
startTime
)
+
"ms"
);
return
newResources
;
}
private
static
String
[]
append
(
String
[]
paths
,
String
newPath
)
{
if
(
contains
(
paths
,
newPath
))
{
return
paths
;
}
final
int
newPathsCount
=
1
+
(
paths
!=
null
?
paths
.
length
:
0
);
final
String
[]
newPaths
=
new
String
[
newPathsCount
];
if
(
paths
!=
null
)
{
System
.
arraycopy
(
paths
,
0
,
newPaths
,
0
,
paths
.
length
);
}
newPaths
[
newPathsCount
-
1
]
=
newPath
;
return
newPaths
;
}
@TargetApi
(
Build
.
VERSION_CODES
.
KITKAT
)
private
static
boolean
contains
(
String
[]
array
,
String
value
)
{
if
(
array
==
null
)
{
return
false
;
}
for
(
int
i
=
0
;
i
<
array
.
length
;
i
++)
{
if
(
Objects
.
equals
(
array
[
i
],
value
))
{
return
true
;
}
}
return
false
;
}
private
static
boolean
isMiUi
(
Resources
resources
)
{
return
resources
.
getClass
().
getName
().
equals
(
"android.content.res.MiuiResources"
);
...
...
@@ -170,4 +277,69 @@ class ResourcesManager {
}
}
private
static
final
class
ResourcesManagerCompatForN
{
public
static
void
resolveResourcesImplMap
(
Map
<
ResourcesKey
,
WeakReference
<
ResourcesImpl
>>
originalMap
,
Map
<
ResourcesKey
,
WeakReference
<
ResourcesImpl
>>
resolvedMap
,
String
baseResDir
,
String
newAssetPath
)
throws
Exception
{
for
(
Map
.
Entry
<
ResourcesKey
,
WeakReference
<
ResourcesImpl
>>
entry
:
originalMap
.
entrySet
())
{
ResourcesKey
key
=
entry
.
getKey
();
if
(
Objects
.
equals
(
key
.
mResDir
,
baseResDir
))
{
resolvedMap
.
put
(
new
ResourcesKey
(
key
.
mResDir
,
append
(
key
.
mSplitResDirs
,
newAssetPath
),
key
.
mOverlayDirs
,
key
.
mLibDirs
,
key
.
mDisplayId
,
key
.
mOverrideConfiguration
,
key
.
mCompatInfo
),
entry
.
getValue
());
}
else
{
resolvedMap
.
put
(
key
,
entry
.
getValue
());
}
}
}
}
private
static
final
class
ResourcesManagerCompatForP
{
@TargetApi
(
Build
.
VERSION_CODES
.
JELLY_BEAN_MR1
)
public
static
void
resolveResourcesImplMap
(
Map
<
ResourcesKey
,
WeakReference
<
ResourcesImpl
>>
originalMap
,
Map
<
ResourcesKey
,
WeakReference
<
ResourcesImpl
>>
resolvedMap
,
Context
context
,
LoadedApk
loadedApk
)
throws
Exception
{
HashMap
<
ResourcesImpl
,
Context
>
newResImplMap
=
new
HashMap
<>();
Map
<
ResourcesImpl
,
ResourcesKey
>
resKeyMap
=
new
HashMap
<>();
Resources
newRes
;
// Recreate the resImpl of the context
// See LoadedApk.getResources()
if
(
mDefaultConfiguration
==
null
)
{
mDefaultConfiguration
=
new
Configuration
();
}
newRes
=
context
.
createConfigurationContext
(
mDefaultConfiguration
).
getResources
();
newResImplMap
.
put
(
newRes
.
getImpl
(),
context
);
// Recreate the ResImpl of the activity
for
(
WeakReference
<
Activity
>
ref
:
PluginManager
.
getInstance
(
context
).
getInstrumentation
().
getActivities
())
{
Activity
activity
=
ref
.
get
();
if
(
activity
!=
null
)
{
newRes
=
activity
.
createConfigurationContext
(
activity
.
getResources
().
getConfiguration
()).
getResources
();
newResImplMap
.
put
(
newRes
.
getImpl
(),
activity
);
}
}
// Mapping all resKey and resImpl
for
(
Map
.
Entry
<
ResourcesKey
,
WeakReference
<
ResourcesImpl
>>
entry
:
originalMap
.
entrySet
())
{
ResourcesImpl
resImpl
=
entry
.
getValue
().
get
();
if
(
resImpl
!=
null
)
{
resKeyMap
.
put
(
resImpl
,
entry
.
getKey
());
}
resolvedMap
.
put
(
entry
.
getKey
(),
entry
.
getValue
());
}
// Replace the resImpl to the new resKey and remove the origin resKey
for
(
Map
.
Entry
<
ResourcesImpl
,
Context
>
entry
:
newResImplMap
.
entrySet
())
{
ResourcesKey
newKey
=
resKeyMap
.
get
(
entry
.
getKey
());
ResourcesImpl
originResImpl
=
entry
.
getValue
().
getResources
().
getImpl
();
resolvedMap
.
put
(
newKey
,
new
WeakReference
<>(
originResImpl
));
resolvedMap
.
remove
(
resKeyMap
.
get
(
originResImpl
));
}
}
}
}
CoreLibrary/src/main/java/com/didi/virtualapk/internal/VAInstrumentation.java
浏览文件 @
0ae4d69f
...
...
@@ -37,6 +37,10 @@ import com.didi.virtualapk.PluginManager;
import
com.didi.virtualapk.utils.PluginUtil
;
import
com.didi.virtualapk.utils.Reflector
;
import
java.lang.ref.WeakReference
;
import
java.util.ArrayList
;
import
java.util.List
;
/**
* Created by renyugang on 16/8/10.
...
...
@@ -46,6 +50,8 @@ public class VAInstrumentation extends Instrumentation implements Handler.Callba
public
static
final
int
LAUNCH_ACTIVITY
=
100
;
private
Instrumentation
mBase
;
private
final
ArrayList
<
WeakReference
<
Activity
>>
mActivities
=
new
ArrayList
<>();
PluginManager
mPluginManager
;
...
...
@@ -98,7 +104,7 @@ public class VAInstrumentation extends Instrumentation implements Handler.Callba
ComponentName
component
=
PluginUtil
.
getComponent
(
intent
);
if
(
component
==
null
)
{
return
mBase
.
newActivity
(
cl
,
className
,
intent
);
return
newActivity
(
mBase
.
newActivity
(
cl
,
className
,
intent
)
);
}
String
targetClassName
=
component
.
getClassName
();
...
...
@@ -107,7 +113,7 @@ public class VAInstrumentation extends Instrumentation implements Handler.Callba
LoadedPlugin
plugin
=
this
.
mPluginManager
.
getLoadedPlugin
(
component
);
if
(
plugin
==
null
)
{
return
mBase
.
newActivity
(
cl
,
className
,
intent
);
return
newActivity
(
mBase
.
newActivity
(
cl
,
className
,
intent
)
);
}
Activity
activity
=
mBase
.
newActivity
(
plugin
.
getClassLoader
(),
targetClassName
,
intent
);
...
...
@@ -120,10 +126,10 @@ public class VAInstrumentation extends Instrumentation implements Handler.Callba
// ignored.
}
return
activity
;
return
newActivity
(
activity
)
;
}
return
mBase
.
newActivity
(
cl
,
className
,
intent
);
return
newActivity
(
mBase
.
newActivity
(
cl
,
className
,
intent
)
);
}
@Override
...
...
@@ -152,7 +158,7 @@ public class VAInstrumentation extends Instrumentation implements Handler.Callba
LoadedPlugin
plugin
=
this
.
mPluginManager
.
getLoadedPlugin
(
intent
);
Reflector
.
with
(
base
).
field
(
"mResources"
).
set
(
plugin
.
getResources
());
Reflector
reflector
=
Reflector
.
with
(
activity
);
reflector
.
field
(
"mBase"
).
set
(
plugin
.
getPluginContext
(
));
reflector
.
field
(
"mBase"
).
set
(
new
PluginContext
(
plugin
,
activity
.
getBaseContext
()
));
reflector
.
field
(
"mApplication"
).
set
(
plugin
.
getApplication
());
// set screenOrientation
...
...
@@ -207,4 +213,21 @@ public class VAInstrumentation extends Instrumentation implements Handler.Callba
return
mBase
.
getComponentName
();
}
private
Activity
newActivity
(
Activity
activity
)
{
synchronized
(
mActivities
)
{
for
(
int
i
=
mActivities
.
size
()
-
1
;
i
>=
0
;
i
--)
{
if
(
mActivities
.
get
(
i
).
get
()
==
null
)
{
mActivities
.
remove
(
i
);
}
}
mActivities
.
add
(
new
WeakReference
<>(
activity
));
}
return
activity
;
}
List
<
WeakReference
<
Activity
>>
getActivities
()
{
synchronized
(
mActivities
)
{
return
new
ArrayList
<>(
mActivities
);
}
}
}
app/src/main/java/com/didi/virtualapk/MainActivity.java
浏览文件 @
0ae4d69f
...
...
@@ -14,15 +14,18 @@ import android.support.annotation.NonNull;
import
android.support.v7.app.AppCompatActivity
;
import
android.util.Log
;
import
android.view.View
;
import
android.view.ViewGroup
;
import
android.webkit.WebView
;
import
android.webkit.WebViewClient
;
import
android.widget.LinearLayout
;
import
android.widget.TextView
;
import
android.widget.Toast
;
import
com.didi.virtualapk.internal.PluginContentResolver
;
import
com.didi.virtualapk.internal.LoadedPlugin
;
import
com.didi.virtualapk.internal.PluginContentResolver
;
import
java.io.File
;
public
class
MainActivity
extends
AppCompatActivity
{
private
static
final
int
PERMISSION_REQUEST_CODE_STORAGE
=
20171222
;
...
...
@@ -105,14 +108,22 @@ public class MainActivity extends AppCompatActivity {
bookUri
=
PluginContentResolver
.
wrapperUri
(
plugin
,
bookUri
);
Cursor
bookCursor
=
getContentResolver
().
query
(
bookUri
,
new
String
[]{
"_id"
,
"name"
},
null
,
null
,
null
);
while
(
bookCursor
.
moveToNext
())
{
int
bookId
=
bookCursor
.
getInt
(
0
);
String
bookName
=
bookCursor
.
getString
(
1
);
Log
.
d
(
"ryg"
,
"query book:"
+
bookId
+
", "
+
bookName
);
if
(
bookCursor
!=
null
)
{
while
(
bookCursor
.
moveToNext
())
{
int
bookId
=
bookCursor
.
getInt
(
0
);
String
bookName
=
bookCursor
.
getString
(
1
);
Log
.
d
(
"ryg"
,
"query book:"
+
bookId
+
", "
+
bookName
);
}
bookCursor
.
close
();
}
bookCursor
.
close
();
}
else
if
(
v
.
getId
()
==
R
.
id
.
about
)
{
showAbout
();
}
else
if
(
v
.
getId
()
==
R
.
id
.
webview
)
{
LinearLayout
linearLayout
=
(
LinearLayout
)
v
.
getParent
();
WebView
webView
=
new
WebView
(
this
);
linearLayout
.
addView
(
webView
,
new
LinearLayout
.
LayoutParams
(
ViewGroup
.
LayoutParams
.
MATCH_PARENT
,
0
,
1
));
webView
.
setWebViewClient
(
new
WebViewClient
());
webView
.
loadUrl
(
"http://github.com/didi/VirtualAPK"
);
}
}
...
...
app/src/main/res/layout/activity_main.xml
浏览文件 @
0ae4d69f
<?xml version="1.0" encoding="utf-8"?>
<
Relative
Layout
xmlns:android=
"http://schemas.android.com/apk/res/android"
<
Linear
Layout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:tools=
"http://schemas.android.com/tools"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:orientation=
"vertical"
android:paddingBottom=
"@dimen/activity_vertical_margin"
android:paddingLeft=
"@dimen/activity_horizontal_margin"
android:paddingRight=
"@dimen/activity_horizontal_margin"
...
...
@@ -10,30 +11,32 @@
tools:context=
"com.didi.virtualapk.MainActivity"
>
<TextView
android:layout_width=
"
wrap_cont
ent"
android:layout_width=
"
match_par
ent"
android:layout_height=
"wrap_content"
android:text=
"Hello World!"
android:id=
"@+id/textView"
/>
<Button
android:layout_width=
"
wrap_cont
ent"
android:layout_width=
"
match_par
ent"
android:layout_height=
"wrap_content"
android:text=
"@string/open_plugin"
android:id=
"@+id/button"
android:onClick=
"onButtonClick"
android:layout_below=
"@+id/textView"
android:layout_alignParentLeft=
"true"
android:layout_alignParentStart=
"true"
android:layout_marginTop=
"42dp"
/>
android:layout_marginTop=
"10dp"
/>
<Button
android:text=
"@string/about"
android:layout_width=
"
wrap_cont
ent"
android:layout_width=
"
match_par
ent"
android:layout_height=
"wrap_content"
android:layout_below=
"@+id/button"
android:onClick=
"onButtonClick"
android:layout_alignParentLeft=
"true"
android:layout_marginTop=
"58dp"
android:id=
"@+id/about"
android:layout_alignRight=
"@+id/button"
/>
</RelativeLayout>
android:layout_marginTop=
"10dp"
android:id=
"@+id/about"
/>
<Button
android:text=
"@string/webview"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:onClick=
"onButtonClick"
android:layout_marginTop=
"10dp"
android:id=
"@+id/webview"
/>
</LinearLayout>
app/src/main/res/values-en/strings.xml
浏览文件 @
0ae4d69f
...
...
@@ -2,5 +2,6 @@
<string
name=
"app_name"
>
VirtualAPK-EN
</string>
<string
name=
"open_plugin"
>
open plugin
</string>
<string
name=
"about"
>
about
</string>
<string
name=
"webview"
>
Append WebView
</string>
<string
name=
"about_detail"
>
VirtualAPK is a plugin framework powered by DiDi company for Android,see the source code : https://github.com/didi/VirtualAPK
</string>
</resources>
\ No newline at end of file
app/src/main/res/values/strings.xml
浏览文件 @
0ae4d69f
...
...
@@ -2,5 +2,6 @@
<string
name=
"app_name"
>
VirtualAPK
</string>
<string
name=
"open_plugin"
>
加载插件
</string>
<string
name=
"about"
>
关于
</string>
<string
name=
"webview"
>
Append WebView
</string>
<string
name=
"about_detail"
>
VirtualAPK 是一款由滴滴出行研发的 Android 插件化框架,项目地址:https://github.com/didi/VirtualAPK
</string>
</resources>
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录