mirror of
https://gitee.com/replugin/RePlugin.git
synced 2024-11-30 02:38:34 +08:00
refactor support multidex feature in plugin application for the ROM below LOLLIPOP
This commit is contained in:
parent
2b67f455b4
commit
58ef28e5d1
@ -77,6 +77,16 @@ public class Constant {
|
||||
*/
|
||||
public static final String LOCAL_PLUGIN_APK_COVER_DIR = "p_c";
|
||||
|
||||
/**
|
||||
* 插件Odex(优化前)释放的以插件名独立隔离的子目录
|
||||
*/
|
||||
public static final String LOCAL_PLUGIN_MULTI_DEX_SUB_DIR = "_md";
|
||||
|
||||
/**
|
||||
* 插件Odex(优化后)释放的以插件名独立隔离的子目录
|
||||
*/
|
||||
public static final String LOCAL_PLUGIN_MULTI_ODEX_SUB_DIR = "_mod";
|
||||
|
||||
/**
|
||||
* 插件文件名,name-low-high-current.jar
|
||||
* 插件文件名规范:barcode-1-10-2.jar
|
||||
|
@ -19,6 +19,7 @@ package com.qihoo360.replugin;
|
||||
import android.os.Build;
|
||||
|
||||
import com.qihoo360.replugin.helper.LogDebug;
|
||||
import com.qihoo360.replugin.model.PluginInfo;
|
||||
import com.qihoo360.replugin.utils.CloseableUtils;
|
||||
import com.qihoo360.replugin.utils.FileUtils;
|
||||
import com.qihoo360.replugin.utils.ReflectUtils;
|
||||
@ -68,7 +69,7 @@ public class PluginDexClassLoader extends DexClassLoader {
|
||||
public PluginDexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) {
|
||||
super(dexPath, optimizedDirectory, librarySearchPath, parent);
|
||||
|
||||
installExtraDexes(dexPath, optimizedDirectory, parent);
|
||||
installMultiDexesBeforeLollipop(dexPath, optimizedDirectory, parent);
|
||||
|
||||
mHostClassLoader = RePluginInternal.getAppClassLoader();
|
||||
|
||||
@ -146,28 +147,34 @@ public class PluginDexClassLoader extends DexClassLoader {
|
||||
* @param dexPath
|
||||
* @param optimizedDirectory
|
||||
* @param parent
|
||||
* @deprecated apply to ROM below Lollipop,may be deprecated
|
||||
*/
|
||||
private void installExtraDexes(String dexPath, String optimizedDirectory, ClassLoader parent) {
|
||||
private void installMultiDexesBeforeLollipop(String dexPath, String optimizedDirectory, ClassLoader parent) {
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<Object[]> allElements = new LinkedList<>();
|
||||
|
||||
try {
|
||||
// get dexElements of main dex
|
||||
Class<?> clz = Class.forName("dalvik.system.BaseDexClassLoader");
|
||||
Object pathList = ReflectUtils.readField(clz, this, "pathList");
|
||||
Object[] mainElements = (Object[]) ReflectUtils.readField(pathList.getClass(), pathList, "dexElements");
|
||||
allElements.add(mainElements);
|
||||
|
||||
// get paths of dex
|
||||
List<File> dexFiles = getDexFiles(dexPath);
|
||||
|
||||
// get dexElements of extra dex (need to load dex first)
|
||||
if (dexFiles != null && dexFiles.size() > 0) {
|
||||
|
||||
List<Object[]> allElements = new LinkedList<>();
|
||||
|
||||
// get dexElements of main dex
|
||||
Class<?> clz = Class.forName("dalvik.system.BaseDexClassLoader");
|
||||
Object pathList = ReflectUtils.readField(clz, this, "pathList");
|
||||
Object[] mainElements = (Object[]) ReflectUtils.readField(pathList.getClass(), pathList, "dexElements");
|
||||
allElements.add(mainElements);
|
||||
|
||||
for (File file : dexFiles) {
|
||||
if (LogDebug.LOG && RePlugin.getConfig().isPrintDetailLog()) {
|
||||
LogDebug.d(TAG, "dex file:" + file.getName());
|
||||
}
|
||||
DexClassLoader dexClassLoader = new DexClassLoader(file.getAbsolutePath(), optimizedDirectory, optimizedDirectory, parent);
|
||||
|
||||
Object obj = ReflectUtils.readField(clz, dexClassLoader, "pathList");
|
||||
@ -180,14 +187,14 @@ public class PluginDexClassLoader extends DexClassLoader {
|
||||
|
||||
// rewrite Elements combined to classLoader
|
||||
ReflectUtils.writeField(pathList.getClass(), pathList, "dexElements", combineElements);
|
||||
}
|
||||
|
||||
//Test whether the Extra Dex is installed
|
||||
if (LogDebug.LOG && RePlugin.getConfig().isPrintDetailLog()) {
|
||||
//Test whether the Extra Dex is installed
|
||||
if (LogDebug.LOG && RePlugin.getConfig().isPrintDetailLog()) {
|
||||
|
||||
Object object = ReflectUtils.readField(pathList.getClass(), pathList, "dexElements");
|
||||
int length = Array.getLength(object);
|
||||
LogDebug.d(TAG, "dexElements length:" + length);
|
||||
Object object = ReflectUtils.readField(pathList.getClass(), pathList, "dexElements");
|
||||
int length = Array.getLength(object);
|
||||
LogDebug.d(TAG, "dexElements length:" + length);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
@ -236,12 +243,29 @@ public class PluginDexClassLoader extends DexClassLoader {
|
||||
* @return the File list of the extra dexes
|
||||
*/
|
||||
private List<File> getDexFiles(String dexPath) {
|
||||
|
||||
PluginInfo pi = null;
|
||||
ZipFile zipFile = null;
|
||||
List<File> files = null;
|
||||
|
||||
try {
|
||||
zipFile = new ZipFile(dexPath);
|
||||
files = traverseZipFile(dexPath.substring(0, dexPath.lastIndexOf("/")), zipFile);
|
||||
} catch (IOException e) {
|
||||
|
||||
String installedFileName = dexPath.substring(dexPath.lastIndexOf(File.separator) + 1).replace(".jar", "");
|
||||
List<PluginInfo> piList = RePlugin.getPluginInfoList();
|
||||
|
||||
for (PluginInfo info : piList) {
|
||||
if (info.makeInstalledFileName().equals(installedFileName)) {
|
||||
pi = info;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pi != null) {
|
||||
zipFile = new ZipFile(dexPath);
|
||||
files = traverseZipFile(pi.getUnoptDexParentDir().getAbsolutePath(), zipFile);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
CloseableUtils.closeQuietly(zipFile);
|
||||
|
@ -22,6 +22,7 @@ import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
@ -380,6 +381,34 @@ public class PluginInfo implements Parcelable, Cloneable {
|
||||
return new File(dir, makeInstalledFileName() + ".jar");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Dex(优化前)生成时所在的目录 <p>
|
||||
* 若为"纯APK"插件,则会位于app_p_od中;若为"p-n"插件,则会位于"app_plugins_v3_odex"中 <p>
|
||||
* 若支持同版本覆盖安装的话,则会位于app_p_c中; <p>
|
||||
* 注意:仅供框架内部使用
|
||||
*
|
||||
* @return 优化前Dex所在目录的File对象
|
||||
*/
|
||||
public File getUnoptDexParentDir() {
|
||||
File dir;
|
||||
// 必须使用宿主的Context对象,防止出现“目录定位到插件内”的问题
|
||||
Context context = RePluginInternal.getAppContext();
|
||||
if (isPnPlugin()) {
|
||||
dir = context.getDir(Constant.LOCAL_PLUGIN_ODEX_SUB_DIR, 0);
|
||||
} else if (getIsPendingCover()) {
|
||||
dir = context.getDir(Constant.LOCAL_PLUGIN_APK_COVER_DIR, 0);
|
||||
} else {
|
||||
dir = context.getDir(Constant.LOCAL_PLUGIN_APK_ODEX_SUB_DIR, 0);
|
||||
}
|
||||
|
||||
File subDir = new File(dir + File.separator + makeInstalledFileName() + Constant.LOCAL_PLUGIN_MULTI_DEX_SUB_DIR);
|
||||
if (!subDir.exists()) {
|
||||
subDir.mkdir();
|
||||
}
|
||||
|
||||
return subDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Dex(优化后)生成时所在的目录 <p>
|
||||
* 若为"纯APK"插件,则会位于app_p_od中;若为"p-n"插件,则会位于"app_plugins_v3_odex"中 <p>
|
||||
@ -389,15 +418,27 @@ public class PluginInfo implements Parcelable, Cloneable {
|
||||
* @return 优化后Dex所在目录的File对象
|
||||
*/
|
||||
public File getDexParentDir() {
|
||||
File dir;
|
||||
// 必须使用宿主的Context对象,防止出现“目录定位到插件内”的问题
|
||||
Context context = RePluginInternal.getAppContext();
|
||||
if (isPnPlugin()) {
|
||||
return context.getDir(Constant.LOCAL_PLUGIN_ODEX_SUB_DIR, 0);
|
||||
dir = context.getDir(Constant.LOCAL_PLUGIN_ODEX_SUB_DIR, 0);
|
||||
} else if (getIsPendingCover()) {
|
||||
return context.getDir(Constant.LOCAL_PLUGIN_APK_COVER_DIR, 0);
|
||||
dir = context.getDir(Constant.LOCAL_PLUGIN_APK_COVER_DIR, 0);
|
||||
} else {
|
||||
return context.getDir(Constant.LOCAL_PLUGIN_APK_ODEX_SUB_DIR, 0);
|
||||
dir = context.getDir(Constant.LOCAL_PLUGIN_APK_ODEX_SUB_DIR, 0);
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
|
||||
File subDir = new File(dir + File.separator + makeInstalledFileName() + Constant.LOCAL_PLUGIN_MULTI_ODEX_SUB_DIR);
|
||||
if (!subDir.exists()) {
|
||||
subDir.mkdir();
|
||||
}
|
||||
dir = subDir;
|
||||
}
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -20,6 +20,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.RemoteException;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.text.TextUtils;
|
||||
@ -437,7 +438,10 @@ public class PluginManagerServer {
|
||||
private void delete(@NonNull PluginInfo pi) {
|
||||
try {
|
||||
FileUtils.forceDelete(new File(pi.getPath()));
|
||||
FileUtils.forceDelete(pi.getDexFile());
|
||||
FileUtils.forceDelete(pi.getDexParentDir());
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
FileUtils.forceDelete(pi.getUnoptDexParentDir());
|
||||
}
|
||||
FileUtils.forceDelete(pi.getNativeLibsDir());
|
||||
} catch (IOException e) {
|
||||
if (LogRelease.LOGR) {
|
||||
|
@ -18,6 +18,7 @@
|
||||
package com.qihoo360.replugin.utils.pkg;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
@ -280,7 +281,10 @@ public class PackageFilesUtil {
|
||||
}
|
||||
try {
|
||||
FileUtils.forceDelete(info.getApkFile());
|
||||
FileUtils.forceDelete(info.getDexFile());
|
||||
FileUtils.forceDelete(info.getDexParentDir());
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
FileUtils.forceDelete(info.getUnoptDexParentDir());
|
||||
}
|
||||
FileUtils.forceDelete(info.getNativeLibsDir());
|
||||
} catch (IOException e) {
|
||||
if (LogRelease.LOGR) {
|
||||
|
@ -139,6 +139,5 @@
|
||||
android:authorities="com.qihoo360.replugin.sample.demo1.provider2" />
|
||||
|
||||
<activity android:name=".activity.for_result.ForResultActivity"/>
|
||||
<activity android:name=".testcase.TestCaseActivity"/>
|
||||
</application>
|
||||
</manifest>
|
||||
|
@ -47,7 +47,6 @@ import com.qihoo360.replugin.sample.demo1.activity.theme.ThemeBlackNoTitleBarAct
|
||||
import com.qihoo360.replugin.sample.demo1.activity.theme.ThemeBlackNoTitleBarFullscreenActivity;
|
||||
import com.qihoo360.replugin.sample.demo1.activity.theme.ThemeDialogActivity;
|
||||
import com.qihoo360.replugin.sample.demo1.service.PluginDemoService1;
|
||||
import com.qihoo360.replugin.sample.demo1.testcase.TestCaseActivity;
|
||||
import com.qihoo360.replugin.sample.demo2.IDemo2;
|
||||
import com.qihoo360.replugin.sample.library.LibMainActivity;
|
||||
|
||||
@ -318,17 +317,6 @@ public class MainActivity extends Activity {
|
||||
v.getContext().startActivity(intent);
|
||||
}
|
||||
}));
|
||||
|
||||
// =========
|
||||
// test case
|
||||
// =========
|
||||
mItems.add(new TestItem("TEST CASE For RePlugin", new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intent = new Intent(v.getContext(), TestCaseActivity.class);
|
||||
v.getContext().startActivity(intent);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private static final int REQUEST_CODE_DEMO2 = 0x021;
|
||||
|
@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2017 Qihoo 360 Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed To in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package com.qihoo360.replugin.sample.demo1.testcase;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.ListView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.qihoo360.replugin.sample.demo1.R;
|
||||
import com.qihoo360.replugin.sample.demo1.TestItem;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by osan on 2017/7/30.
|
||||
*/
|
||||
public class TestCaseActivity extends Activity {
|
||||
|
||||
private List<TestItem> mItems = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.act_testcase);
|
||||
|
||||
initData();
|
||||
|
||||
ListView lv = (ListView) findViewById(R.id.list_view);
|
||||
lv.setAdapter(new TestAdapter());
|
||||
}
|
||||
|
||||
private void initData() {
|
||||
// =========
|
||||
// TestMultiDex
|
||||
// =========
|
||||
mItems.add(new TestItem("Test access MultiDex method", new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
String ret = TestMultiDex.accessMultiDexMethod();
|
||||
Toast.makeText(TestCaseActivity.this, ret, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
private class TestAdapter extends BaseAdapter {
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mItems.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TestItem getItem(int position) {
|
||||
return mItems.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
if (convertView == null) {
|
||||
convertView = new Button(TestCaseActivity.this);
|
||||
}
|
||||
|
||||
TestItem item = getItem(position);
|
||||
((Button) convertView).setText(item.title);
|
||||
convertView.setOnClickListener(item.mClickListener);
|
||||
return convertView;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2005-2017 Qihoo 360 Inc.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
~ use this file except in compliance with the License. You may obtain a copy of
|
||||
~ the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed To in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
~ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
~ License for the specific language governing permissions and limitations under
|
||||
~ the License.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<ListView
|
||||
android:id="@+id/list_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
</ListView>
|
||||
|
||||
</LinearLayout>
|
@ -31,6 +31,7 @@ android {
|
||||
targetSdkVersion 21
|
||||
applicationId "com.qihoo360.replugin.sample.demo2"
|
||||
minSdkVersion 15
|
||||
multiDexEnabled false
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
@ -67,3 +68,7 @@ dependencies {
|
||||
compile 'com.android.support:appcompat-v7:25.3.1'
|
||||
compile 'com.qihoo360.replugin:replugin-plugin-lib:2.1.6'
|
||||
}
|
||||
|
||||
if (android.defaultConfig.multiDexEnabled) {
|
||||
apply from: 'case.gradle'
|
||||
}
|
||||
|
13
replugin-sample/plugin/plugin-demo2/app/case.gradle
Normal file
13
replugin-sample/plugin/plugin-demo2/app/case.gradle
Normal file
@ -0,0 +1,13 @@
|
||||
|
||||
dependencies {
|
||||
|
||||
if (android.defaultConfig.multiDexEnabled){
|
||||
|
||||
compile 'com.squareup.okhttp3:okhttp:3.4.1'
|
||||
compile 'com.google.code.gson:gson:2.6.2'
|
||||
compile 'com.squareup.pagerduty:pagerduty-incidents:2.0.0'
|
||||
compile 'com.google.android.gms:play-services:9.6.1'
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,11 +1,9 @@
|
||||
package com.qihoo360.replugin.sample.demo1.testcase;
|
||||
package com.qihoo360.replugin.sample.demo2.testcase;
|
||||
|
||||
/**
|
||||
* Created by osan on 2017/7/30.
|
||||
*/
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* multidex测试用例类
|
||||
* 用于测试RP框架对运行在android rom 5.0以下版本的插件(含多dex)的multidex支持情况
|
@ -27,7 +27,7 @@
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
# Default value: -Xmx10248m -XX:MaxPermSize=256m
|
||||
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||
|
||||
org.gradle.jvmargs=-Xmx1536M
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
|
Loading…
Reference in New Issue
Block a user