github地址:里面也有个小的面向对象的sqlite框架
https://github.com/Xiemarc/SQLiteDemo
首先看效果图: 这里我使用的是dexClassLoader,没有用Hook技术.
module入下面图所以:
app、pluginapk、pluginapk2都是module类型都是phone类型,其实就是app类型。plugincore是lib类型
在这里我把2个apk都放到了sdk根目录,这里为了方便,实际开发中肯定不会
主要有4个类,分别是BasePluginActivity、PluginInterface、PluginManager、ProxyActivity。
/**
* 加载apk
* Created by marc on 2017/7/12.
*/
public class PluginManager {
//加载外置卡中的的class
private DexClassLoader dexClassLoader;
//上下文
private Context context;
private Resources resources;
private static PluginManager instance;
//包含acitivty的全类名
private PackageInfo packageInfo;
private PluginManager(Context context) {
this.context = context;
}
public static PluginManager getInstance(Context context) {
if (instance == null) {
synchronized (PluginManager.class) {
if (instance == null) {
instance = new PluginManager(context);
}
}
}
return instance;
}
public static PluginManager getInstance() {
if (instance == null) {
throw new RuntimeException("you must new intance construct with contxt");
}
return instance;
}
/**
* 加载apk的路径
*
* @param path 外置卡的绝对路径
*/
public void loadPath(String path) {
File dexOutFile = context.getDir("dex", Context.MODE_PRIVATE);
//实例化一个dexClassLoader
dexClassLoader = new DexClassLoader(path, dexOutFile.getAbsolutePath(), null, context.getClassLoader());
try {
AssetManager assetManager = AssetManager.class.newInstance();
//反射给assetManager设置路径
Method addAssetPath = AssetManager.class.getMethod("addAssetPath", String.class);
addAssetPath.invoke(assetManager, path);
Resources superResource = context.getResources();
//实例化resource
resources = new Resources(assetManager, superResource.getDisplayMetrics(), superResource.getConfiguration());
PackageManager packageManager = context.getPackageManager();
packageInfo = packageManager.getPackageArchiveInfo(path, PackageManager.GET_ACTIVITIES);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
public DexClassLoader getDexClassLoader() {
return dexClassLoader;
}
public Resources getResources() {
return resources;
}
public PackageInfo getPackageInfo() {
return packageInfo;
}
}
public interface PluginInterface {
public void attach(Activity proxyActivity);
public void onCreate(Bundle saveInstanceState);
public void onStart();
public void onResume();
public void onPause();
public void onStop();
public void onDestroy();
public void onSaveInstanceState(Bundle outState);
public boolean onTouchEvent(MotionEvent event);
public void onBackPressed();
}
public class ProxyActivity extends Activity {
//替换插件apk中的activity的全类名
String className;
private PluginInterface pluginInterface;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
className = intent.getStringExtra("classname");
lauchActivity();
}
/**
* 通过反射加载外置卡的apk的activity class文件
*/
private void lauchActivity() {
try {
//通过的dexClassLoader加载外置卡class
Class> loadClass = PluginManager.getInstance().getDexClassLoader().loadClass(className);
Constructor constructor = loadClass.getConstructor(new Class[]{});
//反射得到acitivity实例
Object instance = constructor.newInstance(new Object[]{});
//利用标准接口,将插件apk里的class强转成接口
pluginInterface = (PluginInterface) instance;
pluginInterface.attach(this);
Bundle bundle = new Bundle();
//值传递
pluginInterface.onCreate(bundle);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
@Override
protected void onStart() {
pluginInterface.onStart();
super.onStart();
}
@Override
protected void onResume() {
super.onResume();
}
@Override
protected void onDestroy() {
super.onDestroy();
pluginInterface.onDestroy();
}
@Override
protected void onStop() {
super.onStop();
pluginInterface.onStop();
}
@Override
protected void onPause() {
super.onPause();
pluginInterface.onPause();
}
/**
* 若不重写本方法,那么加载的资源就是本apk的资源
* 必须重写
*
* @return
*/
@Override
public Resources getResources() {
return PluginManager.getInstance().getResources();
}
}
/**
* des:所有能加载插件activity的activity必须实现plugininterface接口
* author: marc
* date: 2017/7/12 22:20
* email:[email protected]
*/
public class BasePluginActivity extends Activity implements PluginInterface {
protected Activity that;
@Override
public void attach(Activity proxyActivity) {
that = proxyActivity;
}
@Override
public void onCreate(Bundle saveInstanceState) {
}
@Override
public void setContentView(View view) {
that.setContentView(view);
}
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
that.setContentView(view, params);
}
@Override
public void setContentView(@LayoutRes int layoutResID) {
that.setContentView(layoutResID);
}
@Override
public void addContentView(View view, ViewGroup.LayoutParams params) {
that.addContentView(view, params);
}
@Override
public T findViewById(int id) {
return that.findViewById(id);
}
@Override
public Intent getIntent() {
return that.getIntent();
}
@Override
public ClassLoader getClassLoader() {
return that.getClassLoader();
}
@Override
public Resources getResources() {
return that.getResources();
}
@NonNull
@Override
public LayoutInflater getLayoutInflater() {
return that.getLayoutInflater();
}
@NonNull
@Override
public MenuInflater getMenuInflater() {
return that.getMenuInflater();
}
@Override
public SharedPreferences getSharedPreferences(String name, int mode) {
return that.getSharedPreferences(name, mode);
}
@Override
public Context getApplicationContext() {
return that.getApplicationContext();
}
@Override
public WindowManager getWindowManager() {
return that.getWindowManager();
}
@Override
public Window getWindow() {
return that.getWindow();
}
@Override
public void startActivity(Intent intent) {
that.startActivity(intent);
}
@Override
public Object getSystemService(String name) {
return that.getSystemService(name);
}
@Override
public void finish() {
that.finish();
}
@Override
public void onBackPressed() {
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
return false;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
}
@Override
public void onStart() {
}
@Override
protected void onRestart() {
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
}
@Override
public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
}
@Override
protected void onNewIntent(Intent intent) {
}
@Override
public void onResume() {
}
@Override
public void onPause() {
}
@Override
public void onStop() {
}
@Override
public void onDestroy() {
}
@Override
public void onSaveInstanceState(Bundle outState) {
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return false;
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
return false;
}
@Override
public void onWindowAttributesChanged(WindowManager.LayoutParams params) {
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
return false;
}
}
这里需要注意了,app需要依赖plugincore。然后再mainfest.xml中写上
public class WelcomeActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_welcome);
PluginManager.getInstance(this);
}
public void jumpsql(View view) {
Intent intent = new Intent(this, DbActivity.class);
startActivity(intent);
}
public void jumpinternet(View v) {
Intent intent = new Intent(this, HttpActivity.class);
startActivity(intent);
}
public void loadapkone(View v) {
File apkFile = new File(Environment.getExternalStorageDirectory(), "pluginone.apk");
PluginManager.getInstance().loadPath(apkFile.getAbsolutePath());
}
public void loadapktwo(View v) {
File apkFile = new File(Environment.getExternalStorageDirectory(), "plugapk2-debug.apk");
PluginManager.getInstance().loadPath(apkFile.getAbsolutePath());
}
public void jumpapk(View v) {
Intent intent = new Intent(WelcomeActivity.this, ProxyActivity.class);
//跳到插件的activity ,activities[].name 这里需要注意下
intent.putExtra("classname", PluginManager.getInstance().getPackageInfo().activities[0].name);
startActivity(intent);
}
}
这个里面就很简单了。跳转过来主要使用了下代理acitivty,然后实现跳转到这里.所以需要继承BasePluginActivity。然后这里的that是BasePluginActivity在attach中绑定的。
public class MainActivity extends BasePluginActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toast.makeText(that, "第一个app弹出个东西", Toast.LENGTH_SHORT).show();
}
}
这里有2个activity,因为是在插件apk中,所以直接在自己的清单文件中去注册activity吧。
public class MainActivity extends BasePluginActivity {
ImageView iv;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toast.makeText(that, "第二个app", Toast.LENGTH_SHORT).show();
iv = findViewById(R.id.image);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(that, ProxyActivity.class);
//activities[1] 是在mainfest.xml中注册的第二个activity
intent.putExtra("classname", PluginManager.getInstance().getPackageInfo().activities[1].name);
startActivity(intent);
}
});
findViewById(R.id.chanimage).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
iv.setImageResource(R.drawable.girl_1);
}
});
}
}
这里需要注意下,如果做点击事件的话,之前可以在xml中直接写onClick=“jump”这种方式,在这里就不行了,因为我们再父类中没有重写,建议还是不要写这种,我这里是为了研究下这个技术,就不去考虑了。从上面代码可以看到,跳转到第二个activity页面就需要activities[1]了.
public class SecondActivity extends BasePluginActivity {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Toast.makeText(that, "第二个activityde seonada", Toast.LENGTH_SHORT).show();
}
}
如果不继承自BasePluginActivity,会报类型转换异常,没办法强制转换成pluginInterface
这里也贴一下 PackageInfo的源码。
public class PackageInfo implements Parcelable {
public String packageName;
public String[] splitNames;
public int versionCode;
public String versionName;
public int baseRevisionCode;
public int[] splitRevisionCodes;
public String sharedUserId;
public int sharedUserLabel;
public ApplicationInfo applicationInfo;
public long firstInstallTime;
public long lastUpdateTime;
public int[] gids;
public ActivityInfo[] activities;
public ActivityInfo[] receivers;
public ServiceInfo[] services;
public ProviderInfo[] providers;
public InstrumentationInfo[] instrumentation;
/**
* Array of all {@link android.R.styleable#AndroidManifestPermission
* <permission>} tags included under <manifest>,
* or null if there were none. This is only filled in if the flag
* {@link PackageManager#GET_PERMISSIONS} was set.
*/
public PermissionInfo[] permissions;
/**
* Array of all {@link android.R.styleable#AndroidManifestUsesPermission
* <uses-permission>} tags included under <manifest>,
* or null if there were none. This is only filled in if the flag
* {@link PackageManager#GET_PERMISSIONS} was set. This list includes
* all permissions requested, even those that were not granted or known
* by the system at install time.
*/
public String[] requestedPermissions;
/**
* Array of flags of all {@link android.R.styleable#AndroidManifestUsesPermission
* <uses-permission>} tags included under <manifest>,
* or null if there were none. This is only filled in if the flag
* {@link PackageManager#GET_PERMISSIONS} was set. Each value matches
* the corresponding entry in {@link #requestedPermissions}, and will have
* the flag {@link #REQUESTED_PERMISSION_GRANTED} set as appropriate.
*/
public int[] requestedPermissionsFlags;
/**
* Flag for {@link #requestedPermissionsFlags}: the requested permission
* is required for the application to run; the user can not optionally
* disable it. Currently all permissions are required.
*
* @removed We do not support required permissions.
*/
public static final int REQUESTED_PERMISSION_REQUIRED = 1<<0;
/**
* Flag for {@link #requestedPermissionsFlags}: the requested permission
* is currently granted to the application.
*/
public static final int REQUESTED_PERMISSION_GRANTED = 1<<1;
/**
* Array of all signatures read from the package file. This is only filled
* in if the flag {@link PackageManager#GET_SIGNATURES} was set.
*/
public Signature[] signatures;
/**
* Application specified preferred configuration
* {@link android.R.styleable#AndroidManifestUsesConfiguration
* <uses-configuration>} tags included under <manifest>,
* or null if there were none. This is only filled in if the flag
* {@link PackageManager#GET_CONFIGURATIONS} was set.
*/
public ConfigurationInfo[] configPreferences;
/**
* Features that this application has requested.
*
* @see FeatureInfo#FLAG_REQUIRED
*/
public FeatureInfo[] reqFeatures;
/**
* Groups of features that this application has requested.
* Each group contains a set of features that are required.
* A device must match the features listed in {@link #reqFeatures} and one
* or more FeatureGroups in order to have satisfied the feature requirement.
*
* @see FeatureInfo#FLAG_REQUIRED
*/
public FeatureGroupInfo[] featureGroups;
/**
* Constant corresponding to auto
in
* the {@link android.R.attr#installLocation} attribute.
* @hide
*/
public static final int INSTALL_LOCATION_UNSPECIFIED = -1;
/**
* Constant corresponding to auto
in the
* {@link android.R.attr#installLocation} attribute.
*/
public static final int INSTALL_LOCATION_AUTO = 0;
/**
* Constant corresponding to internalOnly
in the
* {@link android.R.attr#installLocation} attribute.
*/
public static final int INSTALL_LOCATION_INTERNAL_ONLY = 1;
/**
* Constant corresponding to preferExternal
in the
* {@link android.R.attr#installLocation} attribute.
*/
public static final int INSTALL_LOCATION_PREFER_EXTERNAL = 2;
/**
* The install location requested by the package. From the
* {@link android.R.attr#installLocation} attribute, one of
* {@link #INSTALL_LOCATION_AUTO}, {@link #INSTALL_LOCATION_INTERNAL_ONLY},
* {@link #INSTALL_LOCATION_PREFER_EXTERNAL}
*/
public int installLocation = INSTALL_LOCATION_INTERNAL_ONLY;
/** @hide */
public boolean coreApp;
/** @hide */
public boolean requiredForAllUsers;
/** @hide */
public String restrictedAccountType;
/** @hide */
public String requiredAccountType;
/**
* What package, if any, this package will overlay.
*
* Package name of target package, or null.
* @hide
*/
public String overlayTarget;
public PackageInfo() {
}
@Override
public String toString() {
return "PackageInfo{"
+ Integer.toHexString(System.identityHashCode(this))
+ " " + packageName + "}";
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int parcelableFlags) {
dest.writeString(packageName);
dest.writeStringArray(splitNames);
dest.writeInt(versionCode);
dest.writeString(versionName);
dest.writeInt(baseRevisionCode);
dest.writeIntArray(splitRevisionCodes);
dest.writeString(sharedUserId);
dest.writeInt(sharedUserLabel);
if (applicationInfo != null) {
dest.writeInt(1);
applicationInfo.writeToParcel(dest, parcelableFlags);
} else {
dest.writeInt(0);
}
dest.writeLong(firstInstallTime);
dest.writeLong(lastUpdateTime);
dest.writeIntArray(gids);
dest.writeTypedArray(activities, parcelableFlags | Parcelable.PARCELABLE_ELIDE_DUPLICATES);
dest.writeTypedArray(receivers, parcelableFlags | Parcelable.PARCELABLE_ELIDE_DUPLICATES);
dest.writeTypedArray(services, parcelableFlags | Parcelable.PARCELABLE_ELIDE_DUPLICATES);
dest.writeTypedArray(providers, parcelableFlags | Parcelable.PARCELABLE_ELIDE_DUPLICATES);
dest.writeTypedArray(instrumentation, parcelableFlags);
dest.writeTypedArray(permissions, parcelableFlags);
dest.writeStringArray(requestedPermissions);
dest.writeIntArray(requestedPermissionsFlags);
dest.writeTypedArray(signatures, parcelableFlags);
dest.writeTypedArray(configPreferences, parcelableFlags);
dest.writeTypedArray(reqFeatures, parcelableFlags);
dest.writeTypedArray(featureGroups, parcelableFlags);
dest.writeInt(installLocation);
dest.writeInt(coreApp ? 1 : 0);
dest.writeInt(requiredForAllUsers ? 1 : 0);
dest.writeString(restrictedAccountType);
dest.writeString(requiredAccountType);
dest.writeString(overlayTarget);
}
public static final Parcelable.Creator CREATOR
= new Parcelable.Creator() {
@Override
public PackageInfo createFromParcel(Parcel source) {
return new PackageInfo(source);
}
@Override
public PackageInfo[] newArray(int size) {
return new PackageInfo[size];
}
};
private PackageInfo(Parcel source) {
packageName = source.readString();
splitNames = source.createStringArray();
versionCode = source.readInt();
versionName = source.readString();
baseRevisionCode = source.readInt();
splitRevisionCodes = source.createIntArray();
sharedUserId = source.readString();
sharedUserLabel = source.readInt();
int hasApp = source.readInt();
if (hasApp != 0) {
applicationInfo = ApplicationInfo.CREATOR.createFromParcel(source);
}
firstInstallTime = source.readLong();
lastUpdateTime = source.readLong();
gids = source.createIntArray();
activities = source.createTypedArray(ActivityInfo.CREATOR);
receivers = source.createTypedArray(ActivityInfo.CREATOR);
services = source.createTypedArray(ServiceInfo.CREATOR);
providers = source.createTypedArray(ProviderInfo.CREATOR);
instrumentation = source.createTypedArray(InstrumentationInfo.CREATOR);
permissions = source.createTypedArray(PermissionInfo.CREATOR);
requestedPermissions = source.createStringArray();
requestedPermissionsFlags = source.createIntArray();
signatures = source.createTypedArray(Signature.CREATOR);
configPreferences = source.createTypedArray(ConfigurationInfo.CREATOR);
reqFeatures = source.createTypedArray(FeatureInfo.CREATOR);
featureGroups = source.createTypedArray(FeatureGroupInfo.CREATOR);
installLocation = source.readInt();
coreApp = source.readInt() != 0;
requiredForAllUsers = source.readInt() != 0;
restrictedAccountType = source.readString();
requiredAccountType = source.readString();
overlayTarget = source.readString();
// The component lists were flattened with the redundant ApplicationInfo
// instances omitted. Distribute the canonical one here as appropriate.
if (applicationInfo != null) {
propagateApplicationInfo(applicationInfo, activities);
propagateApplicationInfo(applicationInfo, receivers);
propagateApplicationInfo(applicationInfo, services);
propagateApplicationInfo(applicationInfo, providers);
}
}
private void propagateApplicationInfo(ApplicationInfo appInfo, ComponentInfo[] components) {
if (components != null) {
for (ComponentInfo ci : components) {
ci.applicationInfo = appInfo;
}
}
}
}
可以看到上面activities、receivers、services、providers、等等。如果需要深入做的话,就需要对这些都去做个重写了。貌似很多也是这么做的。