注:本文内容有自己理解部分,若有不对的地方,欢迎指出。
是应用程序的组件,用于显示用户界面,可以与用户交互完成相关的操作。App中可以有很多个Activity。
Activity存储于android系统的返回栈(back stack)中,特点先进先出(返回键或finish()出栈)。
Activity状态 |
说明 |
运行状态 |
处于返回栈栈顶,用户可见,能与用户交互。 |
暂停状态 |
不处于栈顶,可见;例如、弹出窗口页面后。 |
停止状态 |
不处于栈顶,不可见 |
销毁状态 |
从栈中移除。 |
生命周期方法 |
Activity页面状态 |
方法体内容 |
onCreate |
不可见状态 |
第一次创建后调用,设置布局、初始化组件、注册需要的广播接收者和数据绑定。 |
onStart |
可见状态,此时不能与用户交互 |
部分资源(需要更新的)的加载,可以检测设置数据对象是否为空等。 |
onResume |
可见状态,当前界面可以进行交互,activity为栈顶 |
大部分核心功能在此方法中实现。 |
onPause |
可见状态,此时activity正在停止 |
数据存储、动画停止、资源回收。 |
onStop |
不可见状态,activity完全停止或完全被覆盖 |
资源释放操作,不做耗时操作。 |
onDestroy |
Activity销毁 |
回收工作和最终资源释放 |
onRestart |
Activity重新启动时(后台回到前台,新act返回旧act) |
恢复数据操作。 |
可归结三个关键周期:
从onCreate到onDestroy(整个的生命周期)
从onStart开始到onStop结束(可见的生命周期)
从onResume开始到onPause结束(前台的生命周期)
正常启动:onCreate() -> onStart() -> onResume()
正常退出:onPause() -> onStop() -> onDestroy()
横竖屏切换:onPause() -> onStop() -> onDestroy() -> onCreate() -> onStart() -> onResume()
解决方法:在AndroidManifest.xml中的
添加configChanges = “keyboardHidden|screenSize|orientation”
或添加screenOrientation = landscap此方式适合游戏类应用。
当A跳转到B,并按下返回键
A:onCreate -> onStart -> onResume ->onPause
B:onCreate -> onStart -> onResume
A:onStop
当按下返回键:
B:onPause
A:onRestart -> onStart -> onResume
B:onStop -> onDestory
按下Home键
onPause -> onStop
从后台回来:
OnRestart -> onStart -> onResume
Activity的注册
Activity需要在AndroidManiFest.xml中进行配置完成注册。
显示跳转:需要知道activity的类名。适合应用内跳转。
Intent intent = new Intent(this,SecondActivity.class);
startActivity(intent);
隐式跳转:要知道activity的 意图过滤器中的action和Category或data属性。适合应用间的跳转。
public void toBActivity(View view){
Intent intent = new Intent();
intent.setAction("My_Action");
intent.addCategory("MyCategory");
startActivity(intent);
}
注:系统会默认为intent中添加“android.intent.category.DEFAULT”所以为能够正常采用隐式跳转必须设置category有DEFAUL。
在
standard(标准)模式:也是Activity的默认的模式,每次启动Activity都会有新的Activity实例进入栈顶,不管是栈顶还是栈内,有多少个同样的实例都会被创建。
singleTop(栈顶复用)模式:和standard一样 ,不过Activity实例在栈顶就不再创建,复用已有。
singleTask(栈内复用)模式:如果有Activity实例在栈顶就不会创建新的实例,复用已有的Activity即可;若在实例之上有其他Activity的实例则弹出其他的Activity实例,复用已有Activity。
singleInstance(单实例)模式:保证系统无论从哪个Task启动Activity都只会创建一个Activity实例,并将它加入新的Task栈顶 也就是说被该实例启动的其他Activity会自动运行于另一个Task中。已存在无论在那个栈都会到前台显示。 singleInstance的Activity位于栈顶,因为它所在的Task仅有它一个Activity。
四种启动模式的应用场景
standard模式:默认启动模式,大部分页面都是这些模式。
singleTop模式:用于通知栏,推送消息页面。
singleTask模式:应用首页,首页只有一个。
singleInstance模式:独立栈操作的应用,启动与程序分离情况,闹钟的提醒、打开其他应用。
若跳转到一个是窗口的Activity,生命周期回调情况?
A:onPuse
B:onCreate -> onStart -> onResume
点击返回:A: onResume
解释:因为A页面还是可见的,不会调用onStop。
锁屏/解锁生命周期回调情况?
锁屏:onPause -> onStop
解锁:onRestart -> onStart -> onResume
内存不足时候杀死Activity的顺序?
后台(用户不可见) > 可见不能操作 > 前台
在隐式跳转中,若目标activity没有在意图过滤中没加category会发生什么?
例子:
android:exported="true">
Intent intent = new Intent(); intent.setAction("My_Action"); startActivity(intent)
|
在隐式跳转中能匹配到两个Activity是否能够编译通过?运行结果什么样?
可以编译通过,会让你选择进入哪个页面。
Activity结束和进程结束方式有哪些?其他activity结束进程会发生什么?
Activity结束自己finish();
结束整个进程android.os.Process.killProcess(android.os.Process.myPid());System.exit(0);
注:程序主页面结束进程会退出程序,跳转到其他页面结束进程,结束后会自己启动。
可长期运行在后台没有界面的组件。可由其它组件启动,组件通过绑定可以与其交互。
前台服务
执行用户能注意到的操作。音乐播放,用户可以通过通知栏知道当前播放的内容。
后台服务
执行用户不会注意到的操作。上传、定时关闭
绑定服务
绑定后可以与绑定组件进行交互。
与Activity类似需要在AndroidManifest.xml中进行注册。
还能写其他属性android:permission是权限声明,android:process设置具体的进程名称。
startService方式:onCreate -> onStartCommad -> onDestory(想要启动一个后台服务进行某项任务)
bindService方式:onCreate -> onBind -> onUnbin -> onDestroy(需要与服务进行通信)
通过bind方式开启的服务会与调用者的生命周期进行绑定,并能进行通信,单启动时,调用者销毁服务也会销毁,start开启的不会销毁但不会通信。当所有的客户端都和service解除绑定后,系统会销毁service。(除非service也被startService()方法开启)
混合开启服务方式:onCreate-> onStartCommad -> onBind (想长期运行并通信,调用者结束服务不结束)
开启服务,能够确保服务长期运行
绑定服务,能够通信(只把Service的IBinder对象传递给Activity,不会绑定两个对象的生命周期)
退出activity,要解绑服务释放资源
Service一般在主线程中,注意不要进行耗时操作。
重复调用startService会重复执行onStartCommand,onCreate只会执行一次。
重复调用bindService时只回调一次onBind,之后只会直接把IBinder对象传递给后来的客户。
如果Service已经由某个客户端通过StartService()启动,接下来由其他客户端 再调用bindService()绑定到该Service后调用unbindService()解除绑定最后在 调用bindService()绑定到Service的话,此时所触发的生命周期方法如下:
onCreate( )->onStartCommand( )->onBind( )->onUnbind( )->onRebind( )
问题思考
Service与Thread线程的区别?
两者没有太大关系,Thread是线程,程序执行的最小单元,分配CPU的最小单元,可以运行耗时操作。Service是安卓的组件,运行在主进程的主线程上。不同的Activity中无法对同一线程进行控制。
只要回调了onStartCommad服务就会长期运行,尽管调用者销毁。(没有服务内容)65s后会自动销毁。(Android12)
Service应用场景
音乐播放、下载、上传大文件、定时关闭应用的功能。
Service里可以弹Toast吗?为什么?
可以,一它在主线程,可以刷新UI,二是context的子类有上下文。有些时候就可以通过toast告诉用户发生了什么事。
一个服务可以被多个客户端(Activity)绑定吗?
可以,但只有第一个组件绑定时才调用onBind(),其它的只是传递个IBinder对象给绑定组件以便通信。
为什么需要前台服务?
若服务在后台的运行,当内存不足时可能会优先对其进行回收,为了能够保持服务长期工作可置为前台服务。
广播是程序组件之间的传输消息的机制,广播的内容是一个intent,在其中携带数据,他没有用户界面。
同一APP中不同组件传输,不同APP组件间传输消息。
默认情况下广播接收者运行在UI线程中。
标准广播:异步执行,发出后接收者几乎同一时刻收到广播。
有序广播:同步执行,同一时间只有一个接收者能收到,这个接收者可以选则停止,执行完逻辑后继续传递,还可修改广播内容。
粘性广播:粘性广播发送后在没有找到任何接收方的情况下会一直等待,接收者重建会接收到广播。(确保重要的状态改变后的信息被持久保存,并且能随时广播给新的广播接收器)
系统广播:有系统程序发出的广播,是标准广播。主要涉及到手机的基本操作
action 触发时机 android.net.conn.CONNECTIVITY_CHANGE 网络连接发生变化 android.intent.action.SCREEN_ON 屏幕点亮 android.intent.action.SCREEN_OFF 屏幕熄灭 android.intent.action.BATTERY_LOW 电量低,会弹出电量低提示框 android.intent.action.BATTERY_OKAY 电量恢复了 android.intent.action.BOOT_COMPLETED 设备启动完毕 android.intent.action.DEVICE_STORAGE_LOW 存储空间过低 android.intent.action.DEVICE_STORAGE_OK 存储空间恢复 android.intent.action.PACKAGE_ADDED 安装了新的应用 android.net.wifi.STATE_CHANGE WiFi 连接状态发生变化 android.net.wifi.WIFI_STATE_CHANGED WiFi 状态变为启用/关闭/正在启动/正在关闭/未知 android.intent.action.BATTERY_CHANGED 电池电量发生变化 android.intent.action.INPUT_METHOD_CHANGED 系统输入法发生变化 android.intent.action.ACTION_POWER_CONNECTED 外部电源连接 android.intent.action.ACTION_POWER_DISCONNECTED 外部电源断开连接 android.intent.action.DREAMING_STARTED 系统开始休眠 android.intent.action.DREAMING_STOPPED 系统停止休眠 android.intent.action.WALLPAPER_CHANGED 壁纸发生变化 android.intent.action.HEADSET_PLUG 插入耳机 android.intent.action.MEDIA_UNMOUNTED 卸载外部介质 android.intent.action.MEDIA_MOUNTED 挂载外部介质 |
//发送标准广播
private void sendSecondBroadcast() {
Intent intent = new Intent();
intent.setAction("com.archermind.mybroadcast.UNORDER_BROADCAST");
sendBroadcast(intent);
}
//发送有序广播
public void sendOrderBroadcast(View view){
Intent intent = new Intent();
intent.setAction("com.archermind.mybroadcast.ORDER_BROADCAST");
Bundle bundle = new Bundle();
bundle.putInt("money", 1000 * 500);
sendOrderedBroadcast(intent, "com.archermind.mybroadcast.MY_PERMISSION",null,null,1,"1000",bundle);
}
动态注册:IntentFilter添加Action,调用registerReceiver,不用时记得取消注册。Activity启动后才能注册。
静态注册:在AndroidManiFest.xml中采用
注:广播发送给静态注册的接收者时需要为intent设置component包名和路径;发送给动态注册的不需要设置,设置反而接收不到。
//向静态注册接收者发送广播
Intent intent = new Intent();
intent.setAction("com.archermind.mybroadcast.SEND_BROADCAST_ON_CLICK");
intent.setComponent(new ComponentName(getPackageName(),"com.archermind.mybroadcast.SendBroadcastActivity$InnerReceiver"));
intent.putExtra("Content","点击发送广播");
sendBroadcast(intent);
//向静态注册接收者发送广播 Intent intent = new Intent(); intent.setAction("com.archermind.mybroadcast.SEND_BROADCAST_ON_CLICK"); intent.setComponent(new ComponentName(getPackageName(),"com.archermind.mybroadcast.SendBroadcastActivity$InnerReceiver")); intent.putExtra("Content","点击发送广播"); sendBroadcast(intent); |
谁有权接收我的广播:广播发送者需要在AndroidManifest.xml中设置
谁有权给我发送广播:静态注册中的
//广播发送者的AndroidManifest.xml中 //广播的发送 sendBroadcast(intent,"com.archermind.mybroadcast.MY_PERMISSION"); sendOrderedBroadcast(intent, "com.archermind.mybroadcast.MY_PERMISSION",null,null,1,"1000",bundle); //谁有权给我发广播,动态广播接收者的注册 registerReceiver(receiveBroadCast, intentfilter, "com.archermind.mybroadcast.MY_PERMISSION" ,null); //谁有权给我发广播,静态广播接收者的注册 android:exported="true" android:permission="com.archermind.mybroadcast.MY_PERMISSION"> |
问题思考
BroadCastReceiver中有那些应用场景?
APP内部消息通信。
不同APP之间的消息通信。
接收系统发出的设备信息。
广播优先级对无序广播生效吗?
优先级对无序广播生效
动态注册谁的优先级更高?
一自己设置优先级,二谁先注册谁高。
如何判断当前广播接收者收到的广播是有序还是无序?
在onReceive中调用isOrderedBroadcast()
BroadcastReceiver中为什么不能执行耗时操作?
前台广播后台广播?
前台广播对应前台队列,后台广播对应后台队列。可以通过设置Intent.FLAG_RECEIVER_FOREGROUND属性来将广播定义为前台广播,如果未定义,默认使用后台广播。前台广播超时时间是10s,后台是60s。
实现数据共享的重要组件,自身和其他应用所存储数据的访问,并提供与其他应用共享数据的方法。
使用场景:
访问其他应用中的现有的内容提供者;在应用中创建新的内容提供程序,从而实现与其他应用的内容共享。
需要在AndroidManifest.xml中进行注册
当一个应用程序要把自己的数据暴露给其他程序时,通过ContentProvider来实现。
其他应用可以通过ContenrResolver来操作ContentProvider暴露的数据。
定义自己的ContentProvider类,该类需要继承Android系统提供的ContentProvider基类。
在Manifest.xml 文件中注册ContentProvider,(四大组件的使用都需要在Manifest文件中注册) 注册时需要绑定一个URL
调用Activity的ContentResolver获取ContentResolver对象
调用ContentResolver的insert(),delete(),update(),query()进行增删改查。
一般来说,ContentProvider是单例模式,也就是说,当多个应用程序通过ContentResolver来操作ContentProvider提供的数据时,ContentResolver调用的数据操作将会委托给同一个ContentResolver。
ContentProvider内容提供者, 用于对外提供数据。
ContentResolver内容解析者, 用于获取内容提供者提供的数据。
一个应用可以实现ContentProvider来提供给别的应用操作,通过ContentResolver来操作别的应用数据。
为表使用的内容 URI 模式是 content://
为单个行使用的内容 URI 模式则是 content://
ContentProvider和sql的实现上的有什么差别?
ContentProvider屏蔽了数据存储细节,使用者只需要关心操作数据的uri,它还可以实现不同app之间的共享。Sql只能增删改查本应用数据库。
多个进程同时调用一个ContentProvider的query获取数据,ContentProvider是怎么反应的?
多进程访问ContentProvider请求会按队列的形式进入ContentProvider,在一个进程中线程是互斥的。
四大组件都是系统提供的,所以在写代码时都是要继承系统相应的类的。
生命周期方法都是系统管理的,尽量避免自己调用其系统方法。
四大组件都是要注册的,其中Broadcast能用动态注册,其他的都在AndroidManifest.xml中注册的。
其中Activity是有用户界面,其他组件没有。
它们一般都运行在主线程中,所以尽量不把耗时操作写在其中,在主线程中可以修改UI。
ANR报错 |
|
Input dispatching timed out |
输入时间分发超过5s,包括按键和触屏事件 |
Broadcast of Intent |
前台广播需要在10s,后台广播需要在60s |
executing service |
前台服务需要在20s,后台则需要在200s |
ContentProvider |
publish执行未在10s内完成? |
Service.startForeground() |
应用调用startForegroundService,然后5s内未调用startForeground出现ANR或者Crash |
相当于自定义的组件(一个小的Activity),在需要Fragment的Activity的.xml中写上
静态加载Fragment
动态加载Fragment
注:容器是布局,组件的话只能添加或替换一次。
FragmentManager对象(管理fragment) |
|
findFragmentById() |
获取指定的fragment |
popBackStack() |
弹出后台Fragment |
addToBackStack() |
加入栈 |
FragmentTransaction对象(事务管理) |
|
add() |
向Activity中添加一个Fragment |
remove() |
移除一个已存在的Fragment |
replace() |
替换一个已被添加进视图容器的Fragment |
show()hide() |
展示一个fragment |
Fragment传递数据给Activity(F的数据显示到A的UI中)(回调接口方式)
三种调用方式
同步调用:一种阻塞式调用,A的方法中调用B的方法,需要等待B返回结果,A才能继续进行。
异步调用:类似消息和事件处理机制,A通知B后,然后各自运行。
回调:双向的调用模式,A要调用B(A需要F的数据),B在执行完又要调用A。
//在fragment中创建发送信息接口 public class Fragment1 extends Fragment { oneSendValue mMySendValue; … … public interface oneSendValue{ void oneSend(String s); } … … } |
//在Activity中实现这个接口 public class MainActivity extends AppCompatActivity implements Fragment1.oneSendValue { …… @Override public void oneSend(String s) { textView.setText(s); } …… } |
//在fragment调用实现的接口函数 public class Fragment1 extends Fragment { oneSendValue mMySendValue; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { …… Button button=view.findViewById(R.id.button_my); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { String s=editText.getText().toString(); mMySendValue.oneSend(s); } }); return view; } …… } |
动态加载成功后进行横竖屏切换Fragment生命周期函数?
走销毁再创建的生命周期
Fragment堆积怎么解决?
堆积出现出原因:每次加载时都是new的新的Fragment对象,动态加载到Activity中。
加载语句为:
getFragmentManager().beginTransaction().add(R.id.frameLayout1,new BlankFragment1()).commit();
解决方法:将Fragment抽出为成员变量,再加载前判空,若不为空对象show()出来。
if(blankFragment2 == null){ blankFragment2 = new BlankFragment2(); fragmentTransaction.add(R.id.frameLayout2,blankFragment2).commit(); } else { fragmentTransaction.show(blankFragment2); } |
Android的应用与用户的交互大多都是在主线程中完成(UI显示、界面交互),为了用户体验,耗时操作就不能在主线程中(Android有个ANR报错),所以耗时的操作就要在线程中进行。
//两个继承Thread的类 private class MyThread1 extends Thread{ private int ticket = 3; private String name ; public MyThread1(String name){ this.name = name; } public void run(){ while(ticket > 0){ ticket--; System.out.println(name + "卖掉了1张票,剩余票数为:"+ticket); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } private class MyThread2 extends Thread{……} |
//调用的地方 public void startSellTicket1(View view) { MyThread1 myThread1 = new MyThread1("张三"); MyThread2 myThread2 = new MyThread2("李四"); myThread1.start(); myThread2.start(); } |
运行结果
是Java中实现多线程的具体类,封装了线程操作。
线程对象:运行线程的实体,线程对象是控制线程行为的唯一手段。
特点:实现简单 只需要继承Thread类和复写run()方法;局限性大(Java是单继承)、不适合资源共享(一个线程=一个对象,相对独立)、消耗资源(一个线程=一个耗时任务,多此创建多次销毁)。
注:Tread线程不能操作UI。
Runnable类
//实现Runnable接口的类 private class MyThread3 implements Runnable{ private int ticket = 9; @Override public void run() { while(ticket > 0){ ticket--; System.out.println(Thread.currentThread().getName() + "卖掉了1张票,剩余票数为:" + ticket); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } |
public void startSellTicket2(View view) { MyThread3 myThread3 = new MyThread3(); new Thread(myThread3,"张三").start(); new Thread(myThread3,"李四").start(); } |
运行结果
它是一个多线程相关的抽象接口,仅定义了一个run()方法。
特点:适合资源共享(Runnable的代码可被多个先线程共享,适合多个线程处理同一资源)灵活(一个类可以实现多个接口)。
Thread类和Runnable的区别?
继承和接口的区别
class Thread implements Runnable{}
Thread和Runnable的实质是继承关系,没有可比性。无论使用Runnable还是Thread,都会new Thread,然后执行run方法。用法上,如果有复杂的线程操作需求,那就选择继承Thread,如果只是简单的执行一个任务,那就实现runnable
一个Android封装好的轻量级异步类。抽象类。
注:Task的实例必须在主线程中创建;execute方法必须在主线程中调用;不要手动调用回调方法。一个Task实例只能执行一次,多次调用会有异常。
AsyncTask对象调用execute方法(可以传一个或多个参数)中调用onPreExecute(),然后是doInBackground(),在其中可以调用publishProgress(),doInBackground()的结果可以给onPostExecute(),在doBackground方法中,每次调用publishProgress方法都会触发onProgressUpdate()方法。
Android系统中线程间传递消息的一种机制,线程间通信。(异步线程与主线程通信一般都有Handler)
因为Android只允许UI线程中修改UI组件,Handler(发送与处理数据)可以解决周期性修改UI组件的属性。
每个线程只能够有一个Looper,管理MessageQueue,不断地从中取出Message分发给对应的Handler处理!
Message对象是封装了需要传递的数据给Handler处理。
Message.what:标识一个Message对象。
Message.obj:存储任意类型的Object对象。
频繁地为每一个任务创建一个线程,缺乏统一管理,降低性能,并且容易出现问题;线程池能对多线程进行同一管理,避免资源竞争中出现的问题;对线程进行复用;有完整的api操作方便。
Handler引起的内存泄露问题?
当使用内部类(包括匿名类)来创建Handler的时候(内部类持有外部引用),Handler对象会隐式地持有Activity的引用。
在请求网络过程中关闭了Activity,因为此时Handler又持有Activity的引用,就导致该Activity无法被收回,直至网络请求结束。
解决方法:使用弱引用。
一个线程有几个Handler?
可以有多个。
一个线程有几个Looper?如何保证?
一个。Looper的构造只能通过Prepare()方法(同时会初始化一个消息队列),当调用后ThreadLocal中的get()方法检查ThreadLocalMap中是否已经set过Looper。
当Activity重新创建时,(非静态内部类)AsyncTask的Activity的引用无效,怎么处理?
在Activity恢复时的对应方法重启 任务线程。
进程间通信方式:(file、内存共享)Binder机制,Bundle,Socket,AIDL,contentProvider
进程是运行起来的程序,被加载到内存,进程独立运行,数据互不干扰。
前台进程:正在交互的进程
可见进程:可见不可操作
服务进程:不可见、后台、正在忙碌
后台进程:在后台,不做事
空进程:缓存作用
是一种Android中实现跨进程通信的方式,是一种虚拟的的物理设备驱动。
普通进程间通信和采用Binder机制进程间通信。
ServiceManager进程 管理Service注册与查询,Binder驱动一种虚拟驱动(传递进程间的数据:通过内存映射;实现线程控制:采用Binder的线程池)。
用于消息传递,以键值对的形式保存数据,可以传递基本数据类型,也可传递引用类型(要实现Serializable或Parcelable的接口)。
结合Intent对象实现传递。
套接字,来描述IP地址和端口,是通信的句柄,通信双方通过Socket对象获得通信信息。
服务进程后台进程区别?
调用startService方法启动的Service进程组件,没有与Activity绑定;不可见Activity,调用了onStop没有调用onDestroy