Android02——Activity

摘要:
活动目录活动创建空白活动和布局创建活动设置布局在活动中加载布局AndroidManifest文件在活动中注册使用toast菜单销毁活动意图显式意图隐式意图其他意图用途将数据传输到下一个活动将数据返回到上一个活动[不理解]活动的生命周期返回堆栈回收缓存数据的活动生存期的状态?加载布局publicclassSecondActivityextendsAppCompatActivity{@OverrideprotectedvotionCreate{super.onCreate;setContentView;}}在活动中,添加到项目中的任何资源都将在R文件中生成相应的资源id,因此我们刚才创建的第二个资源就是layout。现在应该已经将xml布局的ID添加到R文件中。在SDK版本23及更高版本上,您的说唱数据将自动备份并存储在安装中。考虑到分布式机器人:fullBackupContenttospecifyan@xmlresourcewhichconfigureswhichfilestobackup.设置为命令中的主程序。
Activity

目录

创建空白activity和layout

Android02——Activity第1张

创建活动

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: 
    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: 
        android:title="Add"/>
        <item android: 
            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是可以和用户进行交互的。

Android02——Activity第2张

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);
            }
        });

    }

Android02——Activity第3张

singleTop

实际上在有些情况下,standard并不合理。activity明明已经在栈顶了,为什么再次启动的时候还要创建一个新的activity实例呢?

所以,可以根据自己的需求修改启动模式,比如singleTop,在启动activity时如果发现返回栈的栈顶已经是该activity,则认为可以直接使用他,不会再创建新的activity。

设置方法是在manifest.xml

        <activity android:name=".NormalActivity"
            android:launchMode="singleTop" />

这个时候对于栈顶的Activity就只会创建一个实例,对于button这种intent自己调用自己的行为,就不会再创建新的实例了,这个时候也只需要按一下back就可以返回了。

Android02——Activity第4张

singleTask

使用singleTop模式可以很好地解决重复创建栈顶activity的问题,而对于没有处于栈顶的实例还是会创建多个。而当activity的启动模式指定为singleTask,每次启动该activity时,系统首先会在返回栈中检测是否存在该activity的实例,如果发现已经存在则直接使用该实例,并把在这个activity之上的所有其他activity统统出栈,如果没有发现就会创建一个新的activity实例。

Android02——Activity第5张

singleInstance【没懂】

singleInstance模式应该算是4种启动模式中最特殊最复杂的一个了。不同于以上三种模式,指定为singleInstance模式的activity会启动一个新的返回栈来管理这个activity(其实如果singleTask模式下指定了不同的taskAffinity,也会启动一个新的返回栈)。这样做的意义是为了解决多个应用程序共享activity实例的问题。

Android02——Activity第6张

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);
    }
}

免责声明:文章转载自《Android02——Activity》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇react.js antd-table 可编辑表格验证redux项目实战应用笔录下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

安卓设置按钮监听的四种方式

这里的布局文件就很简单。只是一个简单的按钮。ID为button1。 第一种: 1.找到按钮的Id. Button button= (Button) findViewById(R.id.button1); button.setOnClickListener(this);但this指的是当前的activity,会报错。ALT+enter键实现View.OnCl...

屏蔽微信内置底部前进后退按钮(很迫切的需求)

在使用window.location.href进行页面跳转或者react内部项目使用 this.props.history.push进行页面跳转时,做好的h5页面放在微信里,底部会出选前进后退的按钮,如下图: 废话不多说,解决问题: 第一种情况:如果只是页面之间跳转,使用window.location.replace 代替window.location.h...

从零开始配置TypeScript + React + React-Router + Redux + Webpack开发环境

转载请注明出处! 说在前面的话: 1、为什么不使用现成的脚手架?脚手架配置的东西太多太重了,一股脑全塞给你,我只想先用一些我能懂的库和插件,然后慢慢的添加其他的。而且自己从零开始配置也能学到更多的东西不是么。 2、教程只配置了开发环境,并没有配置生产环境。 3、教程针对人群是有过React + Redux经验,并且想在新项目中使用TypeScript的人(...

uniapp获取用户信息 getuserinfo

<button type="primary" open-type="getUserInfo" @getuserinfo="wxGetUserInfo">登录</button> wxGetUserInfo(){ uni.getUserInfo({ success:...

广播、应用Android BroadcastReceiver(一)by小雨

每日一贴,今天的内容关键字为广播、应用- Android BroadcastReceiver 分析: broadcastReceiver是android的四大件组之一,大部分的广播是统系收回来的。例如,屏幕闭关,电池电量缺乏等等。应用一样可以建创广播,例如:当下载成完的时候,要让其他的应用道知这个情况,要需用到broadcastreceiver,recei...

Bootstrap-CL:字体图标(Glyphicons)

ylbtech-Bootstrap-CL:字体图标(Glyphicons) 1.返回顶部 1、 Bootstrap 字体图标(Glyphicons)本章将讲解字体图标(Glyphicons),并通过一些实例了解它的使用。Bootstrap 捆绑了 200 多种字体格式的字形。首先让我们先来理解一下什么是字体图标。 什么是字体图标? 字体图标是在 Web...