APK动态加载框架解析(一)
来源:互联网 发布:tf卡测试软件 编辑:程序博客网 时间:2024/05/19 23:26
既然项目中刚好用到了动态加载框架DL,我就索性研究了一下DL的实现,现在已有小成,否则也不敢出来写博客,那么今天我们就看一下DL最简单的实现,直接上代码,代码中有足够清楚的解释,这段代码只能做到启动Plugin,暂时还不能获取plugin中的各种资源
先看一下宿主程序的Manifest配置
权限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Activity节点:
<activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <activity android:name=".ProxyActivity"> </activity>
宿主程序的启动页:
public class MainActivity extends AppCompatActivity implements View.OnClickListener { private String mAbsolutePath; private TextView mTv_go; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); initSD(); } //不再赘述 private void initView() { mTv_go = (TextView) findViewById(R.id.tv_go); mTv_go.setOnClickListener(this); } //Android 6.0 以后提高了安全性,对SD的读写加上这段代码就行了 private void initSD() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { if (!ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0); } } } @Override public void onClick(View v) { PackageInfo packageInfo; //获取外部存储路径,注意:需要提前在手机SD卡中新建DL文件夹,将打包后的插件APK放到这个文件夹中 String pluginFolder = Environment.getExternalStorageDirectory() + "/DL"; File pluginFile = new File(pluginFolder); //如果 pluginFile这个文件不存在,就新建 if (!pluginFile.exists()) { pluginFile.mkdir(); } //获取pluginFile文件夹下的所有文件,建议只放一个APK文件,否则还要自己加代码进去 File[] files = pluginFile.listFiles(); //如果pluginFile中没有插件,显示未发现 if (files.length == 0) { mTv_go.setText("没有发现插件"); return; } //如果有,遍历 也就只有一个 for (File file : files) { //APK所在的绝对路径 mAbsolutePath = file.getAbsolutePath(); //获取 PackageManager PackageManager packageManager = this.getPackageManager(); try { //获取包信息 PackageInfo packageInfo = packageManager.getPackageArchiveInfo(mAbsolutePath, PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES); if (packageInfo.activities != null && packageInfo.activities.length > 0) { //获取启动Activity的name String name = packageInfo.activities[0].name; mTv_go.setText("name:" + name + ";path:" + mAbsolutePath); } } catch (Exception e) { e.printStackTrace(); } } // Intent intent = new Intent(this, ProxyActivity.class); intent.putExtra(ProxyActivity.EXTRA_DEX_PATH, mAbsolutePath); startActivity(intent); }}
宿主程序的代理Activity
/** * 这里虽然写的是proxy,但是现在还不算是proxy,根据原著的想法是通过代理模式来托管插件的生命周期 * 以及获取插件的相关资源,后期再说,现在先不管。 */public class ProxyActivity extends AppCompatActivity { public static final String EXTRA_DEX_PATH = "extra.dex.path"; public static final String FROM = "extra.from"; public static final int FROM_EXTERNAL = 0; private String mDexPath; private String mClass; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //接收的值,这里只传递了路径 mDexPath = getIntent().getStringExtra(EXTRA_DEX_PATH); mClass = getIntent().getStringExtra(EXTRA_CLASS); if (mClass == null) { launchTargetActivity(); } else { launchTargetActivity(mClass); } } protected void launchTargetActivity() { PackageInfo packageInfo = getPackageManager().getPackageArchiveInfo( mDexPath, PackageManager.GET_ACTIVITIES); if ((packageInfo.activities != null) && (packageInfo.activities.length > 0)) { mClass = packageInfo.activities[0].name; launchTargetActivity(mClass); } } //这里是主要的逻辑,基本实现就是Java的反射、类装载以及Android的DexClassLoader protected void launchTargetActivity(final String className) { Log.i("aaa","start launchTargetActivity, className=" + className); //创建一个dex差分包的路径 File dexOutputDir = this.getDir("dex", Context.MODE_PRIVATE); //获取dex的绝对路径 final String dexOutputPath = dexOutputDir.getAbsolutePath(); //获取系统的类装载器 ClassLoader localClassLoader = ClassLoader.getSystemClassLoader(); //APK进行处理,生成dex差分文件 DexClassLoader dexClassLoader = new DexClassLoader(mDexPath, dexOutputPath, null, localClassLoader); try { //装载插件的启动页Activity,并返回启动页类对象,className为插件APK启动页Activity所在的绝对路径, Class<?> localClass = dexClassLoader.loadClass(className); //利用反射获取构造方法 Constructor<?> localConstructor = localClass .getConstructor(); //实例化启动页对象 Object instance = localConstructor.newInstance(); //利用反射获取setProxy方法 Method setProxy = localClass.getMethod("setProxy", Activity.class); setProxy.setAccessible(true); //调用setProxy方法 setProxy.invoke(instance, this); //利用发射获取protected方法 Method onCreate = localClass.getDeclaredMethod("onCreate", Bundle.class); onCreate.setAccessible(true); Bundle bundle = new Bundle(); bundle.putInt(FROM, FROM_EXTERNAL); //调用onCreate方法,并传递值 onCreate.invoke(instance, bundle); } catch (Exception e) { e.printStackTrace(); } }}
插件程序的代码:
public class BaseActivity extends AppCompatActivity { public static final String PROXY_VIEW_ACTION = "com.cy.tplugin.VIEW"; public static final String DEX_PATH = "/storage/emulated/0/DL/app-debug.apk"; public static final String EXTRA_DEX_PATH = "extra.dex.path"; public static final String FROM = "extra.from"; public static final int FROM_EXTERNAL = 0; public static final int FROM_INTERNAL = 1; protected int mFrom = FROM_INTERNAL; protected Activity mProxyActivity; public void setProxy(Activity proxyActivity) { mProxyActivity = proxyActivity; } @Override protected void onCreate(Bundle savedInstanceState) { //这里做了一个判断,如果是通过插件启动,则用mProxyActivity.setContentView(generateContentView(mProxyActivity)); //这个布局,如果不是则直接调用原始的R.layout. if (savedInstanceState != null) { mFrom = savedInstanceState.getInt(FROM, FROM_INTERNAL); } if (mFrom == FROM_INTERNAL) { super.onCreate(savedInstanceState); mProxyActivity = this; } Log.i("aaa","onCreate: from= " + mFrom); if (mFrom==FROM_EXTERNAL){ mProxyActivity.setContentView(generateContentView(mProxyActivity)); }else { setContentView(R.layout.main); } } //由于我们只是仅仅实验启动插件程序,并没有去想办法获取其资源文件,所以我们不能用XML,只能通过代码来设置布局 private View generateContentView(final Context context) { LinearLayout layout = new LinearLayout(context); layout.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); layout.setBackgroundColor(Color.parseColor("#ef563a")); Button button = new Button(context); button.setText("button"); layout.addView(button, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(context, "you clicked button", Toast.LENGTH_SHORT).show(); } }); return layout; } protected void startActivityByProxy(String className) { if (mProxyActivity == this) { Intent intent = new Intent(); intent.setClassName(this, className); this.startActivity(intent); } else { Intent intent = new Intent(PROXY_VIEW_ACTION); intent.putExtra(EXTRA_DEX_PATH, DEX_PATH); intent.putExtra(EXTRA_CLASS, className); mProxyActivity.startActivity(intent); } }}
最后一定要尊重原著
http://blog.csdn.net/singwhatiwanna/article/details/39937639/
0 0
- APK动态加载框架解析(一)
- APK动态加载框架(DL)解析(一)
- APK动态加载框架(DL)解析
- APK动态加载框架(DL)解析
- APK动态加载框架(DL)解析
- APK动态加载框架(DL)解析
- APK动态加载框架(DL)解析
- APK动态加载框架(DL)解析
- APK动态加载框架(DL)解析
- APK动态加载框架(DL)解析
- APK动态加载框架(DL)解析
- APK动态加载框架(DL)解析
- APK动态加载框架(DL)解析
- APK动态加载框架(DL)解析
- APK动态加载框架(DL)解析
- APK动态加载框架(DL)解析
- APK动态加载框架(DL)解析
- APK动态加载框架DL解析
- 从零开始的JAVA【一】
- eventbus全解析
- C++实现单词逆序输出
- 使用adb无线连接手机的步骤
- 用python把随机码保存到MySQL数据库中
- APK动态加载框架解析(一)
- 关于找工作:招聘网站&市场行情&工作(iOS&Android)&简历&面试&租房
- CAS的PGTURL、PGTIOU有什么作用?
- Oracle安装的一些问题
- 人工智能最有前景的六大领域
- 【正则表达式】正则表达式处理图片地址、img标签的方法
- 利用中序和前序遍历确定并生成一颗唯一的树
- 给oracle中某个表添加一个新的字段的sql
- 用Sketch和PaintCode快速得到绘制代码