ITPub博客

首页 > IT职业 > IT职场 > android插件化——支付宝如何在不安装淘票票的情况下加载淘票票原生

android插件化——支付宝如何在不安装淘票票的情况下加载淘票票原生

IT职场 作者:smanzheng 时间:2021-09-09 09:11:06 0 删除 编辑
  • 效果
    图片描述

图片描述

图片描述

  • 构建项目模块

图片描述
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;

/**
 * 没有安装
 * 

* 上下文 一般都是系统注入 */ 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);
     * 

* 最终是调用了系统给我们注入的上下文 * * @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.
 * 

* 专门去插桩使用 */ 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权限
 
  • 实现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

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/4759/viewspace-2800109/ ,如需转载,请注明出处,否则将追究法律责任。

下一篇: PHP在线语音合成
请登录后发表评论 登录
全部评论

注册时间:2019-04-25

  • 博文量
    115
  • 访问量
    1241