Ⅰ android 如何调用 底下的 activity 到前台 我不想新建
要达到这个需求,可以设置activity的启动模式为singleTask或者singleInstance
activity一共有4中启动模式
standard
默认模式,可以不用写配置。在这个模式下,都会默认创建一个新的实例。因此,在这种模式下,可以有多个相同的实例,也允许多个相同Activity叠加。
例如:
若有一个Activity名为A1, 上面有一个按钮可跳转到A1。那么如果点击按钮,便会新启一个Activity A1叠在刚才的A1之上,再点击,又会再新启一个在它之上……
点back键会依照栈顺序依次退出。
singleTop
可以有多个实例,但是不允许多个相同Activity叠加。即,如果Activity在栈顶的时候,启动相同的Activity,不会创建新的实例,而会调用其onNewIntent方法。
例如:
若有两个Activity名为B1,B2,两个Activity内容功能完全相同,都有两个按钮可以跳到B1或者B2,唯一不同的是B1为standard,B2为singleTop。
若我意图打开的顺序为B1->B2->B2,则实际打开的顺序为B1->B2(后一次意图打开B2,实际只调用了前一个的onNewIntent方法)
若意图打开的顺序为B1->B2->B1->B2,则实际打开的顺序与意图的一致,为B1->B2->B1->B2。
singleTask
只有一个实例。在同一个应用程序中启动的时候,若Activity不存在,则会在当前task创建一个新的实例,若存在,则会把task中在其之上的其它Activity destory掉并调用它的onNewIntent方法。
如果是在别的应用程序中启动它,则会新建一个task,并在该task中启动这个Activity,singleTask允许别的Activity与其在一个task中共存,也就是说,如果在这个singleTask的实例中再打开新的Activity,这个新的Activity还是会在singleTask的实例的task中。
例如:
若应用程序中有三个Activity,C1,C2,C3,三个Activity可互相启动,其中C2为singleTask模式,那么,无论在这个程序中如何点击启动,如:C1->C2->C3->C2->C3->C1-C2,C1,C3可能存在多个实例,但是C2只会存在一个,并且这三个Activity都在同一个task里面。
但是C1->C2->C3->C2->C3->C1-C2,这样的操作过程实际应该是如下这样的,因为singleTask会把task中在其之上的其它Activity destory掉。
操作:C1->C2 C1->C2->C3 C1->C2->C3->C2 C1->C2->C3->C2->C3->C1 C1->C2->C3->C2->C3->C1-C2
实际:C1->C2 C1->C2->C3 C1->C2 C1->C2->C3->C1 C1->C2
若是别的应用程序打开C2,则会新启一个task。
如别的应用Other中有一个activity,taskId为200,从它打开C2,则C2的taskIdI不会为200,例如C2的taskId为201,那么再从C2打开C1、C3,则C2、C3的taskId仍为201。
注意:如果此时点击home,然后再打开Other,发现这时显示的肯定会是Other应用中的内容,而不会是应用中的C1 C2 C3中的其中一个。
singleInstance
只有一个实例,并且这个实例独立运行在一个task中,这个task只有这个实例,不允许有别的Activity存在。
例如:
程序有三个ActivityD1,D2,D3,三个Activity可互相启动,其中D2为singleInstance模式。那么程序从D1开始运行,假设D1的taskId为200,那么从D1启动D2时,D2会新启动一个task,即D2与D1不在一个task中运行。假设D2的taskId为201,再从D2启动D3时,D3的taskId为200,也就是说它被压到了D1启动的任务栈中。
若是在别的应用程序打开D2,假设Other的taskId为200,打开D2,D2会新建一个task运行,假设它的taskId为201,那么如果这时再从D2启动D1或者D3,则又会再创建一个task,因此,若操作步骤为other->D2->D1,这过程就涉及到了3个task了。
Ⅱ android 如何得到线程id
有时候,在kernel里经常收到一些系统调用,但不知道是谁调的。可以按下面的步骤找到。
1.kernel 里面打印出线程ID、线程名、进程ID
char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1];
printk("pid %u(%s).
",current->pid, get_task_comm(currtask_name, current));
2. 在/proc/pid/task/tid/下面有线程的信息。
另外,在user层可以通过这种方法得到线程ID.
Ⅲ android每个应用都有一个堆栈task来存放启动的Activity实例我不能理taskid在实际开发中的作用是什么
在开发中,Task的概念应该是与Activity栈相对应的。
换言之,Activity通过不同的启动模式去启动,所处的Task是不一样的(如singletask)。
相应的也就会对画面迁移和内存分配等问题产生影响。
暂时就想到这些~
Ⅳ Android 怎么将json对象中的值取出来放到集合中
先解析,然后就可以调用存入了
Ⅳ taskAffinity 属性详解
本篇文章的目的是为了 搞清楚, 哪些情况下开启一个 Activity 会在新的 task 运行,哪些情况下会继续在原来的task 运行。
每个 Activity 运行时都有一个其归属的 task栈,我们可以用 activity.getTaskId() 的方法得到当前 activity 的taskId。 如果两个 activity 的 taskId 不同,则他们肯定不会属于同一个 task。
为了方便,我们在 Application 中注册生命周期回调,类似这样,我们打印出当前 activity 和其归属的 taskid。
taskAffinity 的使用方式如下,
如上图所示,taskaffinity 可以单独对一个 activity 使用,代表该 activity 所想归属的 task;
也能对application 使用,代表该 application 内声明的所有 activity 都归属于这个task。
如果 activity 组件没有声明 taskAffinity 的话,该 activity 的 taskAffinity 属性也是有默认值的。如果 application 指定了 taskAffinity 值,默认值就是 application 指定的 taskAffinity 值;如果 application 未指定的话,默认值就是 manifest 中声明的包名(package 对应的字符串)。
Android 手机的任务列表就是根据不同 task 弹出的,我们可以根据任务管理器有几个 item 图标,来知道我们开启了几个 task。
是不是我指定了一个 Activity 的 taskAffinity 值(跟包名不同),运行该 Activity 时,是否就会新开这个 task栈呢?
答案是否定的, 一个 Activity 运行时所归属的task,默认是启动它 的那个Activity 所在的 task (下文将会验证)。
taskAffinity 单独使用并不会生效。
要想其生效,需要配合其他属性使用,或者配合 Intent.FLAG_ACTIVITY_NEW_TASK,或者配合
allowTaskReparenting 。使用时用其中的一个就行,下面将详细介绍这两个属性。
Intent.FLAG_ACTIVITY_NEW_TASK 使用方式如下,
首先说下这个规则是根据测试结果反推出来的,不对之处还还请指出(捂脸。。。)
==当 AMS 发现启动了一个带有 FLAG_ACTIVITY_NEW_TASK 标签的 Activity 时,会先去寻找当前是否存在这个 Activity 的 task 值(这个值具体是什么可看 知识点2),如果不存在的话,就会创建该task,如果存在就省去了创建 task 这个步骤。然后在把要启动的 Activity 添加到 task 中。
下面开始我们的测试,测试结果为过滤后的log日志,并给出相应分析。
我们假定都是 Activity A 跳转到 Activity B 中,A没有指定 taskAffinity 属性,B 的launchMode 为standard。
可以验证:一个Activity 归属的task 是由 启动它的 Activity 所决定的。
可以验证,一个 Activity 的默认 task 值就是 manifest 定义的包名。
可以验证:不指定 FLAG_ACTIVITY_NEW_TASK的话, 即使 taskAffinity 不同,一个Activity 归属的task 仍然是由 启动它的 Activity 所决定的。
可以验证:即使 使用了 FLAG_ACTIVITY_NEW_TASK,但由于两者的 taskAffinity 相同,所以仍然不会开启一个新的task。
可以验证:开启一个新task 的条件是 FLAG_ACTIVITY_NEW_TASK 和 taskAffinity 不同 缺一不可。
可以得出:未指定 FLAG_ACTIVITY_NEW_TASK 和 新的 taskAffinity 时,这两种启动模式对task 没有影响
可以得出:
singleinstance 启动模式本身就是会开启一个新的task 装载 这个Activity,且task 中只有这一个 Activity。
经判断得知,AMS 是先对 launchMode 做判断 再处理 FLAG_ACTIVITY_NEW_TASK 的,如果是 singleinstance ,则会直接开启一个task。
上面七个 case 均是在同一个app 内的,现在考虑跨进程调用的情况,A在 app1,B在app2, 此时 B 的默认 task 肯定是和 A 不同的 。
我们可以通过隐式启动的方式启动B,B 仍然是标准启动模式。
类似这样
++下文 所有的 App1的log标识为 MyApplication,App2 的标识为 MyApplication2。++
可以得出:case 8 与 case 3 的本质是相同的,仅仅是 A 和 B 的taskA 属性不同,所以没有开启新的task。
我们还可以看出,task 是可以跨进程的,即一个 task 中的 Activities 是可以运行在不同的进程中的。(关于 A 和 B 不在同一个进程读者可自行验证)
可以得出:case 9 与 case 5 的本质是相同的。
至此,Intent 的 FLAG_ACTIVITY_NEW_TASK 属性 应该算是讲解清楚了。
我们还可以得出一个有意思结论,那就是 AMS 分配的taskid 是线性递增的,每次开启一个新的task ,taskid 永远都是 +1 的操作。
测试该属性的话,应该先把 FLAG_ACTIVITY_NEW_TASK 属性去掉。
allowTaskReparenting 这个属性指的是一个 Activity 运行时,可以重新选择自己所属的task。基本是在跨app 间调用时,我们在上面的case 8 的基础上,对 Activity 做如下修改
当 A 启动 B 时,这时虽然是在两个进程中的,但其归属的task 是同一个,这时我们回到后台,在桌面点击 B 的应用图标,我们会发现 log 日志如下:
其中 MyApplication 代表 app1,MyApplication2代表 app2。
log 日志如下:
此 case 正好验证之前的解析规则,若 Activity taskAffinity指定的task 已经存在,是会复用之前的task,而不会再重新创建一个新的task。
Ⅵ Android基础知识
一、activity
1.一个activity就是一个类,继承activity;
2.需要复写onCreate()方法;
3.每一个activity都需要在AndroidMainfest.xml清单上进行配置;
4.为activity添加必要的控件。
二、布局
线性布局:LinearLayout
1.填满父空间:fill_parent、match_parent
2.文本多大空间就有多大:warp_content
3.文字对齐方式:gravity
4.占屏幕的比例:layout_weight="1" 水平方向,则width=0,垂直方向,则height=0
5.一行显示,空间不够会省略:singleLine="ture" false会换行
6.背景:background="#ffffff"
7.水平布局:orientation="horizontal"
垂直布局:orientation="vertivcal"
表格布局:TableLayout
1.内边距:padding
2.外边距:marginLeft\Start、Right\End、Top、Bottom
三、RelativeLayout相对布局
layout_above 将该控件的底部置于给定ID控件之上
layout_below 将该控件的顶部置于给定ID控件之下
layout_toLeftOf 将该控件的右边缘和给定ID控件的左边缘对齐
layout_toRightOf 将该控件的左边缘和给定ID控件的右边缘对齐
layout_alignBaseline 该控件的baseline和给定ID的控件的Baseline对齐
layout_alignBottom 该控件的底部边缘和给定ID的控件的底部边缘对齐
layout_alignLeft 该控件的左边缘和给定ID的控件的左边缘对齐
layout_alignRight 该控件的右边缘和给定ID的控件的右边缘对齐
layout_alignTop 该控件的顶部边缘和给定ID的控件的顶部边缘对齐
layout_alignparentBottom 如果该值为true,则该控件的底部和父控件的底部对齐layout_alignParentLeft 如果该值为true,则该控件的左边和父控件的左边对齐
layout_alignParentRight 如果该值为true,则该控件的右边和父控件的右边对齐
layout_alignParentTop 如果该值为true,则该控件的上边和父控件的上边对齐
layout_centerHorizontal 如果该值为true,则该控件将被置于水平方向的中央
layout_centerInParent 如果该值为true,则该控件将被置于父控件水平和垂直方向的中央
layout_centerVertival 如果该值为true,则该控件将被置于垂直方向的中央
四、一个Intent对象包含一组信息
1.Component name
2.Action
3.Data
4.Category
5.Extras
6.Flags
Intent intent = new Intent(this, SecondActivity.class);
startActivity(intent); //startActivity方法
intent.putExtra("Key", "Value"); //键值对
intent = getIntent();
String value = intent.getStringExtra("Key"); //通过键提取数据
五、初级控件:EditText、TextView、Button
1.获取EditText的值
String value = EditText.getText().toString();
2.将值放到Intent对象中
Intent intent = new Intent();
intent.putExtra("one",value )
intent.setCalss(Activity.this, OtherActivity.class);
3.使用这个Intent对象来启动Otheractivity
Activity.this.startActivity(intent);
4.将监听器的对象绑定到按钮对象上
button.setOnclickListener(new Listener());
5.得到Intent对象当中的值
Intent intent = getIntent();
String value1 = intent.getStringExtra("one");
int value2 = Integer.parseInt(value);
六、其他初级控件使用
①ImageView
②RadioGroup和RadioButton
setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener())
③Checkbox
setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener())
④Menu
1.当客户点击MENU按钮的时候,调用onCreateOptionMenu()方法
public boolean onCreateOptionMenu(Menu menu){
menu.add(0,1,1,R.string.id);
}
2.当客户点击MENU内部的具体某一个选项时,调用onOptionItemSelected()方法
public boolean onOptionItemSelected(MenuItem item){
if(item.getItemId() == 1){
finish();
}
return super.onOptionItemSelected(item);
}
七、Activity的生命周期
1.第一次创建时调用
protected void onCreat(Bundle saveInstanceState);
2.显示出来时调用
protected void onStrat();
3.获得用户焦点时调用(可操作)
protected void onResume();
4.点击弹出第二个Activity时调用
protected void onPause();
5.当第一个Activity不可见时调用
protected void onStop();
6.当返回第一个Activity时调用,代替OnCreate,因为没被销毁
protected void onRestart();
7.当返回第一个Activity时调用(先执行onStop,在执行,因为第二个Activity被销毁,不能返回获取,只能通过onCreat,onStart,onResume再创建)
protected void onDestory();
八、Task
1.Task是存放Activity的Stack栈。当点击启动第二个Activiry时,第一个Activtiy会被压入Stack栈当中,第二个Activity会位于栈的顶部;当返回第一个Activtiy时,第二个Activity会被弹出Stack,第一个Activity会位于栈的顶部,以此类推。
注释:当调用finish()时,当前的Activity会被Destory掉,栈中的Activity会消失。
2.当Activity都从Stack退出后,则就不存在Task。
九、高级控件
①进度条ProgressBar
水平进度条style="?android:attr/progressBarStyleHorizontal"
圆圈进度条style="?android:attr/progressBarStyle"
用户可视的visibility="gone"
②列表ListView
十、其他控件
A.下拉菜单Spinner
1.创建一个ArrayAdapter:
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
this, //指上下文对象
R.array.plant_array, //引用了在文件中定义的String数组
android.R.layout.simple_spinner_item);//用来指定Spinner的样式,可替换自定义
adapter.setDropDownViewResource(
android.R.layout.simple_spinner_dropdown_item);//设置Spinner当中每个条目的样式
2.得到Spinner对象,并设置数据:
spinner=(spinner)findViewById(R.id.spinnerId);
spinner.setAdapter(adapter);
spinner.setPrompt("测试");//标题
3.创建监听器
class SpinnerOnSelectListener implements OnItemSelectedListener{
@override
public void onItemSelected(
AdapterView<?> adapterView,//整个列表对象
View view,//被选中的具体条目对象
int position,//位置
long id){ //id
String selected = adapterView.getItemAtPosition(position).toString();
}
@override
public void onNothingSelected(AdapterView<?> adapterView){
S.o.p("nothingSelected");
}
}
4.绑定监听器
spinner.setOnItemSelectedListener(new SpinnerOnSelectListener());
注:第二种动态设计
1.创建ArrayList对象
List<String> list = new ArrayList<String>();
list.add("test1");
2. 调用方法
ArrayAdapter adapter = new ArrayAdapter(
this, //指上下文对象
R.layout.item, //引用了指定了下拉菜单的自定义布局文件
R.id.textViewId,//id
list);//数据
3.得到Spinner对象,并设置对象
spinner.setAdapter(adapter);
spinner.setPrompt("测试");//标题
3.创建监听器
class SpinnerOnSelectListener implements OnItemSelectedListener{
@override
public void onItemSelected(
AdapterView<?> adapterView,//整个列表对象
View view,//被选中的具体条目对象
int position,//位置
long id){ //id
String selected = adapterView.getItemAtPosition(position).toString();
}
@override
public void onNothingSelected(AdapterView<?> adapterView){
S.o.p("nothingSelected");
}
}
4.绑定监听器
spinner.setOnItemSelectedListener(new SpinnerOnSelectListener());
B.DatePicker和DatePickerDialog
1.声明一个监听器,使用匿名内部类
DatePickerDialog.OnDateSetListener onDateSetListener
= new DatePivkerDialog.OnDateSetListener(){
public void onDateSet(
DatePicker view,
int year,
int monthOfYear,
int dayOfMonth){
S.o.p(year+"-"+motnOfYear+"-"+dayOfMonth)
}
}
2.复写onCreateDialog(int id)方法:
@override
protected Dialog onCreateDialog(int id){
switch(id){
case DATE_PICKER_ID:
return new DatePickerDialog(this,onDateSetListener,2019,11,25);
}
return null;
}
3.使用时调用showDialog()方法
showDialog(DATE_PICKER_ID);
C.AutoCompleteTextView
B.Widget
C.Animatin
十一、实现ContentProvider过程
1.定义一个CONTENT_URI常量
2.定义一个类,继承ContentProvider
3.实现query、insert、update、delete、getType和onCreate方法
4.在AndroidManifest.xml当中进行声明
Ⅶ android启动模式设置为single task任务栈为什么
在Android中每个界面都是一个Activity,切换界面操作其实是多个不同Activity之间的实例化操作。在Android中Activity的启动模式决定了Activity的启动运行方式。 Android总Activity的启动模式分为四种: Activity启动模式设置: <activity android:name=".MainActivity" android:launchMode="standard" /> Activity的四种启动模式: 1. standard 模式启动模式,每次激活Activity时都会创建Activity,并放入任务栈中。 2. singleTop 如果在任务的栈顶正好存在该Activity的实例, 就重用该实例,否者就会创建新的实例并放入栈顶(即使栈中已经存在该Activity实例,只要不在栈顶,都会创建实例)。 3. singleTask 如果在栈中已经有该Activity的实例,就重用该实例(会调用实例的onNewIntent())。重用时,会让该实例回到栈顶,因此在它上面的实例将会被移除栈。如果栈中不存在该实例,将会创建新的实例放入栈中。 4. singleInstance 在一个新栈中创建该Activity实例,并让多个应用共享改栈中的该Activity实例。一旦改模式的Activity的实例存在于某个栈中,任何应用再激活改Activity时都会重用该栈中的实例,其效果相当于多个应用程序共享一个应用,不管谁激活该Activity都会进入同一个应用中。 其中standard是系统默认的启动模式。 下面通过实例来演示standard的运行机制: 1 private TextView text_show; 2 private Button btn_mode; 3 4 @Override 5 public void onCreate(Bundle savedInstanceState) { 6 super.onCreate(savedInstanceState); 7 setContentView(R.layout.activity_main); 8 9 text_show = (TextView) this.findViewById(R.id.text_show); 10 11 text_show.setText(this.toString()); 12 13 btn_mode = (Button) this.findViewById(R.id.btn_mode); 14 15 } 16 //按钮单击事件 17 public void LaunchStandard(View v){ 18 startActivity(new Intent(this,MainActivity.class)); 19 20 text_show.setText(this.toString()); 21 }
Ⅷ 在一个Activity的java程序中,具体看问题描述
1、Intent
Intent是各个组件之间交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,而且还能在各组件之间传递数据。Intent一般可用于启动Activity、启动Service、发送广播等场景。
Intent大致可分为2中:
1、显示Intent
2、隐式Intent
1.1、显示Intent打开Activity
fun openActivity(){
val intent = Intent(this, KotlinFirstActivity::class.java)
intent.putExtra("param", "testParams")
startActivity(intent)
}
注意:KotlinFirstActivity::class.java就相当于Java中的KotlinFirstActivity.class。
1.2、隐式Intent打开程序内的Activity
相比于显示Intent,隐式Intent并不指明启动那个Activity而是指定了一系列的action和category,然后交由系统去分析找到合适的Activity并打开。
什么是合适的Activity,其实就是和隐式Intent中指定的action和category完全匹配的Activity,而action和category我们可以在AdnroidManifest中指定。
在标签中配置了action和category,只有和隐式Intent中的action和category完全匹配才能正常的打开该页面。
val intent = Intent("com.example.abu.alertdialogdemo.ACTION_START")
startActivity(intent)
不是说action和category要完全匹配才能打开页面吗?这是因为android.intent.category.DEFAULT是一种默认的category,在调用startActivity()时会自动将这个category添加到Intent中。所以在Manifest中一定不要忘记配置这个默认的category:android.intent.category.DEFAULT,否则会报错。
还有一点需要注意:Intent中只能添加一个action,但是可以添加多个category。
1.3、隐式Intent打开程序外的Activity
比如我们要打开系统的浏览器
fun openWeb(view: View) {
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse("https://www..com")
startActivity(intent)
}
Intent.ACTION_VIEW是系统内置的动作,然后将https://www..com通过Uri.parse()转换成Uri对象,传递给intent.setData(Uri uri)函数。Kotlin中intent.data=Uri.parse("https://www..com")就相当于Java中的intent.setData(Uri.parse("https://www..com")),这是Kotlin中的语法糖。
与此对应在标签中配置一个标签,用于更精确的指定当前Activity能够相应的数据。标签中主要可以配置一下内容:
1、android:scheme:用于指定数据的协议部分,如https
2、android:host:用于指定数据的主机名部分,如www..com
3、android:port:用于指定数据的端口,一般紧随主机名后
4、android:path:用于指定数据的路径
5、android:mimeType:用于指定支持的数据类型
只有当标签中指定的内容和Intent中携带的data完全一致时,当前Activity才能响应该Intent。下面我们通过设置data,让它也能响应打开网页的Intent。
我们在ThirdActivity的中配置当前Activity能够响应的action是Intent.ACTION_VIEW的常量值,而category指定了默认的category值,另外在标签data中我们通过android:scheme="https"指定了数据的协议必须是https。另外由于AndroidStudio认为能够响应ACTION_VIEW的Activity都应该加上BROWSABLE的category,否则会报出警告。加上BROWSABLE的category是为了实现deep link 功能和目前学习无关,所以我们在intent-filter标签上添加tools:ignore="AppLinkUrlError"忽略警告。
然后我们就能通过隐式Intent的方法打开ThirdActivity了,代码如下:
val intent= Intent(Intent.ACTION_VIEW)
intent.data=Uri.parse("https:")
startActivity(intent)
除了指定android:scheme为https我们也能随意指定它的值,只需要保证AndroidManifest中设置的值和Intent中设置的data相对应即可。
2、Activity的生命周期
Activity类中定义了7个回调方法,覆盖了Activity声明周期的每一个环节。
1、onCreate(): 在Activity第一次创建时调用
2、onStart():在Activity可见但是没有焦点时调用
3、onResume():在Activity可见并且有焦点时调用
4、onPause():这个方法会在准备启动或者恢复另一个Activity时调用,我们通常在该方法中释放消耗CPU的资源或者保存数据,但在该方法内不能做耗时操作,否则影响另一个另一个Activity的启动或恢复。
5、onStop():在Activity不可见时调用,它和onPause主要区别就是:onPause在失去焦点时会调用但是依然可见,而onStop是完全不可见。
6、onDestory():在Activity被销毁前调用
7、onRestart():在Activity由不在栈顶到再次回到栈顶并且可见时调用。
为了更好的理解Activity的生命周期,看下图
Activity生命周期.png
3、体验Activity的生命周期
下面我们通过实例更直观的看下Activity的生命周期过程。
1、打开FirstActivity,生命周期过程如下
FirstActivity onCreate
FirstActivity onStart
FirstActivity onResume
2、在FirstActivity中打开SecondActivity,生命周期过程如下
FirstActivity onPause
SecondActivity onCreate
SecondActivity onStart
SecondActivity onResume
FirstActivity onStop
可以看到在打开SecondActivity时FirstActivity的onPause会执行,所以在onPause中是不能做耗时操作的,否则会影响SecondActivity的打开。
3、按返回键回到FirstActivity,生命周期过程如下
SecondActivity onPause
FirstActivity onRestart
FirstActivity onStart
FirstActivity onResume
SecondActivity onStop
SecondActivity onDestroy
可以看到在返回FirstActivity时会调用SecondActivity的onPause,如果SecondActivity的onPause中做了耗时操作的话,那么也会影响Activity的返回。而且当FirstActivity再次回到栈顶时会调用其onRestart,此时并不会执行onCreate因为FirstActivity并没有销毁。
4、Activity被回收了时的生命周期
现在描述一种场景:打开ActivityA,然后在ActivityA的页面中打开ActivityB,此时ActivityA不在栈顶了如果内存不足可能会被回收,此时从ActivityB再回到ActivityA,下面描述下整个过程的生命周期。
1、打开ActivityA,执行的生命周期
ActivityA onCreate
ActivityA onStart
ActivityA onResume
2、打开ActivityB执行的生命周期
ActivityA onPause
ActivityB onCreate
ActivityB onStart
ActivityB onResume
ActivityA onStop
3、此时因内存不足,导致ActivityA被回收了,并返回ActivityA
ActivityA onDestory
ActivityB onPause
ActivityA onCreate
ActivityA onStart
ActivityA onResume
ActivityB onStop
从上面可以看出在ActivityA被回收后再次回到ActivityA时不再调用ActivityA的onRestart了,而是调用了ActivityA的onCreate,因为ActivityA已经被销毁了。
5、Activity被销毁,其中的临时数据怎么办
ActivityA被销毁了,这也就说明其中的临时数据也会丢失了,比如ActivityA中有一个EditText,我们在其中输入了一段文字,然后启动ActivityB,此时由于内存紧张处于停止状态的ActivityA被回收了,返回到ActivityA你会发现EditText中输入的文字不见了,因为ActivityA重新创建了。
为此Activity提供了onSaveInstanceState方法,该方法能确保在被回收之前被调用,我们就能在onSaveInstanceState方法中进行临时数据的保存,然后我们可以在onCreate(savedInstanceState: Bundle?)中利用savedInstanceState进行数据的恢复。Activity被回收重新创建时onCreate(savedInstanceState: Bundle?)中的savedInstanceState不再为null否则为null,其中带有在savedInstanceState中保存的所有数据。
具体代码如下:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.e("tag", "$tag onCreate")
//恢复数据
val tempData = savedInstanceState?.getString("data") ?: ""
et.setText(tempData)
}
//保存数据
override fun onSaveInstanceState(outState: Bundle?) {
super.onSaveInstanceState(outState)
Log.e("tag", "$tag onSaveInstanceState")
val tempData = et.text.toString().trim()
outState?.putString("data", tempData)
}
6、onSaveInstanceState()调用时机
1、用户按下HOME键
2、长按HOME键进入其他程序
3、按下电源键关闭屏幕
4、从ActivityA跳转到一个新的Activity
5、屏幕方向切换时,如从竖屏变成横屏
总而言之,onSaveInstanceState()是在你的Activity有可能在未经允许的情况下被回收时调用,Activity被销毁之前onSaveInstanceState()肯定会被触发,我们可以onSaveInstanceState()中保存临时数据,持久化的数据可以在onPause()中保存。
另外,虽然屏幕方向切换时,会造成Activity的重建会调用onSaveInstanceState(),但是不建议使用onSaveInstanceState()进行数据的保存,我们可以禁止屏幕的旋转或禁止屏幕旋转时Activity的重建。
禁止屏幕旋转
可以在Mainifest中设置屏幕固定为竖屏
android:screenOrieritation="portrait"
禁止屏幕旋转时Activity的重建
android:configChanges="orientation丨keyboardHidden丨screenSize"
7、Activity的启动模式
启动模式分为4种:
1、standard
2、singleTop
3、singleTask
4、singleInstance
我们可以在Manifest中通过android:launchMode指定启动模式。
7.1、standard模式
如果不显示指定启动模式,那么Activity的启动模式就是standard,在该模式下不管Activity栈中有无该Activity,均会创建一个新的Activity并入栈,并处于栈顶的位置。
7.2、singleTop模式
1、要启动的Activity位于栈顶
在启动一个ActivityA时,如果栈顶的Activity就是ActivityA,那么就不会重新创建ActivityA,而是直接使用,此时并不会调用ActivityA的onCreate(),因为并没有重新创建Activity,ActivityA的生命周期如下:
ActivityA onPause
ActivityA onNewIntent
ActivityA onResume
可以看到调用了onNewIntent(intent: Intent?),我们可以在onNewIntent(intent: Intent?)中通过intent来获取新传递过来的数据,因为此时数据可能已经发生了变化。
2、要启动的Activity不在栈顶
虽然我们指定了ActivityA的启动模式为singleTop,但是如果ActivityA在栈中但是不在栈顶的话,那么此时启动ActivityA的话会重新创建一个新的ActivityA并入栈,此时栈中就有2个ActivityA的实例了。
7.3、singleTask模式
如果准备启动的ActivityA的启动模式为singleTask的话,那么会先从栈中查找是否存在ActivityA的实例:
场景一、如果存在则将ActivityA之上的Activity都出栈,并调用ActivityA的onNewIntent()。
场景二、如果ActivityA位于栈顶,则直接使用并调用onNewInent(),此时和singleTop一样。
场景三、 如果栈中不存在ActivityA的实例则会创建一个新的Activity并入栈。
场景一:ActivityA启动ActivityB,然后启动ActivityA,此时生命周期过程:
ActivityB onPause
ActivityA onRestart
ActivityA onStart
ActivityA onNewIntent
ActivityA onResume
ActivityB onStop
ActivityB onDestroy
此时ActivityA不在栈顶,ActivityA之上有ActivityB,所以在启动ActivityA时ActivityA之上的ActivityB会出栈,ActivityA将置于栈顶,所以ActivityA的onRestart和ActivityB的onDestory会执行。
场景二:ActivityA启动ActivityA,此时生命周期过程:
ActivityA onPause
ActivityA onNewIntent
ActivityA onResume
此时ActivityA位于栈顶,此时singleTask和singleTop作用一样,都是直接使用ActivityA,并且会调用ActivityA的onPause、onNewIntent、onResume。
场景三:ActivityA启动ActivityB,此时生命周期过程:
ActivityA onCreate
ActivityA onStart
ActivityA onResume
7.4、singleInstance模式
不同于另外3个模式,指定singleInstance模式的Activity会启动一个新的返回栈来管理这个Activity(其实如果singleTask模式指定了不同的taskAffinity,也会启动一个新的返回栈),这么做的意义是什么?
想象一个场景:如果我们程序内的一个Activity是允许其他程序访问的,如果想实现其他程序和我们程序能共享这个Activity实例,该怎么实现呢?使用前面3中模式是无法做到的,因为每个应用程序都有自己的返回栈,同一个Activity在不同返回栈中肯定都创建了新的实例,而使用singleInstance就可以解决这个问题。在这种模式下,会有一个单独的返回栈来管理这个Activity,无论哪个应用程序来访问这个Activity,都在同一个返回栈中,也就解决了共享Activity实例的问题。
为了更好的理解下面我们实战一下:
1、将ActivityB的启动模式修改为singleInstance
android:name=".ActivityB"
android:launchMode="singleInstance" />
2、在ActivityA、ActivityB、ActivityC的onCreate中打印taskId
##ActivityA
val tag = javaClass.simpleName
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.e("TAG", "$tag taskId=$taskId")
}
##ActivityB
val tag = javaClass.simpleName
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(activity_second)
Log.e("TAG", "$tag taskId=$taskId")
}
##ActivityC
val tag = javaClass.simpleName
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(activity_c)
Log.e("TAG", "$tag taskId=$taskId")
}
在Kotlin中javaClass是当前实例的Class对象,相当于Java的getClass().
在Kotlin中ActivityB::class.java是获取ActivityB类的Class对象,相当于Java中的ActivityB.class。
3、启动ActivityA->启动ActivityB->启动ActivityC,taskId打印结果如下:
ActivityA taskId=4163
ActivityB taskId=4164
ActivityC taskId=4163
可以看到ActivityB的taskId是不同于ActivityA 和ActivityC的,这也说明了ActivityB 是在一个单独的栈中的,并且返回栈中只有这一个Activity。
4、在ActivityC中按返回键,返回到ActivityA,再按返回键返回到ActivityB,这是为什么呢?其实很好理解:ActivityA 和ActivityC 在同一个返回栈中,在ActivityC 中按返回键ActivityC 出栈,此时ActivityA就位于栈顶了,ActivityA就展示在界面上了,在ActivityA中再按返回键,这是当前的返回栈已经空了,于是就展示了另一个返回栈栈顶的ActivityB了。
————————————————
版权声明:本文为CSDN博主“保瓶儿”的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_31211457/article/details/114052641
Ⅸ Android 深入探讨sigleTask和singletop的区别
1.singleTask和singletop都是保真了Activity在栈中的唯一性
2.singleTask和singleTop实例存在时,都不会重新创建 new task
不同之处有如下几点:
如果singleTask启动的ActivityA位于栈底,在栈顶startActivity到这个ActivityA时会调动
onNewIntent->onStart->onResume。
但singleTask启动的的ActivityA不存在时,会重新创建ActivityA,调用方式OnCreate->OnStart->OnResume
2.如果singletop启动的ActivityB在栈底时,在栈顶startActivity到这个ActivityB时会重新调动
onCreate->onStart->onResume,但不会调用OnDestroy
并且singletop启动的的ActivityB不存在时,会重新创建ActivityB,调用方式OnCreate->OnStart->OnResume
3.singleTask在栈底时,从栈顶startActivity到栈底ActivityA,那么该栈startActivity的发起者的中间部分Activity会被销毁。而singletop是逐级跳跃到栈顶
singleTask
追加一点singleInstance,虽然也是保证唯一性,但改模式的Activity是全局性的唯一性,生命周期和应用程序相同,不能及时回收,此外的话Activity在MVC充当Controller的角色,
这点显然对singleIntance来说不适合,因此不建议使用。
4.singleTask不适合充当ChildActivity,但singletop适合
5.对作业栈,默认情况下,没有任何影响,因为栈的taskid是相同的,可以获取getTaskid(),默认情况下每个activity的taskid相同
但是,如果自定义了栈的标签(这个标签相同的singletask activity在同一个作业栈中),那么taskid就会初选差别
android:taskAffinity="com.sample.lanchmode.tester.main",那么一旦另一个作业栈被销毁,下次必然重建(相信我,这不是废话)。
为了一句不是废话,我需要来证明一下。
当我们的初始加载WelcomeActivity界面--->HomeActivity界面时,如果将HomeActivity的启动模式设置为sigleTask+android:taskAffinity="自定义(一般是类名)",
①那么WelcomeActivity销毁后,在到达HomeActivity之后,HomeActivity回作为一个新的作业栈栈底而存在,所以由HomeActivity启动的Activity的TaskId和HomeActivity相同。
②这个过程,WelcomeActivity和HomeActivity的TaskId是不相同的,问题出现了,当我们按下 Home键,然后在点击桌面上的app图标你会发现,WelcomeActivity再次出现了,
而且再次进入了HomeActivity,并且这个HomeActivity调用了OnNewIntent,却没掉用OnCreate,所以来说他一直存在这,但你需要再次过渡才能找到它。
(对于追求捕获Home键让app退出的读者是一个喜讯,这样可以做到每次Home之后都能再次进入登录界面),在这个过程中没有直接进入HomeActivity,而是WelcomeActivity(有人说不对么,不应该是这样么,在这里不想解释)。
注意,使用了不同作业栈的Activity,会在最近打开的app显示一个应用会出现2个界面。
在这里,需要强调一下singleTask+android:taskAffinity不一定必须使用,请依据需求而定,网上有人推荐使用,但我却想说,不要盲目,并不是所有的应用都适合。
1-->
singleTask+android:taskAffinity对于效率而言,优势并不存在,比如设置进程的,但对于带有登录状态的app需要慎用,否则造成信息泄露等问题,开发中只使用singleTask即可。
2-->
singleTask+android:taskAffinity适用于单用户保密性的app(可做到Home之后下次必须登录才能查看信息),建议在这方面试试。
转载
Ⅹ android 从第一个页面跳转到第二个页面之后 在第二个页面的按钮上开始一个倒计时 要怎么实现
可以使用Handler来做
比如下面的代码片段,开始一个30秒的倒计时
在Application.onCreate()里TestMain.getInstance().setContext(this);
或则在你的第二个页面onCreate()里TestMain.getInstance().setContext(this);
之后启动倒计时并刷新页面
TestMain.getInstance().startCountdownTask(new CountdownTask("taskid1", 30));
publicclassTestMain{
//用于保存倒计时任务已经完成的计数
;
;
=newTestMain();
privateTestMain(){}
(){
returnINSTANCE;
}
//在主线程里调用,比如Application.onCreate()中TestMain.getInstance().setContext(this);
publicvoidsetContext(Contextcontext){
mCountdownPrefs=context.getSharedPreferences("countdwon",Context.MODE_PRIVATE);
mCountdownHandler=newHandler();
}
//在需要倒计时任务的时候调用
publicvoidstartCountdownTask(){
//如果有未完成的倒计时任务,读取上次倒计时到几了,否则就是设置的count次
intcount=mCountdownPrefs.getInt(countdownTask.taskId,countdownTask.count);
countdownTask.count=count;
//首先执行一次
countdownTask.notify(countdownTask.count);
//之后每一秒执行一次
mCountdownHandler.postDelayed(newRunnable(){
@Override
publicvoidrun(){
//1秒执行一次
if(countdownTask.countDown()){
mCountdownHandler.postDelayed(this,1000);
}else{
//倒计时结束
mCountdownHandler.removeCallbacks(this);
//移除任务
mCountdownPrefs.edit().remove((countdownTask.taskId)).commit();
}
}
},1000);
}
{
/**倒计时起始值*/
privateintcount=30;
/**倒计时任务id*/
privateStringtaskId="";
publicCountdownTask(intcount,StringtaskId){
this.count=count;
this.taskId=taskId;
}
privatebooleancountDown(){
count--;
booleanisContinue=count>=0;
notify(Math.max(count,0));
returnisContinue;
}
//通知更新
publicvoidnotify(intcurrent){
//UI更新倒计时数字,current是当前倒计时到多少了
}
}
}