Skip to content

Commit 52e4636

Browse files
author
xiawanli
committed
Use Glide to load app icon async, to avoid loading all the icosn into memory at one time and decrease the app list loading time and decrease memory cost.
1 parent 30a6510 commit 52e4636

File tree

12 files changed

+337
-18
lines changed

12 files changed

+337
-18
lines changed

VirtualApp/app/build.gradle

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,4 +94,10 @@ dependencies {
9494
compile 'com.allenliu.versionchecklib:library:1.8.3'
9595
compile 'com.github.medyo:android-about-page:1.2.2'
9696
compile 'moe.feng:AlipayZeroSdk:1.1'
97+
98+
//Glide
99+
implementation ('com.github.bumptech.glide:glide:4.8.0') {
100+
exclude(group: "com.android.support")
101+
}
102+
annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0'
97103
}

VirtualApp/app/proguard-rules.pro

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,13 @@
3333

3434
#导航
3535
-keep class com.amap.api.navi.**{*;}
36-
-keep class com.autonavi.**{*;}
36+
-keep class com.autonavi.**{*;}
37+
38+
##--Glide--
39+
-keep class com.bumptech.glide.**{*;}
40+
-keep public class * implements com.bumptech.glide.module.GlideModule
41+
-keep public class * extends com.bumptech.glide.module.AppGlideModule
42+
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
43+
**[] $VALUES;
44+
public *;
45+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package io.virtualapp.glide;
2+
3+
import android.content.Context;
4+
import android.support.annotation.DrawableRes;
5+
import android.widget.ImageView;
6+
7+
import com.bumptech.glide.load.engine.DiskCacheStrategy;
8+
9+
import static io.virtualapp.glide.PackageIconResourceLoader.DATA_PACKAGE_FILE_PATH_PREFIX;
10+
import static io.virtualapp.glide.PackageIconResourceLoader.DATA_PACKAGE_PREFIX;
11+
12+
/**
13+
* Created by Windy on 2018/10/25
14+
*/
15+
public class GlideUtils {
16+
17+
public static void loadInstalledPackageIcon(Context context, String packageName, ImageView target, @DrawableRes int placeHolder) {
18+
GlideApp.with(context)
19+
.load(DATA_PACKAGE_PREFIX + packageName)
20+
.placeholder(placeHolder)
21+
.diskCacheStrategy(DiskCacheStrategy.NONE)
22+
.into(target);
23+
}
24+
25+
public static void loadPackageIconFromApkFile(Context context, String apkFilePath, ImageView target, @DrawableRes int placeHolder) {
26+
GlideApp.with(context)
27+
.load(DATA_PACKAGE_FILE_PATH_PREFIX + apkFilePath)
28+
.placeholder(placeHolder)
29+
.diskCacheStrategy(DiskCacheStrategy.NONE)
30+
.into(target);
31+
}
32+
33+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package io.virtualapp.glide;
2+
3+
import android.content.Context;
4+
5+
import com.bumptech.glide.Glide;
6+
import com.bumptech.glide.GlideBuilder;
7+
import com.bumptech.glide.Registry;
8+
import com.bumptech.glide.annotation.GlideModule;
9+
import com.bumptech.glide.load.engine.cache.LruResourceCache;
10+
import com.bumptech.glide.load.engine.cache.MemorySizeCalculator;
11+
import com.bumptech.glide.module.AppGlideModule;
12+
import com.lody.virtual.helper.utils.VLog;
13+
14+
import java.io.InputStream;
15+
16+
/**
17+
* Created by Windy on 2018/10/25
18+
*/
19+
@GlideModule
20+
public class MyGlideModule extends AppGlideModule {
21+
@Override
22+
public void applyOptions(Context context, GlideBuilder builder) {
23+
MemorySizeCalculator calculator = new MemorySizeCalculator.Builder(context)
24+
.build();
25+
builder.setMemoryCache(new LruResourceCache(calculator.getMemoryCacheSize() / 2));
26+
27+
VLog.i("MyGlideModule", "applyOptions");
28+
}
29+
30+
@Override
31+
public boolean isManifestParsingEnabled() {
32+
return false;
33+
}
34+
35+
@Override
36+
public void registerComponents(Context context, Glide glide, Registry registry) {
37+
super.registerComponents(context, glide, registry);
38+
registry.prepend(String.class, InputStream.class, new PackageIconResourceLoaderFactory(context));
39+
}
40+
}
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
package io.virtualapp.glide;
2+
3+
import android.content.Context;
4+
import android.content.pm.PackageInfo;
5+
import android.content.pm.PackageManager;
6+
import android.graphics.Bitmap;
7+
import android.graphics.Canvas;
8+
import android.graphics.drawable.BitmapDrawable;
9+
import android.graphics.drawable.Drawable;
10+
import android.support.annotation.NonNull;
11+
12+
import com.bumptech.glide.Priority;
13+
import com.bumptech.glide.load.DataSource;
14+
import com.bumptech.glide.load.data.DataFetcher;
15+
import com.lody.virtual.helper.utils.VLog;
16+
17+
import java.io.ByteArrayInputStream;
18+
import java.io.ByteArrayOutputStream;
19+
import java.io.IOException;
20+
import java.io.InputStream;
21+
22+
import static io.virtualapp.glide.PackageIconResourceLoader.DATA_PACKAGE_FILE_PATH_PREFIX;
23+
import static io.virtualapp.glide.PackageIconResourceLoader.DATA_PACKAGE_PREFIX;
24+
25+
/**
26+
* Created by Windy on 2018/10/25
27+
*/
28+
public class PackageIconResourceDataFetcher implements DataFetcher<InputStream> {
29+
30+
private static final String TAG = PackageIconResourceDataFetcher.class.getSimpleName();
31+
32+
private Context context;
33+
private String packageModel;
34+
35+
private InputStream data;
36+
37+
public PackageIconResourceDataFetcher(Context context, String packageName) {
38+
this.context = context.getApplicationContext();
39+
this.packageModel = packageName;
40+
}
41+
42+
@Override
43+
public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {
44+
try {
45+
data = loadResource();
46+
} catch (Exception e) {
47+
VLog.e(TAG, "Failed to load data from asset manager", e);
48+
callback.onLoadFailed(e);
49+
return;
50+
}
51+
callback.onDataReady(data);
52+
}
53+
54+
@Override
55+
public void cleanup() {
56+
if (data == null) {
57+
return;
58+
}
59+
try {
60+
data.close();
61+
} catch (IOException e) {
62+
// Ignored.
63+
}
64+
}
65+
66+
@Override
67+
public void cancel() {
68+
69+
}
70+
71+
@NonNull
72+
@Override
73+
public Class<InputStream> getDataClass() {
74+
return InputStream.class;
75+
}
76+
77+
@NonNull
78+
@Override
79+
public DataSource getDataSource() {
80+
return DataSource.LOCAL;
81+
}
82+
83+
//load icon res accord to package name, or apk path
84+
private InputStream loadResource() {
85+
PackageInfo packageInfo = null;
86+
Drawable drawable = null;
87+
try {
88+
packageInfo = getPackageInfo();
89+
if (packageInfo == null) {
90+
return null;
91+
}
92+
93+
drawable = packageInfo.applicationInfo.loadIcon(context.getPackageManager());
94+
} catch (PackageManager.NameNotFoundException e) {
95+
e.printStackTrace();
96+
}
97+
if (drawable == null) {
98+
return null;
99+
}
100+
return drawableToInputStream(drawable);
101+
}
102+
103+
private PackageInfo getPackageInfo() throws PackageManager.NameNotFoundException {
104+
if (packageModel.startsWith(DATA_PACKAGE_PREFIX)) {
105+
return context.getPackageManager().getPackageInfo(getPackageTrueModel(DATA_PACKAGE_PREFIX), 0);
106+
} else if (packageModel.startsWith(DATA_PACKAGE_FILE_PATH_PREFIX)) {
107+
return context.getPackageManager().getPackageArchiveInfo(getPackageTrueModel(DATA_PACKAGE_FILE_PATH_PREFIX), 0);
108+
}
109+
return null;
110+
}
111+
112+
private String getPackageTrueModel(String prefix) {
113+
return packageModel.replaceAll(prefix, "");
114+
}
115+
116+
private InputStream drawableToInputStream(Drawable drawable) {
117+
Bitmap bitmap = drawableToBitmap(drawable);
118+
ByteArrayOutputStream stream = new ByteArrayOutputStream();
119+
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); //use the compression format of your need
120+
return new ByteArrayInputStream(stream.toByteArray());
121+
}
122+
123+
private static Bitmap drawableToBitmap(Drawable drawable) {
124+
if (drawable instanceof BitmapDrawable) {
125+
return ((BitmapDrawable) drawable).getBitmap();
126+
}
127+
128+
int width = drawable.getIntrinsicWidth();
129+
width = width > 0 ? width : 1;
130+
int height = drawable.getIntrinsicHeight();
131+
height = height > 0 ? height : 1;
132+
133+
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
134+
Canvas canvas = new Canvas(bitmap);
135+
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
136+
drawable.draw(canvas);
137+
138+
return bitmap;
139+
}
140+
141+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package io.virtualapp.glide;
2+
3+
import android.content.Context;
4+
import android.support.annotation.NonNull;
5+
import android.support.annotation.Nullable;
6+
7+
import com.bumptech.glide.load.Options;
8+
import com.bumptech.glide.load.model.ModelLoader;
9+
import com.bumptech.glide.signature.ObjectKey;
10+
11+
import java.io.InputStream;
12+
13+
/**
14+
* Created by Windy on 2018/10/25
15+
*/
16+
public class PackageIconResourceLoader implements ModelLoader<String, InputStream> {
17+
18+
public static final String DATA_PACKAGE_PREFIX = "data:packageName/";
19+
public static final String DATA_PACKAGE_FILE_PATH_PREFIX = "data:packageFilePath/";
20+
21+
private Context context;
22+
23+
24+
public PackageIconResourceLoader(Context context) {
25+
this.context = context;
26+
}
27+
28+
@Nullable
29+
@Override
30+
public LoadData<InputStream> buildLoadData(@NonNull String model, int width, int height, @NonNull Options options) {
31+
return new LoadData<>(new ObjectKey(model), new PackageIconResourceDataFetcher(context, model));
32+
}
33+
34+
@Override
35+
public boolean handles(@NonNull String model) {
36+
return model.startsWith(DATA_PACKAGE_PREFIX) || model.startsWith(DATA_PACKAGE_FILE_PATH_PREFIX);
37+
}
38+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package io.virtualapp.glide;
2+
3+
import android.content.Context;
4+
import android.support.annotation.NonNull;
5+
6+
import com.bumptech.glide.load.model.ModelLoader;
7+
import com.bumptech.glide.load.model.ModelLoaderFactory;
8+
import com.bumptech.glide.load.model.MultiModelLoaderFactory;
9+
10+
import java.io.InputStream;
11+
12+
/**
13+
* Created by Windy on 2018/10/25
14+
*/
15+
public class PackageIconResourceLoaderFactory implements ModelLoaderFactory<String, InputStream> {
16+
17+
private Context context;
18+
19+
public PackageIconResourceLoaderFactory(Context context) {
20+
this.context = context;
21+
}
22+
23+
@NonNull
24+
@Override
25+
public ModelLoader<String, InputStream> build(@NonNull MultiModelLoaderFactory multiFactory) {
26+
return new PackageIconResourceLoader(context);
27+
}
28+
29+
@Override
30+
public void teardown() {
31+
32+
}
33+
}

VirtualApp/app/src/main/java/io/virtualapp/home/ListAppFragment.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ public void onViewCreated(View view, Bundle savedInstanceState) {
162162
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(getContext(), DividerItemDecoration.VERTICAL);
163163
dividerItemDecoration.setDrawable(new ColorDrawable(0x1f000000));
164164
mRecyclerView.addItemDecoration(dividerItemDecoration);
165-
mAdapter = new CloneAppListAdapter(getActivity());
165+
mAdapter = new CloneAppListAdapter(getActivity(), getSelectFrom());
166166
mRecyclerView.setAdapter(mAdapter);
167167
mAdapter.setOnItemClickListener(new CloneAppListAdapter.ItemEventListener() {
168168
@Override

VirtualApp/app/src/main/java/io/virtualapp/home/adapters/CloneAppListAdapter.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.virtualapp.home.adapters;
22

33
import android.content.Context;
4+
import android.support.annotation.Nullable;
45
import android.support.v7.widget.RecyclerView;
56
import android.support.v7.widget.StaggeredGridLayoutManager;
67
import android.view.LayoutInflater;
@@ -9,10 +10,12 @@
910
import android.widget.ImageView;
1011
import android.widget.TextView;
1112

13+
import java.io.File;
1214
import java.util.List;
1315

1416
import io.virtualapp.R;
1517
import io.virtualapp.abs.ui.VUiKit;
18+
import io.virtualapp.glide.GlideUtils;
1619
import io.virtualapp.home.models.AppInfo;
1720
import io.virtualapp.widgets.DragSelectRecyclerViewAdapter;
1821
import io.virtualapp.widgets.LabelView;
@@ -28,7 +31,13 @@ public class CloneAppListAdapter extends DragSelectRecyclerViewAdapter<CloneAppL
2831
private List<AppInfo> mAppList;
2932
private ItemEventListener mItemEventListener;
3033

31-
public CloneAppListAdapter(Context context) {
34+
private Context mContext;
35+
private File mFrom;
36+
37+
38+
public CloneAppListAdapter(Context context, @Nullable File from) {
39+
mContext = context;
40+
mFrom = from;
3241
this.mInflater = LayoutInflater.from(context);
3342
mFooterView = new View(context);
3443
StaggeredGridLayoutManager.LayoutParams params = new StaggeredGridLayoutManager.LayoutParams(
@@ -67,7 +76,13 @@ public void onBindViewHolder(ViewHolder holder, int position) {
6776
}
6877
super.onBindViewHolder(holder, position);
6978
AppInfo info = mAppList.get(position);
70-
holder.iconView.setImageDrawable(info.icon);
79+
80+
if (mFrom == null) {
81+
GlideUtils.loadInstalledPackageIcon(mContext, info.packageName, holder.iconView, android.R.drawable.sym_def_app_icon);
82+
} else {
83+
GlideUtils.loadPackageIconFromApkFile(mContext, info.path, holder.iconView, android.R.drawable.sym_def_app_icon);
84+
}
85+
7186
holder.nameView.setText(String.format("%s: %s", info.name, info.version));
7287
if (isIndexSelected(position)) {
7388
holder.iconView.setAlpha(1f);

VirtualApp/app/src/main/java/io/virtualapp/home/repo/AppRepository.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,8 @@ private List<AppInfo> convertPackageInfoToAppData(Context context, List<PackageI
182182
info.packageName = pkg.packageName;
183183
info.fastOpen = fastOpen;
184184
info.path = path;
185-
info.icon = ai.loadIcon(pm);
185+
// info.icon = ai.loadIcon(pm);
186+
info.icon = null; // Use Glide to load the icon async
186187
info.name = ai.loadLabel(pm);
187188
info.version = pkg.versionName;
188189
InstalledAppInfo installedAppInfo = VirtualCore.get().getInstalledAppInfo(pkg.packageName, 0);

0 commit comments

Comments
 (0)