zoukankan      html  css  js  c++  java
  • android从放弃到坚持放弃第二课(下)

    续第二课( 下)

    写app必须掌握活动的生命周期。

    [活动的生命周期]

    [返回栈]

    android每次启动的活动会覆盖在原活动之上,然后点击Back键会销毁最上层的活动。是使用Task来管理活动,一个任务就是一组存放在栈里面的活动的集合,这个栈被称为返回栈。

    每当我们按下这个Back或者调用finish()方法去销毁一个活动,处于栈顶的活动就会出栈。


    [活动状态]

    1. 运行状态

    一个活动位于返回栈栈顶的时候,就是运行状态,也是系统最不愿意回收的状态的活动。

    1. 暂停状态

    当一个活动不再处于栈顶,但还是可见时,就进入了暂停状态。如对话框。仅仅有在内存极低的情况下,系统才会回收这个活动

    1. 停止状态

    当一个活动不处于栈顶且不可见时就进入了停止状态。系统有可能会回收。

    1. 销毁状态

    当一个活动从返回栈中移除后就变成了销毁状态。系统回收这样的状态的活动,保证内存充足。


    [活动的生存期]

    Activity类中定义了7个回调方法,覆盖了活动生命周期的每一个环节。

    方法 简单介绍
    onCreate() 每一个活动,我们都重写了这种方法。他会在活动第一次被创建的时候调用。

    能够用来完毕活动的初始化操作。

    如载入布局,绑定事件

    onStart() 在活动由不可见变为可见时调用
    onResume() 在活动准备好与用户进行交互的时候调用。此时的活动一定位于返回栈的栈顶,处于运行状态。
    onPause() 这种方法在活动准备去启动或者恢复还有一个活动的时候调用。

    通常在这种方法中释放一些消耗CPU的资源,以及保存一些重要数据,但这种方法的运行速度一定要快。否则会影响新栈顶活动的使用

    onStop() 这种方法在活动不可见的时候调用。

    它和onPause()差别:启动的活动是一个对话框式的活动。onStop()不会运行。

    onDestory() 这种方法在活动被销毁之前调用
    onRestart() 这种方法在活动由停止状态变为运行状态之前调用

    除了onRestart()方法外。都为两量相应关系

    三种生存期:

    生存期 简单介绍
    完整生存期 在onCreate()方法和onDestory()之间所经历的就是完整生存期
    可见生存期 onStart()和onStop()方法之间所经历的。

    前台生存期 在onResume()和onPause()之间经历的

    都是从载入资源到释放资源,从而合理的管理资源。

    示意图


    [体验活动的生命周期]

    既然是体验,我们还是又一次new project吧,这次创建子活动勾选Launcher Activity。创建NormalActivity,DialogActivity.

    编辑activity_normal.xml:

    <LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="This is normal activity"
            />
    
    </LinearLayout>

    编辑activity_dialog.xml:

    <LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_height="match_parent"
        android:layout_width="match_parent">
    
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text = "This is a dialog activity"/>
    
    </LinearLayout>

    从名字上能够看出,一个是normal活动,一个是dialog活动(对话框式),可是上面的代码基本一样啊。

    。。

    我们须要去AndroidManifest.xml中改动:

            <activity android:name=".DialogActivity"
                android:theme="@android:style/Theme.Dialog">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>

    我们给它添加了android:theme的属性。

    如今去activity_main.xml中去添加button:

        <Button
            android:id="@+id/start_normal_activity"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Start normalActivity"
            />
    
        <Button
            android:id="@+id/start_dialog_activity"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Start dialogActivity"
            />

    最后改动MainActivity中的代码:

    public class MainActivity extends AppCompatActivity {
    
        private static final String TAG = "MainActivity";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            Button startNormalActivity = (Button) findViewById(R.id.start_normal_activity);
            Button startDialogActivity = (Button) findViewById(R.id.start_dialog_activity);
    
            startNormalActivity.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Intent intent = new Intent(MainActivity.this, NormalActivity.class);
                    startActivity(intent);
                }
            });
    
            startDialogActivity.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Intent intent = new Intent(MainActivity.this, DialogActivity.class);
                    startActivity(intent);
                }
            });
    
    
        }
    
        @Override
        protected void onStart(){
            super.onStart();
            Log.d(TAG, "onStart");
        }
    
        @Override
        protected void onResume(){
            super.onResume();
            Log.d(TAG, "onResume");
        }
    
        @Override
        protected void onPause(){
            super.onPause();
            Log.d(TAG, "onPause");
        }
    
        @Override
        protected void onStop(){
            super.onStop();
            Log.d(TAG, "onStop");
        }
    
        @Override
        protected void onDestroy(){
            super.onDestroy();
            Log.d(TAG, "onDestory");
        }
    
        @Override
        protected void  onRestart(){
            super.onRestart();
            Log.d(TAG, "onRestart");
        }
    }

    跑路。顺带观察一下logcat:

    好了,你如今已经体验了一遍完整的生命周期。

    我碰到了一个问题,start_dialog_activity这个button运行时除了错,原因是给dialog这个activity的theme里面的属性和DialogActivity继承的一个AppCompatActivity类不兼容,所以仅仅要把继承的AppCompatActivity改为Activity就能够了。


    [活动被回收怎么办?]

    当一个活动进入到了停止状态。有可能被系统回收。这样的话。返回之前的处于停止状态的活动是能够的。仅仅只是不会运行onRestart() 方法而是运行的onCreate()方法,也就是说这样的情况下,返回之前 被回收的活动是会被又一次创建的。

    只是问题来了,这样暂时的数据会丢失,只是问题不大。Activity中提供了onSaveInstanceState()回调方法。这种方法能够保证在活动被回收之前一定被调用,因此我们能够通过这种方法来保存暂时数据。

    onSaveInstanceState()方法携带一个Bundle类型的參数,Bundle类提供了一些列方法保存数据,putString)(),putInt()等等,每一个保存方法须要传入两个參数,第一个參数是键。用于取值,第二个是真正要保存的数据。

    和Intent的几乎相同。

    MainActivity:

        protected void onSaveInstanceState(Bundle outState) {
            super.onSaveInstanceState(outState);
            String tempData = "Something i just typed";
            outState.putString("data_key",tempData);
        }

    保存完数据,可是该怎样取出数据呢?

    onCreate()方法中有一个Bundle类型的參数。就靠它

    MainActivity:

    Log.d(TAG, "onCreate");
    if(savedInstanceState != null){
      String tempData = savedInstanceState.getString("data_key");
      Log.d(TAG,tempData);
    }

    并且我们还能够将Bundle对象存放在Intent中。到了目标活动中再取出Bundle,再从Bundle中一一取出数据。


    [活动的启动模式]

    启动模式分为四种:

    1. standard
    2. singleTop
    3. singleTask
    4. singleInstance

    能够在androidManifest.xml中通过给标签中指定android:launchMode属性来选择。


    1.standard

    活动默认的启动模式。因此,我们之前所使用过的都是standard模式。

    每当启动一个新的活动,它就会在返回栈中入栈,并处于栈顶的位置。对于standard模式下的活动,系统不会在乎这个活动是否已在返回栈中存在。每次启动都会创建该活动的一个新的实例。

    打开之前的ActivityTest项目。

    改动FirstActivity:

    public class FirstActivity extends AppCompatActivity {
    
        private static final String Tag = "FirstActivity";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            requestWindowFeature(Window.FEATURE_NO_TITLE);
            setContentView(R.layout.first_layout);
    
            Log.d(Tag, this.toString());
            Button button1 = (Button) findViewById(R.id.button_1);
            button1.setOnClickListener(new View.OnClickListener(){
                @Override
                public void onClick(View v){
                    Toast.makeText(FirstActivity.this, "You click me", Toast.LENGTH_SHORT).show();
                    Intent intent = new Intent(FirstActivity.this , FirstActivity.class);
                    startActivity(intent);
                }
            });
    
            Button button2 = (Button) findViewById(R.id.button_2);
            button2.setOnClickListener(new View.OnClickListener(){
                @Override
                public void onClick(View v){
                    String data = "Hello huchi";
                    Intent intentString = new Intent(FirstActivity.this, SecondActivity.class);
                    intentString.putExtra("extra_data",data);
                    //startActivity(intentString);
                    startActivityForResult(intentString, 1);
    
    
                }
            });
    
            Button button4 = (Button) findViewById(R.id.button_4);
            button4.setOnClickListener(new View.OnClickListener(){
                @Override
                public  void onClick(View v){
                    Intent intentSecond = new Intent("com.example.wrjjrw.activitytest.ACTION_START");
                    intentSecond.addCategory("com.example.wrjjrw.activitytest.MY_CATEGORY");
                    intentSecond.putExtra("extra_data","error");
                    startActivity(intentSecond);
                }
            });
    
    
            Button button5 = (Button) findViewById(R.id.button_5);
            button5.setOnClickListener(new View.OnClickListener(){
                @Override
                public void onClick(View v){
                    Intent intentToBaidu = new Intent(Intent.ACTION_VIEW);
                    intentToBaidu.setData(Uri.parse("http://blog.csdn.net/jaywrzz/article/details/65937639"));
                    startActivity(intentToBaidu);
                }
            });
        }
    
        @Override
        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;
                case R.id.huchi_item:
                    Toast.makeText(this, "I love huchi,too", Toast.LENGTH_LONG).show();
                    break;
                default:
            }
            return true;
        }
    
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            getMenuInflater().inflate(R.menu.main, menu);
            return true;
        }
    
        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            switch (requestCode){
                case 1:
                    if(resultCode == RESULT_OK){
                        String returnedData = data.getStringExtra("data_return");
                        Log.d("FirstActivity",returnedData);
                    }
                    break;
                default:
            }
        }
    }

    我们看到他能够启动他自己,跑路。

    03-30 12:38:54.424 7273-7273/com.example.wrjjrw.activitytest D/FirstActivity: com.example.wrjjrw.activitytest.FirstActivity@42900c50
    03-30 12:38:59.485 7273-7273/com.example.wrjjrw.activitytest D/FirstActivity: com.example.wrjjrw.activitytest.FirstActivity@4293a0c8

    发现假设打开它自己三下,也就须要

    2.singleTop

    似乎standard不是非常合理。singleTop在启动活动时假设发现返回栈的栈顶已是该活动。则觉得能够直接使用它,不会再创建新的活动实例。

    改动AndroidManifest.xml

            <activity
                android:name=".FirstActivity"
                android:launchMode="singleTop"
                android:label="This is huchi&apos;s FirstActivity">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>

    发现无论点多少次logcat都没有信息打印了。

    只是在FirstActivity并未处于栈顶时。这时再启动FirstActivity,还是会创建新的实例的。

    我们发如今FirstActivity和SecondActivity两个活动中跳来跳去能够在logcat看到打印的消息。


    3.singleTask

    singleTask模式能够解决上述问题。每次启动活动时,系统会在返回栈中检查是否存在该活动的实例。假设发现已经存在则直接使用该实例,并把在这个活动上面的全部活动统统出栈,假设没有发现就会创建一个新的活动实例。

    改动FirstActivity:

        @Override
        protected void onRestart() {
            super.onRestart();
            Log.d(Tag, "onRestart");
        }

    改动SecondActivity,加入onDestroy():

    public class SecondActivity extends AppCompatActivity {
    
        private static  final  String Tag = "SecondActivity";
    //    @Override
    //    public void onBackPressed() {
    //        Intent intent = new Intent();
    //        intent.putExtra("data_return", "back_to_firstActivity");
    //        setResult(RESULT_OK, intent);
    //        finish();
    //    }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_second);
    
            Button button3 = (Button) findViewById(R.id.button_3);
            button3.setOnClickListener(new View.OnClickListener(){
                @Override
                public void onClick(View v){
                    Intent intentFirst = new Intent(SecondActivity.this, FirstActivity.class);
                    startActivity(intentFirst);
                }
            });
    
            Button buttonFinishSecond = (Button) findViewById(R.id.button_finish);
            buttonFinishSecond.setOnClickListener(new View.OnClickListener(){
                @Override
                public  void onClick(View v){
                    Intent intent = new Intent();
                    intent.putExtra("data_return","byebye huchi");
                    setResult(RESULT_OK, intent);
                    finish();
                }
            });
    
            Intent intent = getIntent();
            String data = intent.getStringExtra("extra_data");
            Log.d("SecondActivity",data);
    
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            Log.d(Tag, "onDestroy");
        }
    }

    跑路。观察logcat


    4.singleInstance

    最特殊最复杂的模式。

    会启动一个新的返回栈来管理这个活动(假设singleTask模式下指定了不同的taskAffinity).singleInstance 能够实现其它程序和我们的程序共享一个活动的实例。在这样的模式下会有一个单独的返回栈来管理这个活动,无论是哪个应用程序来訪这个活动。都公用同一个返回栈。

    实践出真知:

    改动AndroidManifest.xml:

            <activity android:name=".SecondActivity"
                android:launchMode="singleInstance">
                <intent-filter>
                    <action android:name="com.example.wrjjrw.activitytest.ACTION_START" />
                    <category android:name="android.intent.category.DEFAULT"/>
                    <category android:name="com.example.wrjjrw.activitytest.MY_CATEGORY" />
                </intent-filter>
            </activity>

    FirstActivity:

            Log.d(Tag, "Task id is " + getTaskId());
    
            Button button1 = (Button) findViewById(R.id.button_1);
            button1.setOnClickListener(new View.OnClickListener(){
                @Override
                public void onClick(View v){
                    Toast.makeText(FirstActivity.this, "You click me", Toast.LENGTH_SHORT).show();
                    Intent intent = new Intent(FirstActivity.this , SecondActivity.class);
                    startActivity(intent);
                }
            });

    改动SecondActivity:

            Log.d(Tag, "Task id is "+ getTaskId());
    
            Button button3 = (Button) findViewById(R.id.button_3);
            button3.setOnClickListener(new View.OnClickListener(){
                @Override
                public void onClick(View v){
                    Intent intentFirst = new Intent(SecondActivity.this, ThirdActivity.class);
                    startActivity(intentFirst);
                }
            });

    activity_third:

        <Button
            android:id="@+id/button_to_first"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="return first"
            ></Button>

    改动ThirdActivity

            Log.d(Tag, "Task id is "+ getTaskId());
    
            Button button3 = (Button) findViewById(R.id.button_3);
            button3.setOnClickListener(new View.OnClickListener(){
                @Override
                public void onClick(View v){
                    Intent intentFirst = new Intent(SecondActivity.this, ThirdActivity.class);
                    startActivity(intentFirst);
                }
            });

    我们发现从FirstActivity跳转到SecondActivity。再跳转到ThirdActivity后,此时按back键返回回到的就是FirstActivity。


    [实践出真知]

    [知晓当前是哪一个活动?]

    在集体编程时。可能会找不到界面相应的活动。

    新建一个BaseActivity:

    public class BaseActivity extends AppCompatActivity {
    
        private static final String Tag = "BaseActivity";
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            Log.d(Tag, getClass().getSimpleName());
        }
    }

    由于BaseActivity继承的是AppCompatActivity,我们能够让其它三个活动继承BaseActivity。也就是说其它活动onCreate的时候,就会运行父类的onCreate,也就都会运行Log.d(Tag, getClass().getSimpleName());这句话,然后我们就知道哪个界面相应的是哪个活动了。

    03-30 14:09:16.085 9459-9459/com.example.wrjjrw.activitytest D/BaseActivity: FirstActivity
    03-30 14:09:19.440 9459-9459/com.example.wrjjrw.activitytest D/BaseActivity: SecondActivity
    03-30 14:09:21.617 9459-9459/com.example.wrjjrw.activitytest D/BaseActivity: ThirdActivity

    [随时随地退出程序]

    仅仅须要一个专门的集合类对全部的活动进行管理。

    BaseActivity:

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

    BaseActivity:

    public class BaseActivity extends AppCompatActivity {
    
        private static final String Tag = "BaseActivity";
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            Log.d(Tag, getClass().getSimpleName());
            ActivityCollector.addActivity(this);
        }
    
        @Override
        protected void onDestroy(){
            super.onDestroy();
            ActivityCollector.removeActivity(this);
        }
    
    }

    这样就能够把存在的活动放在一个数组里。

    以后无论想在什么时候地方退出程序。仅仅要调用ActivityCollector.finishAll().

        public static void finishAll(){
            for(Activity activity: activities){
                if(!activity.isFinishing())
                    activity.finish();
            }
            android.os.Process.killProcess(android.os.Process.myPid());
        }

    我进行了一番尝试:

            Button button10 = (Button) findViewById(R.id.button_to_finish);
            button10.setOnClickListener(new View.OnClickListener(){
                @Override
                public void onClick(View v){
                    ActivityCollector.finishAll();
                }
            });

    美好的。

    [启动活动的最佳写法]

    尽管我们写的intent无论是语法上还是规范上都符合。但在对接的时候总是会有疑问???不清楚这个活动须要哪些数据。

    改动SecondActivity:

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

    改动FirstActivity:

            Button button1 = (Button) findViewById(R.id.button_1);
            button1.setOnClickListener(new View.OnClickListener(){
                @Override
                public void onClick(View v){
                    Toast.makeText(FirstActivity.this, "You click me", Toast.LENGTH_SHORT).show();
                    SecondActivity.actionStart(FirstActivity.this, "data1", "data2");
                }
            });

    [问题]

    问:为啥我自己主动生成的都不是Linearlayout?

    答:Click me,下一次学习笔记中将详细学习。

    问:啥是taskAffinity?

    答:Click me.

  • 相关阅读:
    mybatis
    eclipse日志
    最大值与最小值问题
    常见的缓存算法设计策略
    常用垃圾回收算法
    Java中对象的三种状态
    Java中的内存泄漏问题
    单例模式
    约瑟夫环问题
    矩形覆盖问题
  • 原文地址:https://www.cnblogs.com/wzzkaifa/p/7308563.html
Copyright © 2011-2022 走看看