这样的问题 回答的时候 ,首先 Activity 是 四大组件之一,是一个view 对象的容器,可以用来展现一个界面,通过 setcontentView() ,//方法来 设置要显示的布局
activity 是 上下文对象 Context的子类 同时 实现了 window.callback 和 keyevent.callback 这两个接口, 所以 activity 可以响应 与处理 窗体与用户的交互事件,以及与键盘相关的事件。(Context 上下文对象, 其实就是一个变量 ,维护了android 应用 一些公共环境的引用,比如 通过 上下文 ,我们就可以拿到 资源管理的服务,系统的资产目录,或者通过getcachdir()获取当前应用运行所在的目录,一旦拿到了上下文,就相当于拿到了已知相关环境的引用)。拿到activity 就可以重写 onkeydown ontouchevent 去处理窗体事件。
生命周期,描述的是一个类,从创建(new 出来)到死亡(垃圾回收)过程中 执行的方法, 在这个过程中,对于不同的生命阶段会调用不同的方法。
activity 从创建打销毁有多种状态,从一种状态到另一种状态 会激发相应的方法。这些回调方法包括,oncreate ondestroy onstart onstop onresume onpause, 这些方法都是两两对应的 oncreate 创建 与 ondestroy 销毁。 onstart 可见 与 onstop 不可见 onresume可编辑(获取到焦点)与 onpause 失去焦点。 还有一种情况 当 activity 被 stop掉 ,但是没有被 destroy ,再次启动此 activity 时候就会调用 onrestart 方法(而不调用 oncreate)。如果被destroy 了 那么就会调用 oncreate。
结合项目讲 ,为了保证每次进入activity 看到的界面是最新的 我们就把这操作 写到onstart 方法里。或者检查网络(每次进入界面检查是否联网)。 视频播放 ,比如 看着视频,有电话打进来,那么我们就需要把视频播放器 暂停掉,这个操作放在onpause 或者onstop 方法里面 比较好,然后在onresume 里面 在恢复 播放。
常用的c 就是以上这些方法 还有一些方法可以了解下 当做扩展 比如 onpostresume onpostcreate 这是系统自己调用的, 在执行 onresume oncreate 的时候会去执行。
一般 从A activity 跳转到 B activity 首先 A activity 会失去焦点(onpause) 变为不可见 (onstop)B activity 在 创建 (oncreate)可见(onstart) 获得焦点(onreusme),但是一些特殊情况,比如 把 B activity 背景设为透明,或者 是 dialog 样式,那么 A activity 在失去焦点后,还是可见的,所以不会执行 onstop 方法。
这个生命周期 跟清单文件 里面的配置有关。
1.不设置Activity的 android: configchanges 时 ,切换屏幕会重新调用 个生命周期方法 首先onpause onstop ondestroy 然后 oncreate onstart onresume .
如果我们 设置 configChanges 设置为 android:configChanges="orientation|keyboardHidden"时,默认 就不会销毁 在创建,但是 我发现 在我的手机上,打开 自动旋转屏,一样还是会销毁在创建, 关闭旋转屏 就没问题。 一般开发中我们都会把 activity 设置成 orentation的。
退出 activity 直接调用finish 方法(比如用户点击back键 就是退出一个activity ),退出activity 会执行 ondestroy 方法。
1,异常 退出, 要退出的时候抛出一个异常 比如空指针 使程序 force close ,比如
import java.lang.Thread.UncaughtExceptionHandler; import android.os.Bundle; import android.app.Activity; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; public class MainActivity extends Activity implements OnClickListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Thread.setDefaultUncaughtExceptionHandler(new ExtAppExceptionHandler());//Sets the default uncaught exception handler. This handler is invoked in case any Thread dies due to an unhandled exception findViewById(R.id.btn).setOnClickListener(this); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } String s; public void extApp(){ // try { s.equals("aaa");// 这里 s 未进行初始化,肯定会抛出异常, 如果我们要退出 程序 就要拦截掉弹出的 crash 窗口(注意不要自己捕获,否则就会拦截掉) // } catch (Exception e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } } class ExtAppExceptionHandler implements UncaughtExceptionHandler{ @Override public void uncaughtException(Thread thread, Throwable ex) { android.os.Process.killProcess(android.os.Process.myPid());// 这里杀死当前进程。退出app } } @Override public void onClick(View v) { extApp(); } }
2, 可以定义一个集合,每次进入activity的时候 将当前activity 加入到 集合中, 在每个activity 的ondestroy 方法中 remove掉 添加进的当前activity。(以上 可在baseactivity 中), 然后在我们退出app的时候 便利 这个集合,把每一个都finish()。
3. 我们也可以通过广播的方式, 在 每个activity 中注册一个广播, 当我们 退出 的时候 发送一个广播 然后finish 掉 每个activity。
以上三种是比较常用的关闭app的操作。
当问到 对于 activity的理解 问题 可以回答以上几点。
默认 情况,没有显示的指定 service 所在的线程,那么service 和 activity 是运行在当前app 所在进程的 main thread(ui 线程)中的,所以 不可以做耗时操作。
如果service 中要做耗时操作,那么可以在service 中开启一个子线程,或者 使用intentservice (intentservice onCreate方法中 初始化了一个ServiceHandler,在onHandleIntent这个接口中 我们可以执行耗时操作 )让 service 不在主线程中执行,需要在清单文件中注册 service android :process="other process"
一般通过 intent。 intent 可以传递 基本类型 以及 对象 (实现 序列化 或者邮包)或者bundle bundle 实现了Bundle implements Parcelable内部维护了一个 hashmap。可以把bundle理解成map put key value 方式存储数据。
在activity 的oncreate 方法中 调用 startservice 方法 即可。
可以 在开启一个新的activity 的时候通过 intent 给activity 设置intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);即可。注意 当我们在 service 或者broadcastreceiver 中开启一个 activity 时一定要注意设置flag 因为在这两个组件中默认是没有任务栈的。(结合 launchmode )
服务 有两种开启方式 一种是 startservice 一种是 bindservice。
startservice 一旦被开启 与调用者 就没有关系了。这个服务 会长期的在后台运行。
bindservice 是 将调用者 与 服务绑定,如果开启这个服务的 activity 被销毁,那么这个服务也相应的要挂掉
bindservice 有一个特点 就是 可以让我们间接地 调用服务里的方法 bindService(Intent service, ServiceConnection conn, int flags) serviceConnection 中两个方法onServiceConnected(ComponentName name, IBinder service) onServiceDisconnected(ComponentName name)
service的生命周期 比activity 少,常用的 就只有 oncreate onstartCommand onbind onunbind ondestroy 这些。
通常两种启动方式一种是 startservice 一种是 bindservice。
startservice 一旦被开启 与调用者 就没有关系了。这个服务 会长期的在后台运行。’
bindservice 是 将调用者 与 服务绑定,如果开启这个服务的 activity 被销毁,那么这个服务也相应的要挂掉
bindservice 有一个特点 就是 可以让我们间接地 调用服务里的方法 bindService(Intent service, ServiceConnection conn, int flags) serviceConnection 中两个方法onServiceConnected(ComponentName name, IBinder service) onServiceDisconnected(ComponentName name)
两种启动方式 对于 service 生命周期有不同的影响
通过 startservice 方式
service会经历 oncreate onstartCommand 然后处于运行状态,停止时 ondestroy 注意如果你将服务写成内部类的话(广播 用内部类 同理) 这里 一定要将 服务类前面加上static 修饰 否则会报 no empty constructor 这样一个错,不能初始化这个服务,还有在清单文件中声明 内部类的时候 格式要类似 <service android:name=".MainActivity$MyService"/> 外部类 mainactivity+内部类
如果 你通过 bindService 方式 启动 服务 。
那么相应的 会执行 oncreate onBInd 当activity 退出时会执行 onunbInd ondestroy、
PS : 记得 我们在activity 的 Ondestroy 方法中要 执行下stopService 或者unbindService, 否则 会报错 Activity has leaked ServiceConnection 等,应该重写onDestroy方法,取消绑定、
service的 一个原则 无论是 先start 还是 bind oncreate 方法只会执行一次,换句话说, 无论你 执行了多少次 startService 或者 bindservice service 只被创建一次、
多次调用startService onstart 方法会调用多次 , 多次调用 bindService onbind 方法 只会执行一次 多次 destroy 也只执行一次。 多次调用unbindService 会报异常。
普通的Service 默认运行在 UI 线程中, 而 intentService内部维护了 一个handler 我们使用 intentservice 的时候 只要去实现 onhandleIntent 接口 可以在这接口做耗时操作, 注意 我们 使用 intentservice时候 要自己添加一个空参数的构造方法,否则会在初始化 intentService的时候报错 InstantiationException。
intentService 就是 android 提供的一个带有异步处理的service 类。
对于谷歌开发者文档中Processes 根据优先级的高低分为, 前台进程(Foreground process) 可见进程( Visible process) 服务进程(Service process) 后台进程( Background process)空进程( Empty process)
当系统内存不足的 时候 低优先级的进程 容易被回收掉 优先级越高,越靠后被回收掉
关于 service process 的介绍 :A process that is running a service that has been started with the startService()
method and does not fall into either of the two higher categories. Although service processes are not directly tied to anything the user sees, they are generally doing things that the user cares about (such as playing music in the background or downloading data on the network), so the system keeps them running unless there's not enough memory to retain them along with all foreground and visible processes.
后台进程; 就是按 home键 后 应用在后台运行,activity 执行了 onstop 方法, 当系统内存不足 时,可能会试图去回收 后台进程。 当后台进程的activity 被销毁时,会执行 onsavedInstance 方法 。
服务进程: 当一个进程r 中 有服务运行时 比如 应用中开启服务 播放音乐,下载数据,等,系统会尽量维持 有服务的进程 ,知道内存不足时,才会试图去回收 服务进程。这就是为什么有些耗时操作要放在服务中进行,而不是 在activity 中 开启一个子线程,因为activity 优先级比较低,容易被回收, 当前activity 被回收后,当前进程就变成了后台进程 或者空进程,容易被回收。 如果进程被回收了,那么线程也挂掉了,所以 为了保持我们应用在后台一直存在运行,我们一般用service 组件里面 去开启线程执行耗时操作。这样就能保证我们的进程 不会被轻易地回收掉。
前台进程: 就是屏幕直接能看到的,比如launcher 就是一个前台进程,可以响应我们的交互事件
可见进程: 就是 能看到的,比如 我们 将一个mainactivity 设置主题透明,那么这个 应用 就是 前台进程,后面的launcher 就是可见进程。
给 service 设置 setForground(true) 可以使服务类似于 前台进程,能尽量保证 服务不会被回收。 service 的 特点 可以使其在后台一直运行,所以一般耗时操作 放在这里运行,比如 广播接受者 接收到一个广播 要做耗时操作(比如上传资源到服务器),广播接受者的生命周期很短赞,要做耗时操作,就可以在receive 方法里面 开启一个服务
比如 拿网络更新数据来说。 activity 可以显示 最新的数据, 在activity 中 有一个服务 ,去请求 最新的数据,而 intent 可以开启这个service。
广播接受者,就是 接受系统 以及 应用 自身 发出的 广播的 对象。比如系统的 开关机,来电 短信。屏幕 亮暗。。。。等等都有定义广播
广播分两种 有序广播 和无需广播 ,有序广播 是一个同步的操作, 无须广播是一个异步的操作
终止一条广播 要在 接受到广播后调用abortbroadcast 方法, 指定的广播接受者 是不能被拦截的 一定会受到广播,(比如 电话拦截,电话 是通过注册 绑定了接受者 一定会受到广播,如果我们要拦截掉,只能 在自己定义的 接受者里面 setResultData(null ); 将数据 置为null. 而无法拦截让电话 不收到广播。
private class MyReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub setResultData(null ); } }
sendStickyBroadcast(intent): 这是粘性广播,也就表示 阴魂不散的广播,比如 一个普通的广播接受者,如果我们没有注册,那么 就不会接收到广播。一个广播(一般生命周期只有20秒,接收不到,那么就接收不到了)而 sticiky 则不同 我们发送一个 stickky的 广播,那么这个广播会停留一段时间,比如 手机中 网络的广播 开启wifi 需要初始化 网卡,接收网络热点等等,20秒的时间可能不够用,所以这里更改手机网络状况 就用了 sendstickyBroadCast 保证 网卡状态一定会被更新。(这个方法了解即可)wifi 设置。
广播接受者 的注册方式 ,一般有两种
一种是 清单文件中注册 : 注册 receiver 过滤 intentfiler 一旦应用部署到手机上面 就生效了(现在可能需要手动启动一次应用,安装后 未启动过 好像 还不能用,android 安全优化。) 一种是 代码中注册 registReceiver(receiver,filter) (代码注册的广播 必须要执行后 广播才会生效) 发送自定义广播 intent itent=new Intent("com.self.broadcast") sendBroadCast(intent);
两种注册类型的区别:
静态注册是当程序关闭后,如果有广播发过来,还能启动程序
动态注册的生命周期跟程序的生命周期是一样的,程序关闭后动态注册的广播是不能在接收到广播的
动态注册的优点:在Android的广播机制中,动态注册的优先级高于静态注册的优先级,因此在必要情况下,我们需要动态注册广播接收器。
静态注册的有点:动态注册广播接收器还有一个优点就是当用来注册广播的Activity关闭后,广播也就失效了,同时反映了静态注册广播的一个优势,就是无需担心广播接收器是否关闭,只要设备处于开启状态,广播接收器就能接收。
什么是内容提供者 ?
内容提供者是android 中的四大组件之一,可以将应用各种的数据 对外进行共享(比如我们应用的数据库,只有我们应用有权限读写,如果让其他应用访问,就需要共享出去)。
内容提供者将数据风筝,只暴露出我们洗完共提供给其他程序的数据
内容提供者将数据访问方式统一(crud),不必针对不同数据类型采取
怎么使用contentprovider?
跟 其他三大组件一样, 只要我们自己写一个类 继承 Contentprovider 类,然后实现父类的方法即可,注意 四大组件 都需要在 清单文件中注册才可以生效。
清单文件中注册<provider android:name=".xx" android:authorities=".xxx"> 内容提供者其实就是通过url name 就是我们自定义的provider 子类 名,这个authorities 其实就是 一个别名,供其他应用 找到 我们定义的这个类。
在其他应用中,我们要拿到一个 contentresolver。 然后获取到uri. 内容提供者 就是通过uri 进行数据共享的, uri 有三部分组成
1.scheme:ContentProvider(内容提供者)的scheme已经由Android所规定为:content://。
2.主机名(或Authority):用于唯一标识这个ContentProvider,外部调用者可以根据这个标识来找到它。
3.路径(path):可以用来表示我们要操作的数据,路径的构建应根据业务而定,如下:
• 要操作contact表中id为10的记录,可以构建这样的路径:/contact/10
• 要操作contact表中id为10的记录的name字段, contact/10/name
• 要操作contact表中的所有记录,可以构建这样的路径:/contact
然后拿contentresolver对象 就可以访问 内容提供者的方法。
1 文件 访问权限 sdcard /data/data/包名/files..
2 数据库 sqlite
3 sharedpreferences
4 网络
5 内容提供者
内容提供者 可以在不同应用间共享数据,比如 有些 数据是私有的, 让没有权限的其他应用访问 本应用中的数据,同时 内容提供者 可以屏蔽掉 数据存储的细节,,只提供用户访问方式不需要去关心怎么实现的,内容提供者既可以操作数据库 也可以操作xml 本地文件等,sql 只能操作数据库。
如果我们知道 包名 类型,可以通过intent 显式的指定
Intent intent=new Intent(); intent.setAction(Intent.ACTION_VIEW); intent.addCategory(Intent.CATEGORY_BROWSABLE); intent.setData(Uri.parse("http://www.baidu.com")); startActivity(intent);
或者
intent.setClassName("com.android.browser", "com.android.browser.BrowserActivity");
若我们不知道包名,那么 我们可以通过 packagemanager 拿到 能开启的所有activity 去便利 ,代码如下
Intent intent=new Intent(); intent.setAction(Intent.ACTION_VIEW); intent.addCategory(Intent.CATEGORY_BROWSABLE); intent.setData(Uri.parse("http://www.baidu.com")); PackageManager pm = getPackageManager(); List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0); for (ResolveInfo resolveInfo : resolveInfos) { System.out.println("pm-----------------"+resolveInfo.activityInfo.applicationInfo.packageName+" ====="+resolveInfo.activityInfo.applicationInfo.className); 05-14 18:01:02.207: I/System.out(16983): pm-----------------com.android.browser =====com.android.browser.Browser 05-14 18:01:02.207: I/System.out(16983): pm-----------------com.browser2345 =====com.browser2345.Browser 05-14 18:01:02.207: I/System.out(16983): pm-----------------com.ijinshan.browser_fast =====com.ijinshan.browser.KApplication }
可以看到 我这里 的三个 浏览器包名 类名。
四大组件 都是运行在主线程中的, 主线程负责维护一些与生命周期相关的方法的回调, 比如 activity oncreate onstart 这些与生命周期相关的方法都是运行在主线程中的
所有界面的更新 都是通过主线程来处理的。比如显示一个button。还有广播,比如电量低 ,插入sd卡,发出的广播 也都是通过主线程消息机制 完成的。主线程非常重要,不能做耗时操作,否则 会导致 ANR。
复用 convertview,使用 viewholder 将少findviewbyid的次数 ,大数据量 采用分页加载,分批加载 异步加载。 一些图片等资源 采用缓存机制啊,本地保存 内存保存等等, 提前把数据加载进内存,而不是用的时候再去加载。
android 的任务栈 有四种启动模式, standard singletop singletask singleInstance
standard模式 : 也是标准的 启动模式 此模式下 启动一个activity 就会有一个activity的实例加载到 任务栈中,对于使用频率启动频率一般的activity 使用standard 很合适 所以它是默认方式。
singletop : 大部分时候跟standard 一致,区别在于,当前activity 正在栈顶 时,再次启动不会去创建一个新的activity 。android 自带浏览器的书签activity 就是使用的 singletop 模式。
singletask 与 singleInstance 都是另外开辟任务栈的模式。 标志位 singletask 的activity ,最多仅有一个实例存在,并且 以它位根的task中 所有对该activity的请求吗,都会跳到该activity的task 中进行,singletask 很想概念中的单例模式, 所有的修改都是基于一个实例, 这通常用在 构造成本很大,但切换成本叫小的activity,比如 浏览器的主activity, 天是展示当前的tab, 当前内容的窗口, 它的构造成本很大,但是也没的切换 还是比较快的,于是 singletask 相配合
singleInstance: 与singletask的不同就是 ,如果activity 设置了singleInstance 那么 它所在的任务栈中仅有自己一个activity, 如果涉及到其他activity 都在其他的任务栈中,
扩展
有些时候 我们希望不同程序 中一些activity 运行在相同的任务栈中 , 我们可以在清单文件中的activity 或者application 节点下设置着两个属性 ,
android:allowTaskReparenting="true" 允许重新寻找父母任务栈
android:taskAffinity="name..亲缘关系" 任务栈亲缘关系
尽量多使用内部类,提高变成效率
官方推荐 在我们的javabean 中将变量 用public 修饰, 直接访问,而不是使用get set 方法
application 这个变量 的生命周期很长,只有当进程结束 才会回收,所以不必要的对象 不要放在application 中。
合理设置变量的 作用范围,比如 只在一个方法中用到的变量 不要定义成全局变量, 只在一个activity 中的变量 不要定义在application节点中。
手动提示 我们 dalivk 虚拟机去回收垃圾, system.gc()
可以的 AIDL 跨进程通信 访问远程服务。。。 比如支付宝 ,就是在其他应用中调用支付宝服务。
DVM指dalivk的虚拟机。每一个Android应用程序都在它自己的进程中运行,都拥有一个独立的Dalvik虚拟机实例。而每一个DVM都是在Linux 中的一个进程,所以说可以认为是同一个概念。
与sim卡有关的东西,存储了一些数据,一般都是一些标示信息之类的,文件头,只要有文档 就可以解析。
26. 如何判断是否 有SD卡?
Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) 如果是true 则有。
其实 说 70个汉字,140byte 都是不标准的,这个 与手机的制式有关,可以查看发送短信 通过 SmsManager 对象 调用dividemessage。 看这个源码你会发现跟phonetype 有关 最大值有两种一种是 153个字母 134字节(小灵通手机最大支持67个汉字)
另一种是 160英文字母 140byte(传统的gsm cdma 等等手机是 70个汉字 )
*res/raw和assets的相同点:
1.两者目录下的文件在打包后会原封不动的保存在apk包中,不会被编译成二进制。
*res/raw和assets的不同点:
1.res/raw中的文件会被映射到R.java文件中,访问的时候直接使用资源ID即R.id.filename;assets文件夹下的文件不会被映射到R.java中,访问的时候需要AssetManager类。
2.res/raw不可以有目录结构,而assets则可以有目录结构,也就是assets目录下可以再建立文件夹
*读取文件资源:
1.读取res/raw下的文件资源,通过以下方式获取输入流来进行写操作
· InputStream is =getResources().openRawResource(R.id.filename);
2.读取assets下的文件资源,通过以下方式获取输入流来进行写操作
· AssetManager am = null;
· am = getAssets();
· InputStream is = am.open("filename");
注意1:Google的Android系统处理Assert有个bug,在AssertManager中不能处理单个超过1MB的文件,不然会报异常,raw没这个限制可以放个4MB的Mp3文件没问题。
注意2:assets 文件夹是存放不进行编译加工的原生文件,即该文件夹里面的文件不会像 xml, java 文件被预编译,可以存放一些图片,html,js, css 等文件。