- 浏览: 2195645 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (1240)
- mac/IOS (287)
- flutter (1)
- J2EE (115)
- android基础知识 (582)
- android中级知识 (55)
- android组件(Widget)开发 (18)
- android 错误 (21)
- javascript (18)
- linux (70)
- 树莓派 (18)
- gwt/gxt (1)
- 工具(IDE)/包(jar) (18)
- web前端 (17)
- java 算法 (8)
- 其它 (5)
- chrome (7)
- 数据库 (8)
- 经济/金融 (0)
- english (2)
- HTML5 (7)
- 网络安全 (14)
- 设计欣赏/设计窗 (8)
- 汇编/C (8)
- 工具类 (4)
- 游戏 (5)
- 开发频道 (5)
- Android OpenGL (1)
- 科学 (4)
- 运维 (0)
- 好东西 (6)
- 美食 (1)
最新评论
-
liangzai_cool:
请教一下,文中,shell、C、Python三种方式控制led ...
树莓派 - MAX7219 -
jiazimo:
...
Kafka源码分析-序列5 -Producer -RecordAccumulator队列分析 -
hp321:
Windows该命令是不是需要安装什么软件才可以?我试过不行( ...
ImageIO读jpg的时候出现javax.imageio.IIOException: Unsupported Image Type -
hp321:
Chenzh_758 写道其实直接用一下代码就可以解决了:JP ...
ImageIO读jpg的时候出现javax.imageio.IIOException: Unsupported Image Type -
huanghonhpeng:
大哥你真强什么都会,研究研究。。。。小弟在这里学到了很多知识。 ...
android 浏览器
还记得我们在代理Activity模式里谈到启动插件APK里的Activity的两个难题吗,由于插件里的Activity没在主项目的Manifest里面注册,所以无法经历系统Framework层级的一系列初始化过程,最终导致获得的Activity实例并没有生命周期和无法使用res资源。
使用代理Activity能够解决这两个问题,但是有一些限制:
特别是最后一个,无法直接把一个普通的APK作为插件使用。这个其实也不算是限制,如果我们需要进行插件化开发,我们总希望能够通过一些框架限制和规范插件的行为,在加载插件前就知道插件大概有哪些功能,这样不仅能方便对插件行为的控制,还能在一定程度上确保插件的安全(运行一个完全未知的可执行文件鬼知道它会做些什么)。不过这样做就要求插件必须依赖特定的框架,这对插件是一种侵入式开发,也就是说,开发插件时不能像开发普通APP那样自由。
那么有办法避开这些限制,做到完全非侵入式开发吗?比如,通过动态加载框架,不用安装就直接运行《Flappy Bird》的APK安装包。这听起来好像是只有获得ROOT权限才能做到的事情,要不然随便写个空壳的APK加载别人的游戏安装包就直接能运行了。不过,确实有人做到了,通过动态生成Activity类的方式。
动态创建Activity模式
插件的Activity不是标准的Activity对象才会有上述的这些限制,使其成为标准的Activity是解决问题的关键,而要使其成为标准的Activity,则需要在主项目里注册这些Activity。总不能把插件APK所有的Activity都事先注册到宿主项目里面吧,想到代理模式需要注册一个代理的ProxyActivity,那么能不能在主项目里 注册一个通用的Activity(比如TargetActivity)给插件里所有的Activity用呢?解决对策就是,在需要启动插件的某一个Activity(比如PlugActivity)的时候,动态创建一个TargetActivity,新创建的TargetActivity会继承PlugActivity的所有共有行为,而这个TargetActivity的包名与类名刚好与我们事先注册的TargetActivity一致,我们就能以标准的方式启动这个Activity。
运行时动态创建并编译一个Activity类,这种想法不是天方夜谭,动态创建类的工具有dexmaker和asmdex,二者均能实现动态字节码操作,最大的区别是前者是创建DEX文件,而后者是创建CLASS文件。
使用DexMaker动态创建一个类
这种运行时创建一个编译好并能运行的类的方式叫做“动态字节码操作”(runtime bytecode manipulation),使用DexMaker工具能创建一个DEX文件,之后我们再反编译这个DEX看看创建出来的类是什么样子。
运行后在SD卡的dexmaker目录下找到刚创建的文件“Generated1532509318.jar”,把里面的“classes.dex”解压出来,然后再用“dex2jar”工具转化成jar文件,最后再用“jd-gui”工具反编译jar的源码。
至此,我们已经成功在运行时创建一个编译好的类(的DEX)。
修改需要启动的目标Activity
接下来的问题是如何把需要启动的、在Manifest里面没有注册的PlugActivity换成有注册的TargetActivity。
在Android,虚拟机加载类的时候,是通过ClassLoader的loadClass方法,而loadClass方法并不是final类型的,这意味着我们可以创建自己的类去继承ClassLoader,以重载loadClass方法并改写类的加载逻辑,在需要加载PlugActivity的时候,偷偷把其换成TargetActivity。
大致思路如下:
这样就能把启动插件里的PlugActivity变成启动动态创建的TargetActivity。
不过还有一个问题,主项目启动插件Activity的时候,我们可以替换Activity,但是如果在插件Activity(比如MainActivity)启动另一个Activity(SubActivity)的时候怎么办?插件时普通的第三方APK,我们无法更改里面跳转Activity的逻辑。其实,从主项目启动插件MainActivity的时候,其实启动的是我们动态创建的TargetActivity(extends MainActivity),而我们知道Activity启动另一个Activity的时候都是使用其“startActivityForResult”方法,所以我们可以在创建TargetActivity时,重写其“startActivityForResult”方法,让它在启动其他Activity的时候,也采用动态创建Activity的方式,这样就能解决问题。
动态创建Activity开源项目 android-pluginmgr
这种脑洞大开的动态加载思路来自于 houkx 的开源项目 android-pluginmgr。
android-pluginmgr项目中有三种ClassLoader,一是用于替换宿主APK的Application的CJClassLoader,二是用于加载插件APK的PluginClassLoader,再来是用于加载启动插件Activity时动态生成的PlugActivity的dex包的DexClassLoader(存放在Map集合proxyActivityLoaderMap里面)。其中CJClassLoader是PluginClassLoader的Parent,而PluginClassLoader又是第三种DexClassLoader的Parent。
ClassLoader类加载Class的时候,会先使用Parent的ClassLoader,但Parent不能完成加载工作时,才会调用Child的ClassLoader去完成工作。
具体分析请参考 Android动态加载基础 ClassLoader的工作机制。
所以每加载一个Activity的时候都会调用到最上级的CJClassLoader的loadClass方法,从而保证启动插件Activity的时候能顺利替换成PlugActivity。当然如何控制着三种ClassLoader的加载工作,也是pluginmgr项目的设计难度之一。
所以每加载一个Activity的时候都会调用到最上级的CJClassLoader的loadClass方法,从而保证启动插件Activity的时候能顺利替换成PlugActivity。当然如何控制着三种ClassLoader的加载工作,也是pluginmgr项目的设计难度之一。
存在的问题
动态类创建的方式,使得注册一个通用的Activity就能给多给Activity使用,对这种做法存在的问题也是明显的:
其中不稳定的问题出现在对Service的支持上,使用动态创建类的方式可以搞定Activity和Broadcast Receiver,但是使用类似的方式处理Service却不行,因为“ContextImpl.getApplicationContext” 期待得到一个非ContextWrapper的context,如果不是则继续下次循环,目前的Context实例都是wrapper,所以会进入死循环。
据 houkx 称他现在有另外的思路实现“启动为安装的普通第三方APK”的目的,而且不是基于动态类创建的原理,期待他的开源项目的更新。
代理Activity模式与动态创建Activity模式的区别
简单地说,最大的不同是代理模式使用了一个 代理的Activity,而动态创建Activity模式使用了一个 通用的Activity。
代理模式中,使用一个代理Activity去完成本应该由插件Activity完成的工作,这个代理Activity是一个标准的Android Activity组件,具有生命周期和上下文环境(ContextWrapper和ContextCompl),但是它自身只是一个空壳,并没有承担什么业务逻辑;而插件Activity其实只是一个普通的Java对象,它没有上下文环境,但是却能正常执行业务逻辑的代码。代理Activity和不同的插件Activity配合起来,就能完成不同的业务逻辑了。所以代理模式其实还是使用常规的Android开发技术,只是在处理插件资源的时候强制调用了系统的隐藏API(除非某些ROM蛋疼修改了这个API),因此这种模式还是可以稳定工作和升级的。
动态创建Activity模式,被动态创建出来的Activity类是有在主项目里面注册的,它是一个标准的Activity,它有自己的Context和生命周期,不需要代理的Activity。
使用代理Activity能够解决这两个问题,但是有一些限制:
- 实际运行的Activity实例其实都是ProxyActivity,并不是真正想要启动的Activity;
- ProxyActivity只能指定一种LaunchMode,所以插件里的Activity无法自定义LaunchMode;
- 不支持静态注册的BroadcastReceiver;
- 往往不是所有的APK都可作为插件被加载,插件项目需要依赖特定的框架,还有需要遵循一定的”开发规范”;
特别是最后一个,无法直接把一个普通的APK作为插件使用。这个其实也不算是限制,如果我们需要进行插件化开发,我们总希望能够通过一些框架限制和规范插件的行为,在加载插件前就知道插件大概有哪些功能,这样不仅能方便对插件行为的控制,还能在一定程度上确保插件的安全(运行一个完全未知的可执行文件鬼知道它会做些什么)。不过这样做就要求插件必须依赖特定的框架,这对插件是一种侵入式开发,也就是说,开发插件时不能像开发普通APP那样自由。
那么有办法避开这些限制,做到完全非侵入式开发吗?比如,通过动态加载框架,不用安装就直接运行《Flappy Bird》的APK安装包。这听起来好像是只有获得ROOT权限才能做到的事情,要不然随便写个空壳的APK加载别人的游戏安装包就直接能运行了。不过,确实有人做到了,通过动态生成Activity类的方式。
动态创建Activity模式
插件的Activity不是标准的Activity对象才会有上述的这些限制,使其成为标准的Activity是解决问题的关键,而要使其成为标准的Activity,则需要在主项目里注册这些Activity。总不能把插件APK所有的Activity都事先注册到宿主项目里面吧,想到代理模式需要注册一个代理的ProxyActivity,那么能不能在主项目里 注册一个通用的Activity(比如TargetActivity)给插件里所有的Activity用呢?解决对策就是,在需要启动插件的某一个Activity(比如PlugActivity)的时候,动态创建一个TargetActivity,新创建的TargetActivity会继承PlugActivity的所有共有行为,而这个TargetActivity的包名与类名刚好与我们事先注册的TargetActivity一致,我们就能以标准的方式启动这个Activity。
运行时动态创建并编译一个Activity类,这种想法不是天方夜谭,动态创建类的工具有dexmaker和asmdex,二者均能实现动态字节码操作,最大的区别是前者是创建DEX文件,而后者是创建CLASS文件。
使用DexMaker动态创建一个类
这种运行时创建一个编译好并能运行的类的方式叫做“动态字节码操作”(runtime bytecode manipulation),使用DexMaker工具能创建一个DEX文件,之后我们再反编译这个DEX看看创建出来的类是什么样子。
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void onMakeDex(View view){ try { DexMaker dexMaker = new DexMaker(); // Generate a HelloWorld class. TypeId<?> helloWorld = TypeId.get("LHelloWorld;"); dexMaker.declare(helloWorld, "HelloWorld.generated", Modifier.PUBLIC, TypeId.OBJECT); generateHelloMethod(dexMaker, helloWorld); // Create the dex file and load it. File outputDir = new File(Environment.getExternalStorageDirectory() + File.separator + "dexmaker"); if (!outputDir.exists())outputDir.mkdir(); ClassLoader loader = dexMaker.generateAndLoad(this.getClassLoader(), outputDir); Class<?> helloWorldClass = loader.loadClass("HelloWorld"); // Execute our newly-generated code in-process. helloWorldClass.getMethod("hello").invoke(null); } catch (Exception e) { Log.e("MainActivity","[onMakeDex]",e); } } /** * Generates Dalvik bytecode equivalent to the following method. * public static void hello() { * int a = 0xabcd; * int b = 0xaaaa; * int c = a - b; * String s = Integer.toHexString(c); * System.out.println(s); * return; * } */ private static void generateHelloMethod(DexMaker dexMaker, TypeId<?> declaringType) { // Lookup some types we'll need along the way. TypeId<System> systemType = TypeId.get(System.class); TypeId<PrintStream> printStreamType = TypeId.get(PrintStream.class); // Identify the 'hello()' method on declaringType. MethodId hello = declaringType.getMethod(TypeId.VOID, "hello"); // Declare that method on the dexMaker. Use the returned Code instance // as a builder that we can append instructions to. Code code = dexMaker.declare(hello, Modifier.STATIC | Modifier.PUBLIC); // Declare all the locals we'll need up front. The API requires this. Local<Integer> a = code.newLocal(TypeId.INT); Local<Integer> b = code.newLocal(TypeId.INT); Local<Integer> c = code.newLocal(TypeId.INT); Local<String> s = code.newLocal(TypeId.STRING); Local<PrintStream> localSystemOut = code.newLocal(printStreamType); // int a = 0xabcd; code.loadConstant(a, 0xabcd); // int b = 0xaaaa; code.loadConstant(b, 0xaaaa); // int c = a - b; code.op(BinaryOp.SUBTRACT, c, a, b); // String s = Integer.toHexString(c); MethodId<Integer, String> toHexString = TypeId.get(Integer.class).getMethod(TypeId.STRING, "toHexString", TypeId.INT); code.invokeStatic(toHexString, s, c); // System.out.println(s); FieldId<System, PrintStream> systemOutField = systemType.getField(printStreamType, "out"); code.sget(systemOutField, localSystemOut); MethodId<PrintStream, Void> printlnMethod = printStreamType.getMethod( TypeId.VOID, "println", TypeId.STRING); code.invokeVirtual(printlnMethod, null, localSystemOut, s); // return; code.returnVoid(); } }
运行后在SD卡的dexmaker目录下找到刚创建的文件“Generated1532509318.jar”,把里面的“classes.dex”解压出来,然后再用“dex2jar”工具转化成jar文件,最后再用“jd-gui”工具反编译jar的源码。
至此,我们已经成功在运行时创建一个编译好的类(的DEX)。
修改需要启动的目标Activity
接下来的问题是如何把需要启动的、在Manifest里面没有注册的PlugActivity换成有注册的TargetActivity。
在Android,虚拟机加载类的时候,是通过ClassLoader的loadClass方法,而loadClass方法并不是final类型的,这意味着我们可以创建自己的类去继承ClassLoader,以重载loadClass方法并改写类的加载逻辑,在需要加载PlugActivity的时候,偷偷把其换成TargetActivity。
大致思路如下:
public class CJClassLoader extends ClassLoader{ @override public Class loadClass(String className){ if(当前上下文插件不为空) { if( className 是 TargetActivity){ 找到当前实际要加载的原始PlugActivity,动态创建类(TargetActivity extends PlugActivity )的dex文件 return 从dex文件中加载的TargetActivity }else{ return 使用对应的PluginClassLoader加载普通类 } }else{ return super.loadClass() //使用原来的类加载方法 } } }
这样就能把启动插件里的PlugActivity变成启动动态创建的TargetActivity。
不过还有一个问题,主项目启动插件Activity的时候,我们可以替换Activity,但是如果在插件Activity(比如MainActivity)启动另一个Activity(SubActivity)的时候怎么办?插件时普通的第三方APK,我们无法更改里面跳转Activity的逻辑。其实,从主项目启动插件MainActivity的时候,其实启动的是我们动态创建的TargetActivity(extends MainActivity),而我们知道Activity启动另一个Activity的时候都是使用其“startActivityForResult”方法,所以我们可以在创建TargetActivity时,重写其“startActivityForResult”方法,让它在启动其他Activity的时候,也采用动态创建Activity的方式,这样就能解决问题。
动态创建Activity开源项目 android-pluginmgr
这种脑洞大开的动态加载思路来自于 houkx 的开源项目 android-pluginmgr。
android-pluginmgr项目中有三种ClassLoader,一是用于替换宿主APK的Application的CJClassLoader,二是用于加载插件APK的PluginClassLoader,再来是用于加载启动插件Activity时动态生成的PlugActivity的dex包的DexClassLoader(存放在Map集合proxyActivityLoaderMap里面)。其中CJClassLoader是PluginClassLoader的Parent,而PluginClassLoader又是第三种DexClassLoader的Parent。
ClassLoader类加载Class的时候,会先使用Parent的ClassLoader,但Parent不能完成加载工作时,才会调用Child的ClassLoader去完成工作。
引用
java.lang.ClassLoader
Loads classes and resources from a repository. One or more class loaders are installed at runtime. These are consulted whenever the runtime system needs a specific class that is not yet available in-memory. Typically, class loaders are grouped into a tree where child class loaders delegate all requests to parent class loaders. Only if the parent class loader cannot satisfy the request, the child class loader itself tries to handle it.
Loads classes and resources from a repository. One or more class loaders are installed at runtime. These are consulted whenever the runtime system needs a specific class that is not yet available in-memory. Typically, class loaders are grouped into a tree where child class loaders delegate all requests to parent class loaders. Only if the parent class loader cannot satisfy the request, the child class loader itself tries to handle it.
具体分析请参考 Android动态加载基础 ClassLoader的工作机制。
所以每加载一个Activity的时候都会调用到最上级的CJClassLoader的loadClass方法,从而保证启动插件Activity的时候能顺利替换成PlugActivity。当然如何控制着三种ClassLoader的加载工作,也是pluginmgr项目的设计难度之一。
所以每加载一个Activity的时候都会调用到最上级的CJClassLoader的loadClass方法,从而保证启动插件Activity的时候能顺利替换成PlugActivity。当然如何控制着三种ClassLoader的加载工作,也是pluginmgr项目的设计难度之一。
存在的问题
动态类创建的方式,使得注册一个通用的Activity就能给多给Activity使用,对这种做法存在的问题也是明显的:
- 使用同一个注册的Activity,所以一些需要在Manifest注册的属性无法做到每个Activity都自定义配置;
- 插件中的权限,无法动态注册,插件需要的权限都得在宿主中注册,无法动态添加权限;
- 插件的Activity无法开启独立进程,因为这需要在Manifest里面注册;
- 动态字节码操作涉及到Hack开发,所以相比代理模式起来不稳定;
其中不稳定的问题出现在对Service的支持上,使用动态创建类的方式可以搞定Activity和Broadcast Receiver,但是使用类似的方式处理Service却不行,因为“ContextImpl.getApplicationContext” 期待得到一个非ContextWrapper的context,如果不是则继续下次循环,目前的Context实例都是wrapper,所以会进入死循环。
据 houkx 称他现在有另外的思路实现“启动为安装的普通第三方APK”的目的,而且不是基于动态类创建的原理,期待他的开源项目的更新。
代理Activity模式与动态创建Activity模式的区别
简单地说,最大的不同是代理模式使用了一个 代理的Activity,而动态创建Activity模式使用了一个 通用的Activity。
代理模式中,使用一个代理Activity去完成本应该由插件Activity完成的工作,这个代理Activity是一个标准的Android Activity组件,具有生命周期和上下文环境(ContextWrapper和ContextCompl),但是它自身只是一个空壳,并没有承担什么业务逻辑;而插件Activity其实只是一个普通的Java对象,它没有上下文环境,但是却能正常执行业务逻辑的代码。代理Activity和不同的插件Activity配合起来,就能完成不同的业务逻辑了。所以代理模式其实还是使用常规的Android开发技术,只是在处理插件资源的时候强制调用了系统的隐藏API(除非某些ROM蛋疼修改了这个API),因此这种模式还是可以稳定工作和升级的。
动态创建Activity模式,被动态创建出来的Activity类是有在主项目里面注册的,它是一个标准的Activity,它有自己的Context和生命周期,不需要代理的Activity。
发表评论
-
带你深入理解 FLUTTER 中的字体“冷”知识
2020-08-10 23:40 626本篇将带你深入理解 Flutter 开发过程中关于字体和文 ... -
Flutter -自定义日历组件
2020-03-01 17:56 1099颜色文件和屏幕适配的文件 可以自己给定 import ... -
Dart高级(一)——泛型与Json To Bean
2020-02-23 19:13 989从 Flutter 发布到现在, 越来越多人开始尝试使用 Da ... -
flutter loading、Progress进度条
2020-02-21 17:03 1166Flutter Progress 1 条形无固定值进度条 ... -
Flutter使用Https加载图片
2020-02-21 01:39 1003Flutter使用Https加载图片 使用http加载图片出 ... -
flutter shared_preferences 异步变同步
2020-02-21 00:55 838前言 引用 在开发原生iOS或Native应用时,一般有判断上 ... -
Flutter TextField边框颜色
2020-02-19 21:31 924监听要销毁 myController.dispose(); T ... -
flutter Future的正确用法
2020-02-18 21:55 799在flutter中经常会用到异步任务,dart中异步任务异步处 ... -
记一次Flutter简单粗暴处理HTTPS证书检验方法
2020-02-18 14:13 948最近在做Flutter项目到了遇到一个无解的事情,当使用Ima ... -
flutter 获取屏幕宽度高度 通知栏高度等屏幕信息
2019-07-27 08:39 1327##MediaQuery MediaQuery.of(con ... -
关于flutter RefreshIndicator扩展listview下拉刷新的问题
2019-07-10 19:40 1112当条目过少时listview某些嵌套情况下可能不会滚动(条目 ... -
flutter listview 改变状态的时候一直无限添加
2019-07-10 16:01 774setstate的时候会一直无限的调用listview.bui ... -
Flutter Android端启动白屏问题的解决
2019-07-09 00:51 1506问题描述 Flutter 应用在 Android 端上启动时 ... -
Flutter中SnackBar使用
2019-07-08 23:43 766底部弹出,然后在指定时间后消失。 注意: build(Bui ... -
Flutter 之点击空白区域收起键盘
2019-07-08 18:43 1780点击空白处取消TextField焦点这个需求是非常简单的,在学 ... -
Flutter 弹窗 Dialog ,AlertDialog,IOS风格
2019-07-08 18:04 1369import 'package:flutter/mate ... -
flutter ---TextField 之 输入类型、长度限制
2019-07-08 14:30 2313TextField想要实现输入类型、长度限制需要先引入impo ... -
【flutter 溢出BUG】键盘上显示bottom overflowed by 104 PIXELS
2019-07-08 11:13 1542一开始直接使用Scaffold布局,body:new Colu ... -
解决Flutter项目卡在Initializing gradle...界面的问题
2019-07-07 12:53 864Flutter最近很火,我抽出了一点时间对Flutter进行了 ... -
关于android O 上 NotificationChannel 的一些注意事项
2019-07-04 11:47 931最近在适配android O,遇到个问题,应用中原本有设置界面 ...
相关推荐
singleTask模式会为Activity创建一个新的任务栈,每次启动Activity,如果任务栈中不存在该Activity,就会创建一个新实例并作为栈底。如果栈中已存在该Activity实例,那么所有在其之上的Activity都将被销毁,当前...
综上所述,动态加载Activity是Android开发中的高级技术,它结合了反射和代理模式,使得应用能够更加灵活地加载和管理Activity,为插件化开发提供了可能。然而,这也需要开发者对Android系统有深入的理解,以及良好的...
此模式下,系统只会创建Activity的一个实例,并且这个实例会在一个单独的任务栈中。这意味着,无论从哪个应用或任务启动这个Activity,都将进入同一个任务栈,其他Activity不能与之位于同一栈。因此,它是唯一一个...
但是,如果在运行时更改夜间模式设置,通常需要重新创建Activity以使主题更改生效。这个库的目标就是解决这个问题,通过某种方式避免了Activity的重启,实现了动态切换。 夜间模式的实现通常涉及到以下几个关键知识...
`ClassLoader`是Java中负责加载类的对象,它会根据Intent解析出要加载的Activity类,并通过`mInstrumentation.newActivity()`创建Activity实例。在这个过程中,如果出现异常,例如找不到或无法创建Activity,系统会...
1. 创建队列:在ActivityMQ服务器上定义一个队列,或者在C#客户端动态创建。 2. 生产者发送:C#客户端创建Producer对象,向指定的队列发送消息。 3. 消费者接收:创建Consumer对象,绑定到相同的队列,设置消息接收...
在Android应用开发中,Activity是用户界面的基本单元,它的启动模式(launchMode)是一个非常重要的概念,它决定了当用户点击应用图标或者从其他地方启动Activity时,系统如何管理和创建Activity实例。理解并合理...
首次启动时,系统会创建一个新的任务栈,并在其中创建Activity的实例。如果栈中已经存在该Activity的实例,那么系统会直接返回到该实例,而不是创建新的实例。此模式常用于作为应用的主入口点,可以确保用户始终回到...
这是默认的启动模式,当一个新的Intent指向一个Activity时,该Activity会被创建并压入Activity栈。如果该Activity已经在栈中,那么它会被重新创建,并且之前的实例会被销毁。这种模式适用于大多数场景,因为它提供了...
- 这种模式通常用于无特殊需求的Activity,易于创建新的任务栈。 2. **SingleTop** - 当Activity位于栈顶时,再次启动不会创建新实例,而是重用现有的。 - 如果Activity不在栈顶,系统会创建新实例并压入栈。 -...
在使用“Android-skin-sprite”时,我们需要在项目的res目录下创建对应的皮肤资源文件夹,如`skin_day`和`skin_night`,分别存放日间和夜间模式的资源。然后,对于需要动态更换的UI元素,如按钮、背景色、字体颜色等...
- 动态创建:虽然不常见,但可以通过`Context.createActivity()`方法动态创建Activity,但这需要对Android框架有深入理解。 2. **Activity的生命周期**: - `onCreate(Bundle)`:这是Activity的初始化阶段,通常...
要在AndroidManifest.xml中创建Activity别名,首先需要有一个已经声明的Activity。接着,添加一个<activity-alias>标签,指定它的targetActivity为原Activity的名称。例如: ```xml <activity-alias android:name=...
在AndroidManifest.xml文件中,也可以对Activity进行全局配置,例如设置主题、权限、启动模式等。这些配置在应用运行期间通常不能被修改,但可以通过在代码中创建并启动新的Intent来改变某些行为。 对于提供的...
在Android开发中,"Tab中的Activity嵌入子Activity"是一个常见的设计模式,它涉及到UI设计、Activity栈管理和用户交互。这种做法通常是为了解决多视图切换和保持应用层级结构清晰的问题。以下是对这一主题的详细说明...
创建两个资源目录:`res/values`(用于默认模式)和`res/values-night`(用于夜间模式),分别存放不同主题的XML资源文件。 1. **主题设置**: 在`res/values/styles.xml`中定义默认的主题,而在`res/values-night...
每种模式都决定了Activity如何被创建和如何在任务栈中管理。理解这些模式可以帮助我们设计更高效的应用流程。 三、Activity栈管理 Android采用任务(Task)和回退栈(Back Stack)来管理Activity。每个任务是一个...
- 标准模式(standard):默认模式,每次启动都会创建一个新的Activity实例。 - 单例模式(singleTop):如果栈顶已经存在该Activity的实例,就不会再创建新的,而是调用onNewIntent()方法。 - 单任务模式...
Fragment与Activity之间的通信是Android开发中常见的需求,特别是在创建复杂、动态布局时。本实例将深入探讨这一主题,讲解如何在Fragment和Activity之间有效地传递数据和执行操作。 首先,让我们了解Fragment和...
Fragment是Android系统中的一种组件,它可以在Activity中嵌入,实现界面的动态组合。一个Activity可以包含多个Fragment,每个Fragment都可以有自己的生命周期和UI。Fragment的使用可以使得应用程序的界面更加模块化...