《第一行代码》读书笔记(一)----活动

基本用法

有点意思

Android 5.0系统中, Android Studio开发环境下. 新建一个活动继承自Activity, 运行是没有标题栏的. 但默认继承的是ActionBarActivity, 运行的话是有标题栏的. ActionBarActivity是过时的API….

在活动中使用Toast:

Toast.makeText(FirstActivity.this, "You clicked Button 1", Toast.LENGTH_SHORT).show();

静态方法makeText(), 三个参数.

用完不要忘记调用show()方法.

在活动中使用Memu

  • 新建menu文件.
        <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>
  • 在活动中重写onCreateOptionsMenu()方法.
        public boolean onCreateOptionsMenu(Menu menu) {
             getMenuInflater().inflate(R.menu.main, menu);
             return true;
        }
  • 定义菜单响应事件. 重写onOptionsItemSelected()方法.
        public boolean onOptionsItemSelected(MenuItem item) {
            switch (item.getItemId()) {
             case R.id.add_item:
                 Toast.makeText(this, "You clicked Add", Toast.LENGTH_SHORT).show();
                 break;
            case R.id.remove_item:
                Toast.makeText(this, "You clicked Remove", Toast.LENGTH_SHORT).show();
                break;
             default:
            }
            return true;
        }

注意:在5.0以下版本中, 继承自Activity, 这样写的话按menu键没有问题.

但是在5.0以上版本中, 该活动只有继承自ActionBarActivity, 在上面的ActionBar中才会有menu菜单. 按底部三个键右边那个会让你选择哪个应用.
这就是5.0以上系统的不同之处吧.

  • 销毁一个活动:

finish()方法.

效果等同于按下Back键.

使用Intent

使用显式Intent

startActivity(new Intent(FirstActivity.this, SecondActivity.class));

这种”意图”非常明显的方式, 就是显式.

使用隐式Intent

不明确指定要启动哪个活动, 而是指定抽象的action category等信息, 让系统去分析该启动哪个活动.

am文件中给想要启动的活动添加intent-filter:

    <intent-filter>
        <action android:name="me.zipstream.activity.ACTION_START" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>

可以指定action和category等信息.
注意: category一项, 系统提示时要注意别选成全是大写的那个.前面三个词都是小写.不然会报错. android.intent.category.DEFAULT表示默认的category, 调用startActivity()方法时会自动添加到Intent中.如果不是DEFAULT, 就需要手工指定了.

    Intent intent = new Intent("me.zipstream.activity.ACTION_START");
    startActivity(intent);

这就是隐式Intent的使用方式.
要求和中内容同时匹配Intent中指定的相应信息.才能正确响应.

如果am文件中的category除了默认的还有别的, 怎么在Intent中添加呢?
比如<category android:name="me.zipstream.activity.MY_CATEGORY" />
这时在隐式使用Intent时,

    Intent intent = new Intent("me.zipstream.activity.ACTION_START");
    intent.addCategory("me.zipstream.activity.MY_CATEGORY");
    startActivity(intent);

隐式Intent的更多用法

隐式Intent还可以用于启动其他程序的活动. 即实现多个应用程序之间的功能共享.
比如调用系统浏览器打开某个网页:

    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setData(Uri.parse("http://www.baidu.com"));
    startActivity(intent);

其中Intent.ACTION_VIEW是系统内置常量, 其值为android.intent.action.VIEW
setData()方法其实和addCategory()差不多.也就是说, 在标签中可以配置一个标签, 用于更精确的指定当前活动能够响应什么类型的数据.标签中可以配置以下内容:

1.android:scheme
    指定数据协议部分, 如http
2. android:host
    指定数据的主机名部分, 如www.baidu.com
3. android:port
    指定数据的端口部分
4. android:path
    指定主机名和端口之后的部分, 如一段网址中跟在域名之后的内容.
5. android:mimeType
    指定可以处理的数据类型, 允许使用通配符的方式进行指定.

同样, 只有标签中指定的内容和Intent携带的Data完全一致时, 当前活动才可以响应该Intent.

比如第三方浏览器的实现原理, 就是在am文件中这么注册活动的:

    <activity android:name=".ThirdActivity" >
        <intent-filter>
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:scheme="http" />
        </intent-filter>
    </activity>

再比如调用系统拨号盘:

    Intent intent = new Intent(Intent.ACTION_DIAL);
    intent.setData(Uri.parse("tel:10086"));
    startActivity(intent);

向下一个活动传递数据

Intent中提供了一系列putExtra()方法的重载, 把要传递的数据暂存在Intent中, 只需要在下一个活动中从Intent中取出就可以了. 数据是以键值对的形式存放的. 取出数据也很简单, 首先调用getIntent()方法得到Intent对象, 再调用Intent的get[type]Extra()方法即可取出.

比如, 一个活动向下一个活动传递数据:

    String data = "Hello SecondActivity";
    Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
    intent.putExtra("extra_data", data);
    startActivity(intent);

下一个活动取出数据:

    Intent intent = getIntent();
    String data = intent.getStringExtra("extra_data");
    Log.d("SecondActivity", data);

返回数据给上一个活动

安卓中返回上一个活动只需按一下Back键就可以了, 没有一个用于启动活动的Intent来传递数据. 在API中, Activity提供了一个startActivityForResult()方法, 用来实现这个需求. 该方法接受两个参数, 一个是Intent, 另一个是int类型的请求码, 只要是一个唯一值就可以了.

在被启动的活动中, 通过setResult()方法向上返回数据, 该方法接受两个参数, 第一个参数用于向上一个活动返回处理结果, 一般只使用RESULT_OKRESULT_CANCELED这两个值. 第二个参数是Intent对象, 用于把带有的数据传回去.

数据返回是返回了, 但是上一个活动怎么接受这个数据呢? 由于在一个返回数据的活动被销毁之后会回调上一个活动的onActivityResult()方法, 因此重写当前活动的onActivityResult()方法. 这个方法接受3个参数(int requestCode, int resultCode, Intent data), 第一个参数是启动活动时传入的请求码, 第二个参数是返回数据时传入的处理结果, 第三个参数是携带者返回数据的Intent. 方法体中通过swtich(requestCode)来确定哪一个活动返回来的数据(requestCode唯一).

startActivityForResult: 字面理解, “为了得到数据而启动活动”…
onActivityResult: 字面理解, “在活动得到返回数据时调用, 在方法体里处理逻辑…”

启动下一个活动, 要求返回数据:

    Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
    startActivityForResult(intent, 1);

在下一个活动中返回数据:

    Intent intent = new Intent();
    intent.putExtra("data_return", "Hello FirstActivity");
    setResult(RESULT_OK, intent);
    finish();

接受返回的数据:

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            case 1:
                if (resultCode == RESULT_OK) {
                    String returnData = data.getStringExtra("data_return");
                    Log.d("FirstActivity", returnData);
                }
                break;
            default:
                break;
        }
    }

如果用户在返回上一个活动时, 是以按下Back键来返回的, 就需要重写onBackPressed()方法来解决:

    @Override
    public void onBackPressed() {
        Intent intent = new Intent();
        intent.putExtra("data_return", "Hello FirstActivity");
        setResult(RESULT_OK, intent);
        finish();
    }

活动的生命周期

返回栈

Android使用任务(Task)来管理活动. 一个任务就是一组存放在栈里的活动的集合, 这个栈也叫返回栈.

系统总是会显示栈顶的活动给用户.

活动的生存期

七个回调方法, 覆盖了活动生命周期的每一个环节.

  • onCreate()

    • 活动第一次创建时调用. 在此方法里完成初始化操作, 例如加载布局\绑定事件等.
  • onStart()

    • 活动由不可见变为可见的时候调用.
  • onResume()

    • 活动准备好和用户进行交互时候调用. 处于栈顶状态.
  • onPause()

    • 系统准备启动或者恢复另一个活动时候调用. 通常在此方法中将一些消耗CPU的资源释放, 以及保存一些关键数据. 这个方法的执行速度必须要快.
  • onStop()

    • 活动完全不可见的时候调用. 与onPause()的区别: 如果启动的新活动是一个对话框式的, 则onPause()方法执行, onStop()方法不执行.
  • onDestroy()

    • 活动被销毁之前调用.
  • onRestart()

    • 活动由停止状态变为运行状态之前调用. 即活动被重新启动了.

根据七个方法, 活动可分为三个生存期:

  1. 完整生存期: onCreate()和onDestroy()之间.
  2. 可见生存期: onStart()和onStop()之间.
  3. 前台生存期: onResume()和onPause()之间.

一张图说明活动的生命周期:

体验活动的生命周期

创建一个项目, 主活动的布局时两个按钮, 用于切换到子活动. 创建两个子活动, 分别用来显示NormalActivity和DialogActivity. 分别加载normal_layout和dialog_layout这两个布局文件. 这两个布局文件里都只含有一个TextView, 一行说明文字. 主活动中覆盖七个回调方法, 方法体内打印一行log.

那么, 既然两个布局文件几乎一样, 怎么设定一个是普通活动另一个是对话框活动呢?

在am文件中注册活动时, 给对话框的活动指定一个主题就好了:

    <activity android:name=".NormalActivity" />
    <activity
        android:name=".DialogActivity"
        android:theme="@android:style/Theme.Dialog" />

编好代码运行程序, 观察活动的七个回调方法的运行时机, 加深理解.

活动被回收了怎么办

活动A的基础上启动了活动B, 这时A进入了停止状态, 若此时系统内存不足, A被回收掉了. 用户按下Back返回A时, 并不会执行onRestart(), 而是执行A的onCreate()方法. 这时产生一个严重的问题, 如果A中存在临时的数据或状态, 重新创建A活动这些数据都会消失, 用户体验极差.

Activity中提供了一个onSaveInstanceState()回调方法, 保证一定在活动被回收之前调用. 因此可以用这个方法来解决上述问题.

这个方法会携带一个Bundle类型的参数, Bundle提供了一系列方法put[Type]用于保存各种基本数据类型的数据. 数据同样以键值对的形式存储.

于是重写onSaveInstanceState()方法, 用来保存数据:

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        String tempData = "Sth u just typed";
        outState.putString("data_key", tempData);
    }

那么, 保存下来的数据在哪里取出呢? 其实onCreate()方法就携带了一个Bundle对象, 在此方法中判断Bundle对象是否为空, 不为空的话可以用getString()方法取出数据:

    if (savedInstanceState != null) {
            String tempData = savedInstanceState.getString("data_key");
            Log.d(TAG, tempData);
            //可以加入各种处理逻辑...
    }

活动的启动模式

可以在am文件中通过给<activity>标签指定android:launchMode属性来选择启动模式.

活动的启动模式一共有四种: standard\singleTop\singleTask\singleInstance.

实际项目中应该根据特定的需求为每个活动指定恰当的启动模式.

standard

不显式指定情况下, 活动默认都是standard启动模式.这种模式下, 每当启动一个活动, 系统不会在乎此活动在返回栈中是否已经存在, 都会创建该活动的一个新的实例, 在返回栈中入栈, 并处于栈顶位置.

比如在FirstActivity这个活动为按钮设置点击事件, 来启动这个活动:

    Intent intent = new Intent(FirstActivity.this, FirstActivity.class);
    startActivity(intent);

启动后点击两次这个按钮, 观察Logcat, 会发现这个活动又创建了两次新的实例, 此时返回栈中应该存在三个FirstActivity的实例. 需要连按三次Back键才能退出程序.

singleTop

该模式下, 启动活动时如果发现返回栈的栈顶已经是该活动, 则直接使用它, 不会再创建新的实例.

在am文件中注册该活动的标签内添加:

    android:launchMode="singleTop"

再启动FirstActivity, 不管点击多少次按钮, 都只有一个实例, 只按一下Back键就能退出程序.

要注意, 如果要启动的活动目前不在栈顶位置, 则启动时, 还是会创建这个活动的实例.

singleTask

singleTop模式能够解决重复创建栈顶活动的问题, 但是如果活动没在栈顶, 还会创建多个实例的.

singleTask模式可以使活动在整个应用程序的上下文中只存在一个实例. 具体过程是:

首先在返回栈中检查是否存在该活动的实例, 如果存在则直接使用该实例, 并把这个活动之上的所有活动统统出栈, 如果没有发现就会创建一个新的实例.

singleInstance

该模式会有一个单独的返回栈来管理这个活动, 不管是哪个应用程序来访问这个活动, 都共用同一个返回栈, 可以解决共享活动实例的问题.

比如有三个活动, A, B, C, B的启动模式为singleInstance, 活动A点击按钮跳转到活动B, 活动B点击按钮跳转到活动C, 在每个活动的onCreate()方法中打印这个活动的返回栈id, (通过getTaskID()方法), 观察Logcat, 发现A和C处于同一个栈, B是单独的返回栈. 如果当前显示的是C活动, 则按下Back键返回的是A活动, 再按下Back键返回到B活动, 再按Back键退出程序.

活动的最佳实践

知道当前处于哪一个活动

首先新建一个 BaseActivity 类, 来继承Activity, 重写它的 onCreate() 方法:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("BaseActivity", getClass().getSimpleName());
    }

然后让当前程序的所有活动都继承这个 BaseActivity 类, 这样我们在进入某个页面时, Logcat就会输出当前活动的名字了.

随时随地退出程序

用一个专门的集合类对所有活动进行管理.

新建一个 ActivityCollector 类:

    public class ActivityCollector {

        public static List<Activity> activities = new ArrayList<Activity>();

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

这个类提供三个方法, 用来添加活动, 移除活动, 结束所有活动.

然后, 在 BaseActivity 的 onCreate() 方法中调用 addActicity() 方法, 在 onDestroy()
方法中调用 removeActivity() 方法.

    public class BaseActivity extends Activity {

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            ActivityCollector.addActivity(this);
        }

        @Override
        protected void onDestroy() {
            super.onDestroy();
            ActivityCollector.removeActivity(this);
        }
    }

此后, 只要我们想在某一个活动中退出程序时, 只需要直接调用 ActivityCollector.finishAll() 就可以了.

启动活动的最佳写法

活动A要启动活动B, 最好在活动B中把启动活动的代码单独封装起来, 并把传递数据等语句都写在这个方法中, 可以使启动活动更简单, 代码更清晰易懂.

在活动B的类中添加方法:

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

这样, 想要在活动A中启动它, 只需要一行代码就搞定了.

    SecondActivity.actionStart(FirstActivity.this, "data1", "data2");
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值