Android02——Activity
Activity
创建空白activity和layout
创建活动
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
设置布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/button_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="1"/>
</LinearLayout>
如果需要在xml中引用一个id就使用@id/id_name
这种语法。而如果你需要在xml中定义一个id则需要使用@+id/id_name
这种语法。
- 加+表示定义
- 不加+表示引用
match_parent
表示当前元素和父元素一样宽。
wrap_content
表示当前元素的高度只要能刚好包含里面的内容就行。
在活动中加载布局
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sencond_layout);
}
}
项目中添加任何资源都会在R文件中生成一个相应的资源id,因此我们刚才创建的sencond_layout.xml布局的id现在应该是已经添加到R文件中了。
AndroidManfiest文件中注册
On SDK version 23 and up, your app data will be automatically backed up and restored on app install. Consider adding the attribute android:fullBackupContent to specify an @xml resource which configures which files to backup.
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
在manfiest中设置为主程序。
在活动中使用toast
toast是Android系统中一种消dao息框类型,系统自带Toast采用的是队列的方式, 等当前Toast消失后, 下一个Toast才能显示出来;原因是Toast的管理是在队列中,点击一次,就会产生一个新的Toast,要等这个队列中的Toast处理完,这个显示Toast的任务才算结束。 so~ 我们可以把Toast改成单例模式,没有Toast再新建它,这样也就解决了连续点击Toast,一直在显示的问题。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sencond_layout);
Button button1 =(Button) findViewById(R.id.button_1);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(SecondActivity.this, "You click button 1", Toast.LENGTH_SHORT).show();
}
});
}
首先通过findViewById()方法获取布局文件中定义的元素,返回的是一个View对象。然后重写button的setOnCLickListener接口的方法。这其实是在为按钮注册一个监听器。
Toast用于很简单:直接通过静态方法makeTest()
传入三个参数,上下文,toast显示的文本内容和显示时长。
Menu
-
首先在res中创建一个menu文件夹
-
在menu文件夹中创建一个空menu的xml(可以叫main.xml)
-
在xml中定义添加(@+id)两个item
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/add_item" android:title="Add"/> <item android:id="@+id/remove_item" android:title="Remove"/> </menu>
-
在主活动中显示出这个memu
@Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main,menu); return super.onCreateOptionsMenu(menu); }
-
同时在这个活动类中重写选择方法:
@Override public boolean onOptionsItemSelected(@NonNull MenuItem item) { switch (item.getItemId()) { case R.id.add_item: Toast.makeText(this, " You click add", Toast.LENGTH_SHORT).show(); break; case R.id.remove_item: Toast.makeText(this, "You click remove", Toast.LENGTH_SHORT).show(); break; default: } // return super.onOptionsItemSelected(item); return true; }
销毁一个活动
通常销毁一个活动的方法就是按back键。当然你也可以通过代码来销毁活动。=>Activity类提供了一个finish()方法。
Intent
intent大致可以分为两种:显式Intent和隐式Intent
显式intent
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(SecondActivity.this, "You click button 1", Toast.LENGTH_SHORT).show();
Intent intent = new Intent(SecondActivity.this,FirstActivity.class);
startActivity(intent);
// finish(); // 自动销毁
}
});
当点击按钮1,则会切换到第二个activity去。切换方法是通过创建一个Intent对象,两个参数, 当前活动上下文(content)和目标活动。
隐式intent
通过更为抽象的action和category等信息,让系统自己分析这个intent并帮助我们找出合适的activity去启动。
打开Androidmanifest.xml添加如下代码:
<activity android:name=".FirstActivity">
<intent-filter>
<action android:name="com.ssozh.activitytest.ACTION_START"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
其中的intent-filter 添加了action和category的tag。其中category是默认的。
Intent intent = new Intent("com.ssozh.activitytest.ACTION_START");
// 在调用下面方法的时候会自动将android.intent.category.DEFAULT这个category添加到intent中。
startActivity(intent);
可以看到Intent绑定了action找到了Androidmanifest.xml中相同的activity。同时 startActivity会自动将category添加到这个intent中。
上面的代码表明,我们想要启动能够响应com.ssozh.activitytest.ACTION_START
这个action的活动。
而如果想给intent提供一个具体的category,则通过对象方法addCategory
来实现。
intent.addCategory("com.ssozh.activitystest.MY_CATEGORY");
同时添加到Androidmanifest.xml中:
<intent-filter>
<action android:name="com.ssozh.activitytest.ACTION_START"/>
<!-- 可以添加多个category?-->
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="com.ssozh.activitystest.MY_CATEGORY"/>
</intent-filter>
其他intent用法
使用隐式intent不仅可以启动自己程序的activity,还可以启动其他程序的活动,这使得Android多个app之间的功能共享称为了可能。比如你想展示一个网页:
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(FirstActivity.this,"打开网页",Toast.LENGTH_SHORT).show();
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.baidu.com"));
startActivity(intent);
}
});
首先指定了action的是Intent.ACTION_VIEW,这个是Android系统内置的动作,其常量值为Android.intent.action.VIEW。然后通过Uri.parse方法,将网址字符串解析成一个uri对象,再调用intent的setData()方法将这个uri对象传递出去。
setData()方法是接收一个uri对象,主要用于指定当前intent正在操作的数据,而这些数据通常都是以字符串的形式传入到URI.parse()方法中去解析的。
与此相对应,还可以在<intent-filter>
tag中再配置一个<data>
tag,更加精确的指定当前活动能够响应什么类型的数据。其可以配置以下属性:
- android:scheme。协议,除了http,还有geo表示地理位置、tel表示拨打电话。
- androd:host。主机名
- android:port。端口
- android:path。路径
- Android:mineType:用于指定可以处理的数据类型,允许使用通配符的形式进行指定。
public void onClick(View v) {
Toast.makeText(FirstActivity.this,"打开网页",Toast.LENGTH_SHORT).show();
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:10068"));
startActivity(intent);
}
});
首先指定了intent的action是ACTION_DIAL,这又是一个Android系统的内置动作。然后在data部分指定了协议是tel,号码是10086。
向下一个活动传递数据
Intent提供了一个方法putExtra(),可以吧我们想要传递的数据暂存在Intent中,启动了另一个活动后,只需要把这些数据再从Intent中取出来就可以了。
@Override
public void onClick(View v) {
Toast.makeText(SecondActivity.this, "You click button 1", Toast.LENGTH_SHORT).show();
// 显式调用activity
// Intent intent = new Intent(SecondActivity.this,FirstActivity.class);
Intent intent = new Intent("com.ssozh.activitytest.ACTION_START");
intent.addCategory("com.ssozh.activitystest.MY_CATEGORY");
// 把Second 传递给First
String data = "second Activity send data to First One.";
intent.putExtra("Extra Data",data);
// 在调用下面方法的时候会自动将android.intent.category.DEFAULT这个category添加到intent中。
startActivity(intent);
// finish(); // 自动销毁
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
Button button2 =(Button) findViewById(R.id.button_2);
// 获取从Second Activity 传递过来的data并使用toast显示出来
Intent intent = getIntent();
String data = intent.getStringExtra("Extra Data");
Log.d("FirstActivity",data);
Toast.makeText(FirstActivity.this, data,Toast.LENGTH_SHORT).show();
}
首先可以通过getIntent()方法获取到用于启动SecondActivity的Intent,然后调用getStringExtra()方法,根据key找到value即可。而如果传递是的整形数据,则使用getIntExtra()方法,以此类推。
返回数据给上一个活动[没懂]
很显然 传递给下一个活动是通过intent的。而返回给上一个活动则是通过back来完成的。
但是activity中还有一个startActivityForResult()方法。他接受两个参数 第一个是intent第二个是请求吗,用于在之后的回调中判断数据的来源。我们还是来实战一下,修改FirstActivity中按钮的点击事件,代码如下所示:
activity的生命周期
返回栈
Android是使用task来管理activity的,一个task就是一组存放在栈里的活动的集合,这个栈也被称作返回栈(Back Stack)。如果我们启动一个新的活动,那么他就会在返回栈中入栈,而当我们按下Back或者finish的时候,处于栈顶的activity就会出栈。
活动的状态
每个活动在其生命周期中最多可能会有4个状态。
- 运行状态:位于栈顶的activity。系统最不愿意回收的就是运行状态的activity。因为这会给用户带来非常差的体验
- 暂停状态:当一个活动不再位于栈顶位置,但仍然可见时,这时活动就进入了暂停状态。比如 一个dialog站在栈顶,因为可见,系统是不愿意回收的
- 停止状态:当一个活动不再处于栈顶位置,并且完全不可见的时候,就进入了停止状态。系统仍然会为这种活动保存相应的状态和成员变量,但是不可靠,可能会被系统回收。
- 销毁状态:当一个活动从返回栈中移除后就变成了销毁状态。系统会最倾向于回收处于这种状态的活动,从而保证手机内存充足。
活动的生存期
activity类定义了7个回调方法,覆盖了生命周期的每个环节:
- onCreate():活动第一次被创建时调用。这个方法完成初始化操作,包括加载布局、绑定事件等等。
- onStart():这个方法由不可见变成可见的时候调用。
- onResume():这个方法在活动准备好和用户进行交互的时候调用。【一定位于栈顶】
- onPause():这个方法在系统准备去启动或者回复另外一个activity的时候调用。【这个方法执行一定要快、不然会影响新的activity的使用】
- onStop():这个方法在activity完全不可见的时候调用。和上面的主要区别在于,如果新的activity是一个对话框式的activity,那么onPause方法会执行,而onStop不会。
- onDestroy():销毁前调用,之后activity变成销毁状态
- onRestart():这个方法在activity由停止状态变为运行状态之前调用,也就是activity被重启了。
以上7个方法除了onRestart之外,其他都是两两相对的,这样又可以将activity分为以下是3种生存期:
- 完整生存期。Activity在onCreate和onDestory之间经历的。其中包含初始化和释放内存的操作。
- 可见生存期。Activity在onstart和onStop之间经历的。对用户总是可见的,
- 前台生存期。在onResume和onPause之间经历的。activity是可以和用户进行交互的。
Activity被回收缓存数据怎么办?
存在这么一种情况,现在有两个activityA 和B,当在A的基础上启动了B,A进入了停止状态,这个时候如果系统内存不足,就会回收A,然后用户按下back键返回了A,其实还是会正常显示A的,只不过这时并不会执行onRestart而是执行onCreate方法,因为A在这种情况下会被重新创建一次。
这里有一个重要问题:Activity A中是可能存在临时数据和状态的。所以我们必须解决onCreate缺失缓存数据的问题。因此Activity中提供了一个onSaveInstanceState()回调方法,这个方法可以保证在activity被回收之前一定会被调用,因此可以通过这个方法来解决这个问题。
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
String tempData = "Something you just typed";
outState.putString("dataKey", tempData);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG,"onCreate");
setContentView(R.layout.activity_main);
/**
* 假设MainActivity被回收
* 则通过savedInstanceState判断有没有缓存数据
*/
if(savedInstanceState!=null){
String tempData = savedInstanceState.getString("data_key");
Log.d(TAG,tempData);
}
// ...
}
很容易发现Bundle保存和取出数据和Intent十分相似。另外,Intent可以结合Bundle一起用于传递数据。首先可以把需要传递的数据都保存在Bundle对象中,然后再将Bundle对象存放在Intent里。到了目标Activity之后,先从Intent中取出Bundle,再从Bundle,再从Bundle中一一取出数据。
另外,当手机的屏幕发生旋转的死火海,Activity也会经历一个重新粗行间的过程,因而在这种情况下,activity中的数据也会丢失。但是存在更加优雅的解决放哪,在13.2学习。
Activity的启动模式
启动模式是一个全新的概念,包括四种:standard、singleTop、singleTask、和singleInstance,可以在AndroidManifest.xml中给<activity>
tag指定android:lauchMode
属性来选择启动模式。
standard
standard是默认启动模式。在standard模式下,每当启动一个新的activity,他就会在返回栈中入栈,并处于栈顶的位置。对于使用standard模式的activity,系统不会在乎这个activity是否已经在返回栈中存在,每次启动都会创建一个该activity的新实例。因此,你点了很多button后,如果退出则需要按很多back。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, this.toString());
setContentView(R.layout.activity_normal);
Button button1 =(Button) findViewById(R.id.button1);
// set的时候 new一个Listener
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// NormalActivity的基础上启动NormalActivity。
Intent intent = new Intent(NormalActivity.this, NormalActivity.class);
startActivity(intent);
}
});
}
singleTop
实际上在有些情况下,standard并不合理。activity明明已经在栈顶了,为什么再次启动的时候还要创建一个新的activity实例呢?
所以,可以根据自己的需求修改启动模式,比如singleTop,在启动activity时如果发现返回栈的栈顶已经是该activity,则认为可以直接使用他,不会再创建新的activity。
设置方法是在manifest.xml
<activity android:name=".NormalActivity"
android:launchMode="singleTop" />
这个时候对于栈顶的Activity就只会创建一个实例,对于button这种intent自己调用自己的行为,就不会再创建新的实例了,这个时候也只需要按一下back就可以返回了。
singleTask
使用singleTop模式可以很好地解决重复创建栈顶activity的问题,而对于没有处于栈顶的实例还是会创建多个。而当activity的启动模式指定为singleTask,每次启动该activity时,系统首先会在返回栈中检测是否存在该activity的实例,如果发现已经存在则直接使用该实例,并把在这个activity之上的所有其他activity统统出栈,如果没有发现就会创建一个新的activity实例。
singleInstance【没懂】
singleInstance模式应该算是4种启动模式中最特殊最复杂的一个了。不同于以上三种模式,指定为singleInstance模式的activity会启动一个新的返回栈来管理这个activity(其实如果singleTask模式下指定了不同的taskAffinity,也会启动一个新的返回栈)。这样做的意义是为了解决多个应用程序共享activity实例的问题。
Activity的最佳实践
知晓当前是在哪个活动
创建一个BaseActivity,然后让所有的activity继承这个BaseActivity。同时修改BaseActiivity的代码如下:
/**
* 1. 直接创建一个java class 然后继承extends AppCompatActivity
* 2. 打印getSimpleName
* 3. 得知当前运行的activity
*/
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("BaseActivity",getClass().getSimpleName());
}
}
随时随地退出程序
只需要一个专用的集合对所有的activity进行管理就可以了。
新建一个单例类,ActivityController作为活动管理器,代码如下:
import android.app.Activity;
import java.util.ArrayList;
import java.util.List;
/**
* 添加一个Android工具类,用于管理活动, 注意这个是用于管理Android的工具类,
* 虽然是个java程序但是必须创建在app中而不是javalib中。
* 另外很显然这个类应该是个单例模式
* 双重检测的单例模式
*/
public class ActivityController {
private static volatile ActivityController singleton;
private final List<Activity> activityList;
public static ActivityController createActivityController(){
if(singleton == null){
synchronized (ActivityController.class){
if(singleton == null){
singleton = new ActivityController();
}
}
}
return singleton;
}
private ActivityController(){
activityList = new ArrayList<>();
}
public void addActivity(Activity activity){
activityList.add(activity);
}
public void removeActivity(Activity activity){
activityList.remove(activity);
}
public void finishAll(){
for(Activity activity:activityList){
if(!activity.isFinishing()){
activity.finish();
}
}
}
}
启动activity的最佳写法
启动activity的方法具体步骤:首先通过Intent构建出当前的"意图",然后调用startActivity或startActivityForResult方法将activity启动起来,如果有数据需要在activity之间传递,也可以借助Intent。而如果直接在onCreate中写如下代码:
Intent intent = new Intent(context, SecondActivity.class);
intent.putExtra("param1",data1);
intent.putExtra("param2",data2);
startActivity(intent);
实际上,这样写会在对接的时候出现问题,我们应该对其进行封装成静态方法,用于说明启动活动需要的数据:
/**
*
* 这个时候别人调用,就可以直接通过
* NormalActivity.actionStart(context,param1,param2)直接启动本activity了
*/
public static void actionStart(Context context, String data1, String data2){
Intent intent = new Intent(context,NormalActivity.class);
intent.putExtra("param1",data1);
intent.putExtra("param2",data2);
context.startActivity(intent);
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
2019-11-13 Pycharm远程Linux显示图片