android从放弃到坚持放弃第二课(下)
2017-03-30 15:27
218 查看
续第二课( 下)
续第二课 下活动的生命周期
返回栈
活动状态
活动的生存期
体验活动的生命周期
活动被回收怎么办
活动的启动模式
standard
singleTop
singleTask
singleInstance
实践出真知
知晓当前是哪一个活动
随时随地退出程序
启动活动的最佳写法
问题
写app必须掌握活动的生命周期。
[活动的生命周期]
[返回栈]
android每次启动的活动会覆盖在原活动之上,然后点击Back键会销毁最上层的活动。是使用Task来管理活动,一个任务就是一组存放在栈里面的活动的集合,这个栈被称为返回栈。每当我们按下这个Back或者调用finish()方法去销毁一个活动,处于栈顶的活动就会出栈。[活动状态]
运行状态一个活动位于返回栈栈顶的时候,就是运行状态,也是系统最不愿意回收的状态的活动。
暂停状态
当一个活动不再处于栈顶,但还是可见时,就进入了暂停状态。如对话框。只有在内存极低的情况下,系统才会回收这个活动
停止状态
当一个活动不处于栈顶且不可见时就进入了停止状态。系统有可能会回收。
销毁状态
当一个活动从返回栈中移除后就变成了销毁状态。系统回收这种状态的活动,保证内存充足。
[活动的生存期]
Activity类中定义了7个回调方法,覆盖了活动生命周期的每一个环节。方法 | 简介 |
---|---|
onCreate() | 每个活动,我们都重写了这个方法,他会在活动第一次被创建的时候调用。可以用来完成活动的初始化操作。如加载布局,绑定事件 |
onStart() | 在活动由不可见变为可见时调用 |
onResume() | 在活动准备好与用户进行交互的时候调用。此时的活动一定位于返回栈的栈顶,处于运行状态。 |
onPause() | 这个方法在活动准备去启动或者恢复另一个活动的时候调用。通常在这个方法中释放一些消耗CPU的资源,以及保存一些关键数据,但这个方法的执行速度一定要快,否则会影响新栈顶活动的使用 |
onStop() | 这个方法在活动不可见的时候调用。它和onPause()区别:启动的活动是一个对话框式的活动,onStop()不会执行。 |
onDestory() | 这个方法在活动被销毁之前调用 |
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中一一取出数据。
[活动的启动模式]
启动模式分为四种:standard
singleTop
singleTask
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'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.
相关文章推荐
- android从放弃到坚持放弃第二课(上)
- android从放弃到坚持放弃第三课(下)
- android从放弃到坚持放弃第五课
- android从放弃到坚持放弃第一课
- android从放弃到坚持放弃第六课(上)
- android从放弃到坚持放弃第六课(下)
- android从放弃到坚持放弃第七课
- android从放弃到坚持放弃第三课(上)
- 放弃容易,难的是坚持
- 联发科放弃千元Android市场?
- androidUI第二部分---1.2.1Dialog,AlertDialog如何生成
- Android深入浅出之Audio 第二部分 AudioFlinger分析
- 在公司给资源池中的和不在项目组和有兴趣培训的android资料_第二课
- Android用户界面设计学习之旅-第二站
- Android深入浅出之Audio 第二部分 AudioFlinger分析
- androidUI第二部分---1.2.2Activity对Dialog的管理及Dialog的自定义布局
- Android 创建一个可拖拽图表-第二部分
- Android OpenGL教程-第二课
- 3年后,Android上升到第二大手机平台。
- Gartner:Android将在2012年成为第二大手机系统