Service 是什么,启动方式区别,生命周期,如何停用
- Android 的 Service有两个作用,后台或跨进程(AIDL)
- 两个启动方法,分别是Context.startService()和Context.bindService()。
- 生命周期根据启动的方式有两条
- onCreate(), onStart(),onDestroy()。onCreate()在重复启动时不会执行。
- onCreate(), onBind(),onUnBind(),onDestroy()。只能绑定一次。
-销毁方法是onDestroy()。
- 执行耗时操作建议使用Intent Service,自带多线程且自动停止。
BroadcastReceiver是什么,种类,注册方式,生命周期
- BroadcastReceiver系统或应用活动通知接收
- 广播分类
- 普通广播:
Context.sendBroadcast(Intent myIntent)
- 有序广播:
Context.sendOrderedBroadcast(intent, receiverPermission)
第二个参数为[-1000,1000]的优先级,1000为最高优先等级。 - 异步广播:
Context.sendStickyBroadcast(Intent myIntent)
,需要权限<uses-permission android:name="android.permission.BROADCAST_STICKY" />
,需要手动去掉removeStickyBroadcast(intent)
。 - 有序异步广播:
sendStickyOrderedBroadcast(intent, resultReceiver, scheduler, initialCode, initialData, initialExtras)
- 普通广播:
- 动态注册,注册后才存在,需手动解除注册;静态(系统)注册,App未启动也存在
- 生命周期会调方法:
void onReceive(Context curContext,Intent broadcastMsg)
,只有10秒时间,如果超过则报ANR错误。
Broadcast、EventBus和自己实现观察者模式的不同
- Observable耦合度三者最高
- Broadcast重量级,消耗资源较多,跨进程传递消息。与SDK链接紧密,许多系统级事件用广播通知:网络变化、电量、短信发送和接受状态
- EventBus轻量且快速,灵活不依赖Context,使用简单。采用观察者可能会造成接口膨胀
现在比较流行的开源控件RxJava也是用的观察者模式,可以很方便的使用RxJava实现个RxBus取代EventBus。
ContentProvider
- ContentProvider使一个应用程序的指定数据集提供给其他应用程序。这些数据可以存储在文件系统中、在一个SQLite数据库、或以任何其他合理的方式。其他应用可以通过ContentResolver类(见ContentProviderAccessApp例子)从该内容提供者中获取或存入数据
- 通过Uri进行访问。
Uri uri = Uri.parse("content://com.bing.provider.personprovider/person")
- ContentUris类使用
Uri uri = Uri.parse("content://com.bing.provider.personprovider/person");
Uri resultUri = ContentUris.withAppendedId(uri, 10);
//生成后的Uri为:content://com.bing.provider.personprovider/person/10
Uri uri = Uri.parse("content://com.ljq.provider.personprovider/person/10")
long personid = ContentUris.parseId(uri);
//获取的结果为:10
Android系统中GC什么情况下会出现内存泄露呢
导致内存泄露主要的原因是:没有释放申请的内存空间,垃圾回收器GC无法回收内存。如下情况能导致内存溢出:
- 数据链接没有关闭。如数据库contentprovider,io,sokect,cursor等
- (匿名)内部类对象会持有宿主类的强应用this。如果是new Thread这类操作,线程没有执行结束时当前Activity不会被回收。(常见Handler操作)
- Context引用。TextView等等都会持有上下文引用,如果有static drawable,就会导致内存无法释放
- 静态集合类。,ashMap,Vector等。如果是静态集合,要及时setnull,否则就会一直持有这些对象
- observer 我们在使用监听器的时候,addxxxListener(),记得不用时removexxxListener,否则内存leak
- Bitmap对象不使用时,采用recycle()释放内存
Android UI中的View如何刷新
要分清View刷新的情景:多线程和双缓冲。
- 不使用多线程和双缓冲。View发生改变重绘,使用View.invalidate()激活View.onDraw()方法。
- 使用多线程和不使用双缓冲。注意非UI线程不能对UI进行直接操作,需要用Handler进行线程通讯,在UI线程对View进行重绘。
- 使用多线程和双缓冲。SurfaceView直接实现了双缓冲,实现Surfaceholder.Callback接口。
Surfaceholder.lockCanvas()
锁定画布,Surfaceholder.unlockCanvasandPost()
解锁画布。
事件分发机制
Handler机制,线程通讯
Android提供Handler来满足线程间的通讯。
- Handler:Message处理和发送。
- 通过Looper获取MessageQueue中的消息(FIFO)
- 通过Looper将消息放入MessageQueue
- Looper:轮询器
- 每个线程有且只有一个私人的Looper
- 初始化Looper对象,此对象会拥有一个Message Queue对象
- 主线程(UI线程)会自动初始化Looper。其他线程需要
Looper.prepare()
初始化,Looper.loop()
开始轮询
- MessageQueue:消息队列
- 存放一个线程的Message所有
- Message被发送给Hander并处理后不会回收或删除,会被标记为闲置状态
- Handler发送消息是先检索Message Queue中没有没闲置Message,有直接复用,否则新建Message。减少GC回收工作压力,增加内存Leak危险
Handler使用时要特别注意内存Leak:
- 要使用 static 静态类定义Handler派生类
- static 下访问当前对象的字段属性时需要弱引用(WeakReference)
- 在Activity销毁时调用 Handler.removeMessages()将MessageQueue中的消息全部移除
- 自定义控件在定义Handler时,尽量使用new Handler(Looper.getMainLooper()), 不要使用new Handler()。因为后者默认在主线程中执行,事实上有些View的创建在子线程中执行。
进程通讯(AIDL等)
- 访问其他App的Activity:需要App开放支持
- 内容提供者Content Provider :使用Content Provider提供数据,使用Content Resolver读取数据。
- 广播Broadcast:被动接受信息
- AIDL服务:Android Interface Definition Language 安卓接口定义语言
- AIDL服务提供程序:建立AIDL文件;在文件中创建接口申明方法;注册Service实现声明的接口方法
- AIDL服务使用程序:copy自动生成的Java文件;建立ServiceConnection对象并绑定AIDL服务;获取AIDL中的接口对象并调用放发获取Value
AIDL步骤(一):提供AIDL服务
在package目录下建立aidl.aidl文件,申明方法,类似接口
package 包名.aidl;
interface IMyService {
//为AIDL服务的接口方法,调用AIDL服务的程序需要调用该方法
String getValue();
}
ODT会在gen目录下产生一个IMyService.java文件,此文件ODT自动维护,开发者无需在意。
public class MyService extends Service {
// IMyService.Stub类是根据IMyService.aidl文件生成的类
// 该类中包含了接口方法(getValue)
public class MyServiceImpl extends IMyService.Stub {
@Override
public String getValue() throws RemoteException {
return "从AIDL服务获得的值." ;
}
}
@Override
public IBinder onBind(Intent intent) {
//该方法必须返回MyServiceImpl类的对象实例
return new MyServiceImpl();
}
}
manifest配置信息
<!-- 注册服务 -->
<service android:name=".MyService" >
<intent-filter>
<!-- 指定调用AIDL服务的ID -->
<action android:name="net.blogjava.mobile.aidlservice.IMyService" />
</intent-filter>
</service>
AIDL步骤(二):使用AIDL服务
public class Main extends Activity implements OnClickListener {
private IMyService myService = null;
// 创建ServiceConnection对象
private ServiceConnection serviceConnection = new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName name, IBinder service){
// 获得AIDL服务对象
// IMyService.java是AIDL提供工程中自动生成的
myService = IMyService.Stub.asInterface(service);
try {
// 调用AIDL服务对象中的getValue方法
// 并以对话框中显示该方法的返回值
new AlertDialog
.Builder(Main.this)
.setMessage(myService.getValue())
.setPositiveButton("确定", null)
.show();
}catch (Exception e) {
}
}
@Override
public void onServiceDisconnected(ComponentName name)
{
}
};
@Override
public void onClick(View view){
//绑定AIDL服务
bindService(new Intent("提供程序包名.aidl.IMyService"),
serviceConnection, Context.BIND_AUTO_CREATE);
}
}
如何对 Android 应用进行性能分析
android 性能主要之响应速度 和UI刷新速度。
可以参考博客:Android系统性能调优工具介绍
首先从函数的耗时来说,有一个工具TraceView 这是androidsdk自带的工作,用于测量函数耗时的。
UI布局的分析,可以有2块,一块就是Hierarchy Viewer 可以看到View的布局层次,以及每个View刷新加载的时间。
这样可以很快定位到那块layout & View 耗时最长。
还有就是通过自定义View来减少view的层次。
请介绍下Android的数据存储方式。
- 使用Shared Preferences存储数据,用来存储key-value,pairs格式的数据,它是一个轻量级的键值存储机制,只可以存储基本数据类型。
- 使用文件存储数据,通过FileInputStream和FileOutputStream对文件进行操作。在Android中,文件是一个应用程序私有的,一个应用程序无法读写其他应用程序的文件。
- 使用SQLite数据库存储数据,Android提供的一个标准数据库,支持SQL语句。
- 使用Content Provider存储数据,是所有应用程序之间数据存储和检索的一个桥梁,它的作用就是使得各个应用程序之间实现数据共享。它是一个特殊的存储数据的类型,它提供了一套标准的接口用来获取数据,操作数据。系统也提供了音频、视频、图像和个人信息等几个常用的Content Provider。如果你想公开自己的私有数据,可以创建自己的Content Provider类,或者当你对这些数据拥有控制写入的权限时,将这些数据添加到Content Provider中实现共享。外部访问通过Content Resolver去访问并操作这些被暴露的数据。
- 使用网络存储数据
描述一下Intent 和 Intent Filter
- Intent在Android中被翻译为”意图”,他是三种应用程序基本组件-Activity,Service和broadcast receiver之间相互激活的手段。在调用Intent名称时使用ComponentName也就是类的全名时为显示调用。这种方式一般用于应用程序的内部调用,因为你不一定会知道别人写的类的全名。
- Intent Filter是指意图过滤,不出现在代码中,而是出现在android Manifest文件中,以
<intent-filter>
的形式。(有一个例外是broadcast receiver的intent
filter是使用Context.registerReceiver()来动态设定的,其中intent filter也是在代码中创建的)而一个intent有action,data,category等字段。一个隐式intent为了能够被某个intent filter接收,必须通过3个测试,一个intent为了被某个组件接收,则必须通过它所有的intent filter中的一个。
谈谈对Android NDK的理解。
android NDK是一套工具,允许Android应用开发者嵌入从C、C++源代码编译来的本地机器代码到各自的应用软件包中。
1、 NDK是一系列工具的集合。
NDK提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,并能自动将so和java应用一起打包成apk。这些工具对开发者帮助时巨大的。
NDK集成了交叉编辑器,并提供了相应的mk文件隔离CPU、平台、API等差异,开发人员只需要简单修改mk文件(指出“那些文件需要编译”、“编译特性要求”等),就可以创建出so。NDK可以自动将so和Java应用一起打包,极大的减轻了开发人员的打包工作。
2、NDK提供了一份稳定、功能有限的API头文件声明。
这些API支持的功能非常有限,包含有:C标准库(libc)、标准数学库(libm)、压缩库(libz)、log库(liblog)。