)
Activity 是一个应用组件用户可与其提供的屏幕进行交互以执行拨打电话、拍摄照片、发送电子邮件或查看地图等操作。 每个 Activity 都会获得一个用于绘制其用户界面的窗口。窗口通常会充满屏幕但也可小于屏幕并浮动在其他窗口之上。Activity1. Activity 的使用我们新建的工程中带有一个基础 activity。新建工程中需要注意3个文件。MainActivity.java在src/main/java里对应的包名目录下。activity_main.xml在res/layout里。AndroidManifest.xml在src/main里。这里叫做“清单文件”。这3个文件分布在不同的地方。简单来说java文件可以控制界面逻辑。 layout文件这里指的是activity_main.xml预设了UI如何摆放。 清单文件告诉系统我这个app有哪些组件申请了什么权限。2. layout - 界面布局新建的 layout 中as 一般会默认给一个ConstraintLayout。比如activity_main.xml。activity_main.xml?xml version1.0encodingutf-8?androidx.constraintlayout.widget.ConstraintLayout xmlns:androidhttp://schemas.android.com/apk/res/androidxmlns:apphttp://schemas.android.com/apk/res-autoxmlns:toolshttp://schemas.android.com/toolsandroid:layout_widthmatch_parentandroid:layout_heightmatch_parenttools:context.MainActivity!--省略默认的TextView--/androidx.constraintlayout.widget.ConstraintLayout这里为了用起来方便我们把它换成LinearLayout 有的朋友会问都2021年了为什么不直接用 ConstraintLayout现在不做什么功能先用LinearLayout就是为了方便。 换成LinearLayout后layout文件长这样。换成LinearLayout后的activity_main.xml?xml version1.0encodingutf-8?LinearLayout xmlns:androidhttp://schemas.android.com/apk/res/androidxmlns:apphttp://schemas.android.com/apk/res-autoxmlns:toolshttp://schemas.android.com/toolsandroid:layout_widthmatch_parentandroid:layout_heightmatch_parenttools:context.MainActivity!--省略默认的TextView--/LinearLayout可以看到标签的开头和结尾都换成了LinearLayout。其他地方暂时不修改。as功能强大非常便利。我们可以用鼠标选中标签开始的androidx...Layout然后直接键盘输入LinearLayout的前几位字母。as会自动弹出选择框在里面双击选择LinearLayout或者回车选择标签就替换完成了。3. Java - 控制界面layout文件设计的是界面的初始布局。它决定了初始界面上放着什么UI组件以及组件是怎么组织安排的。这里我们说的是「初始界面」或者「初始布局」。也就是说我们可以控制界面上的UI元素。先看默认的MainActivity.java。在onCreate方法里R.layout.activity_main指的就是activity_main.xml。现在layout中有一个TextView它可以用来显示文字。我们想在MainActivity中控制它该怎么做呢现在改一下这个TextView。删掉原来ConstraintLayout用到的那些属性。给它添加一个id。这个id在这个layout文件中必须是独一无二的。给它分配一个id叫做tv1就像下面。TextViewandroid:idid/tv1android:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:textHello World!/现在TextView有了身份证我们在activity中就可以找到它。用的是findViewById方法。TextView tv1findViewById(R.id.tv1);现在我们就拿到了界面上的这个TextView对象。可以对它进行操作了。 比如改变它显示的文字。TextView tv1findViewById(R.id.tv1);// 拿到textView的对象tv1.setText(Today is a good day.);// 改变文字4. AndroidManifest.xml - 清单文件也可以简称为「manifest文件」。清单文件非常重要它告诉系统我们的app有哪些activity用到了什么权限等等信息。如果要新建activity需要在清单中注册。AndroidManifest.xmlapplicationandroid:allowBackuptrueandroid:iconmipmap/ic_launcherandroid:labelstring/app_nameandroid:roundIconmipmap/ic_launcher_roundandroid:supportsRtltrueandroid:themestyle/AppThemeactivity android:name.MainActivityintent-filteraction android:nameandroid.intent.action.MAIN/category android:nameandroid.intent.category.LAUNCHER//intent-filter/activity/application从这个默认的清单文件中我们可以得知activity 是属于 application 的。application 就是我们的应用。application 标签中也指定了各种元素例如应用的图标名字主题等等。MainActivity 是应用启动的第一个 activity。可以观察到它设置了 action 和category 属性。android.intent.action.MAIN决定应用程序最先启动的Activity。android.intent.category.LAUNCHER表示可以在手机“桌面”上看到应用图标。设置了这 2 个标签决定了这个 activity 是用户点击应用图标时第一个启动的界面。小结activity是应用重要的组件之一。纷繁复杂的内容需要activity来承载。之后我们会在activity中控制各种各样的UI组件处理用户的操作申请权限等等。还要了解activity的生命周期启动方式和跳转方法。Activity 生命周期生命周期图示1. 生命周期变化执行一些常见的操作打log看一下生命周期的变化。测试机型RedMi。启动然后退出onCreate onStart onResumeonWindowFocusChanged:hasFocus:trueonWindowFocusChanged:hasFocus:falseonPause onStop onDestroy启动后按home键Act1:onCreateAct1:onStartAct1:onResumeAct1:onWindowFocusChanged:hasFocus:true// 按home键Act1:onWindowFocusChanged:hasFocus:falseAct1:onPauseAct1:onStop// 再回来Act1:onRestartAct1:onStartAct1:onResumeAct1:onWindowFocusChanged:hasFocus:true// 按返回键退出actAct1:onWindowFocusChanged:hasFocus:falseAct1:onPauseAct1:onStopAct1:onDestroy旋转手机activity 在切换横竖屏的时候的生命周期。[Life]:onCreate[Life]:onStart[Life]:onResume[Life]:onWindowFocusChanged:hasFocus:true// 横屏[Life]:onPause[Life]:onStop[Life]:onDestroy[Life]:onCreate[Life]:onStart[Life]:onResume[Life]:onWindowFocusChanged:hasFocus:true// 竖屏[Life]:onPause[Life]:onStop[Life]:onDestroy[Life]:onCreate[Life]:onStart[Life]:onResume[Life]:onWindowFocusChanged:hasFocus:true// 返回[Life]:onWindowFocusChanged:hasFocus:false[Life]:onPause[Life]:onStop[Life]:onDestroy来回切换的生命周期变化以2个Activity启动为例。Act1:onCreateAct1:onStartAct1:onResumeAct1:onWindowFocusChanged:hasFocus:trueAct1:onPauseAct1:onWindowFocusChanged:hasFocus:falseAct2:onCreateAct2:onStartAct2:onResumeAct2:onWindowFocusChanged:hasFocus:trueAct1:onStopAct2:onWindowFocusChanged:hasFocus:falseAct2:onPauseAct1:onRestartAct1:onStartAct1:onResumeAct1:onWindowFocusChanged:hasFocus:trueAct2:onStopAct2:onDestroyAct1:onWindowFocusChanged:hasFocus:falseAct1:onPauseAct1:onStopAct1:onDestroy弹出 AlertDialog点击按钮弹出一个AlertDialog。观察发现调用onWindowFocusChanged。onWindowFocusChanged:hasFocus:falseonWindowFocusChanged:hasFocus:true这里也可以用 DialogFragment 来做测试。recreate调用 recreate() 方法[Life]:onCreate[Life]:onStart[Life]:onResume[Life]:onWindowFocusChanged:hasFocus:true[Life]:click[recreate][Life]:onPause[Life]:onStop[Life]:onDestroy[Life]:onCreate[Life]:onStart[Life]:onResume可以看到调用recreate()方法后并没有走onWindowFocusChanged回调。2. onCreate 和 onStart 的区别activity的状态区别onCreate在系统首次创建 Activity 时触发。Activity会在创建后进入已创建状态。当 Activity 进入“已开始”状态时系统会调用此回调。onStart()调用使 Activity 对用户可见因为应用会为 Activity 进入前台并支持交互做准备。onStart()方法会非常快速地完成并且与“已创建”状态一样Activity 不会一直处于“已开始”状态。一旦此回调结束Activity 便会进入已恢复状态系统将调用onResume()方法。3. onPause 和 onStop 的区别onPause()执行非常简单而且不一定要有足够的时间来执行保存操作。 因此您不应使用onPause()来保存应用或用户数据、进行网络调用或执行数据库事务。因为在该方法完成之前此类工作可能无法完成。已进入已停止状态因此系统将调用onStop()回调。举例而言如果新启动的 Activity 覆盖整个屏幕就可能会发生这种情况。在onStop()方法中应用应释放或调整应用对用户不可见时的无用资源。例如应用可以暂停动画效果或从细粒度位置更新切换到粗粒度位置更新。 使用onStop()而非onPause()可确保与界面相关的工作继续进行即使用户在多窗口模式下查看您的 Activity 也能如此。 您还应该使用onStop()执行 CPU 相对密集的关闭操作。Activity 启动携带参数启动前面大致了解了Activity是一个应用组件能为用户提供一个界面。以及如何新增activity。 一个App中通常有多个界面。假设每一个界面对应一个activity不同界面之间怎么跳转呢1. Intent通常activity之间的跳转离不开Intent这个类。 Intent直译为“意图”。我们把信息包裹在intent对象中然后执行。 比如启动RelativeLayoutGuideAct这个activity。startActivity(newIntent(getApplicationContext(),RelativeLayoutGuideAct.class));这里用到一个很常见的方法startActivity (Intent intent)。startActivity属于Context类Activity是Context的子类。java.lang.Object android.content.Context android.content.ContextWrapper android.view.ContextThemeWrapper android.app.Activity现在我们知道了启动activity需要使用Intent调用startActivity方法。2. 带参数的跳转在跳转去下一个页面时我们可能会想携带一些信息到下一个界面去。例如携带一些文本数字等等。 或者是一个对象。 这些信息我们可以交给Intent传递到下一个activity去。下一个activity中拿到我们传入的Intent。携带基本类型和String我们直接看intent的方法。Intent intentnewIntent(getApplicationContext(),SendParamsDemo.class);intent.putExtra(SendParamsDemo.K_INT,100);intent.putExtra(SendParamsDemo.K_BOOL,true);intent.putExtra(SendParamsDemo.K_STR,Input string);startActivity(intent);intent的putExtra方法可以传入参数。它接受1个String作为key然后是具体参数。 例子中我们跳转去了SendParamsDemo。publicclassSendParamsDemoextendsAbsActivity{publicstaticfinal StringK_INTk_int;publicstaticfinal StringK_BOOLk_bool;publicstaticfinal StringK_STRk_str;OverrideprotectedvoidonCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);gotInput();}privatevoidgotInput(){Intent intentgetIntent();if(intent!null){int iintent.getIntExtra(K_INT,-1);boolean bintent.getBooleanExtra(K_BOOL,false);String strintent.getStringExtra(K_STR);Log.d(TAG,gotInput: i:i, b: b, str: str);}else{Log.d(TAG,gotInput: input null.);}}}// log:// com.rustfisher.tutorial2020 D/rustAppSendParamsDemo: gotInput: i:100, b: true, str: Input string在这个activity中我们接收到了传入的参数。观察intent的putExtra方法我们发现它支持传入很多种参数。intbyte, char, float, double, long, booleanstringCharSequence或是它们的数组。 也可以传入ParcelableSerializable对象或是对象数组。传入Serializable对象除了基本类型和String可以传送对象吗 答案是肯定的。Intent可以携带Serializable对象。Serializable本身是一个接口自定义的对象实现这个接口后就可以被Intent携带。 比如我们改造一下DataTest类让它实现Serializable接口。publicclassDataTestimplementsSerializable{// 实现接口然后将对象送给intent再启动activity。Intent intentnewIntent(getApplicationContext(),RecyclerViewDemo2Act.class);DataTest outnewDataTest(input time,233,666,999);Log.d(TAG,startInputData: sending object: out);intent.putExtra(RecyclerViewDemo2Act.K_INPUT_DATA,out);startActivity(intent);被启动的activity接受传入的intent并取出对象。Intent intentgetIntent();if(intent!null){DataTest d(DataTest)intent.getSerializableExtra(K_INPUT_DATA);// 取出了对象拿去显示}Serializable接口不含任何方法。实现了这个接口的类系统会自动将其序列化。我们打印出发送和接收到的对象。startInputData:sending object:com.rustfisher.tutorial2020.recycler.data.DataTestfb43df5getInputData:input data object:com.rustfisher.tutorial2020.recycler.data.DataTesta588b5c可以发现这2个对象并不是同一个引用。但它们的“内容”是一样的。对象经历了序列化和反序列化的过程。值得注意的是Intent 能携带的对象大小并不是无限制的。实际开发中需要开发者自己预估传输的数据大小。传送 Parcelable 对象和传送 Serializable 对象类似用同样的存入和取出操作。Activity 相关面试题1. 谈一下返回栈首先理解android是使用Task来管理活动一个Task就是一组存放在栈里的活动的集合这个栈就叫做返回栈每启动一个新的活动就会将其放入栈顶当我们点击back回退或调用activity的finish函数处于栈顶的活动就会出栈前一个入栈的活动就会到栈顶系统总是显示处于栈顶的活动。2. 说下Activity的生命周期onCreate()方法活动第一次创建的时候被调用常做初始化的操作比如加载布局setContentView绑定事件findViewById。表示Activity正在创建。onStart()方法活动由不可见到可见的时候被调用表示Activity正在启动此时Activity可见但不在前台。onResume方法活动准备好和用户进行交互时调用。表示Acitivity获得焦点此时Activity可见且在前台。onPause方法系统准备去启动或恢复另一个活动时调用。表示Activity正在停止此时可做存储数据停止动画等操作。onStop方法在活动完全不可见的时候调用。表示Activity即将停止。onDestory方法在活动被销毁之前调用表示Activity即将销毁常做回收工作、资源释放。onRestart方法在活动由停止状态变为运行状态之前调用。表示Activity即将重启。3. 说下活动的生存期活动的生存期分为三个完整生存期可见生存期前台生存期完整生存期onCreate方法与onDestory都处于完整生存期一般情况下Activity会在onCreate方法中完成各种初始化操作而在onDestory方法中完成释放内存的操作。**可见生存期**onStart方法与onStop方法就是可见生存期Activity对于用户是可见的但无法与用户交互。onStart方法中对资源进行加载onStop方法中对资源进行释放。**前台生存期**onResume方法与onPause方法就是前台生存期在前台生存期内活动处于运行状态此时可以与用户交互。4. 说下Activity处于onPasue下可以执行那些操作用户返回该Activity调用onResume方法重新running用户打开了其他Activity就会调用onStop方法系统内存不足拥有更高权限的应用需要内存该Activity就会被系统回收如果用户返回到onStop的Activity又显示在前台了系统会调用onRestart()-onStart()-onResume()然后重新running当Activity结束调用finish方法就会调用onDestory方法释放所有占用的资源。生命周期的切换过程启动一个Activity onCreate-onStart-onResume当一个Activity打开另一个Activity都会回调哪些方法如果ActivityB是完全透明的呢如果启动的是一个对话框Activity呢 AonPause-B:onCreate-B:onStart-B:onResume-A:onStop 如果ActivityB是完全透明的或对话框Activity则不会调用onStop。启动新Activity后又返回到旧的Activity BonPause-AonRestart-AonStart-AonResume-BonStop-BonDestory关闭屏幕/按Home键 onPause-onStop当一个Activity按Home键切换到桌面后又回到该Activity回调哪些方法。 onPause-onStop-onRestart-onStart-onResume当一个Activity按back键回退时回调哪些方法 onPause-onStop-onDestoryActivity的优先级可见且可以交互前台Acitivity正在和用户交互优先级最高。可见但不可以交互可见但非前台Activity比如当前Activity启动了一个对话框Activity当前Activity就是可见但不可以交互。后台Activity已经被暂停的Activity比如执行了onStop优先级最低。 当系统内存不足会按照优先级顺序从低到高去杀死目标Activity所在的进程。5. 优先级低的Activity在内存不足被回收后怎样做可以恢复到销毁前状态优先级低的 Activity 在内存不足被回收后重新打开横竖屏切换的过程中会引发Activity重建。在 Activity 由于异常情况被终止时系统会调用onSaveInstanceState方法来保存当前 Activity 的状态该方法调用于 onStop 之前与 onPause 方法没有时序关系。当异常终止的 Activity 被重建时会调用onRestoreInstanceState方法该方法在 onStart 之后并且把 Activity 销毁时onSaveInstanceState保存的 Bundle 对象参数同时传递给onCreate方法和onRestoreInstanceState方法。该方法的调用是在onStart之前。因此可通过onRestoreInstanceState(Bundle savedInstanceState)和onCreate((Bundle savedInstanceState)来判断Activity是否被重建并取出数据进行恢复。但需要注意的是在onCreate取出数据时一定要先判断savedInstanceState是否为空。补充其中 onCreate 和onRestoreInstanceState方法来恢复 Activity 的状态的区别onRestoreInstanceState方法回调则说明 bundle 对象非空不需要加非空判断而 onCreate 需要非空判断。6. 谈谈onSaveInstanceState()与onRestoreIntanceState()onSaveInstanceState这两个方法并不是生命周期方法它们并不一定会被触发。当应用遇到意外情况如内存不足、用户直接按Home键由系统销毁一个Activity时onSaveInstanceState() 会被调用该方法的调用在onStop之前与onPause没有时序关系。但是当用户主动去销毁一个Activity时例如在应用中按返回键onSaveInstanceState()就不会被调用。因为在这种情况下用户的行为决定了不需要保存Activity的状态。onSaveInstanceState()时机 1用户按下Home键 2横竖屏切换 3按下电源按钮关闭屏幕显示 4内存不足导致优先级的Activity被杀死onRestoreIntanceState()当被系统异常销毁的 Activity 被重建时会调用 onRestoreIntanceState 或 onCreate 方法来恢复而 onRestoreInstance 与 Oncreate 方法中传入的 Bundle 对象是销毁时 onSaveInstanceState 保存的onRestoreIntanceState 在 onStart之后。7. onSaveInstanceState()与onPause()的区别onSaveInstanceState()只适合用于保存一些临时性的状态而onPause()适合用于数据的持久化保存。8. 谈谈横竖屏切换过程中调用的函数要切记这里活动已经被销毁了。 onPause-onSaveInstanceState-onStop-onDestory()-onCreate-onStart-onRestoreIntanceState-onResume9. 如何防止横竖屏切换配置改变时Activity销毁并切换通过对AndroidManifest文件的Activity中指定configChanges属性androidconfigChanges“orientation|screensize”来避免横竖屏切换时Activity的销毁和重建而是回调了onCofigurationChanged方法OverridepublicvoidonConfigurationChanged(Configuration newConfig){super.onConfigurationChanged(newConfig);}这里附上android configChanges 的所有属性解释“mcc“ 移动国家号码由三位数字组成每个国家都有自己独立的MCC可以识别手机用户所属国家。 “mnc“ 移动网号在一个国家或者地区中用于区分手机用户的服务商。 “locale“ 所在地区发生变化。 “touchscreen“ 触摸屏已经改变。这不应该常发生。 “keyboard“ 键盘模式发生变化例如用户接入外部键盘输入。 “keyboardHidden“ 用户打开手机硬件键盘 “navigation“ 导航型发生了变化。这不应该常发生。 “orientation“ 设备旋转横向显示和竖向显示模式切换。 “fontScale“ 全局字体大小缩放发生改变10. 说下Activity的四种启动模式standard模式标准模式普通启动模式每次启动Activity时就会创建一个实例。singletop模式栈顶模式当启动Activity时会判断任务栈的栈顶是否为该Activity如果是该Activity则不会创建实例去回调onNewIntentintent方法否则会创建实例singletask模式栈内模式当启动Activity时只要该Activity在指定的栈中就不会创建实例去回调onNewIntentintent**方法。如果不存在会判断是否指定的栈不存在就创建一个栈并将Activity的实例压入如果指定的栈存在就直接压入该栈中。singleInstance模式单实例模式该模式下创建Activity实例时直接创建一个栈栈中只有该Activity实例。之后无论哪个应用程序启动该Activity都只会调用栈中该实例。11. 谈谈 singleTop 和 singleTask 的区别以及应用场景singleTop 模式的含义是参考上面问题singleTask 模式的含义是参考上面问题因此二者的差别为singleTop 模式该模式下任务栈中可能有多个相同 Activity 实例因为它只是判断当前启动的 Activity 是否在栈顶。 该模式的 Activity 会默认进入启动它所属的任务栈不涉及任务栈的转换。常用于防止快速连续点击而创建多个 Activity 实例。singleTask 模式该模式向任务栈中只会有一个Activity实例因为它会判断当前启动的Activity是否在当前指定的栈中。该模式下Activity可以通过taskAffinity去指定需要的任务栈可能涉及任务栈的转换常用于首页或登录页。因为不论我们在进入首页后进入了多少个Activity当我们返回首页后还是希望退出首页直接可以退出应用。该模式下会把栈中位于要启动的Activity上面的Activity都出栈。12. onNewIntent()调用时机有两个调用时机分别是singleTop模式下与singleTask模式下启动Activity。 singleTop模式当启动的Activity是在任务栈的栈顶时会回调onNewIntent方法。 singleTask模式当启动的Activity存在于任务栈中会回调onNewIntent方法。13. 了解哪些Activity启动模式的标记位FLAG_ACTIVITY_SINGLE_TOP:对应singleTop启动模式FLAG_ACTIVITY_NEW_TASK对应singleTask模式更多Android零基础入门到精通的资料完整资料可以扫码免费领取【腾讯技术团队出品】Android零基础入门到精通Android Studio安装教程全套安卓基础教程Android编程入门教程Java语言基础从入门到熟悉Kotlin语言基础从入门到熟悉Android 技术栈从入门到熟悉Android Jetpack 全家桶全面学习对于新手来说可能安装Android Studio存在一定困难你可以看着以下视频一步步的跟着学习安装运行Android Studio 安装教程有了Java阶段的学习这一阶段建议以视频学习为主辅以图书查漏补缺。如果以图书为主可以根据图书讲解敲代码辅以教学视频查漏补缺。遇到问题可以去百度入门的问题一般会有很多人遇到并且给出比较好的解答。需要掌握基本知识点比如四大组件如何使用、如何创建Service、如何进行布局、简单的自定义View、动画、网络通信等常见技术。全套零基础教程已经为你们准备好了需要的可以添加下方二维码免费领取全套安卓基础教程