Android中资源加载源码分析
来源:互联网 发布:淘宝类目 编辑:程序博客网 时间:2024/05/16 04:19
1.简介
我们在布局文件中使用View的一些属性时,有没有想过是怎么加载进来的? 比如说在布局文件中使用ImageView设置图片时;
<ImageView android:id="@+id/iv_skin" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/skin1" />
下面我们尝试着去源码里边寻找答案;
2.扒源码分析,资源是如何加载的
这里我们还是以最常用的ImageView为例,查看src属性是怎么加载进来的:先查看ImageView.java源码(6.0源码为例)
public ImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); initImageView(); // 部分代码如下 final TypedArray a = context.obtainStyledAttributes( attrs, com.android.internal.R.styleable.ImageView, defStyleAttr, defStyleRes); // 通过TypedArray获取图片drawable Drawable d = a.getDrawable(com.android.internal.R.styleable.ImageView_src); if (d != null) { setImageDrawable(d); // 设置到imageView上 } ...省略代码...}
下面追踪 TypedArray.java的getDrawable(int index)方法
@Nullable public Drawable getDrawable(int index) { if (mRecycled) { throw new RuntimeException("Cannot make calls to a recycled instance!"); } final TypedValue value = mValue; if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); } //我们可以看到是通过Resources类来加载drawable图片的--加载资源的方式 return mResources.loadDrawable(value, value.resourceId, mTheme); } return null; }
其实多看几个View的资源加载过程,都是通过Resources类来加载资源的
3.源码中Resources对象的创建过程分析
我们在activity中也经常这样使用 getResources().getDrawable(R.drawable.account)
那么这个Resources实例是如何创建的呢?
我们点进去追踪 ContextThemeWrapper.java 的
` @Overridepublic Resources getResources() { if (mResources != null) { return mResources; } if (mOverrideConfiguration == null) { mResources = super.getResources(); return mResources; } else { Context resc = createConfigurationContext(mOverrideConfiguration); mResources = resc.getResources(); return mResources; }}`
我们接着看 super.getResources()调用,发现
`ContextWrapper.java类@Overridepublic Resources getResources(){ return mBase.getResources();}`
继续追踪,最终发现是Context.java的抽象方法
public abstract Resources getResources();
我们只能来找Context的实现类了,那么我们从ContextImpl.java这个实现类入手(android.app包下的),查找mResources = 在那里赋值的
private ContextImpl(ContextImpl container, ActivityThread mainThread, LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted, Display display, Configuration overrideConfiguration, int createDisplayWithId) { ...省略代码,我们只看我们关心的代码... mPackageInfo = packageInfo; mResourcesManager = ResourcesManager.getInstance(); // 是通过LoadedApk的getResources()方法来加载的 Resources resources = packageInfo.getResources(mainThread); if (resources != null) { if (displayId != Display.DEFAULT_DISPLAY || overrideConfiguration != null || (compatInfo != null && compatInfo.applicationScale != resources.getCompatibilityInfo().applicationScale)) { resources = mResourcesManager.getTopLevelResources(packageInfo.getResDir(), packageInfo.getSplitResDirs(), packageInfo.getOverlayDirs(), packageInfo.getApplicationInfo().sharedLibraryFiles, displayId, overrideConfiguration, compatInfo); } } mResources = resources;
继续追踪LoadedApk.java
public Resources getResources(ActivityThread mainThread) { // 我们看到采用了缓存策略,继续追踪 if (mResources == null) { mResources = mainThread.getTopLevelResources(mResDir, mSplitResDirs, mOverlayDirs, mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY, null, this); } return mResources; }
ActivityThread.java类 /** * Creates the top level resources for the given package. */ Resources getTopLevelResources(String resDir, String[] splitResDirs, String[] overlayDirs, String[] libDirs, int displayId, Configuration overrideConfiguration, LoadedApk pkgInfo) { // 通过资源管理类来加载,继续追踪 return mResourcesManager.getTopLevelResources(resDir, splitResDirs, overlayDirs, libDirs, displayId, overrideConfiguration, pkgInfo.getCompatibilityInfo()); }
/** * Creates the top level Resources for applications with the given compatibility info. * * @param resDir the resource directory. * @param splitResDirs split resource directories. * @param overlayDirs the resource overlay directories. * @param libDirs the shared library resource dirs this app references. * @param displayId display Id. * @param overrideConfiguration override configurations. * @param compatInfo the compatibility info. Must not be null. */ Resources getTopLevelResources(String resDir, String[] splitResDirs, String[] overlayDirs, String[] libDirs, int displayId, Configuration overrideConfiguration, CompatibilityInfo compatInfo) { ...省略不关心的代码... Resources r; ...... AssetManager assets = new AssetManager(); // resDir can be null if the 'android' package is creating a new Resources object. // This is fine, since each AssetManager automatically loads the 'android' package // already. if (resDir != null) { // 等下,我们还找着重看下,资源管理类是如何加载资源的 if (assets.addAssetPath(resDir) == 0) { return null; } } DisplayMetrics dm = getDisplayMetricsLocked(displayId); Configuration config; final boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY); final boolean hasOverrideConfig = key.hasOverrideConfiguration(); if (!isDefaultDisplay || hasOverrideConfig) { config = new Configuration(getConfiguration()); if (!isDefaultDisplay) { applyNonDefaultDisplayMetricsToConfigurationLocked(dm, config); } if (hasOverrideConfig) { config.updateFrom(key.mOverrideConfiguration); if (DEBUG) Slog.v(TAG, "Applied overrideConfig=" + key.mOverrideConfiguration); } } else { config = getConfiguration(); } // 可以看到Resources类最终是通过new来进行实例化的 r = new Resources(assets, dm, config, compatInfo);
这里附下AssetManager.java的添加资(产)源文件的方法,可以接收资产文件的目录,或者是压缩文件的路径, 另外观察到该方法是隐藏的方法,是调用不到的,只能通过反射 /** * Add an additional set of assets to the asset manager. This can be * either a directory or ZIP file. Not for use by applications. Returns * the cookie of the added asset, or 0 on failure. * {@hide} */ public final int addAssetPath(String path) { synchronized (this) { int res = addAssetPathNative(path); makeStringBlocks(mStringBlocks); return res; } }
4.总结下Resources实例的创建流程
packageInfo.getResources(mainThread) --> mainThread.getTopLevelResources --> mResourceManager.getTopLevelResources() --> r = new Resources(assets, dm, config, compatInfo);
好了,我们知道了Resources的创建流程,就可以自己来实例化一个Resources对象,然后加载一个我们本地的资源文件,我们可以用来做一键换肤功能;
5.通过Resources类,加载另外一个apk文件中的资源
我事先准备了一个plugin.apk,在其res/drawable目录下有一个skin2.png图片,下面我们实现一键切换实现图片应用到到我们apk中来,我们修改该apk后缀为plugin.zip,然后放置到SD卡的跟目下备用;新建一个工程,在布局文件中放置一个Button和ImageView,比较简单,上代码不解释.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <!-- 无论该根布局是线性还是相对,我们都可以添加NavigationBar了 --> <Button android:text="一键换肤" android:id="@+id/btn_change_skin" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <ImageView android:id="@+id/iv_skin" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/skin1" /></LinearLayout>
在代码中为Button添加点击事件,我们实现一键实现切换图片的功能(一键换肤)
final ImageView ivSkin = (ImageView) findViewById(R.id.iv_skin); findViewById(R.id.btn_change_skin).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //AssetManager assets = new AssetManager(); // 使用{@hide} 标注,隐藏了,我们只能通过反射来调用了 AssetManager assets = null; try { assets =AssetManager.class.newInstance(); // 加载资源 // assets.addAssetPath(String path) 又隐藏了 ,反射获取方法,然后再调方法 Method method = AssetManager.class.getDeclaredMethod("addAssetPath",String.class); // method.setAccessible(true); 如果是私用的,修改权限 method.invoke(assets, Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "plugin.zip"); Resources superRes = getResources(); Resources resources = new Resources(assets,superRes.getDisplayMetrics(), superRes.getConfiguration()); // 其实metrics和config都是可以直接new的,这里我们就不直接new了 // DisplayMetrics mMetrics = new DisplayMetrics(); // DisplayMetrics mMetrics = new DisplayMetrics(); int drawableId = resources.getIdentifier("skin2","drawable","com.xialm.skinplugin"); Drawable drawable = resources.getDrawable(drawableId); ivSkin.setImageDrawable(drawable); } catch (Exception e) { e.printStackTrace(); } } });
附效果图为:
1 0
- Android中资源加载源码分析
- Android资源加载源码分析
- Android源码分析-资源加载机制
- Android源码分析-资源加载机制
- Android源码分析-资源加载机制
- Android源码分析-资源加载机制
- Android源码分析-资源加载机制
- Android源码分析-资源加载机制
- Android源码分析-资源加载机制
- Android源码分析-资源加载机制解析
- Android源码分析-资源加载机制
- Android源码分析-资源加载机制
- Android App启动时Apk资源加载机制源码分析
- android中jni加载流程源码分析
- Android Apk资源加载机制源码分析以及资源动态加载实现系列文章
- Android drawable资源源码分析
- vc资源中加载png图片源码
- Android OpenGL库加载过程源码分析
- jvm class文件结构
- js实现页面跳转并传值(不需后台)
- python socket通信的小例子和几点反思
- (十)ROS在rviz中显示空间中的直线(visualization_msgs/Marker 消息)
- Android:常用控件属性
- Android中资源加载源码分析
- ASP.NET TextBox 当鼠标点击后清空默认提示文字
- python爬虫实例之一
- redis命令详解与使用场景举例——String
- 装船问题 (sdut oj)
- iOS 监听键盘高度,监听键盘出现 收回事件
- 剑指offer(C++)——替换空格
- C++ 简单的 Tcp 实现[socket] 服务器端与客户端通信
- NSNotification的理解