- 效果
- 构建项目模块
1、alipaystander 为通讯接口(lib)
2、app为支付宝项目(module)
3、taopiaopiao 淘票票项目(module)
- alipaystander 通讯接口
主要功能是为了解决app与taopiaopiao之间的通讯
package tsou.cn.alipaystander;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.MotionEvent;
/**
* Created by Administrator on 2018/4/10 0010.
*/
public interface AlipayInterface {
public void onCreate(Bundle savedInstanceState);
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 void attach(AppCompatActivity appCompatActivity);
}
注意:在为安装的apk中上下文系统是无法注入的,需要主模块传入
- 引入alipaystander
在app与taopiaopiao模块中的build.gradle引入
compile project(':alipaystander')
- 创建taopiaopiao项目
1、taopiaopiao主页
package tsou.cn.taopiaopiao;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;
/**
* 没有安装
* <p>
* 上下文 一般都是系统注入
*/
public class MainActivity extends BaseActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageView imageView = (ImageView) findViewById(R.id.img);
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//系统注入的上下文:MainActivity.this
Toast.makeText(that,"点击",Toast.LENGTH_SHORT).show();
startActivity(new Intent(that,SceondActivity.class));
}
});
}
}
注意:taopiaopiao项目为外部apk上下文需要传入,startActivity也要重新
2、构建BaseActivty
package tsou.cn.taopiaopiao;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.IdRes;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import tsou.cn.alipaystander.AlipayInterface;
/**
* Created by Administrator on 2018/4/10 0010.
*/
public class BaseActivity extends AppCompatActivity implements AlipayInterface {
protected AppCompatActivity that;
/**
* super.setContentView(layoutResID);
* <p>
* 最终是调用了系统给我们注入的上下文
*
* @param layoutResID
*/
@Override
public void setContentView(@LayoutRes int layoutResID) {
if (that == null) {
super.setContentView(layoutResID);
} else {
that.setContentView(layoutResID);
}
}
@Override
public View findViewById(@IdRes int id) {
if (that == null) {
return super.findViewById(id);
} else {
return that.findViewById(id);
}
}
@Override
public ClassLoader getClassLoader() {
if (that == null) {
return super.getClassLoader();
} else {
return that.getClassLoader();
}
}
@NonNull
@Override
public LayoutInflater getLayoutInflater() {
if (that == null) {
return super.getLayoutInflater();
} else {
return that.getLayoutInflater();
}
}
@Override
public Window getWindow() {
if (that == null) {
return super.getWindow();
} else {
return that.getWindow();
}
}
@Override
public WindowManager getWindowManager() {
if (that == null) {
return super.getWindowManager();
} else {
return that.getWindowManager();
}
}
@Override
public void startActivity(Intent intent) {
if (that == null) {
super.startActivity(intent);
} else {
Intent newIntent = new Intent();
newIntent.putExtra("className", intent.getComponent().getClassName());
that.startActivity(newIntent);
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
}
@Override
public void onStart() {
}
@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 void onBackPressed() {
}
@Override
public void attach(AppCompatActivity appCompatActivity) {
this.that = appCompatActivity;
}
}
需要上下文的都需要重新,编译taopiaopiao项目产生apk,放到手机sd卡根目录下。
注意:有时debug模式下打包,有时无法实现app向tiaopiaopiao的跳转,需要打正式包!
- 在app项目中使用DexClassLoader加载第三方APK
package tsou.cn.alipay;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.content.res.Resources;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import dalvik.system.DexClassLoader;
/**
* Created by Administrator on 2018/4/10 0010.
*/
public class PluginManager {
private DexClassLoader dexClassLoader;
private Resources resources;
private Context context;
private String entryActivityName;
private static final PluginManager ourInstance = new PluginManager();
public static PluginManager getInstance() {
return ourInstance;
}
private PluginManager() {
}
public void loadPath(String path) {
File dexOutFile = context.getDir("dex", Context.MODE_PRIVATE);
dexClassLoader = new DexClassLoader(path, dexOutFile.getAbsolutePath(), null, context.getClassLoader());
// 获取包名
PackageManager packageManager = context.getPackageManager();
PackageInfo packageInfo = packageManager.getPackageArchiveInfo(path, PackageManager.GET_ACTIVITIES);
entryActivityName = packageInfo.activities[0].name;
//实例化resources
try {
AssetManager assetManager = AssetManager.class.newInstance();
Method addAssetPath = AssetManager.class.getMethod("addAssetPath", String.class);
addAssetPath.invoke(assetManager, path);
resources = new Resources(assetManager, context.getResources().getDisplayMetrics(),
context.getResources().getConfiguration());
} 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 String getEntryActivityName() {
return entryActivityName;
}
public void setContext(Context context) {
this.context = context.getApplicationContext();
}
}
因为加载的apk为外部的,所以getDexClassLoader()与getResources()需要重写
- 创建ProxyActivity承载taopiaopiao主页
package tsou.cn.alipay;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import tsou.cn.alipaystander.AlipayInterface;
/**
* Created by Administrator on 2018/4/10 0010.
* <p>
* 专门去插桩使用
*/
public class ProxyActivity extends AppCompatActivity {
//要跳转的 淘票票Activity
private String className;
private AlipayInterface alipayInterface;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
className = getIntent().getStringExtra("className");
/**
* 通过className不能拿到类名
* 原生是加载过的类名
*/
// Class clazz = getClassLoader().loadClass(className);
// Class.forName(className);
try {
//加载该Activity的字节码对象
Class activityClass = getClassLoader().loadClass(className);
Constructor constructor = activityClass.getConstructor(new Class[]{});
//创建该Activity的示例
Object newInstance = constructor.newInstance(new Object[]{});
//程序健壮性检查
if (newInstance instanceof AlipayInterface) {
/**
* 使用定义接口标准
* 传递生命周期
*/
alipayInterface = (AlipayInterface) newInstance;
//将代理Activity的实例传递给三方Activity
alipayInterface.attach(this);
//创建bundle用来与三方apk传输数据
Bundle bundle = new Bundle();
//调用三方Activity的onCreate,
alipayInterface.onCreate(bundle);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
/**
* 重新,通过className拿到类名
*
* @return
*/
@Override
public ClassLoader getClassLoader() {
return PluginManager.getInstance().getDexClassLoader();
}
/**
* @return
*/
@Override
public Resources getResources() {
return PluginManager.getInstance().getResources();
}
@Override
protected void onStart() {
if (alipayInterface != null)
alipayInterface.onStart();
super.onStart();
}
@Override
protected void onResume() {
if (alipayInterface != null)
alipayInterface.onResume();
super.onResume();
}
@Override
protected void onPause() {
if (alipayInterface != null)
alipayInterface.onPause();
super.onPause();
}
@Override
protected void onStop() {
if (alipayInterface != null)
alipayInterface.onStop();
super.onStop();
}
@Override
protected void onDestroy() {
if (alipayInterface != null)
alipayInterface.onDestroy();
super.onDestroy();
}
@Override
public void startActivity(Intent intent) {
String className = intent.getStringExtra("className");
Intent intent1 = new Intent(this, ProxyActivity.class);
intent1.putExtra("className",className);
super.startActivity(intent1);
}
}
- 添加sd权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
- 实现app加载taopiaopiao项目
package tsou.cn.alipay;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import java.io.File;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
/**
* 加载
*/
private Button mLoad;
/**
* 点击
*/
private Button mClick;
String[] mPermissionList = new String[]{
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
PluginManager.getInstance().setContext(this);
initView();
}
private void initView() {
mLoad = (Button) findViewById(R.id.load);
mLoad.setOnClickListener(this);
mClick = (Button) findViewById(R.id.click);
mClick.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
default:
break;
case R.id.load://加载
// 缺少权限时, 进入权限配置页面
ActivityCompat.requestPermissions(MainActivity.this,mPermissionList, 100);
break;
case R.id.click://点击
Intent intent = new Intent(this, ProxyActivity.class);
// intent.putExtra("className", "淘票票的全类名 MainActivity");
intent.putExtra("className", PluginManager.getInstance().getEntryActivityName());
startActivity(intent);
break;
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case 100:
boolean camera = grantResults[0] == PackageManager.PERMISSION_GRANTED;
boolean readExternalStorage = grantResults[1] == PackageManager.PERMISSION_GRANTED;
if (grantResults.length > 0 && camera && readExternalStorage) {
File file = new File(Environment.getExternalStorageDirectory(), "plugin.apk");
PluginManager.getInstance().loadPath(file.getAbsolutePath());
} else {
Toast.makeText(this.getApplicationContext(),"请设置必要权限",Toast.LENGTH_SHORT).show();
}
break;
}
}
}
- taopiaopiao项目更改UI页面
例如:在taopiaopiao主页弹出一个Toast,
//系统注入的上下文:MainActivity.this
Toast.makeText(that,"点击",Toast.LENGTH_SHORT).show();
注意:上下文,一定是传递过来的,本地上下文无效
注意tiaopiaopiao中的activity不需要进行注册
- 在taopiaopiao中的activity的跳转
startActivity(new Intent(that,SceondActivity.class));
package tsou.cn.taopiaopiao;
import android.os.Bundle;
public class SceondActivity extends BaseActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sceond);
}
}
需要对startActivity进行重新
1、在BaseActivity中进行传递
@Override
public void startActivity(Intent intent) {
if (that == null) {
super.startActivity(intent);
} else {
Intent newIntent = new Intent();
newIntent.putExtra("className", intent.getComponent().getClassName());
that.startActivity(newIntent);
}
}
2、在承载页面ProxyActivity中重新
@Override
public void startActivity(Intent intent) {
String className = intent.getStringExtra("className");
Intent intent1 = new Intent(this, ProxyActivity.class);
intent1.putExtra("className",className);
super.startActivity(intent1);
}
实现页面跳转!
(后续需要继续跳转其他的activity直接使用startActivity即可,但是activity要继承BaseActivity,同样新的activity不需要在清单文件中注册)
Demo地址:https://download.csdn.net/download/huangxiaoguo1/10341038
热门评论
简但示例说明了使用代理来动态加载apk的方式,赞