《第一行代码Andorid》阅读笔记-第一章
这篇文章是我自己的《第一行代码Andorid》的阅读笔记,虽然大量参考了别人已经写好的一些笔记和代码但是也有自己的提炼和新的问题在里面,我也会放上参考文章链接。
学习重点
Android系统的四大组件:
(1)活动(Activity):是所有Android应用程序的门面,凡是在应用中你看得到的东西,都是放在活动中的。
(2)服务(Service):你无法看到它,但它会一直在后台默默地运行,即使用户退出了应用,服务仍然是可以继续运行的。
(3)广播接收器(Broadcast Receiver):广播接收器允许你的应用接收来自各处的广播消息,比如电话、短信等,当然你的应用同样也可以向外发出广播消息。
(4)内容提供器(Content Provider):为应用程序之间共享数据提供了可能,比如你想要读取系统电话簿中的联系人,就需要通过内容提供器来实现。
参考笔记
https://www.cnblogs.com/1693977889zz/p/16256303.html
开始第一个Android项目
注意点:
- 如果要跟着《第一行代码:Android篇(第2版)》这本书学习一定一定一定要把SDK版本以及设备模拟器设置成和书里一样的,我一开始就是因为SDK版本太高出现了很多的错误和不同之处。以前的旧版本虽然和新版本有不一样的地方但是不影响理解Android架构。书中的配置是:
Minimum SDK:API 15
设备模拟器:Nexus 5X
操作系统版本:API Level: 24 ,Target: Android 7.0 (Google Play) - 多用调试工具Log而不是System.out
除了Log.v()、Log.d()、Log.i()、Log.w()、Log.w()之外,logcat中还能很轻松地添加过滤器
目前只有3个过滤器:
- Show only selected application表示只显示当前选中程序的日志;
- Firebase是谷歌提供的一个分析工具,我们可以不用管它;
- NoFilters相当于没有过滤器,会把所有的日志都显示出来;
- 目录结构略,可以参考上面提供的笔记链接,和书上的内容一样。
第一章 活动
注意点:
- 第一次Greadle构建需要点时间,要下载很多东西,需要翻墙,可能失败,没关系多试几次。
- 新建project的时候记得一定要选择语言为Java,但是注意如果选择Empty Activity就会没有语言选择的选项,所以我们跟着书上选择No Activity或者Empty View Activity
暂时无法在飞书文档外展示此内容
1. 活动的基本用法🎯
1.1 创建活动
- 在创建新的活动的时候会遇到几个选项:
- 勾选Generate Layout File表示会自动为FirstActivity创建一个对应的布局文件
- 勾选Launcher Activity表示会自动将FirstActivity设置为当前项目的主活动
- 创建的.xml的layout布局文件在右上角可以有几种模式选择
- Code Mode(代码模式):
这个模式允许您直接编辑布局文件的 XML 代码。您可以在这里手动编写 XML 代码来定义布局结构、属性和样式。这是最灵活的方式,适用于对 XML 布局代码熟悉的开发者。 - Split Mode(分割模式):
这个模式将布局文件分为两个窗格,上半部分显示布局的设计预览,下半部分显示布局的 XML 代码。这允许您同时查看设计和代码,方便调整布局时的实时预览以及在代码中进行必要的修改。 - Design Mode(设计模式):
这个模式提供了一个可视化的界面,允许您通过拖放组件、设置属性和布局,以图形方式创建和编辑布局。在这个模式下,您可以快速预览 UI 的外观,而不需要直接编辑代码。
1.2 活动注册
所有的活动都要在AndroidManifest.xml中进行注册才能生效,一般编辑器会帮我们自动注册。
- intent-filter用于指定主活动,即点击桌面应用程序图标时首先打开的就是这个活动。
<intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
- android:label指定活动中标题栏的内容
android:label="This is FirstActivity"
1.3 提醒小弹窗Toast
Toast是Android系统提供的一种非常好的提醒方式,在程序中可以使用它将一些短小的信息通知给用户,这些信息会在一段时间后自动消失,并且不会占用任何屏幕空间。
下面这段代码我们一般放在点击事件里面
Toast.makeText(FirstActivity.this,“You clicked Button 1”,Toast.LENGTH_SHORT).show();
makeText()方法需要传入3个参数。
- 第一个参数是Context,是Toast要求的上下文,由于活动本身就是一个Context对象,因此这里直接传入FirstActivity.this即可。
- 第二个参数是Toast显示的文本内容。
- 第三个参数是Toast显示的时长,有两个内置常量可以选择Toast.LENGTH_SHORT和Toast.LENGTH_LONG。
1.4 顶部小菜单Menu
效果是在标题栏的右侧多了一个三点的符号,这个就是菜单按钮。点击之后会显示多个我们定义好的菜单按键,使用方式如下:
- 新建菜单文件:文件夹→New→Menu resource file
- 编写菜单按钮:在main.xml中添加代码
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"><itemandroid:id="@+id/add_item"android:title="Add"/><itemandroid:id="@+id/remove_item"android:title="Remove"/>
</menu>
上面的代码建了两个菜单项,其中标签就是用来创建具体的某一个菜单项,然后通过android:id给这个菜单项指定一个唯一的标识符,通过android:title给这个菜单项指定一个名称。
3. 给当前活动创建菜单:回到FirstActivity中来重写onCreateOptionsMenu()方法,重写的内容里面通过getMenuInflater()方法能够得到MenuInflater对象再调用它的inflate()方法就可以给当前活动创建菜单。
@Overridepublic boolean onCreateOptionsMenu(Menu menu) {getMenuInflater().inflate(R.menu.main,menu);return true;}
- 给菜单按钮编写逻辑处理:在FirstActivity中重写onOptionsItemSelected()方法,
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {if(item.getItemId() == R.id.add_item)Toast.makeText(this,"You clicked Add",Toast.LENGTH_SHORT).show();if(item.getItemId() == R.id.remove_item)Toast.makeText(this,"You clicked Remove",Toast.LENGTH_SHORT).show();return true;
}
注意这里的代码和书上的不一样,因为现在好像不能使用 case R.id.add_item了,必须是一个静态常量才行,所以我换成了if语句判断。这样写完后点击菜单按钮就会有不同的处理回应了。
1.5 销毁一个活动 finish()
销毁活动的方式有两种,一种是按一下Back键,另一种是通过代码 finish();
2. 活动之间的跳转🎯
2.1 活动间的跳转Intent
Intent是Android程序中各组件之间进行交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据。Intent一般可被用于启动活动、启动服务以及发送广播等场景。
Intent大致可以分为两种:显式Intent和隐式Intent。
2.1.1 显式使用
在FirstActivity的中按钮的点击事件中加入以下代码:
Intent intent=new Intent(FirstActivity.this,SecondActivity.class);
startActivity(intent);
第一个参数是FirstActivity.this作为上下文
第二个参数是SecondActivity这个活动
这样就会在点击按钮时运行这段代码启动SecondActivity活动
2.1.2 隐式使用
它并不明确指出我们想要启动哪一个活动,而是指定了一系列更为抽象的action和category等信息,然后交由系统去分析这个Intent,并帮我们找出合适的活动去启动。
- 在标签下配置的内容,可以指定当前活动能够响应的action和category
<intent-filter><action android:name="com.example.activitytest.ACTION_START" /><category android:name="android.intent.category.DEFAULT" /><category android:name="com.example.activitytest.MY_CATEGORY" />
</intent-filter>
这时候的FirstActivity中按钮的点击事件中的Intent 要这么写:
Intent intent=new Intent("com.example.activitytest.ACTION_START");
startActivity(intent);
注意:
- 这里的是在SecondActivity的标签里面哦,表示的是SecondActivity活动可以响应Intent中指定的action和category中的内容同时能够匹配上和标签里面的内容的Intent。
- SecondActivity的category 可以指定多个,匹配上一个就行,FirstActivity的Intent 可以不指定category ,这时会默认给一个DEFAULT的category ,但是如果想换其他的category 就需要指定。
Intent intent=new Intent("com.example.activitytest.ACTION_START");
intent.addCategory("com.example.activitytest.MY_CATEGORY");
startActivity(intent);
2.1.3 隐式Intent 的更多用法-跳转网页、拨打电话…
隐式Intent除了用于Activity跳转,还可以用于拨打电话、发送短信、访问网页、调用音乐播放器、调用视频播放器、发送邮件和打开地图等。
拨打电话:
//隐式Intent拨打电话
Intent intent=new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);
发送短信:
//隐式发送短信
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.putExtra("这里填写短信收信人", "这里填写短信内容");
intent.setType("vnd.android-dir/mms-sms");
startActivity(intent);
访问网页:
//隐式Intent访问网页
Intent intent=new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.baidu.com"));
startActivity(intent);
调用音乐播放器:
//调用音乐播放器
Intent intent = new Intent(Intent.ACTION_VIEW);
Uri uri = Uri.parse("file://sdcard/demo.mp3");
intent.setDataAndType(uri, "audio/mp3");
startActivity(intent);
调用视频播放器:
//调用视频播放器
Intent intent = new Intent(Intent.ACTION_VIEW);
Uri uri = Uri.parse("file://sdcard/demo.mp4");
intent.setDataAndType(uri, "video/mp4");
startActivity(intent);
发送邮件:
//发送邮件
Intent intent = new Intent(Intent.ACTION_SENDTO);
intent.setData(Uri.parse("mailto:xxx@gmail.com"));
intent.putExtra(Intent.EXTRA_SUBJECT, "这是标题");
intent.putExtra(Intent.EXTRA_TEXT, "这是内容");
startActivity(intent);
打开地图:
//打开地图查看某经纬度的地点(如:天安门)
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("geo:39.9073,116.3912"));
startActivity(intent);
2.2 向下一个活动传递数据 intent.putExtra
Intent中提供了一系列putExtra()方法的重载,可以把想要传递的数据暂存在Intent中,启动了另一个活动后,只需要把这些数据再从Intent中取出就可以了。
比如在FirstActivity中:
button1.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {String data="Hello SecondActivity";Intent intent= new Intent(FirstActivity.this,SecondActivity.class);intent.putExtra("extra data",data);startActivity(intent);}
});
使用显式Intent的方式来启动SecondActivity,并通过putExtra()方法传递了一个字符串。注意这里putExtra()方法接收两个参数,第一个参数是键,用于后面从Intent中取值,第二个参数才是真正要传递的数据。
然后在SecondActivity中将传递的数据取出,并打印出来,代码如下所示:
public class SecondActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.second_layout);Intent intent = getIntent();String data = intent.getStringExtra("extra data");Log.d("SecondActivity",data);}
注意:
- 如果传递的是整型数据,则使用getIntExtra()方法;如果传递的是布尔型数据,则使用getBooleanExtra()方法,以此类推。
2.3 返回数据给上一个活动ActivityResultLauncher 实例
这一部分的内容需要注意,书上给的例子是使用 startActivityForResult()但是在 Android 11(API 级别 30)及以后的版本中,方法 startActivityForResult(Intent, int) 已经被弃用。尽管它仍然可以使用,但为了确保兼容性和未来的应用程序发展,建议使用新的替代方案。现在,可以使用 ActivityResultLauncher API 来替代 startActivityForResult()。这个新的 API 提供了更灵活和现代的方式来处理活动结果。
- 声明 ActivityResultLauncher:
在FirstActivity中,声明一个 ActivityResultLauncher 对象。你需要指定你想使用的合同(例如,
ActivityResultContracts.StartActivityForResult())。
ActivityResultLauncher<Intent> myLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),result -> {if (result.getResultCode() == RESULT_OK) {// 处理结果Intent data = result.getData();// ...}}
);
- 启动活动:
现在,不再使用 startActivityForResult(),而是在 ActivityResultLauncher 实例上调用 launch() 方法来启动活动。下面的例子中我通过intent.putExtra传入了参数。
@Override
public void onClick(View view) {Intent intent = new Intent();intent.putExtra("data_return","Hello FirstActivity");setResult(RESULT_OK,intent);finish();
}
- 处理结果:
现在我要在FirstActivity提取我们传来的参数,只需要修改声明 ActivityResultLauncher时候的代码:
private final ActivityResultLauncher<Intent> myLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),result -> {if (result.getResultCode() == RESULT_OK) {Intent data = result.getData();//拿取传过来的数据if (data != null) {String returnedData = data.getStringExtra("data_return");if (returnedData != null) {// 弹出提示框AlertDialog.Builder builder = new AlertDialog.Builder(this);builder.setMessage(returnedData).setTitle("Returned Data").setPositiveButton("OK", null);AlertDialog dialog = builder.create();dialog.show();}}}}
);
通过String returnedData = data.getStringExtra(“data_return”);拿到我们的数据再进行处理。
补充:如果通过按下Back键返回呢
如果用户在SecondActivity中并不是通过点击按钮,而是通过按下Back键回到FirstActivity,我们可以通过在SecondActivity中重写onBackPressed()方法来处理返回的数据。
@Override
public void onBackPressed() {Intent intent = new Intent();intent.putExtra("data_return","Hello FirstActivity");setResult(RESULT_OK,intent);finish();
}
在用户点击Back键的时候就会调用这个函数,那么我们的intent.putExtra存进去的数据仍然可以传回去。
3. 活动的生命周期🎯
3.1 返回栈
- Android中的活动是可以层叠的。每启动一个新的活动,就会覆盖在原活动之上,然后点击Back键会销毁最上面的活动,下面的一个活动就会重新显示出来。
- Android是使用任务(Task)来管理活动的,一个任务就是一组存放在栈里的活动的集合,这个栈也被称作返回栈(Back Stack)。
- 系统总是会显示处于栈顶的活动给用户。
返回栈是如何管理活动入栈出栈操作的示意图:
3.2 活动状态
每个活动在其生命周期中最多可能会有4种状态。
- 运行状态
当一个活动位于返回栈的栈顶时,这时活动就处于运行状态。系统最不愿意回收的就是处于运行状态的活动,因为这会带来非常差的用户体验。 - 暂停状态
当一个活动不再处于栈顶位置,但仍然可见时,这时活动就进入了暂停状态。处于暂停状态的活动仍然是完全存活着的,系统也不愿意去回收这种活动(因为它还是可见的,回收可见的东西都会在用户体验方面有不好的影响),只有在内存极低的情况下,系统才会去考虑回收这种活动。 - 停止状态
当一个活动不再处于栈顶位置,并且完全不可见的时候,就进入了停止状态。系统仍然会为这种活动保存相应的状态和成员变量,但是这并不是完全可靠的,当其他地方需要内存时,处于停止状态的活动有可能会被系统回收。 - 销毁状态
当一个活动从返回栈中移除后就变成了销毁状态。系统会最倾向于回收处于这种状态的活动,从而保证手机的内存充足。
3.3 活动的生存期
Activity类中定义了7个回调方法,覆盖了活动生命周期的每一个环节,下面就来一一介绍这7个方法。
- onCreate():它会在活动第一次被创建的时候调用。你应该在这个方法中完成活动的初始化操作,比如说加载布局、绑定事件等。
- onStart():在活动由不可见变为可见的时候调用。
- onResume():在活动准备好和用户进行交互的时候调用。此时的活动一定位于返回栈的栈顶,并且处于运行状态。
- onPause():在系统准备去启动或者恢复另一个活动的时候调用。我们通常会在这个方法中将一些消耗CPU的资源释放掉,以及保存一些关键数据,但这个方法的执行速度一定要快,不然会影响到新的栈顶活动的使用。
- onStop():在活动完全不可见的时候调用。它和onPause()方法的主要区别在于,如果启动的新活动是一个对话框式的活动,那么onPause()方法会得到执行,而onStop()方法并不会执行。
- onDestroy():在活动被销毁之前调用,之后活动的状态将变为销毁状态。
- onRestart():在活动由停止状态变为运行状态之前调用,也就是活动被重新启动了。
以上7个方法中除了onRestart()方法,其他都是两两相对的,从而又可以将活动分为3种生存期。 - 完整生存期:活动在onCreate()方法和onDestroy()方法之间所经历的,就是完整生存期。一般情况下,一个活动会在onCreate()方法中完成各种初始化操作,而在onDestroy()方法中完成释放内存的操作。
- 可见生存期:活动在onStart()方法和onStop()方法之间所经历的,就是可见生存期。在可见生存期内,活动对于用户总是可见的,即便有可能无法和用户进行交互。我们可以通过这两个方法,合理地管理那些对用户可见的资源。比如在onStart()方法中对资源进行加载,而在onStop()方法中对资源进行释放,从而保证处于停止状态的活动不会占用过多内存。
- 前台生存期:活动在onResume()方法和onPause()方法之间所经历的就是前台生存期。在前台生存期内,活动总是处于运行状态的,此时的活动是可以和用户进行交互的,我们平时看到和接触最多的也就是这个状态下的活动。
3.4 活动回收后
想象以下场景:应用中有一个活动A,用户在活动A的基础上启动了活动B,活动A就进入了停止状态,这个时候由于系统内存不足,将活动A回收掉了,然后用户按下Back键返回活动A,会出现什么情况呢?
其实还是会正常显示活动A的,只不过这时并不会执行onRestart()方法,而是会执行活动A的onCreate()方法,因为活动A在这种情况下会被重新创建一次。这样看上去好像一切正常,可是别忽略了一个重要问题,活动A中是可能存在临时数据和状态的。打个比方,MainActivity中有一个文本输入框,现在你输入了一段文字,然后启动NormalActivity,这时MainActivity由于系统内存不足被回收掉,过了一会你又点击了Back键回到MainActivity,你会发现刚刚输入的文字全部都没了,因为MainActivity被重新创建了。因此,我们引入了可以在活动回收后保存数据的方法onSaveInstanceState()回调方法,这个方法可以保证在活动被回收之前一定会被调用,因此我们可以通过这个方法来解决活动被回收时临时数据得不到保存的问题。
- 重写onSaveInstanceState()方法
@Overrideprotected void onSaveInstanceState(Bundle outState) {super.onSaveInstanceState(outState);String tempData = "Something you just typed";outState.putString("data_key",tempData);}
- 在onCreate()方法中加入判断
@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);Log.d(TAG,"onCreate");setContentView(R.layout.activity_main);if (savedInstanceState!=null) {String tempData = savedInstanceState.getString("data_key");Log.d(TAG,tempData);}
不过这里注意一点:
onSaveInstanceState()方法,它不是生命周期方法,它不同于生命周期方法,它并不会一定会被触发,它只有具备以下条件的时候才会触发:
- 当按下HOME键的时
- 长按HOME键,选择运行程序的时
- 按下电源(关闭屏幕显示)时
- 从Activity中启动其他Activity时
- 屏幕方向切换时(例如从竖屏切换到横屏时)
如果想实现以上效果请使用屏幕方向切换(例如从竖屏切换到横屏时)
Intent还可以结合Bundle一起用于传递数据,首先可以把需要传递的数据都保存在Bundle对象中,然后再将Bundle对象存放在Intent里。到了目标活动之后先从Intent中取出Bundle,再从Bundle中一一取出数据。
4. 活动的启动模式🎯
活动的启动模式一共有4种,分别是standard、singleTop、singleTask和singleInstance,可以在AndroidManifest.xml中通过给标签指定android:launchMode属性来选择启动模式。
4.1 四种启动模式
- Standard(标准模式):
- 在这种模式下,每次启动活动时都会创建一个新的活动实例。
- 如果您多次启动同一活动,将会创建多个不同的实例,它们位于活动堆栈中的顶部。
- 这是默认的启动模式。
- SingleTop(单顶模式):
- 在这种模式下,如果新活动的实例已经位于栈的顶部,系统将重用该实例而不创建新的。
- 如果新活动不在栈的顶部,系统将创建新的实例并将其推入栈的顶部。
- 这允许在同一活动的不同任务之间共享实例,但在任务内仍然可以创建多个实例。
- SingleTask(单任务模式):
- 在这种模式下,对于同一个活动系统只允许一个活动实例存在于任务中。
- 如果新活动已经存在于任务中,系统将通过调用 onNewIntent() 方法来重新激活它,而不会创建新实例。
- 这通常用于定义应用的入口点,例如主界面。
- SingleInstance(单实例模式):
- 这是最特殊的启动模式,每个活动实例都会独立存在于自己的任务中。
- 即使是相同活动的不同实例也不会共享任务。
- 这通常用于具有独立性的组件,如电话应用中的来电界面。
每一个活动都可以单独设置启动模式,但是当有不同的启动模式的时候,会创建不同的返回栈,也可以叫做任务栈(Task Stack),此时活动退出时候的顺序和活动启动的顺序可能不一样。 - 当用户从一个活动退出时,优先返回到该活动所属返回栈的栈顶活动。
- 当该活动所属返回栈已经没有活动了时,会返回到还存在活动的返回栈,读取里面的活动
4.2 “任务”(Task)
在 Android 中,“任务”(Task)是一种管理应用活动(Activity)的方式。一个任务通常包含一个或多个活动,并用于组织和管理这些活动的生命周期。
- 任务栈(Task Stack):每个任务都有一个与之关联的活动栈,它是活动实例的有序堆栈。当您启动一个新的活动时,它将被推入栈的顶部。用户可以通过返回键按照栈的顺序导航回先前的活动。
- 任务的启动:任务可以从应用的入口点(通常是主活动)开始,并随着用户与应用的互动而增长。您可以使用不同的启动模式来定义新活动如何与任务中的现有活动实例进行交互。
- 任务之间的分离:不同任务的活动通常是相互分离的,它们不共享相同的任务栈。这意味着每个任务都有自己独立的活动堆栈。这在某些情况下很有用,例如使用 singleInstance 启动模式的活动。
- 任务的生命周期:任务具有自己的生命周期,当任务中的最后一个活动关闭时,整个任务将被销毁。这可以在某些情况下用于应用的清理和管理。
5. 活动的最佳实践技巧🎯
5.1 知晓当前是在哪一个活动
- 新建一个BaseActivity类,创建时选择一个普通的Java类,不需要在AndroidManifest.xml中注册。
- 让BaseActivity继承自AppCompatActivity,并重写onCreate()方法
public class BaseActivity extends AppCompatActivity {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);Log.d("BaseActivity",getClass().getSimpleName());}
- 让BaseActivity成为ActivityTest项目中所有活动的父类
现在每当我们进入到一个活动的界面,该活动的类名就会被打印出来,这样我们就可以时时刻刻知晓当前界面对应的是哪一个活动了。
5.2 随时随地退出程序
用一个专门的集合类对所有的活动进行管理就可以随时随地都能退出程序,而不需要一个一个注销和退出。
- 新建一个ActivityCollector类作为活动管理器
public class ActivityCollector {public static List<Activity> activities = new ArrayList<>();public static void addActivity(Activity activity){activities.add(activity);}public static void removeActivity(Activity activity){activities.remove(activity);}public static void finishAll(){for (Activity activity : activities) {if (!activity.isFinishing()){activity.finish();}activities.clear();}}
}
在活动管理器中,我们通过一个List来暂存活动,然后提供了一个addActivity()方法用于向List中添加一个活动,提供了一个removeActivity()方法用于从List中移除活动,最后提供了一个finishAll()方法用于将List中存储的活动全部销毁掉。
2. 修改BaseActivity中的代码
public class BaseActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);Log.d("BaseActivity",getClass().getSimpleName());ActivityCollector.addActivity(this);}@Overrideprotected void onDestroy() {super.onDestroy();ActivityCollector.removeActivity(this);}
}
在BaseActivity的onCreate()方法中调用了ActivityCollector的addActivity()方法,表明将当前正在创建的活动添加到活动管理器里。然后在BaseActivity中重写onDestroy()方法,并调用了ActivityCollector的removeActivity()方法,表明将一个马上要销毁的活动从活动管理器里移除。
从此以后,不管你想在什么地方退出程序,只需要调用ActivityCollector.finishAll()方法就可以了。例如在ThirdActivity界面想通过点击按钮直接退出程序:
Button button3 = (Button) findViewById(R.id.button_3);button3.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {ActivityCollector.finishAll();}});
当然你还可以在销毁所有活动的代码后面再加上杀掉当前进程的代码,以保证程序完全退出,杀掉进程的代码如下所示:
android.os.Process.killProcess(android.os.Process.myPid())
其中,killProcess()方法用于杀掉一个进程,它接收一个进程id参数,我们可以通过myPid()方法来获得当前程序的进程id。需要注意的是,killProcess()方法只能用于杀掉当前程序的进程,我们不能使用这个方法去杀掉其他程序。
5.3 启动活动的最佳写法
当我们启动一个活动需要两个特别重要的参数时,可以使用以下办法
假设SecondActivity中需要用到两个非常重要的字符串参数,在启动SecondActivity的时候必须要传递过来,
public class SecondActivity extends BaseActivity {public static void actionStart(Context context, String data1, String data2){Intent intent = new Intent (context,SecondActivity.class);intent.putExtra("param1",data1);intent.putExtra("param2",data2);context.startActivity(intent);}......
}
在SecondActivity中添加了一个actionStart()方法,在这个方法中完成了Intent的构建,另外所有SecondActivity中需要的数据都是通过actionStart()方法的参数传递过来的,然后把它们存储到Intent中,最后调用startActivity()方法启动SecondActivity。
这样写的好处就是SecondActivity所需要的数据在方法参数中全部体现出来了,这样即使不用阅读SecondActivity中的代码,不去询问负责编写SecondActivity的同事,你也可以非常清晰地知道启动SecondActivity需要传递哪些数据。另外,这样写还简化了启动活动的代码,现在只需要一行代码就可以启动SecondActivity,如下所示:
在FirstActivity中:
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);Log.d("FirstActivity","Task id is "+getTaskId());setContentView(R.layout.first_layout);Button button1=(Button) findViewById(R.id.button_1);button1.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {//Intent intent = new Intent(FirstActivity.this,SecondActivity.class);//startActivity(intent);SecondActivity.actionStart(FirstActivity.this,"data1","data2");}});
}
相关文章:
《第一行代码Andorid》阅读笔记-第一章
这篇文章是我自己的《第一行代码Andorid》的阅读笔记,虽然大量参考了别人已经写好的一些笔记和代码但是也有自己的提炼和新的问题在里面,我也会放上参考文章链接。 学习重点 Android系统的四大组件: (1)活动ÿ…...
Educational Codeforces Round 146 (Rated for Div. 2)(VP)
写个题解 A. Coins void solve(){ll n, k; cin >> n >> k;bl ok true;if (n &1 && k %2 0) ok false;print(ok ? yes : no); } B. Long Legs void solve(){db x, y; cin >> x >> y;if (x < y) swap(x, y);int t1 ceil(sqrt(x))…...
9.30国庆
消息队列完成进程间通信 #include <myhead.h>#define size sizeof(msg_ds)-sizeof(long) //正文大小//消息结构体 typedef struct {long msgtype; //消息类型char data[1024]; //消息正文 }msg_ds;//创建子线程构造体 void *task1(void *arg) {//创造第二个key值ke…...
java基础-第4章-面向对象(二)
一、static关键字 静态(static)可以修饰属性和方法。 称为静态属性(类属性)、静态方法(类方法)。 静态成员是全类所有对象共享的成员。 在全类中只有一份,不因创建多个对象而产生多份。 不必创…...
flex加 grid 布局笔记
<style> .flex-container { display: flex; height: 100%; /* 设置容器的高度 */ } .wide { display: flex; padding: 10px; border: 1px solid lightgray; text-align: center; justify-content: …...
最高评级!华为云CodeArts Board获信通院软件研发效能度量平台先进级认证
9月26日,华为云CodeArts Board获得了中国信通院《云上软件研发效能度量分级模型》的先进级最高级评估,达到了软件研发效能度量平台评估的通用效能度量能力、组织效能模型、项目效能模型、资源效能模型、个人效能模型、研发效能评价模型、项目管理域、开发…...
图像上传功能实现
一、后端 文件存放在images.path路径下 package com.like.common;import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annot…...
03_Node.js模块化开发
1 Node.js的基本使用 1.1 NPM nodejs安装完成后,会跟随着自动安装另外一个工具npm。 NPM的全称是Node Package Manager,是一个NodeJS包管理和分发工具,已经成为了非官方的发布Node模块(包)的标准。 2020年3月17日&…...
Nginx支持SNI证书,已经ssl_server_name的使用
整理了一些网上的资料,这里记录一下,供大家参考 什么是SNI? 传统的应用场景中,一台服务器对应一个IP地址,一个域名,使用一张包含了域名信息的证书。随着云计算技术的普及,在云中的虚拟机有了一…...
Hive【Hive(六)窗口函数】
窗口函数(window functions) 概述 定义 窗口函数能够为每行数据划分 一个窗口,然后对窗口范围内的数据进行计算,最后将计算结果返回给该行数据。 语法 窗口函数的语法主要包括 窗口 和 函数 两个部分。其中窗口用于定义计算范围…...
Met no ‘TRANSLATIONS’ entry in project
这里写自定义目录标题 问题描述:解决方法: 问题描述: 多工程项目,执行完update Translation生成了.ts文件,也用翻译工具翻译完了,执行release时,报错“Met no ‘TRANSLATIONS’ entry in proje…...
Leetcode901-股票价格跨度
一、前言 本题基于leetcode901股票价格趋势这道题,说一下通过java解决的一些方法。并且解释一下笔者写这道题之前的想法和一些自己遇到的错误。需要注意的是,该题最多调用 next 方法 10^4 次,一般出现该提示说明需要注意时间复杂度。 二、解决思路 ①…...
“传统文化宣传片+虚拟人动捕设备”前景如何?
在数字化时代的发展下,动捕设备的加入,让传播传统文化的虚拟人更具生动表现,拉近人们与传统文化的距离,通过虚拟人动作捕捉动画宣传片,引起更多人对传统文化的关注与传承。 *图片源于网络 深圳文博会创意短片《嗨ICIF…...
节假日moc服务数据:解决用户99%的IT问题
Hi~ 伙伴们,这个国庆假期过得怎么样? 节后第一个工作日如期而至, 忙碌是消除倦怠的最佳良药。 回顾8天假日moc工程师的一组服务数据, 处理事件184起,工单23条。 其中,较为典型案例如下: 1、福建某附属医院…...
WOL唤醒配置(以太网、PHY、MAC)
目录 wol 以太网 MAC PHY RMII 通信配置 总结 wol Wake-on-LAN简称WOL,WOL(网络唤醒) 是一种标准网络协议,它的功效在于让已经进入休眠状态或关机状态的计算机,透过局域网(多半为以太网ÿ…...
MySQL复制,约束条件,查询与安全控制
MySQL之复制 复制表 我有一个表 mysql> show tables; ------------------ | Tables_in_school | ------------------ | student | ------------------mysql> select * from student; -------------------------------------------- | id | name | sec |…...
Java ES 滚动查询
滚动查询(Scroll Query)是 Elasticsearch 提供的一种机制,用于处理大量数据的查询。它允许你在多个请求之间保持“游标”,以便在后续请求中获取更多的结果。 以下是滚动查询的基本工作原理: 1 初始查询: 客户端发送一…...
机器学习算法基础--KNN算法分类
文章目录 1.KNN算法原理介绍2.KNN分类决策原则3.KNN度量距离介绍3.1.闵可夫斯基距离3.2.曼哈顿距离3.3.欧式距离 4.KNN分类算法实现5.KNN分类算法效果6.参考文章与致谢 1.KNN算法原理介绍 KNN(K-Nearest Neighbor)工作原理: 在一个存在标签的…...
深入探究 C++ 编程中的资源泄漏问题
目录 1、GDI对象泄漏 1.1、何为GDI资源泄漏? 1.2、使用GDIView工具排查GDI对象泄漏 1.3、有时可能需要结合其他方法去排查 1.4、如何保证没有GDI对象泄漏? 2、进程句柄泄漏 2.1、何为进程句柄泄漏? 2.2、创建线程时的线程句柄泄漏 …...
BLE协议栈1-物理层PHY
从应届生开始做ble开发也差不读四个月的时间了,一直在在做上层的应用,对蓝牙协议栈没有过多的时间去了解,对整体的大方向概念一直是模糊的状态,在开发时也因此遇到了许多问题,趁有空去收集了一下资料来完成了本次专栏&…...
光伏储能直流系统MATLAB仿真(PV光伏阵列+Boost DCDC变换器+负载+双向DCDC变换器+锂离子电池系统)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
C++三大特性——继承(上篇)
文章目录 目录 一、继承的概念及定义 1.1继承的概念 1.2 继承定义 1.2.1定义格式 1.2.2继承关系和访问限定符 1.2.3继承基类成员访问方式的变化 二、基类和派生类对象赋值转换 三、继承中的作用域 四、派生类的默认成员函数 一、继承的概念及定义 1.1继承的概念 继承(inherita…...
docker系列(9) - docker-compose
文章目录 9. compose编排9.1 介绍9.2 安装9.3 compose常用命令9.4 实战Springboot部署9.4.1 准备组件配置文件9.4.1.1 redis的配置文件9.4.1.2 MySQL的配置文件9.4.1.3 SpringBoot打包文件 9.4.2 准备docker-compose.yml9.4.3 启动服务9.4.4 测试验证 9.5 实战ElasticsearchKib…...
Vue中如何进行日历展示与操作
在Vue中创建交互式日历应用 在Web开发中,创建一个交互式的日历应用是一项常见的任务。Vue.js作为一个流行的JavaScript框架,提供了许多便捷的工具和组件来简化日历的开发。本文将介绍如何使用Vue来创建一个简单但功能强大的日历应用,包括展示…...
SpringBoot 返回图片、Excel、音视频等流数据几种处理方式
方式一:直接针对响应对象(response)实现 @RestController @Slf4j @Api(tags = SwaggerConfig.TAG_IMAGE) @RequestMapping(SwaggerConfig.TAG_IMAGE) public class ImageController {@GetMapping(value = "/getImage")@ApiOperation("获取图片-以ImageIO流形…...
【Vue面试题一】、说说你对 Vue 的理解
文章底部有个人公众号:热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享? 踩过的坑没必要让别人在再踩,自己复盘也能加深记忆。利己利人、所谓双赢。 面试官:有使用过vue吗ÿ…...
vue3 axios
npm install axios import axios from axios // 创建axios实例 const request axios.create({baseURL: ,// 所有的请求地址前缀部分(没有后端请求不用写)timeout: 80000, // 请求超时时间(毫秒)withCredentials: true,// 异步请求携带cookie// headers: {// 设置后端需要的传…...
划片机:半导体生产的必备设备
划片机是半导体加工行业中的重要设备,主要用于将晶圆切割成晶片颗粒,为后道工序粘片做好准备。随着国内半导体生产能力的提高,划片机市场的需求也在逐渐增加。 在市场定位上,划片机可以应用于半导体芯片和其他微电子器件的制造过程…...
电路维修——双端队列BFS
达达是来自异世界的魔女,她在漫无目的地四处漂流的时候,遇到了善良的少女翰翰,从而被收留在地球上。 翰翰的家里有一辆飞行车。有一天飞行车的电路板突然出现了故障,导致无法启动。电路板的整体结构是一个 R 行 C 列的网格&#…...
乌班图22.04 kubeadm简单搭建k8s集群
1. 我遇到的问题 任何部署类问题实际上对于萌新来说都不算简单,因为没有经验,这里我简单将部署的步骤和想法给大家讲述一下 2. 简单安装步骤 准备 3台标准安装的乌班图server22.04(采用vm虚拟机安装,ip为192.168.50.3࿰…...
微信音乐做mp3下载网站/网络营销的八种方式
THCTERMINAL HANDLING CHARGE,碼頭操作費转载于:https://www.cnblogs.com/DTWolf/p/4681950.html...
深圳官方网站建设/优化大师网页版
$(.xxx).data(width,30%); 内容最少的一篇博客,嗯。...
wordpress底部制作/重庆seo排名扣费
原因:没有导入servlet-api.jar和jsp-spi.jar;解决方法:点击File--->Project Structure----->Modules点击左边的绿色“”按钮,选第一个JARs or directories...然后找到tomcat目录下的lib目录下的servlet-api.jar和jsp-spi.ja…...
设计网网站/外贸网站建站和推广
1 集合 集合是一个无序的,不重复的数据组合,它的主要作用如下: 去重,把一个列表变成集合,就自动去重了关系测试,测试两组数据之前的交集、差集、并集等关系创建集合 a {3, 5, 9, 9, 10} print(a) b ([1, …...
滨江区做网站公司/网站seo搜索
刚刚接触swift以及ios,不是很理解有的逻辑,导致某些问题。这里分享一下swift自定义uicollectionviewcell 首先我的viewcontroller不是直接继承uicollectionviewcontroller,而是添加的uicollectionview到我的storyboard, 然后再新建…...
深圳商城网站制作/短链接
为何要使用同步? java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查),将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前被…...