目录
- Activity异常退出数据保存
- Activity生命周期
- Fragment生命周期
- BroadCast
- Service
- APP启动流程
- Activity启动流程
- 启动模式
- Intent显示跳转与隐式跳转
- Activity A跳转B,B跳转C,A不能直接跳转到C,A如何传递消息给C?
- Activity如何保存状态的?
- Android进程回收
- apk打包
- Gradle
- Android多线程
- AsyncTask
- Handler机制
- HandlerThread
- IntentService
- Parcelable 和 Serializable
- Binder
- Messager
- Messager和AIDL对比
- AIDL
- Binder连接池
- 故障问题
- ANR
- OOM
Activity异常退出数据保存
activity退出
activity正常退出:点击back键;点击锁屏键;点击home键;其他APP进入前台;启动了另一个Activity。
activity异常退出:屏幕方向旋转;内存不足APP被Kill;因异常(RuntimException)未捕获出现crash时,弹出force close弹窗。
防止activity重建
不仅仅当屏幕方向切换时会重建Activity,当系统配置发生改变的时候Activity都会被重建,例如用户插入外接键盘,运营商改变,界面模式(例如开启夜间模式)等都会导致Activity重建。
设备配置的更改会导致Acitivity销毁重建,而设置android:configChanges则避免Activity销毁重建,系统会回调onConfigurationChanged方法,不再调用onCreate()
例如我们不希望屏幕旋转时重建,则需要如下设置:
android:screenOrientation=“portrait”
android:configChanges=“orientation”
android:configChanges
VALUE | DESCRIPTION |
---|---|
mcc | 国际移动用户识别码所属国家代号是改变了,sim被侦测到了,去更新mcc MCC是移动用户所属国家代号 |
mnc | 国际移动用户识别码的移动网号码是改变了, sim被侦测到了,去更新mnc MNC是移动网号码,最多由两位数字组成,用于识别移动用户所归属的移动通信网 |
locale | 用户所在区域发生变化,一般是用户切换了语言时,切换后的语言会显示出来 |
touchscreen | 触摸屏是改变了------通常是不会发生的 |
keyboard | 键盘发生了改变----例如用户用了外部的键盘 |
keyboardHidden | 键盘的可用性发生了改变 |
navigation | 导航发生了变化-----通常也不会发生 |
screenLayout | 屏幕的显示发生了变化------不同的显示被激活 |
fontScale | 字体比例发生了变化----选择了不同的全局字体 |
uiMode | 用户的模式发生了变化 |
orientation | 屏幕方向改变了—横竖屏切换 |
screenSize | 屏幕大小改变了 |
smallestScreenSize | 屏幕的物理大小改变了,如:连接到一个外部的屏幕上 |
android:screenOrientation
VALUE | DESCRIPTION |
---|---|
unspecified | 默认值,由系统决定,不同手机可能不一致 |
landscape | 强制横屏显示 |
portrait | 强制竖屏显示 |
behind | 与前一个activity方向相同 |
sensor | 根据物理传感器方向转动,用户90度、180度、270度旋转手机方向,activity都更着变化 |
sensorLandscape | 横屏旋转,一般横屏游戏会这样设置 |
sensorPortrait | 竖屏旋转 |
nosensor | 旋转设备时候,界面不会跟着旋转。初始化界面方向由系统控制 |
user | 用户当前设置的方向 |
fullSensor | 显示的方向(4个方向)是由设备的方向传感器来决定的,除了它允许屏幕有4个显示方向之外,其他与设置为“sensor”时情况类似,不管什么样的设备,通常都会这么做。例如,某些设备通常不使用纵向倒转或横向反转,但是使用这个设置,还是会发生这样的反转。这个值在API Level 9中引入 |
fulluser | 如果用户锁定了基于传感器的旋转,其行为与 user 相同,否则,其行为与 fullSensor 相同,允许所有 4 种可能的屏幕方向。 API 级别 18 中的新增配置。 |
locked | 将方向锁定在其当前的任意旋转方向。API 级别 18 中的新增配置。 |
onPause保存持久化数据
在执行了onPause()方法之后,直到执行onResume()之前,该activity所在的进程都是有可能“突然死亡”的,onStop()和onDestroy()方法并不能保证一定会被执行。所以onPause()方法是持久化相关数据的最后的可靠时机。
Android数据持久化
Android数据持久化即是将数据保存至外存中,主要有以下几种:Shared Preferences、Internal Storage、External Storage、SQLite Databases。
Shared Preferences
SharedPreferences可以保存的数据类型有:int、boolean、float、long、String、StringSet。
SharedPreferences.Editor editor = getSharedPreferences("data",MODE_PRIVATE).edit();
editor.putString("name","xiaoming");
editor.apply();
SharedPreferences pref = getSharedPreferences("data",MODE_PRIVATE);
String name = pref.getString("name","");
SharedPreference.Editor的apply和commit方法异同
- apply没有返回值而commit返回boolean表明修改是否提交成功
- apply是将修改数据原子提交到内存, 而后异步真正提交到硬件磁盘, 而commit是同步的提交到硬件磁盘,因此,在多个并发的提交commit的时候,他们会等待正在处理的commit保存到磁盘后在操作,从而降低了效率。而apply只是原子的提交到内容,后面有调用apply的函数的将会直接覆盖前面的内存数据,这样从一定程度上提高了很多效率。
Internal Storage
通过文件的形式将数据保存到手机内部存储空间中,并且这些文件是私有的,其他程序无法访问。当卸载掉程序之后,这些文件也会被相应移除。
写数据时,调用openFileOutput(String name, int mode)方法获得一个输出流,参数一是输出的文件名而输出文件的路径是系统自定的/data/data/…/files/;参数二是操作模式,主要有两种模式:Context.MODE_PRIVATE,覆盖原有的文件;Context.MODE_APPEND,内容追加到原来的文件上。
FileOutputStream out = null;
BufferedWriter writer = null;
try{
out = openFileOutput("data", Context.MODE_PRIVATE);
writer = new BufferedWriter(new OutputStreamWriter(out));
writer.write(str);
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(writer != null)
writer.close();
}catch (IOException e){
e.printStackTrace();
}
}
读数据时,调用openFileInput(String name)方法获得一个输入流,由于路径由系统自定,所以参数就是要读取数据的文件名。
FileInputStream in = null;
BufferedReader reader = null;
StringBuilder content = new StringBuilder();
try{
in = openFileInput("data");
reader = new BufferedReader(new InputStreamReader(in));
String line = "";
while((line = reader.readLine()) != null)
content.append(line);
}catch (IOException e){
e.printStackTrace();
}finally {
if(reader != null){
try{
reader.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
External Storage
通过Environment类的getExternalStorageState()方法来判断手机是否有SDcard: Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)
最通过getExternalStorageDirectory()方法来获取文件目录:File file = new File(Environment.getExternalStorageDirectory().getCanonicalPath() + “/test.txt”);
读写的文件都在sdcrad文件夹中通过File Explorer可以导出来
读写操作和基本IO操作相同了
SQLite Databases
数据库版本号更新,新版数据库中表一些字段做修改或删减,数据库旧版如何升级?
- 只是新增字段
在onUpgrade()执行以下spl语句即可
ALTER TABLE [表名] ADD COLUMN [字段名] BLOB; - 遇到复杂的修改字段操作
可将原表改为临时表,将临时表数据导入到新表,删除临时表
把数据库的版本后修改一下,再次创建数据库操作对象的时候就会自动更新(注:更新的时候第一个创建的操作数据的对象必须是可写的,也就是通过这个方法getWritableDatabase()获取的数据库操作对象)
示例代码如下
// 原有的数据库表
CREATE_BOOK = "create table book(bookId integer primarykey,bookName text);";
// 增加一个字段:
CREATE_BOOK = "create table book(bookId integer primarykey,bookName text,bookContent text);";
// 把原来的数据库表重命名一下
CREATE_TEMP_BOOK = "alter table book rename to _temp_book";
// 把备份表中的数据copy到新创建的数据库表中
INSERT_DATA = "insert into book select *,' ' from _temp_book";(注意' '是为新加的字段插入默认值的必须加上,否则就会出错)。
// 把备份表干掉就行啦。
DROP_BOOK = "drop table _temp_book";
public class DBservice extends SQLiteOpenHelper{
private String CREATE_BOOK = "create table book(bookId integer primarykey,bookName text);";
private String CREATE_TEMP_BOOK = "alter table book rename to _temp_book";
private String INSERT_DATA = "insert into book select *,'' from _temp_book";
private String DROP_BOOK = "drop table _temp_book";
public DBservice(Context context, String name, CursorFactory factory,int version) {
super(context, name, factory, version);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_BOOK);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
switch (newVersion) {
case 2:
db.execSQL(CREATE_TEMP_BOOK);
db.execSQL(CREATE_BOOK);
db.execSQL(INSERT_DATA);
db.execSQL(DROP_BOOK);
break;
}
}
创建数据库
public class MyDatabaseHelper extends SQLiteOpenHelper {
public static final String CREATE_TIME = "create table Time(time date)";
private Context myContext;
public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
myContext = context;
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_TIME);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
onSaveInstanceState & onRestoreInstanceState
对onSaveInstanceState如此设计就是让其完成对一些临时的、非永久数据存储并进行恢复。
onSaveInstanceState函数的源代码
Window 的 HierarchyState,可以理解为窗口的阶级状态,里面包含的数据已经打印在上面的图片中,其实也就是一些panels,一些包含ID的UI控件,还有一些ActionBar等信息,还包括一些用户自己保存的信息
用一个Paracelable 对象保存 Fragment的所有状态,再将这个Paracelable对象添加到Bundle对象中
protected void onSaveInstanceState(Bundle outState) {
outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
Parcelable p = mFragments.saveAllState();
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);
}
getApplication().dispatchActivitySaveInstanceState(this, outState);
}
恢复数据时调用onCreate方法
onCreate方法恢复数据时是恢复了Paracelable对象下的Fragment的allstate,前提是 saveInstanceState!=null ,这个条件一般不容易满足,只有在Activity被系统销毁时,onCreate方法中的参数saveInstanceState才不会空
protected void onCreate(Bundle savedInstanceState) {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onCreate " + this + ": " + savedInstanceState);
if (mLastNonConfigurationInstances != null) {
mAllLoaderManagers = mLastNonConfigurationInstances.loaders;
}
if (mActivityInfo.parentActivityName != null) {
if (mActionBar == null) {
mEnableDefaultActionBarUp = true;
} else {
mActionBar.setDefaultDisplayHomeAsUpEnabled(true);
}
}
if (savedInstanceState != null) {
Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.fragments : null);
}
mFragments.dispatchCreate();
getApplication().dispatchActivityCreated(this, savedInstanceState);
mCalled = true;
}
调用了onCreate方法之后,系统再调用onRestoryInstanceState方法恢复数据,onRestoryInstanceState方法的源代码如下
protected void onRestoreInstanceState(Bundle savedInstanceState) {
if (mWindow != null) {
Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
if (windowState != null) {
mWindow.restoreHierarchyState(windowState);
}
}
}
Activity生命周期
启动Activity:系统会先调用onCreate->onStart->onResume
当前Activity被其他Activity覆盖其上或被锁屏:系统会调用onPause方法
当前Activity由被覆盖状态回到前台或解锁屏:系统会调用onResume方法
当前Activity转到新的Activity界面或按Home键回到主屏,自身退居后台:系统会先调用onPause->onStop,进入停滞状态。
.用户后退回到此Activity:系统会先调用onRestart->onStart->onResume
用户退出当前Activity:系统先调用onPause->onStop->onDestory
onSaveInstanceState
(1)在Activity被覆盖或退居后台之后,系统资源不足将其杀死,此方法会被调用;无法保证什么时候发生,系统根据资源紧张程度去调度
(2)在用户改变屏幕方向时,此方法会被调用;调用此方法时,我们可以保存一些临时数据
(3)在当前Activity跳转到其他Activity或者按Home键回到主屏,自身退居后台时,此方法会被调用。调用此方法是为了保存当前窗口各个View组件的状态。onSaveInstanceState的调用顺序是在onPause之前。
onWindowFocusChanged
在Activity窗口获得或失去焦点时被调用
例如创建时首次呈现在用户面前;
当前Activity被其他Activity覆盖;
当前Activity转到其他Activity或按Home键回到主屏,自身退居后台;
用户退出当前Activity。
当Activity被创建时是在onResume之后被调用,当Activity被覆盖或者退居后台或者当前Activity退出时,它是在onPause之后被调用
onRestoreInstanceState
(1)在Activity被覆盖或退居后台之后,系统资源不足将其杀死,然后用户又回到了此Activity,此方法会被调用;
(2)在用户改变屏幕方向时,重建的过程中,此方法会被调用。
我们可以重写此方法,以便可以恢复一些临时数据。onRestoreInstanceState的调用顺序是在onStart之后。
如何摧毁一个Activity?
finish(), exit()
Fragment生命周期
生命周期调用
1)创建Fragment
onAttach() —> onCreate() —> onCreateView() —> onActivityCreated() —> onStart() —> onResume()
2)按下Home键回到桌面 / 锁屏
onPause() —> onStop()
3)从桌面回到Fragment / 解锁
onStart() —> onResume()
4)切换到其他Fragment
onPause() —> onStop() —> onDestroyView()
5)切换回本身的Fragment
onCreateView() —> onActivityCreated() —> onStart() —> onResume()
6) 按下Back键退出
onPause() —> onStop() —> onDestroyView() —> onDestroy() —> onDetach()
总结
1)onAttach() 和 onCreate() 只在 Fragment 与 Activity 第一次关联时调用。
2)onDestroy() 和 onDetach() 只在 Fragment 的宿主 Activity 销毁时才会被调用。
3)将 Fragment 通过 addToBackStack() ,以及Fragment 与 ViewPager 结合使用时只涉及 onCreateView() 和 onDestroyView() 这之间的生命周期。add() 和 replace() 不会对 Fragment 的生命周期产生影响,但 add() 方法会造成 Fragment 叠加显示。
4)通过 hide() 、 show() 来隐藏、显示Fragment,此时 Fragment 只改变了可见性,并不涉及生命周期的改变
5)不要在 Fragment 的 onCreate() 方法中操作宿主Activity 的 UI。因为你无法保证此时 宿主Activity 的 UI 已经完全初始化。
Fragment的构造函数为什么不让传参?
因为Fragment的构造方法是无参构造法。
如果需要传递参数,可以调用setArgument(),因为Fragment的初始化方法中将args都保存在mArgument中
public static Fragment instantiate(Context context, String fname, Bundle args) {
try {
Class<?> clazz = sClassMap .get(fname);
if (clazz == null) {
// Class not found in the cache, see if it's real, and try to add it
clazz = context.getClassLoader().loadClass(fname);
sClassMap .put(fname, clazz);
}
Fragment f = (Fragment)clazz.newInstance();
//获取Bundle原先的值,这样一开始利用Bundle传递进来的值,就放入f. mArguments
//只需要在Fragment中利用getArguments().getString("key");就能将参数取出来继续用
if (args != null) {
args.setClassLoader(f.getClass().getClassLoader());
f. mArguments = args;
}
return f;
} catch (ClassNotFoundException e) {
throw new InstantiationException( "Unable to instantiate fragment " + fname
+ ": make sure class name exists, is public, and has an"
+ " empty constructor that is public" , e);
} catch (java.lang.InstantiationException e) {
throw new InstantiationException( "Unable to instantiate fragment " + fname
+ ": make sure class name exists, is public, and has an"
+ " empty constructor that is public" , e);
} catch (IllegalAccessException e) {
throw new InstantiationException( "Unable to instantiate fragment " + fname
+ ": make sure class name exists, is public, and has an"
+ " empty constructor that is public" , e);
}
//...
setArguments使用示例
Bundle bundle = new Bundle();
bundle.putString("key", value);
fragment.setArguments(bundle);
Fragment add与replace的区别,分别对Fragment的生命周期影响
add()
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.hide(Fragment1);
ft.add(R.id.simple_fragment, Fragment2);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.commit();
Fragment1不走任何生命周期,但会调onHiddenChanged方法
Fragment2 onCreate > onCreateView > onStart >onResume
replace()
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.simple_fragment, Fragment2);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.commit();
Fragment1 onPause > onStop >onDestoryView >onDestory
Fragment2 onCreate >onCreateView >onStart > onResume
BroadCast
BroadcastReciver的静态注册与动态注册的区别?
动态注册广播不是常驻型广播,也就是说广播跟随Activity的生命周期。注意在Activity结束前,移除广播接收器。
静态注册是常驻型,也就是说当应用程序关闭后,如果有信息广播来,程序也会被系统调用自动运行。
广播的分类
标准广播
标准广播是一种完全异步执行的广播,在广播发出之后,所有的广播接收器几乎都会在同一时刻接受到这条广播消息,因此它们之间没有任何先后顺序可言
//注册广播
mBroadcastReceiver = new MyBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BROADCAST_ACTION_NORMAL);
registerReceiver(mBroadcastReceiver, intentFilter);
//取消注册广播
unregisterReceiver(mBroadcastReceiver);
//发送广播
button1.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
Intent it = new Intent();
it.setAction(BROADCAST_ACTION_NORMAL);
it.putExtra("name","normal broadcast");
sendBroadcast(it);
}
});
//接收广播
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
showlog("MyBroadcastReceiver-onReceive");
String name = intent.getStringExtra("name");
text1.setText(name);
showlog("name="+name);
}
}
静态注册
<receiver
//此广播接收者类是mBroadcastReceiver
android:name=".mBroadcastReceiver" >
//用于接收网络状态改变时发出的广播
<intent-filter>
<action android:name="BROADCAST_ACTION" />
</intent-filter>
</receiver>
有序广播
有序广播则是一种同步执行的广播,在广播发出之后,同一时刻只会有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递。所以此时的广播接受器是有先后顺序的,优先级高的广播接收器就可以先收到广播消息,并且前面的广播接收器还可以截断正在传递的广播
public class SendOrderBroadcastActivity extends Activity {
static final String TAG = "SendOrderBroadcastActivity";
static final String ACTION = "com.x.text.SendOrderBroadcastActivity";
static final String KEY = "com.x.text.SendOrderBroadcastActivity";
static final String KEY_MAIN = "com.x.text.main";
@BindView(R.id.order_btn)
TextView orderBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_order_broadcast);
ButterKnife.bind(this);
initReceiver1();
initReceiver2();
initReceiver3();
}
EndReceiver myReceiver;
private void initReceiver3() {
myReceiver = new EndReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ACTION);
registerReceiver(myReceiver, intentFilter);
}
private void initReceiver2() {
MyReceiver2 myReceiver = new MyReceiver2();
IntentFilter intentFilter = new IntentFilter();
intentFilter.setPriority(-200);
intentFilter.addAction(ACTION);
registerReceiver(myReceiver, intentFilter);
}
private void initReceiver1() {
MyReceiver myReceiver = new MyReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.setPriority(200);
intentFilter.addAction(ACTION);
registerReceiver(myReceiver, intentFilter);
}
@Override
protected void onDestroy() {
super.onDestroy();
}
@OnClick(R.id.order_btn)
public void onViewClicked() {
Intent intent = new Intent(ACTION);
intent.putExtra(KEY_MAIN,"存入数值KEY_MAIN");
sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG,"最后的广播");
}
}, null,
0, null, null);
}
private class EndReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = getResultExtras(true);
String name = bundle.getString(KEY);
Log.i(TAG, "EndReceiver onReceive:"+name);
}
}
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "MyReceiver onReceive");
Bundle bundle = new Bundle();
bundle.putString(KEY,"优先级高的广播存入数值");
setResultExtras(bundle);
}
}
public class MyReceiver2 extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = getResultExtras(true);
String name = bundle.getString(KEY);
Log.i(TAG, "MyReceiver2 onReceive:"+name);
}
}
}
粘性广播
粘性广播也属于标准广播,在发送后就一直存在于系统的消息容器里面,等待对应的处理器去处理,如果暂时没有处理器处理这个消息则一直在消息容器里面处于等待状态,粘性广播的Receiver如果被销毁,那么下次重建时会自动接收到消息数据。
系统广播
Android 内置了很多系统级别的广播,我们可以在应用中通过监听这些广播来得到各种系统的状态信息。
本地广播
App应用内广播可以理解成一种局部广播的形式,广播的发送者和接收者都同属于一个App。
/**注册应用内广播接收器*/
mBroadcastReceiver4 = new MyBroadcastReceiver4();
IntentFilter intentFilter4 = new IntentFilter();
intentFilter4.addAction(BROADCAST_ACTION_LOCAL);
localBroadcastManager = LocalBroadcastManager.getInstance(this);
localBroadcastManager.registerReceiver(mBroadcastReceiver4, intentFilter4);
//取消注册应用内广播接收器
localBroadcastManager.unregisterReceiver(mBroadcastReceiver4);
//发送应用内广播
button3.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
Intent it = new Intent();
it.setAction(BROADCAST_ACTION_LOCAL);
it.putExtra("name","local broadcast");
localBroadcastManager.sendBroadcast(it);
}
});
//接收应用内广播
public class MyBroadcastReceiver4 extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
showlog("MyBroadcastReceiver4-onReceive");
String name = intent.getStringExtra("name");
text3.setText(name);
showlog("name="+name);
}
}
广播工作原理
广播注册
实际调用ContextWrapper中的registerReceiver,实际处理的是ContextImpl 的registerReceiver,registerReceiver又调用registerReceiverInternal,可以看出BroadcastReceiver的注册是通过ActivityManagerService完成的,AMS将receiver封装成一个InnerReceiver对象和IntentFilter对象保存起来。
广播的发送和接收
AMS的broadcastIntent()完成广播的发送;
在broadcastintentLocked的内部,会根据intent-filter查找出匹配的广播接收者加入到BroadcastQueue 中,接着BroadcastQueue 就会将广播发送给相应的广播接收者。
BroadcastQueue 的scheduleBroadcastsLocked方法并没有立即发送广播,而是发送了一个BROADCAST_INTENT_MSG类型的消息,BroadcastQueue 收到消息后会调用processNextBroadcast方法。
ActivityThread的scheduleRegisteredReceiver实现如下,它通过IntentReceiver来实现广播的接收,调用LoadedApk的performReceive方法,创建一个Args 对象并通过 mActivityThread.post(args.getRunnable()),在args中调用 receiver.onReceive(mContext, intent);
Service
Service两种生命周期以及区别
应用组件(如 Activity)通过调用 startService() 启动服务,以无限期运行
一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也不受影响。
已启动的服务通常是执行单一操作,而且不会将结果返回给调用方。
应用组件通过调用 bindService() 绑定到服务
提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。
仅当与另一个应用组件绑定时,绑定服务才会运行。多个组件可以同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。
创建服务
startService()
public class MyService extends Service {
private static final String TAG = "MyService";
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
}
}
// Activity中MainActivity
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = "MainActivity";
private Button mStartService;
private Button mStopService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iniView();
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.button_start_service:
Intent startIntent = new Intent(MainActivity.this, MyService.class);
//startService启动形式
startService(startIntent);
break;
case R.id.button_stop_service:
Intent stopIntent = new Intent(MainActivity.this, MyService.class);
//“启动”服务的停止
stopService(stopIntent);
break;
}
}
private void iniView() {
mStartService = (Button) findViewById(R.id.button_start_service);
mStopService = (Button) findViewById(R.id.button_stop_service);
mStartService.setOnClickListener(this);
mStopService.setOnClickListener(this);
}
}
onStartCommand
int onStartCommand (Intent intent, int flags, int startId)
1 intent
这个intent是startService(intent)中的intent,在创建启动的服务所在的组件中如果需要传递给Service中数据,可以将要传递的数据放放到这个Intent里面
2 flags
服务被系统杀死后的重新启动方式,有3种
START_NOT_STICKY(非粘性启动,系统杀死后不再重新创建该Service)
如果系统在 onStartCommand() 返回后终止服务,则除非有挂起 Intent 要传递,否则系统不会重建服务。这是最安全的选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行服务
START_STICKY(粘性启动,默认的返回值)
如果系统在 onStartCommand() 返回后终止服务,则会重建服务并调用 onStartCommand(),但不会重新传递最后一个 Intent(意思就是说此时intent的数据已经是null了)。相反,除非有挂起 Intent 要启动服务(在这种情况下,将传递这些 Intent ),否则系统会通过空 Intent 调用 onStartCommand()。这适用于不执行命令、但无限期运行并等待作业的媒体播放器(或类似服务)。
START_REDELIVER_INTENT(再交付)
如果系统在 onStartCommand() 返回后终止服务,则会重建服务,并通过传递给服务的最后一个 Intent 调用 onStartCommand()。任何挂起 Intent 均依次传递。这适用于主动执行应该立即恢复的作业(例如下载文件)的服务(就是需要重启,需要保留Intent的服务)
3 startId
每次创建启动Service的唯一身份ID,每次每次startService,这个startId均不相同
用于处理多个onStartCommand请求时,关闭Service时使用
创建启动的Service实际使用业务逻辑
在onStartCommand中开启子线程进行耗时操作
子线程结束用stopSelf(startId)(在本Service中)杀死自己这个Service
或者另外在别的组件中用stopService结束Service
你会在什么情况下使用Service?
用于处理网络事务(下载文件)
播放音乐(音乐播放器)
执行文件I/O(读写文件)
与内容提供器进行交互
服务非常适合那些不需要和用户交互而且还要求长期运行的任务
startServer和bindServier的区别?
生命周期上的区别
执行startService时,Service会经历onCreate->onStartCommand。当执行stopService时,直接调用onDestroy方法。调用者如果没有stopService,Service会一直在后台运行,下次调用者再起来仍然可以stopService。
执行bindService时,Service会经历onCreate->onBind。这个时候调用者和Service绑定在一起。调用者调用unbindService方法或者调用者Context不存在了(如Activity被finish了),Service就会调用onUnbind->onDestroy。这里所谓的绑定在一起就是说两者共存亡了。
多次调用startService,该Service只能被创建一次,即该Service的onCreate方法只会被调用一次。但是每次调用startService,onStartCommand方法都会被调用。
第一次执行bindService时,onCreate和onBind方法会被调用,但是多次执行bindService时,onCreate和onBind方法并不会被多次调用,即并不会多次创建服务和绑定服务。
Service和Thread的区别?
1). Thread:Thread 是程序执行的最小单元,它是分配CPU的基本单位。可以用 Thread 来执行一些异步的操作。
2). Service:Service 是android的一种机制,当它运行的时候如果是Local Service,那么对应的 Service 是运行在主进程的 main 线程上的。如:onCreate,onStart 这些函数在被系统调用的时候都是在主进程的main 线程上运行的。如果是Remote Service,那么对应的 Service 则是运行在独立进程的 main 线程上。
IntentService与Service的区别?
APP启动流程
- Launcher通过Binder进程间通信机制通知ActivityManagerService,它要启动一个Activity;
- ActivityManagerService通过Binder进程间通信机制通知Launcher进入Paused状态;
- Launcher通过Binder进程间通信机制通知ActivityManagerService,它已经准备就绪进入Paused状态;
- ActivityManagerServicey利用Zygote.fork()创建一个新的进程,用来启动一个ActivityThread实例, 即将要启动的Activity就是在这个ActivityThread实例中运行;
- ActivityThread通过Binder进程间通信机制将一个ApplicationThread类型的Binder对象传递给ActivityManagerService,以便以后ActivityManagerService能够通过这个Binder对象和它进行通信;
- ActivityManagerService通过Binder进程间通信机制通知ActivityThread,现在一切准备就绪,它可以真正执行Activity的启动操作了。
Activity启动流程
Launcher通知AMS要启动activity。
AMS: PMS的resoveIntent验证要启动activity是否匹配。如果匹配,通过ApplicationThread发消息给Launcher所在的主线程,暂停当前Activity(Launcher);
AMS,根据要启动的Activity配置ActivityStack。然后判断要启动的Activity进程是否存在?
- 存在:发送消息LAUNCH_ACTIVITY给需要启动的Activity主线程,执行handleLaunchActivity
- 不存在:通过socket向zygote请求创建进程。进程启动后,ActivityThread.attach
在通过ActivityStackSupervisor来获取当前需要显示的ActivityStack。
ApplicationThread来发送消息给主线程的Handler来启动Activity(handleLaunchActivity)。
handleLauchActivity:调用了performLauchActivity,里边Instrumentation生成了新的activity对象,继续调用activity生命周期。
启动模式
standard
系统在启动它的任务中创建 activity 的新实例
应用场景:默认启动模式
singleTop
只要不在栈顶,都会创建新的实例。如果在栈顶则直接复用。
应用场景:适合接收通知,启动的内容显示页面。
singleTask
系统创建新 task 并在 task 的根目录下实例化 activity。
但如果 activity 的实例已存在于单独的任务中,则调用其 onNewIntent() 方法,其上面的实例会被移除栈。如果在栈顶则直接复用。
应用场景:从外界可能多次跳转到一个界面,只会启动主界面一次,并且会清空主界面上面的其他页面。
singleInstance
相同 singleTask,activity 始终是其 task 的唯一成员。
应用场景:适合需要与程序分离开的页面。例如闹铃提醒,将闹铃提醒与闹铃设置分离。singleInstance不要用于中间页面,如果用于中间页面,跳转会有问题,比如:A -> B (singleInstance) -> C,完全退出后,在此启动,首先打开的是B。
singleTop singleTask singleInstance区别
如果singleTask启动的ActivityA位于栈底,在栈顶startActivity到这个ActivityA时会调动 onNewIntent->onStart->onResume。中间的Activity会被销毁。
但singleTask启动的的ActivityA不存在时,会重新创建ActivityA,使其位于栈顶,调用方式OnCreate->OnStart->OnResume。
如果singletop启动的ActivityB在栈底时,在栈顶startActivity到这个ActivityB时会重新调动 onCreate->onStart->onResume,但不会调用OnDestroy销毁中间的Activity,而是逐级跳跃的方式。
singletop启动的的ActivityB不存在时,会重新创建ActivityB,使其位于栈顶,调用方式OnCreate->OnStart->OnResume。
singleInstance,虽然也是保证唯一性,但改模式的Activity是全局性的唯一性,生命周期和应用程序相同,不能及时回收
singleInstance如果不指定栈名,是怎么分配的?
重新开启一个栈;
在singleInstance模式下的activity作为中间界面跳转时可能出错
如假设有activityA,activityB,activityC这三个activity,我们将activityB设置为SingleInstance
(1)A开启B,然后按home键,再从左面点开应用,显示的是A,这是因为launch启动我们应用的时候 会从默认的栈找到栈顶的activity显示
(2) A开启C,C开启B,B开启A,结果显示的是C,这还是两个栈造成的,B开启A的时候,其实是到达A所处的栈,栈顶是C,所以就显示C了
解决办法:
如果想让C和B同一个栈,那就使用taskinfinity,给他俩设置同样的栈名;
不用在中间层使用SingleInstance;
onActivityResult不能与SingleInstance不能一起使用,因为不同栈。
Intent显示跳转与隐式跳转
显示跳转
Intent intent = new Intent(mContext, XXActivity.class);
startActivity(intent);
隐式跳转
指定action
<activity android:name=".ActionActivity";
<intent-filter
action android:name="customer_action_here"
</intent-filter>
</activity>
//创建一个隐式的 Intent 对象:Action 动作
Intent intent = new Intent();
//设置 Intent 的动作为清单中指定的action
intent.setAction("customer_action_here");
startActivity(intent);
指定category
<activity android:name=".CategoryActivity" >
<intent-filter>
<action android:name="customer_action_here" />
<category android:name="customer_category_here" />
</intent-filter>
</activity>
//创建一个隐式的 Intent 对象:Category 类别
Intent intent = new Intent();
intent.setAction("customer_action_here");
//添加与清单中相同的自定义category
intent.addCategory("customer_category_here");
startActivity(intent);
指定Data
<activity android:name=".DataActivity">
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<data
android:scheme="content"
android:host="com.example.intentdemo"
android:port="8080"
android:pathPattern=".*pdf"
android:mimeType="text/plain"/>
</intent-filter>
</activity>
//创建一个隐式的 Intent 对象,方法四:Date 数据
Intent intent = new Intent();
Uri uri = Uri.parse("content://com.example.intentdemo:8080/abc.pdf");
//以 setDataAndType 方法设置 URI 及 mime type
intent.setDataAndType(uri, "text/plain");
startActivity(intent);
Activity A跳转B,B跳转C,A不能直接跳转到C,A如何传递消息给C?
SharedPreferences
// Activity A
SharedPreferences sp = this.getSharedPreferences("info", 1);
//获取sp编辑器
Editor edit = sp.edit();
edit.putString("data", str);
edit.commit();
// Activity C
SharedPreferences sp = this.getSharedPreferences("info", 1);
String str = sp.getString("data", "");
广播
自定义全局广播,动态注册
Intent intent = new Intent("com.example.broadcastbestpractice");
sendBroadcast(intent);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.example.broadcastbestpractice");
receiver = new MyReceiver();
registerReceiver(receiver, intentFilter);
class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(final Context context, final Intent intent) {
Log.d(TAG, "onReceive: ");
}
}
EventBus
EventBus.getDefault().post(MessageWrap.getInstance(msg));
EventBus.getDefault().register(this);
@Subscribe(threadMode = ThreadMode.MAIN)
public void onGetMessage(MessageWrap message) {
tvMessage.setText(message.message);
}
Handler
Handler handler = new Handler();
Message message=handler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putString("msg", "传递我这个消息");
message.setData(bundle);
message.what = 0x11;
handler.sendMessage(message);
final Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 0x11) {
Bundle bundle = msg.getData();
String date = bundle.getString("msg");
}
}
};
Activity如何保存状态的?
在onSaveInstanceState中保存数据
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//保存销毁之前的数据
outState.putString("number",mMNumber.getText().toString());
Log.d(TAG, "onSaveInstanceState");
}
在onRestoreInstanceState对数据进行恢复
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
Log.d(TAG, "onRestoreInstanceState");
//恢复数据
String s = savedInstanceState.getString("number");
mMNumber.setText(s);
}
Android进程回收
Low Memory Killer机制
在系统内存减少到达一个阈值时,系统就会开始根据进程的优先级来进行回收机制杀掉一部分进程来释放出内存供后面需要启动的App使用。这套回收内存的机制就叫Low Memory Killer。
oom_adj值
要查看进程的oom_adj值,可以使用adb命令:adb sehll cat /proc/进程id/oom_adj
此值越大,进程的优先级越低,越容易被回收,普通应用进程的oom_adj值是>=0,而系统进程的oom_adj值是<0的。
进程划分
Foreground process、Visible process、Service process、Background process、Empty process。所以它们的oom_adj值也是从小到大。
Foreground process
前台进程,就是用户正在使用中的应用当前的进程,例如:正在与用户交互的Activity所在的进程; 一个与正在交互的Activity绑定的Service所在的进程; 一个调用了startForeground的前台Service所在的进程,等。一般地前台进程是不会被系统回收杀死的。
Visible process
可见进程,就是用户正在使用,但不能直接操作的进程,例如:不在前台但仍可见的Activity,像已调用了onPause()回调的Activity,等。一般情况下,系统不会杀死可见进程,除非要在资源吃紧的情况下。
Service process
服务进程,就是没有界面一直在后台工进的进程,例如:通过startService()启动的Service正在后台中工作的所在进程。当系统内存不足以维持所有前台进程和可见进程同时运行的情况下,服务进程就会被杀死。
Background process
后台进程,就是用户按了Home键或Back键后,应用完全进入了后台,但还在运行的进程。这类进程是随时都会被系统杀死的。
Empty process
空进程,某个进程不包含任何活跃组件时就会被置为空进程,此类进程已经处于完全无用状态。
提升进程优先级
使用前台Service
Intent notificationIntent = new Intent(this, ExampleActivity.class);
PendingIntent pendingIntent =
PendingIntent.getActivity(this, 0, notificationIntent, 0);
Notification notification =
new Notification.Builder(this, CHANNEL_DEFAULT_IMPORTANCE)
.setContentTitle(getText(R.string.notification_title))
.setContentText(getText(R.string.notification_message))
.setSmallIcon(R.drawable.icon)
.setContentIntent(pendingIntent)
.setTicker(getText(R.string.ticker_text))
.build();
startForeground(ONGOING_NOTIFICATION_ID, notification);
apk打包
APK文件: 其实就是一个压缩包,当你解压以后会发现它内部主要就是资源文件和classes.dex,这个classes.dex就是Android系统Dalvik虚拟机的可执行文件。
APK打包的流程图:
打包资源文件,生成R.java文件
输入
Resource文件(就是工程中res中的文件)、
Assets文件(相当于另外一种资源,这种资源Android系统并不像对res中的文件那样优化它)、
AndroidManifest.xml文件(包名就是从这里读取的,因为生成R.java文件需要包名)、
Android基础类库(Android.jar文件)
输出 打包好的资源(bin目录中的resources.ap_文件)、R.java文件(gen目录中)
工具 aapt(The Android Asset Packaing Tool),位于android-sdk/platform-tools目录下
生成过程
调用了aapt源码目录下的Resource.cpp文件中的buildResource()函数,该函数首先检查AndroidManifest.xml的合法性;
然后对res目录下的资源子目录进行处理,处理的函数为makeFileResource(),处理的内容包括资源文件名的合法性检查,向资源表table添加条目等;
调用compileResourceFile()函数编译res与asserts目录下的资源并生成resources.arsc文件,compileResourceFile()函数位于aapt源码目录的ResourceTable.cpp文件中,该函数最后会调用parseAndAddEntry()函数生成R.java文件;
调用compileXmlfile()函数对res目录的子目录下的xml文件分别进行编译,这样处理过的xml文件就简单的被“加密”了;
将所有的资源与编译生成的resorces.arsc文件以及“加密”过的AndroidManifest.xml文件打包压缩成resources.ap_文件(使用Ant工具命令行编译则会生成与build.xml中“project name”指定的属性同名的ap_文件)。
处理aidl文件,生成相应的Java文件
输入 源码文件、aidl文件、framework.aidl文件
工具 aidl工具,位于android-sdk/platform-tools目录下
输出 对应的.java文件
编译项目源代码,生成class文件
输入 源码文件(包括R.java和AIDL生成的.java文件)、库文件(.jar文件)
工具 javac工具
输出 .class文件
javac编译工程src目录下所有的java源文件,生成的class文件位于工程的bin\classes目录下,上图假定编译工程源代码时程序是基于android SDK开发的,实际开发过程中,也有可能会使用android NDK来编译native代码
转换所有的class文件,生成classes.dex文件
输入 .class文件(包括Aidl生成.class文件,R生成的.class文件,源文件生成的.class文件),库文件(.jar文件)
工具 javac工具
输出 .dex文件,位于android-sdk/platform-tools 目录下
dx工具的主要工作是将Java字节码转成成Dalvik字节码、压缩常量池、消除冗余信息等。
打包生成APK文件
【输入】 .class文件(包括Aidl生成.class文件,R生成的.class文件,源文件生成的.class文件),库文件(.jar文件)
【工具】dx工具,apkbuilder位于 android-sdk/tools目录下
【输出】.dex文件
打包过程:
apkbuilder为一个脚本文件,实际调用的是android-sdk/tools/lib/sdklib.jar文件中的com.android.sdklib.build.ApkbuilderMain类。代码构建了一个ApkBuilder;
以包含resources.arsc的文件为基础生成apk文件,这个文件一般为ap_结尾,接着调用addSourceFolder()函数添加工程资源,addSourceFolder()会调用processFileForResource()函数往apk文件中添加资源,处理的内容包括res目录与asserts目录中的文件;
添加完资源后调用addResourceFromJar()函数往apk文件中写入依赖库;
接着调用addNativeLibraries()函数添加工程libs目录下的Native库(通过android NDK编译生成的so或bin文件);
最后调用sealApk()关闭apk文件。
对APK文件进行签名
【输入】未签名的.apk文件
【工具】jarsigner
【输出】签名的.apk文件
作用:一旦APK文件生成,它必须被签名才能被安装在设备上。
签名工具:用于调试的debug.keystore,它主要用于调试,在Eclipse或者Android Studio中直接run以后跑在手机上的就是使用的debug.keystore。
发布正式版本的keystore:一种是使用jdk中提供的jarsigner工具签名;另一种是使用android源码中提供的signapk工具,它的代码位于android系统源码build\tools\signapk目录下。
对签名后的APK文件进行对齐处理
【输入】签名后的.apk文件
【工具】zipalign工具,它位于android-sdk/tools目录下。
【输出】对齐后的.apk文件
对齐过程:验证apk文件是否对齐过的工作由ZipAlign.cpp文件的verify()函数完成,处理对齐的工作则由process()函数完成。
对齐作用:如果你发布的apk是正式版的话,就必须对APK进行对齐处理。将APK包中所有的资源文件距离文件起始偏移为4字节整数倍,这样通过内存映射访问apk文件时的速度会更快。对齐的作用就是减少运行时内存的使用。
Gradle
implementation
Gradle 会将依赖项添加到编译类路径,并将依赖项打包到编译输出。该依赖方式所依赖的库不会传递,只会在当前module中生效。
举个例子,A依赖B,B依赖C,如果B依赖C是使用的implementation依赖,那么在A中是访问不到C中的方法的,如果需要访问,请使用api依赖,这样设计可以提高编译速度。
implementation和compile区别:
implementation不可以传递依赖,但是compile可以传递依赖。因此使用compile会使得module之间的耦合性增大
implementation和api区别:
api该依赖方式会传递所依赖的库,当其他module依赖了该module时,可以使用该module下使用api依赖的库,api等同于compile。
provided、compileOnly
只在编译时有效,不会参与打包
apk、runtimeOnly
只在生成apk的时候参与打包,编译时不会参与
annotationProcessor
添加注解处理器的库依赖关系。
Android多线程
基础使用:继承Thread类;实现Runnable接口;Handler
复合使用:AsyncTask;HandlerThread;IntentService
高级使用:线程池
AsyncTask
AsyncTask是对Handler与线程池的封装。使用它的方便之处在于能够更新用户界面,当然这里更新用户界面的操作还是在主线程中完成的,AsyncTask内部包含一个Handler,所以可以发送消息给主线程让它更新UI。
AsyncTask内还包含了一个线程池。使用线程池的主要原因是避免不必要的创建及销毁线程的开销。
使用时的注意点
关于 生命周期
结论: AsyncTask不与任何组件绑定生命周期
使用建议: 在Activity 或 Fragment中使用 AsyncTask时,最好在Activity 或 Fragment的onDestory()调用 cancel(boolean);
关于 内存泄漏
结论 :若AsyncTask被声明为Activity的非静态内部类,当Activity需销毁时,会因AsyncTask保留对Activity的引用 而导致Activity无法被回收,最终引起内存泄露
使用建议: AsyncTask应被声明为Activity的静态内部类
线程任务执行结果 丢失
结论: 当Activity重新创建时(屏幕旋转 / Activity被意外销毁时后恢复),之前运行的AsyncTask(非静态的内部类)持有的之前Activity引用已无效,故复写的onPostExecute()将不生效,即无法更新UI操作
使用建议: 在Activity恢复时的对应方法 重启 任务线程
AsyncTask类的声明
public abstract class AsyncTask<Params, Progress, Result>
Params:在执行AsyncTask时需要传入的参数,可用于在后台任务中使用。
Progress:后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位。
Result:当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。
自定义AsyncTask就可以写成如下方式:
class DownloadTask extends AsyncTask<Void, Integer, Boolean> {
……
}
AsyncTask类提供方法
onPreExecute() //此方法会在后台任务执行前被调用,用于进行一些准备工作,比如显示一个进度条对话框等。
doInBackground(Params... params) //此方法中定义要执行的后台任务,在这个方法中可以调用publishProgress来更新任务进度(publishProgress内部会调用onProgressUpdate方法)
onProgressUpdate(Progress... values) //由publishProgress内部调用,表示任务进度更新
onPostExecute(Result result) //后台任务执行完毕后,此方法会被调用,参数即为后台任务的返回结果
onCancelled() //此方法会在后台任务被取消时被调用
AsyncTask工作原理
任务线程池类(THREAD_POOL_EXECUTOR)实际上是1个已配置好的可执行并行任务的线程池
调用THREAD_POOL_EXECUTOR.execute()实际上是调用线程池的execute()去执行具体耗时任务
而该耗时任务则是步骤2中初始化 WorkerRunnable实例对象时复写的call()内容
在call()方法里,先调用 我们复写的doInBackground(mParams)执行耗时操作
再调用postResult(result), 通过 InternalHandler 类 将任务消息传递到主线程;根据消息标识(MESSAGE_POST_RESULT)判断,最终通过finish()调用我们复写的onPostExecute(result),从而实现UI更新操作
AsyncTask初始化字段
实例化了一个静态线程池THREAD_POOL_EXECUTOR
默认情况下,AsyncTask的实际工作就是通过该THREAD_POOL_EXECUTOR完成的。
private static final String LOG_TAG = "AsyncTask";
//CPU_COUNT为手机中的CPU核数
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
//将手机中的CPU核数加1作为AsyncTask所使用的线程池的核心线程数的大小
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
//将CPU_COUNT * 2 + 1作为AsyncTask所使用的线程池的最大线程数的大小
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;
//实例化线程工厂ThreadFactory,sThreadFactory用于在后面创建线程池
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
//mCount为AtomicInteger类型,AtomicInteger是一个提供原子操作的Integer类,
//确保了其getAndIncrement方法是线程安全的
private final AtomicInteger mCount = new AtomicInteger(1);
//重写newThread方法的目的是为了将新增线程的名字以"AsyncTask #"标识
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
//实例化阻塞式队列BlockingQueue,队列中存放Runnable,容量为128
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
//根据上面定义的参数实例化线程池
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
Status枚举类型
一个AsyncTask正常情况下会经历PENDING->RUNNING->FINISHED三个状态
public enum Status {
//PENDING表示还没有开始执行任务
PENDING,
//RUNNING表示已经开始执行任务
RUNNING,
//FINISHED表示任务已经执行完成或被取消了,总之onPostExecute方法已经被调用了
FINISHED,
}
execute执行任务
在实例化了AsyncTask对象之后,我们就可以调用AsyncTask的execute方法执行任务
exec通过execute方法在执行mFuture。
后面会在exec的工作线程中执行mWorker的call方法,首先在工作线程中执行doInBackground方法,并返回结果,然后将结果传递给postResult方法。
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
//如果当前AsyncTask已经处于运行状态,那么就抛出异常,不再执行新的任务
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
//如果当前AsyncTask已经把之前的任务运行完成,那么也抛出异常,不再执行新的任务
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
//将AsyncTask的状态置为运行状态
mStatus = Status.RUNNING;
//在真正执行任务前,先调用onPreExecute方法
onPreExecute();
mWorker.mParams = params;
//Executor的execute方法接收Runnable参数,由于mFuture是FutureTask的实例,
//且FutureTask同时实现了Callable和Runnable接口,所以此处可以让exec执行mFuture
exec.execute(mFuture);
//最后将AsyncTask自身返回
return this;
}
SerialExecutor执行后台任务
串行执行:SerialExecutor实现了Executor接口中的execute方法,该类用于串行执行任务,即一个接一个地执行任务,而不是并行执行任务.会在线程池的工作线程中执行匿名内部类Runnable中的r.run()方法去执行任务,无论任务r正常完成还是抛出异常,都会在finally中执行scheduleNext方法,用于执行mTasks中的下一个任务。
SerialExecutor内部维护了一个存放Runnable的双端队列mTasks。
入队:当执行SerialExecutor的execute方法时,会传入一个Runnable变量r,但是mTasks并不直接存储r,而是又新new了一个匿名Runnable对象,其内部会调用r,这样就对r进行了封装,将该封装后的Runnable对象通过队列的offer方法入队,添加到mTasks的队尾。
出队执行:如果mActive为null,即当前没有任务Runnable正在运行,那么就会执行scheduleNext()方法。当执行scheduleNext方法的时候,会首先从mTasks中通过poll方法出队,删除并返回队头的Runnable;
将返回的Runnable赋值给mActive,如果不为空,那么就让将其作为参数传递给THREAD_POOL_EXECUTOR的execute方法进行执行。
private static class SerialExecutor implements Executor {
//mTasks是一个维护Runnable的双端队列,ArrayDeque没有容量限制,其容量可自增长
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
//mActive表示当前正在执行的任务Runnable
Runnable mActive;
public synchronized void execute(final Runnable r) {
//execute方法会传入一个Runnable类型的变量r
//然后我们会实例化一个Runnable类型的匿名内部类以对r进行封装,
//通过队列的offer方法将封装后的Runnable添加到队尾
mTasks.offer(new Runnable() {
public void run() {
try {
//执行r的run方法,开始执行任务
//此处r的run方法是在线程池中执行的
r.run();
} finally {
//当任务执行完毕的时候,通过调用scheduleNext方法执行下一个Runnable任务
scheduleNext();
}
}
});
//只有当前没有执行任何任务时,才会立即执行scheduleNext方法
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
//通过mTasks的poll方法进行出队操作,删除并返回队头的Runnable,
//将返回的Runnable赋值给mActive,
//如果不为空,那么就让将其作为参数传递给THREAD_POOL_EXECUTOR的execute方法进行执行
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
WorkerRunnable
由于AsyncTask能够取消任务,所以AsyncTask使用了FutureTask以及与其相关的Callable
Callable
是一个接口,其内部定义了call方法,在call方法内需要编写代码执行具体的任务,Callable的call方法可以指定返回值。
FutureTask类
同时实现了Callable接口和Runnable接口,FutureTask的构造函数中需要传入一个Callable对象以对其进行实例化。
任务取消/阻塞/完成
Executor的execute方法接收一个Runnable对象,由于FutureTask实现了Runnable接口,所以可以把一个FutureTask对象传递给Executor的execute方法去执行。
当任务执行完毕的时候会执行FutureTask的done方法,我们可以在这个方法中写一些逻辑处理。
在任务执行的过程中,我们也可以随时调用FutureTask的cancel方法取消执行任务,任务取消后也会执行FutureTask的done方法。
我们也可以通过FutureTask的get方法阻塞式地等待任务的返回值(即Callable的call方法的返回值),如果任务执行完了就立即返回执行的结果,否则就阻塞式等待call方法的完成。
mWorker
我们之前提到,mWorker其实是一个Callable类型的对象。实例化mWorker,实现了Callable接口的call方法。call方法是在线程池的某个线程中执行的,而不是运行在主线程中。在线程池的工作线程中执行doInBackground方法,执行实际的任务,并返回结果。当doInBackground执行完毕后,将执行完的结果传递给postResult方法。
mFuture
mFuture是一个FutureTask类型的对象,用mWorker作为参数实例化了mFuture。在这里,其实现了FutureTask的done方法,我们之前提到,当FutureTask的任务执行完成或任务取消的时候会执行FutureTask的done方法。
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
//AsyncTask的构造函数需要在UI线程上调用
public AsyncTask() {
//实例化mWorker,实现了Callable接口的call方法
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
//call方法是在线程池的某个线程中执行的,而不是运行在主线程中
//call方法开始执行后,就将mTaskInvoked设置为true,表示任务开始执行
mTaskInvoked.set(true);
//将执行call方法的线程设置为后台线程级别
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//在线程池的工作线程中执行doInBackground方法,执行实际的任务,并返回结果
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
//将执行完的结果传递给postResult方法
return postResult(result);
}
};
//用mWorker实例化mFuture
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
//任务执行完毕或取消任务都会执行done方法
try {
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
postResultIfNotInvoked
无论任务正常执行完成还是任务取消,都会执行postResultIfNotInvoked方法。
如果AsyncTask正常执行完成的时候,call方法都执行完了,mTaskInvoked设置为true,并且在call方法中最后执行了postResult方法,然后进入mFuture的done方法,然后进入postResultIfNotInvoked方法,由于mTaskInvoked已经执行,所以不会执行再执行postResult方法。
如果在调用了AsyncTask的execute方法后立马就执行了AsyncTask的cancel方法(实际执行mFuture的cancel方法),那么会执行done方法,且捕获到CancellationException异常,从而执行语句postResultIfNotInvoked(null),由于此时还没有来得及执行mWorker的call方法,所以mTaskInvoked为false,这样就可以把null传递给postResult方法。
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
//只有mWorker的call没有被调用才会执行postResult方法
postResult(result);
}
}
publishProgress 发送阶段msg
我们的任务有可能分成多个部分,每当我完成其中的一部分任务时,我们可以在doInBackground中多次调用AsyncTask的publishProgress方法,将阶段性数据发布出去。
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
postResult 发送结果msg
会根据InternalHandler创建一个Message Code为MESSAGE_POST_RESULT的Message,此处还将doInBackground返回的result通过new AsyncTaskResult(this, result)封装成了AsyncTaskResult,将其作为message的obj属性。
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
//通过getHandler获取InternalHandler,InternalHandler绑定主线程
//根据InternalHandler创建一个Message Code为MESSAGE_POST_RESULT的Message
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
//将该message发送给InternalHandler
message.sendToTarget();
return result;
}
AsyncTaskResult用于发布result或用于发布阶段性的数据
@SuppressWarnings({"RawUseOfParameterizedType"})
private static class AsyncTaskResult<Data> {
//mTask表示当前AsyncTaskResult是哪个AsyncTask的结果
final AsyncTask mTask;
//mData表示其存储的数据
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
//此处的data是可变数组
mTask = task;
mData = data;
}
}
InternalHandler handleMessage
用主线程的Looper初始化了InternalHandler,说明InternalHandler绑定了主线程
注意的是InternalHandler是指向主线程的,所以其handleMessage方法是在主线程中执行的,从而此处的finish方法也是在主线程中执行的,进而onPostExecute, onCancel也是在主线程中执行的。
private static class InternalHandler extends Handler {
public InternalHandler() {
//Looper类的getMainLooper方法是个静态方法,该方法返回主线程的Looper
//此处用主线程的Looper初始化InternalHandler,表示InternalHandler绑定了主线程
super(Looper.getMainLooper());
}
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
//发布最后结果
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
//发布阶段性处理结果
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
msg.obj是AsyncTaskResult类型,result.mTask表示当前AsyncTaskResult所绑定的AsyncTask。result.mData[0]表示的是doInBackground所返回的处理结果。将该结果传递给AsyncTask的finish方法
private void finish(Result result) {
if (isCancelled()) {
//如果任务被取消了,那么执行onCancelled方法
onCancelled(result);
} else {
//将结果发传递给onPostExecute方法
onPostExecute(result);
}
//最后将AsyncTask的状态设置为完成状态
mStatus = Status.FINISHED;
}
Handler机制
Handler 的工作主要包含消息的发送和接收的过程。消息的发送可以通过 post/send 的一系列方法实现,post 最终也是通过 send 来实现的。Handler的工作流程如下:
Handler 有两个主要用途:
(1)安排 Message 和 runnables 在将来的某个时刻执行;
(2)将要在不同于自己的线程上执行的操作排入队列。(在多个线程并发更新 UI 的同时保证线程安全。)
Handler成员要素
Message:Handler 接收和处理的消息对象
MessageQueue:Message 的队列,先进先出,每一个线程最多可以拥有一个
Looper : 消 息 泵 , 是 MessageQueue 的 管 理 者 , 会 不 断 从 MessageQueue 中取出消息,并将消息分给对应的 Handler 处理,每个 线程只有一个 Looper。
1个线程(Thread)只能绑定 1个循环器(Looper),但可以有多个处理者(Handler)
1个循环器(Looper) 可绑定多个处理者(Handler)
1个处理者(Handler) 只能绑定1个1个循环器(Looper)
Handler使用条件
Handler 就必须为线程创建 Looper,因为默认的 UI 主线程,也就是 ActivityThread,ActivityThread 被创建的时候就会初始化 Looper,这也是在主线程中默认可以使用 Handler 的原因。
Handler.sendMessage()源码分析
创建Handler, 自动关联Looper & MessageQueue
当创建Handler对象时,则通过构造方法自动关联当前线程的Looper对象 & 对应的消息队列对象(MessageQueue),从而自动绑定了实现创建Handler对象操作的线程
/**
* 具体使用
*/
private Handler mhandler = new Handler(){
// 通过复写handlerMessage()从而确定更新UI的操作
@Override
public void handleMessage(Message msg) {
...// 需执行的UI操作
}
};
/**
* 源码分析:Handler的构造方法
* 作用:初始化Handler对象 & 绑定线程
* 注:
* a. Handler需绑定 线程才能使用;绑定后,Handler的消息处理会在绑定的线程中执行
* b. 绑定方式 = 先指定Looper对象,从而绑定了 Looper对象所绑定的线程(因为Looper对象本已绑定了对应线程)
* c. 即:指定了Handler对象的 Looper对象 = 绑定到了Looper对象所在的线程
*/
public Handler() {
this(null, false);
// ->>分析1
}
/**
* 分析1:this(null, false) = Handler(null,false)
*/
public Handler(Callback callback, boolean async) {
// 1. 指定Looper对象
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
// Looper.myLooper()作用:获取当前线程的Looper对象;若线程无Looper对象则抛出异常
// 即 :若线程中无创建Looper对象,则也无法创建Handler对象
// 故 若需在子线程中创建Handler对象,则需先创建Looper对象
// 注:可通过Loop.getMainLooper()可以获得当前进程的主线程的Looper对象
// 2. 绑定消息队列对象(MessageQueue)
mQueue = mLooper.mQueue;
// 获取该Looper对象中保存的消息队列对象(MessageQueue)
// 至此,保证了handler对象 关联上 Looper对象中MessageQueue
}
创建Looper & MessageQueue
(1)即 主线程的Looper对象自动生成,不需手动生成;而子线程的Looper对象则需手动通过Looper.prepare()创建
(2)在子线程若不手动创建Looper对象 则无法生成Handler对象
/**
* 源码分析1:Looper.prepare()
* 作用:为当前线程(子线程) 创建1个循环器对象(Looper),同时也生成了1个消息队列对象(MessageQueue)
* 注:需在子线程中手动调用该方法
*/
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 1. 判断sThreadLocal是否为null,否则抛出异常
//即 Looper.prepare()方法不能被调用两次 = 1个线程中只能对应1个Looper实例
// 注:sThreadLocal = 1个ThreadLocal对象,用于存储线程的变量
sThreadLocal.set(new Looper(true));
// 2. 若为初次Looper.prepare(),则创建Looper对象 & 存放在ThreadLocal变量中
// 注:Looper对象是存放在Thread线程里的
// 源码分析Looper的构造方法->>分析a
}
/**
* 分析a:Looper的构造方法
**/
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
// 1. 创建1个消息队列对象(MessageQueue)
// 即 当创建1个Looper实例时,会自动创建一个与之配对的消息队列对象(MessageQueue)
mRun = true;
mThread = Thread.currentThread();
}
/**
* 源码分析2:Looper.prepareMainLooper()
* 作用:为 主线程(UI线程) 创建1个循环器对象(Looper),同时也生成了1个消息队列对象(MessageQueue)
* 注:该方法在主线程(UI线程)创建时自动调用,即 主线程的Looper对象自动生成,不需手动生成
*/
// 在Android应用进程启动时,会默认创建1个主线程(ActivityThread,也叫UI线程)
// 创建时,会自动调用ActivityThread的1个静态的main()方法 = 应用程序的入口
// main()内则会调用Looper.prepareMainLooper()为主线程生成1个Looper对象
/**
* 源码分析:main()
**/
public static void main(String[] args) {
... // 仅贴出关键代码
Looper.prepareMainLooper();
// 1. 为主线程创建1个Looper对象,同时生成1个消息队列对象(MessageQueue)
// 方法逻辑类似Looper.prepare()
// 注:prepare():为子线程中创建1个Looper对象
ActivityThread thread = new ActivityThread();
// 2. 创建主线程
Looper.loop();
// 3. 自动开启 消息循环 ->>下面将详细分析
}
消息循环
分析的是Looper类中的loop()方法
消息循环的操作 = 消息出队 + 分发给对应的Handler实例
分发给对应的Handler的过程:根据出队消息的归属者通过dispatchMessage(msg)进行分发,最终回调复写的handleMessage(Message msg),从而实现 消息处理 的操作
特别注意:在进行消息分发时(dispatchMessage(msg)),会进行1次发送方式的判断:
(1)若msg.callback属性不为空,则代表使用了post(Runnable r)发送消息,则直接回调Runnable对象里复写的run()
(2)若msg.callback属性为空,则代表使用了sendMessage(Message msg)发送消息,则回调复写的handleMessage(msg)
/**
* 源码分析: Looper.loop()
* 作用:消息循环,即从消息队列中获取消息、分发消息到Handler
* 特别注意:
* a. 主线程的消息循环不允许退出,即无限循环
* b. 子线程的消息循环允许退出:调用消息队列MessageQueue的quit()
*/
public static void loop() {
...// 仅贴出关键代码
// 1. 获取当前Looper的消息队列
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// myLooper()作用:返回sThreadLocal存储的Looper实例;若me为null 则抛出异常
// 即loop()执行前必须执行prepare(),从而创建1个Looper实例
final MessageQueue queue = me.mQueue;
// 获取Looper实例中的消息队列对象(MessageQueue)
// 2. 消息循环(通过for循环)
for (;;) {
// 2.1 从消息队列中取出消息
Message msg = queue.next();
if (msg == null) {
return;
}
// next():取出消息队列里的消息
// 若取出的消息为空,则线程阻塞
// ->> 分析1
// 2.2 派发消息到对应的Handler
msg.target.dispatchMessage(msg);
// 把消息Message派发给消息对象msg的target属性
// target属性实际是1个handler对象
// ->>分析2
// 3. 释放消息占据的资源
msg.recycle();
}
}
/**
* 分析1:queue.next()
* 定义:属于消息队列类(MessageQueue)中的方法
* 作用:出队消息,即从 消息队列中 移出该消息
*/
Message next() {
...// 仅贴出关键代码
// 该参数用于确定消息队列中是否还有消息
// 从而决定消息队列应处于出队消息状态 or 等待状态
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// nativePollOnce方法在native层,若是nextPollTimeoutMillis为-1,此时消息队列处于等待状态
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
// 出队消息,即 从消息队列中取出消息:按创建Message对象的时间顺序
if (msg != null) {
if (now < msg.when) {
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 取出了消息
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// 若 消息队列中已无消息,则将nextPollTimeoutMillis参数设为-1
// 下次循环时,消息队列则处于等待状态
nextPollTimeoutMillis = -1;
}
......
}
.....
}
}// 回到分析原处
/**
* 分析2:dispatchMessage(msg)
* 定义:属于处理者类(Handler)中的方法
* 作用:派发消息到对应的Handler实例 & 根据传入的msg作出对应的操作
*/
public void dispatchMessage(Message msg) {
// 1. 若msg.callback属性不为空,则代表使用了post(Runnable r)发送消息
// 则执行handleCallback(msg),即回调Runnable对象里复写的run()
// 上述结论会在讲解使用“post(Runnable r)”方式时讲解
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 2. 若msg.callback属性为空,则代表使用了sendMessage(Message msg)发送消息(即此处需讨论的)
// 则执行handleMessage(msg),即回调复写的handleMessage(msg) ->> 分析3
handleMessage(msg);
}
}
/**
* 分析3:handleMessage(msg)
* 注:该方法 = 空方法,在创建Handler实例时复写 = 自定义消息处理方式
**/
public void handleMessage(Message msg) {
... // 创建Handler实例时复写
}
创建消息对象
/**
* 具体使用
*/
Message msg = Message.obtain(); // 实例化消息对象
msg.what = 1; // 消息标识
msg.obj = "AA"; // 消息内容存放
/**
* 源码分析:Message.obtain()
* 作用:创建消息对象
* 注:创建Message对象可用关键字new 或 Message.obtain()
*/
public static Message obtain() {
// Message内部维护了1个Message池,用于Message消息对象的复用
// 使用obtain()则是直接从池内获取
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
// 建议:使用obtain()”创建“消息对象,避免每次都使用new重新分配内存
}
// 若池内无消息对象可复用,则还是用关键字new创建
return new Message();
}
发送消息到消息队列
Handler发送消息的本质 = 为该消息定义target属性(即本身实例对象) & 将消息入队到绑定线程的消息队列中。
/**
* 具体使用
*/
mHandler.sendMessage(msg);
/**
* 源码分析:mHandler.sendMessage(msg)
* 定义:属于处理器类(Handler)的方法
* 作用:将消息 发送 到消息队列中(Message ->> MessageQueue)
*/
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
// ->>分析1
}
/**
* 分析1:sendMessageDelayed(msg, 0)
**/
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
// ->> 分析2
}
/**
* 分析2:sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)
**/
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
// 1. 获取对应的消息队列对象(MessageQueue)
MessageQueue queue = mQueue;
// 2. 调用了enqueueMessage方法 ->>分析3
return enqueueMessage(queue, msg, uptimeMillis);
}
/**
* 分析3:enqueueMessage(queue, msg, uptimeMillis)
**/
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// 1. 将msg.target赋值为this
// 即 :把 当前的Handler实例对象作为msg的target属性
msg.target = this;
// 请回忆起上面说的Looper的loop()中消息循环时,会从消息队列中取出每个消息msg,然后执行msg.target.dispatchMessage(msg)去处理消息
// 实际上则是将该消息派发给对应的Handler实例
// 2. 调用消息队列的enqueueMessage()
// 即:Handler发送的消息,最终是保存到消息队列->>分析4
return queue.enqueueMessage(msg, uptimeMillis);
}
/**
* 分析4:queue.enqueueMessage(msg, uptimeMillis)
* 定义:属于消息队列类(MessageQueue)的方法
* 作用:入队,即 将消息 根据时间 放入到消息队列中(Message ->> MessageQueue)
* 采用单链表实现:提高插入消息、删除消息的效率
*/
boolean enqueueMessage(Message msg, long when) {
...// 仅贴出关键代码
synchronized (this) {
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
// 判断消息队列里有无消息
// a. 若无,则将当前插入的消息 作为队头 & 若此时消息队列处于等待状态,则唤醒
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
// b. 判断消息队列里有消息,则根据 消息(Message)创建的时间 插入到队列中
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p;
prev.next = msg;
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
// 之后,随着Looper对象的无限消息循环
// 不断从消息队列中取出Handler发送的消息 & 分发到对应Handler
// 最终回调Handler.handleMessage()处理消息
Handler.post()源码分析
/**
* 具体使用
* 需传入1个Runnable对象、复写run()从而指定UI操作
*/
mHandler.post(new Runnable() {
@Override
public void run() {
... // 需执行的UI操作
}
});
/**
* 源码分析:Handler.post(Runnable r)
* 定义:属于处理者类(Handler)中的方法
* 作用:定义UI操作、将Runnable对象封装成消息对象 & 发送 到消息队列中(Message ->> MessageQueue)
* 注:
* a. 相比sendMessage(),post()最大的不同在于,更新的UI操作可直接在重写的run()中定义
* b. 实际上,Runnable并无创建新线程,而是发送 消息 到消息队列中
*/
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
// getPostMessage(r) 的源码分析->>分析1
// sendMessageDelayed()的源码分析 ->>分析2
}
/**
* 分析1:getPostMessage(r)
* 作用:将传入的Runable对象封装成1个消息对象
**/
private static Message getPostMessage(Runnable r) {
// 1. 创建1个消息对象(Message)
Message m = Message.obtain();
// 注:创建Message对象可用关键字new 或 Message.obtain()
// 建议:使用Message.obtain()创建,
// 原因:因为Message内部维护了1个Message池,用于Message的复用,使用obtain()直接从池内获取,从而避免使用new重新分配内存
// 2. 将 Runable对象 赋值给消息对象(message)的callback属性
m.callback = r;
// 3. 返回该消息对象
return m;
} // 回到调用原处
/**
* 分析2:sendMessageDelayed(msg, 0)
* 作用:实际上,从此处开始,则类似方式1 = 将消息入队到消息队列,
* 即 最终是调用MessageQueue.enqueueMessage()
**/
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
// 请看分析3
}
/**
* 分析3:sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)
**/
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
// 1. 获取对应的消息队列对象(MessageQueue)
MessageQueue queue = mQueue;
// 2. 调用了enqueueMessage方法 ->>分析3
return enqueueMessage(queue, msg, uptimeMillis);
}
/**
* 分析4:enqueueMessage(queue, msg, uptimeMillis)
**/
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// 1. 将msg.target赋值为this
// 即 :把 当前的Handler实例对象作为msg的target属性
msg.target = this;
// 请回忆起上面说的Looper的loop()中消息循环时,会从消息队列中取出每个消息msg,然后执行msg.target.dispatchMessage(msg)去处理消息
// 实际上则是将该消息派发给对应的Handler实例
// 2. 调用消息队列的enqueueMessage()
// 即:Handler发送的消息,最终是保存到消息队列
return queue.enqueueMessage(msg, uptimeMillis);
}
// 注:实际上从分析2开始,源码 与 sendMessage(Message msg)发送方式相同
Handler.post()和 Handler.sendMessage()异同
MesageQueue
MessageQueue 主要包含两个操作:插入和读取。
读取操作本身会伴随着删除操作,插入和读取对应的方法分别是 enqueueMessage 和 next。
MessageQueue 内部实现并是一个单链表,next 方法是一个无限循环的方法,如果消息队列中没有消息,那么 next 方法会一直阻塞。当有新消息到来时,next 方法会放回这条消息并将其从单链表中移除。
enqueueMessage
sendMessage最终会把消息发送的时间确定下来,调用 sendMessageAtTime来处理消息。
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
最终去调用queue.enqueueMessage 去把消息插入队列中
MessageQueue类
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
//消息是否在使用
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
//Loop.quit() 调用了 MessageQueue quit(),会设置mQuitting = true,表示正在退出
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
//设置消息正在使用
msg.markInUse();
msg.when = when;
//下一个要处理的消息
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// p 为空,队列中没有消息,或者延迟时间为0,或者延迟时间小于p的延迟时间(也就是需要在p之前执行)
// 就把这个消息放在p前面,也就是队首(因为p为下一个要处理的消息)
msg.next = p;
// 更新mMessages (下一个要处理的消息)
mMessages = msg;
// 是否需要唤醒,因为可能在MessageQueue 中获取message的时候,可能被阻塞
needWake = mBlocked;
} else {
// mBlocked 为阻塞。在取消息函数next()可知,当没有消息的时候,这个参数会置位true
// p.target == null 下一个消息是同步屏障 ,就是优先处理异步消息
// msg 是异步消息。
//这三个消息同时为true ,才去唤醒线程
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
//prev 表示队列第N个消息,p 表示队列第N+1个消息
if (p == null || when < p.when) {
//p为空,说明已经是最后一个消息了
//when < p.when,说明需要在p之前执行
break;
}
if (needWake && p.isAsynchronous()) {
//在msg插入到队列中,已有消息是异步的,那就遵从前面的线程状态,不进行唤醒
needWake = false;
}
}
// 下面两行代码后,prev 表示队列第N个消息,msg表示队列第N+1个消息,p表示队列第N+2个消息
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
// 唤醒线程
nativeWake(mPtr);
}
}
return true;
}
next
从开始获取消息到获取到一个可用的消息,这之间都是处于阻塞状态。
开启同步屏障后,会屏蔽同步消息,执行下一个异步消息。记得需要取消同步屏障,否则会阻塞在nativePollOnce()
如果消息队列为空,就会去执行所有IdleHandlers,只执行一次
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
//第一次进来才会设置为-1
int pendingIdleHandlerCount = -1;
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//会阻塞线程 时间为nextPollTimeoutMillis
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
//mMessages 是队列的头
Message msg = mMessages;
if (msg != null && msg.target == null) {
//找出下一个异步消息 ,也就是说屏蔽了同步消息。所以 msg.target == null 这样的消息叫做同步屏障的消息。可调用postSyncBarrier()来发出
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//获取到一个新消息,才把阻塞标志设为false
mBlocked = false;
if (prevMsg != null) {
//如果是同步屏障了,那么prevMsg !=null, 这里把msg 取出,调整链表指向队列的头 mMessages没有变化
prevMsg.next = msg.next;
} else {
// 从队列的头取出消息msg,所以需要更新mMessages
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
nextPollTimeoutMillis = -1;
}
if (mQuitting) {
dispose();
return null;
}
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
//第一次进来pendingIdleHandlerCount = -1
//没有需要处理的消息,或者消息要过一会再处理。
//这两个条件同时满足,会进入下面的for循环,也就是IdleHandler的调用
pendingIdleHandlerCount = mIdleHandlers.size();
}
//如果后续的循环不会再执行IdleHandlers,因为下面最后代码会把pendingIdleHandlerCount 设置为0
if (pendingIdleHandlerCount <= 0) {
//如果没有要执行的IdleHandler或者是第二次循环,则设置为阻塞中,开始下一次循环
mBlocked = true;
continue;
}
//创建mPendingIdleHandlers 数组,用来存放设置的IdleHandler
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
//返回值,true = 下次循环需要继续执行 (也就是keep的意思)
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
//下次不需要执行,则删除这个IdleHandler
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
pendingIdleHandlerCount = 0;
nextPollTimeoutMillis = 0;
}
}
Looper
Looper 会不停地从 MessageQueue 中 查看是否有新消息,如果有新消息就会立刻处理,否则会一直阻塞。
Looper 创建、退出
在使用Handler的时候,要先调用静态函数Looper.prepare()创建属于当前线程的Looper,在调用Looper.loop()来开启消息循环,这个循环是个死循环,等待Handler发送过来的消息。
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
Looper的退出:
当 Looper 的 quit 方法被调用时,Looper 就会调用 MessageQueue 的 quit 或者 qutiSafely 方法来通知消息队列退出,当消息队列被标记为退出状态时,它的 next 方法就会返回 null。
Message的复用
Message通过静态单链表来全局完成消息的复用,而在每次回收的过程中消息数据重置防止Message持有其他对象而造成内存泄漏操作,所有在日常开发开发中尽量使用Mesaage.obtain 来获取Message.
消息回收策略
在主线程Looper的loop()中不断的从消息队列中取消息diapatch,分发之后会调用Message的回收操作
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
return;
}
msg.target.dispatchMessage(msg); // 分发消息
msg.recycleUnchecked(); // 回收
}
}
当消息分发完成后将Message进行recycle
/*package*/ Message next; // 为了形成消息链表
public static final Object sPoolSync = new Object(); // 对象锁
private static Message sPool; // 消息链表的头节点
private static int sPoolSize = 0; // 当前链表中数据的个数
private static final int MAX_POOL_SIZE = 50; // 总共可使用的消息链表大小
void recycleUnchecked() {
flags = FLAG_IN_USE; // 标记改Message将加入消息池
// 重置所有消息属性
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) { // 线程安全锁
if (sPoolSize < MAX_POOL_SIZE) { // MAX_POOL_SIZE = 50 ,表明消息池最多50个
//头节点设置给Next 将当前对象最为最新的头节点sPool
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
消息获取的策略
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) { // 判断头节点是否null
Message m = sPool; //取出头节点
sPool = m.next; // 将头节点的下一个作为最新的头节点
m.next = null; // 设置需要返回的消息的next为空
m.flags = 0; // clear in-use flag // 清楚是否还在链表中
sPoolSize--;
return m;
}
}
return new Message(); // 如果消息链表为空 创建新的message
}
消息屏障
Message分为3中:普通消息(同步消息)、屏障消息(同步屏障)和异步消息。
我们通常使用的都是普通消息,而屏障消息就是在消息队列中插入一个屏障,在屏障之后的所有普通消息都会被挡着,不能被处理。不过异步消息却例外,屏障不会挡住异步消息。
postSyncBarrier
同步屏障是通过MessageQueue的postSyncBarrier方法插入到消息队列的。
屏障消息和普通消息的区别在于屏障没有tartget,普通消息有target是因为它需要将消息分发给对应的target,而屏障不需要被分发,它就是用来挡住普通消息来保证异步消息优先处理的。
屏障和普通消息一样可以根据时间来插入到消息队列中的适当位置,并且只会挡住它后面的同步消息的分发。
postSyncBarrier返回一个int类型的数值,通过这个数值可以撤销屏障。
postSyncBarrier方法是私有的,如果我们想调用它就得使用反射。
插入普通消息会唤醒消息队列,但是插入屏障不会。
private int postSyncBarrier(long when) {
synchronized (this) {
final int token = mNextBarrierToken++;
// 1、屏障消息和普通消息的区别是屏障消息没有tartget。
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
// 2、根据时间顺序将屏障插入到消息链表中适当的位置
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
// 3、返回一个序号,通过这个序号可以撤销屏障
return token;
}
}
屏障消息的工作原理
Message next() {
//1、如果有消息被插入到消息队列或者超时时间到,就被唤醒,否则阻塞在这。
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {//2、遇到屏障 msg.target == null
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());//3、遍历消息链表找到最近的一条异步消息
}
if (msg != null) {
//4、如果找到异步消息
if (now < msg.when) {//异步消息还没到处理时间,就在等会(超时时间)
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//异步消息到了处理时间,就从链表移除,返回它。
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// 如果没有异步消息就一直休眠,等待被唤醒。
nextPollTimeoutMillis = -1;
}
//。。。。
}
}
如何发送异步消息
在发送消息时通过 message.setAsynchronous(true)将消息设为异步的,这个方法是公开的,我们可以正常使用。
removeSyncBarrier
移除屏障可以通过MessageQueue的removeSyncBarrier方法:
//注释已经写的很清楚了,就是通过插入同步屏障时返回的token来移除屏障
/**
* Removes a synchronization barrier.
*
* @param token The synchronization barrier token that was returned by
* {@link #postSyncBarrier}.
*
* @throws IllegalStateException if the barrier was not found.
*
* @hide
*/
public void removeSyncBarrier(int token) {
// Remove a sync barrier token from the queue.
// If the queue is no longer stalled by a barrier then wake it.
synchronized (this) {
Message prev = null;
Message p = mMessages;
//找到token对应的屏障
while (p != null && (p.target != null || p.arg1 != token)) {
prev = p;
p = p.next;
}
final boolean needWake;
//从消息链表中移除
if (prev != null) {
prev.next = p.next;
needWake = false;
} else {
mMessages = p.next;
needWake = mMessages == null || mMessages.target != null;
}
//回收这个Message到对象池中。
p.recycleUnchecked();
// If the loop is quitting then it is already awake.
// We can assume mPtr != 0 when mQuitting is false.
if (needWake && !mQuitting) {
nativeWake(mPtr);//唤醒消息队列
}
}
Handler.postDelayed()的实现原理
总结
- postDelay()一个10秒钟的Runnable A、消息进队,MessageQueue调用nativePollOnce()阻塞,Looper阻塞;
- 紧接着post()一个Runnable B、消息进队,判断现在A时间还没到、正在阻塞,把B插入消息队列的头部(A的前面),然后调用nativeWake()方法唤醒线程;
- MessageQueue.next()方法被唤醒后,重新开始读取消息链表,第一个消息B无延时,直接返回给Looper;
- Looper处理完这个消息再次调用next()方法,MessageQueue继续读取消息链表,第二个消息A还没到时间,计算一下剩余时间(假如还剩9秒)继续调用nativePollOnce()阻塞;
- 直到阻塞时间到或者下一次有Message进队;
Handler没有自己处理Delay,而是交给了MessageQueue处理
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
...
}
Looper读取MessageQueue是在loop()调用的是MessageQueue.next()
如果头部的这个Message是有延迟而且延迟时间没到的(now < msg.when),会计算一下时间(保存为变量nextPollTimeoutMillis),然后在循环开始的时候判断如果这个Message有延迟,就调用nativePollOnce(ptr, nextPollTimeoutMillis)进行阻塞。nativePollOnce()的作用类似与object.wait()
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
...
}
}
Handler内存泄露
主线程的Looper对象的生命周期 = 该应用程序的生命周期
在Java中,非静态内部类 & 匿名内部类都默认持有 外部类的引用
泄露原因
Handler实例的消息队列有来自线程
在Handler消息队列 还有未处理的消息 / 正在处理消息时,消息队列中的Message持有Handler实例的引用
由于Handler = 非静态内部类 / 匿名内部类(2种使用方式),故又默认持有外部类的引用(即MainActivity实例),引用关系如下图
上述的引用关系会一直保持,直到Handler消息队列中的所有消息被处理完毕
在Handler消息队列 还有未处理的消息 / 正在处理消息时,此时若需销毁外部类MainActivity,但由于上述引用关系,垃圾回收器(GC)无法回收MainActivity,从而造成内存泄漏。如下图:
解决方案-静态内部类+弱引用
原理
静态内部类 不默认持有外部类的引用,从而使得 “未被处理 / 正处理的消息 -> Handler实例 -> 外部类” 的引用关系 的引用关系 不复存在。
具体方案
将Handler的子类设置成 静态内部类。同时,还可加上 使用WeakReference弱引用持有Activity实例
原因:弱引用的对象拥有短暂的生命周期。在垃圾回收器线程扫描时,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存
public class MainActivity extends AppCompatActivity {
public static final String TAG = "carson:";
private Handler showhandler;
// 主线程创建时便自动创建Looper & 对应的MessageQueue
// 之后执行Loop()进入消息循环
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//1. 实例化自定义的Handler类对象->>分析1
//注:
// a. 此处并无指定Looper,故自动绑定当前线程(主线程)的Looper、MessageQueue;
// b. 定义时需传入持有的Activity实例(弱引用)
showhandler = new FHandler(this);
// 2. 启动子线程1
new Thread() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// a. 定义要发送的消息
Message msg = Message.obtain();
msg.what = 1;// 消息标识
msg.obj = "AA";// 消息存放
// b. 传入主线程的Handler & 向其MessageQueue发送消息
showhandler.sendMessage(msg);
}
}.start();
// 3. 启动子线程2
new Thread() {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// a. 定义要发送的消息
Message msg = Message.obtain();
msg.what = 2;// 消息标识
msg.obj = "BB";// 消息存放
// b. 传入主线程的Handler & 向其MessageQueue发送消息
showhandler.sendMessage(msg);
}
}.start();
}
// 分析1:自定义Handler子类
// 设置为:静态内部类
private static class FHandler extends Handler{
// 定义 弱引用实例
private WeakReference<Activity> reference;
// 在构造方法中传入需持有的Activity实例
public FHandler(Activity activity) {
// 使用WeakReference弱引用持有Activity实例
reference = new WeakReference<Activity>(activity); }
// 通过复写handlerMessage() 从而确定更新UI的操作
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
Log.d(TAG, "收到线程1的消息");
break;
case 2:
Log.d(TAG, " 收到线程2的消息");
break;
}
}
}
}
解决方案-生命周期结束,清空消息队列
当 外部类(此处以Activity为例) 结束生命周期时(此时系统会调用onDestroy()),清除 Handler消息队列里的所有消息(调用removeCallbacksAndMessages(null))
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
// 外部类Activity生命周期结束时,同时清空消息队列 & 结束Handler生命周期
}
HandlerThread
使用步骤
// 步骤1:创建HandlerThread实例对象
// 传入参数 = 线程名字,作用 = 标记该线程
HandlerThread mHandlerThread = new HandlerThread("handlerThread");
// 步骤2:启动线程
mHandlerThread.start();
// 步骤3:创建工作线程Handler & 复写handleMessage()
// 作用:关联HandlerThread的Looper对象、实现消息处理操作 & 与其他线程进行通信
// 注:消息处理操作(HandlerMessage())的执行线程 = mHandlerThread所创建的工作线程中执行
Handler workHandler = new Handler( handlerThread.getLooper() ) {
@Override
public boolean handleMessage(Message msg) {
...//消息处理
return true;
}
});
// 步骤4:使用工作线程Handler向工作线程的消息队列发送消息
// 在工作线程中,当消息循环时取出对应消息 & 在工作线程执行相关操作
// a. 定义要发送的消息
Message msg = Message.obtain();
msg.what = 2; //消息的标识
msg.obj = "B"; // 消息的存放
// b. 通过Handler发送消息到其绑定的消息队列
workHandler.sendMessage(msg);
// 步骤5:结束线程,即停止线程的消息循环
mHandlerThread.quit();
注意问题
在HandlerThread中,有2个问题需注意的:内存泄漏 & 连续发送消息
连续发送消息
当你连续点击3下时,发现并无按照最新点击的按钮操作显示,而是按顺序的一个个显示出来
原因:使用HandlerThread时只是开了一个工作线程,当你点击了n下后,只是将n个消息发送到消息队列MessageQueue里排队,等候派发消息给Handler再进行对应的操作
源码分析
创建HandlerThread
HandlerThread类继承自Thread类
创建HandlerThread类对象 = 创建Thread类对象 + 设置线程优先级 = 新开1个工作线程 + 设置线程优先级
/**
* 具体使用
* 传入参数 = 线程名字,作用 = 标记该线程
*/
HandlerThread mHandlerThread = new HandlerThread("handlerThread");
/**
* 源码分析:HandlerThread类的构造方法
*/
public class HandlerThread extends Thread {
// 继承自Thread类
int mPriority; // 线程优先级
int mTid = -1; // 当前线程id
Looper mLooper; // 当前线程持有的Looper对象
// HandlerThread类有2个构造方法
// 区别在于:设置当前线程的优先级参数,即可自定义设置 or 使用默认优先级
// 方式1. 默认优先级
public HandlerThread(String name) {
// 通过调用父类默认的方法创建线程
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
// 方法2. 自定义设置优先级
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
...
}
启动线程
为当前工作线程(即步骤1创建的线程)创建1个Looper对象 & MessageQueue对象
通过持有锁机制来获得当前线程的Looper对象
发出通知:当前线程已经创建mLooper对象成功
工作线程进行消息循环,即不断从MessageQueue中取消息 & 派发消息
/**
* 具体使用
*/
mHandlerThread.start();
/**
* 源码分析:此处调用的是父类(Thread类)的start(),最终回调HandlerThread的run()
*/
@Override
public void run() {
// 1. 获得当前线程的id
mTid = Process.myTid();
// 2. 创建1个Looper对象 & MessageQueue对象
Looper.prepare();
// 3. 通过持有锁机制来获得当前线程的Looper对象
synchronized (this) {
mLooper = Looper.myLooper();
// 发出通知:当前线程已经创建mLooper对象成功
// 此处主要是通知getLooper()中的wait()
notifyAll();
// 此处使用持有锁机制 + notifyAll() 是为了保证后面获得Looper对象前就已创建好Looper对象
}
// 4. 设置当前线程的优先级
Process.setThreadPriority(mPriority);
// 5. 在线程循环前做一些准备工作 ->>分析1
// 该方法实现体是空的,子类可实现 / 不实现该方法
onLooperPrepared();
// 6. 进行消息循环,即不断从MessageQueue中取消息 & 派发消息
Looper.loop();
mTid = -1;
}
}
/**
* 分析1:onLooperPrepared();
* 说明:该方法实现体是空的,子类可实现 / 不实现该方法
*/
protected void onLooperPrepared() {
}
创建工作线程Handler & 复写handleMessage()
同步的问题:只有当线程创建成功 & 其对应的Looper对象也创建成功后才能获得Looper的值,才能将创建的Handler 与
工作线程的Looper对象绑定,从而将Handler绑定工作线程
解决方案:即保证同步的解决方案 = 同步锁、wait() 和 notifyAll(),即 在run()中成功创建Looper对象后,立即调用notifyAll()通知 getLooper()中的wait()结束等待 &
返回run()中成功创建的Looper对象,使得Handler与该Looper对象绑定
/**
* 具体使用
* 作用:将Handler关联HandlerThread的Looper对象、实现消息处理操作 & 与其他线程进行通信
* 注:消息处理操作(HandlerMessage())的执行线程 = mHandlerThread所创建的工作线程中执行
*/
Handler workHandler = new Handler( handlerThread.getLooper() ) {
@Override
public boolean handleMessage(Message msg) {
...//消息处理
return true;
}
});
/**
* 源码分析:handlerThread.getLooper()
* 作用:获得当前HandlerThread线程中的Looper对象
*/
public Looper getLooper() {
// 若线程不是存活的,则直接返回null
if (!isAlive()) {
return null;
}
// 若当前线程存活,再判断线程的成员变量mLooper是否为null
// 直到线程创建完Looper对象后才能获得Looper对象,若Looper对象未创建成功,则阻塞
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
// 此处会调用wait方法去等待
wait();
} catch (InterruptedException e) {
}
}
}
// 上述步骤run()使用 持有锁机制 + notifyAll() 获得Looper对象后
// 则通知当前线程的wait()结束等待 & 跳出循环
// 最终getLooper()返回的是在run()中创建的mLooper对象
return mLooper;
}
结束线程,即停止线程的消息循环
/**
* 具体使用
*/
mHandlerThread.quit();
/**
* 源码分析:mHandlerThread.quit()
* 说明:
* a. 该方法属于HandlerThread类
* b. HandlerThread有2种让当前线程退出消息循环的方法:quit() 、quitSafely()
*/
// 方式1:quit()
// 特点:效率高,但线程不安全
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
// 方式2:quitSafely()
// 特点:效率低,但线程安全
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
// 注:上述2个方法最终都会调用MessageQueue.quit(boolean safe)->>分析1
/**
* 分析1:MessageQueue.quit(boolean safe)
*/
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked(); // 方式1(不安全)会调用该方法 ->>分析2
} else {
removeAllMessagesLocked(); // 方式2(安全)会调用该方法 ->>分析3
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
/**
* 分析2:removeAllMessagesLocked()
* 原理:遍历Message链表、移除所有信息的回调 & 重置为null
*/
private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) {
Message n = p.next;
p.recycleUnchecked();
p = n;
}
mMessages = null;
}
/**
* 分析3:removeAllFutureMessagesLocked()
* 原理:先判断当前消息队列是否正在处理消息
* a. 若不是,则类似分析2移除消息
* b. 若是,则等待该消息处理处理完毕再使用分析2中的方式移除消息退出循环
* 结论:退出方法安全与否(quitSafe() 或 quit()),在于该方法移除消息、退出循环时是否在意当前队列是否正在处理消息
*/
private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
// 判断当前消息队列是否正在处理消息
// a. 若不是,则直接移除所有回调
if (p.when > now) {
removeAllMessagesLocked();
} else {
// b. 若是正在处理,则等待该消息处理处理完毕再退出该循环
Message n;
for (;;) {
n = p.next;
if (n == null) {
return;
}
if (n.when > now) {
break;
}
p = n;
}
p.next = null;
do {
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}
IntentService
Android里的一个封装类,继承四大组件之一的Service
处理异步请求 & 实现多线程
线程任务需按顺序、在后台执行
工作任务队列 = 顺序执行
即 若一个任务正在IntentService中执行,此时你再发送1个新的任务请求,这个新的任务会一直等待直到前面一个任务执行完毕后才开始执行
由于onCreate()只会调用一次 = 只会创建1个工作线程;
当多次调用 startService(Intent)时(即 onStartCommand()也会调用多次),其实不会创建新的工作线程,只是把消息加入消息队列中 & 等待执行。 所以,多次启动 IntentService 会按顺序执行事件
若服务停止,则会清除消息队列中的消息,后续的事件不执行
不建议通过 bindService() 启动 IntentService
采用 bindService()启动 IntentService的生命周期如下:
onCreate() ->> onBind() ->> onunbind()->> onDestory()
并不会调用onStart() 或 onStartcommand(),故不会将消息发送到消息队列,那么onHandleIntent()将不会回调,即无法实现多线程的操作,此时,你应该使用Service,而不是IntentService
使用步骤
步骤1:定义 IntentService的子类,需复写onHandleIntent()方法
步骤2:在Manifest.xml中注册服务
步骤3:在Activity中开启Service服务
定义 IntentService的子类
传入线程名称、复写onHandleIntent()方法
public class myIntentService extends IntentService {
/**
* 在构造函数中传入线程名字
**/
public myIntentService() {
// 调用父类的构造函数
// 参数 = 工作线程的名字
super("myIntentService");
}
/**
* 复写onHandleIntent()方法
* 根据 Intent实现 耗时任务 操作
**/
@Override
protected void onHandleIntent(Intent intent) {
// 根据 Intent的不同,进行不同的事务处理
String taskName = intent.getExtras().getString("taskName");
switch (taskName) {
case "task1":
Log.i("myIntentService", "do task1");
break;
case "task2":
Log.i("myIntentService", "do task2");
break;
default:
break;
}
}
@Override
public void onCreate() {
Log.i("myIntentService", "onCreate");
super.onCreate();
}
/**
* 复写onStartCommand()方法
* 默认实现 = 将请求的Intent添加到工作队列里
**/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("myIntentService", "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.i("myIntentService", "onDestroy");
super.onDestroy();
}
}
在Manifest.xml中注册服务
<service android:name=".myIntentService">
<intent-filter >
<action android:name="cn.scu.finch"/>
</intent-filter>
</service>
在Activity中开启Service服务
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 同一服务只会开启1个工作线程
// 在onHandleIntent()函数里,依次处理传入的Intent请求
// 将请求通过Bundle对象传入到Intent,再传入到服务里
// 请求1
Intent i = new Intent("cn.scu.finch");
Bundle bundle = new Bundle();
bundle.putString("taskName", "task1");
i.putExtras(bundle);
startService(i);
// 请求2
Intent i2 = new Intent("cn.scu.finch");
Bundle bundle2 = new Bundle();
bundle2.putString("taskName", "task2");
i2.putExtras(bundle2);
startService(i2);
startService(i); //多次启动
}
}
与Service的区别
与其他线程的区别
工作原理
源码分析
通过HandlerThread 单独开启1个工作线程:IntentService
创建1个内部 Handler :ServiceHandler
绑定 ServiceHandler 与 IntentService
通过 onStartCommand() 传递服务intent 到ServiceHandler 、依次插入Intent到工作队列中 & 逐个发送给 onHandleIntent()
通过onHandleIntent() 依次处理所有Intent对象所对应的任务
因此我们通过复写onHandleIntent() & 在里面 根据Intent的不同进行不同线程操作 即可
@Override
public void onCreate() {
super.onCreate();
// 1. 通过实例化andlerThread新建线程 & 启动;故 使用IntentService时,不需额外新建线程
// HandlerThread继承自Thread,内部封装了 Looper
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
// 2. 获得工作线程的 Looper & 维护自己的工作队列
mServiceLooper = thread.getLooper();
// 3. 新建mServiceHandler & 绑定上述获得Looper
// 新建的Handler 属于工作线程 ->>分析1
mServiceHandler = new ServiceHandler(mServiceLooper);
}
/**
* 分析1:ServiceHandler源码分析
**/
private final class ServiceHandler extends Handler {
// 构造函数
public ServiceHandler(Looper looper) {
super(looper);
}
// IntentService的handleMessage()把接收的消息交给onHandleIntent()处理
@Override
public void handleMessage(Message msg) {
// onHandleIntent 方法在工作线程中执行
// onHandleIntent() = 抽象方法,使用时需重写 ->>分析2
onHandleIntent((Intent)msg.obj);
// 执行完调用 stopSelf() 结束服务
stopSelf(msg.arg1);
}
}
/**
* 分析2: onHandleIntent()源码分析
* onHandleIntent() = 抽象方法,使用时需重写
**/
@WorkerThread
protected abstract void onHandleIntent(Intent intent);
问题2:IntentService 如何通过onStartCommand() 将Intent 传递给服务 & 依次插入到工作队列中
/**
* onStartCommand()源码分析
* onHandleIntent() = 抽象方法,使用时需重写
**/
public int onStartCommand(Intent intent, int flags, int startId) {
// 调用onStart()->>分析1
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
/**
* 分析1:onStart(intent, startId)
**/
public void onStart(Intent intent, int startId) {
// 1. 获得ServiceHandler消息的引用
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
// 2. 把 Intent参数 包装到 message 的 obj 发送消息中,
//这里的Intent = 启动服务时startService(Intent) 里传入的 Intent
msg.obj = intent;
// 3. 发送消息,即 添加到消息队列里
mServiceHandler.sendMessage(msg);
}
Parcelable 和 Serializable
Serializable
Serializable 是java的序列化技术,最简单的使用方式为在需要序列化的class增加implements Serializable
优点
Seralizable相对Parcelable而言,好处就是非常简单,只需对需要序列化的类class执行就可以,不需要手动去处理序列化和反序列化的过程,所以常常用于网络请求数据处理,Activity之间传递值的使用。
缺点
使用了反射技术,并且期间产生临时对象,因此效率过慢
Seralizable无法序列化静态变量,使用transient修饰的对象也无法序列化。
Parcelable
Parcelable是android特有的序列化API,它的出现是为了解决Serializable在序列化的过程中消耗资源严重的问题,但是因为本身使用需要手动处理序列化和反序列化过程
实现原理是在内存中建立一块共享数据块,序列化和反序列化均是操作这一块的数据,如此来实现。
public class MyParcelable implements Parcelable {
private int mData;
private String mStr;
public int describeContents() {
return 0;
}
// 写数据进行保存
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mData);
out.writeString(mStr);
}
// 用来创建自定义的Parcelable的对象
public static final Parcelable.Creator<MyParcelable> CREATOR
= new Parcelable.Creator<MyParcelable>() {
public MyParcelable createFromParcel(Parcel in) {
return new MyParcelable(in);
}
public MyParcelable[] newArray(int size) {
return new MyParcelable[size];
}
};
// 读数据进行恢复
private MyParcelable(Parcel in) {
mData = in.readInt();
mStr = in.readString();
}
}
Binder
Binder是客户端和服务端进行通信的媒介,当bindService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通过这个对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于AIDL的服务。
优点
高效
Binder数据拷贝只需要一次,而管道、消息队列、Socket都需要2次
通过驱动在内核空间拷贝数据,不需要额外的同步处理
安全性高
Binder 机制为每个进程分配了 UID/PID 来作为鉴别身份的标示,并且在 Binder 通信时会根据 UID/PID 进行有效性检测
传统的进程通信方式对于通信双方的身份并没有做出严格的验证
如,Socket通信 ip地址是客户端手动填入,容易出现伪造
使用简单
采用Client/Server 架构
实现 面向对象 的调用方式,即在使用Binder时就和调用一个本地对象实例一样
进程空间分配
一个进程空间分为 用户空间 & 内核空间(Kernel),即把进程内 用户 & 内核 隔离开来
二者区别:
进程间,用户空间的数据不可共享,所以用户空间 = 不可共享空间
进程间,内核空间的数据可共享,所以内核空间 = 可共享空间
进程内用户与内核 进行交互称为系统调用
跨进程通信
跨进程间通信的原理
先通过进程间的内核空间进行数据交互
再通过进程内的用户空间 & 内核空间进行数据交互,从而实现进程间的用户空间的数据交互
Binder 跨进程通信机制
角色说明
Binder驱动作用
- 传递进程间数据,当Client向Server发起IPC请求,将Client数据从用户空间拷贝到内核空间,再将内核空间的数据拷贝到Server的用户空间;
- 实现线程控制,管理Binder线程池;
- 持有Server进程在内核的Binder实体,给Client提供Binder实体引用。
ServiceManager进程作用
管理server进程的注册和查询
Client进程、Server进程 & Service Manager进程属于进程空间的用户空间,不可进行进程间交互
Binder驱动属于进程空间的内核空间,可进行进程间 & 进程内交互
注册服务
Server进程向Binder驱动发起注册请求;
Binder驱动将注册请求转发给ServiceManager进程;
ServiceManager进程添加Server进程
获取服务
Client进程向Binder驱动发起获取服务请求,并传递要获取服务的名称;
Binder驱动将请求转发给ServiceManager进程,ServiceManager查询Client所需的Server;
ServiceManager将Server信息通过Binder驱动转发给Client
使用服务
Client发数据到Server
Client将数据放入共享内存,Binder从Client共享内存读出数据,依据ServiceManager找到对应的Server,Binder将数据拷贝到Server共享内存,通知server进行解包。
Server依据Client调用目标方法
Server收到Binder通知,进行解包和调用目标方法,将结果数据写入共享内存。
Server将目标方法结果返回给Client
Binder将Server共享内存数据写入Client共享内存,通知Client获取返回结果。
Binder Android实现原理
在IBookManager.aidl中,对于自动生成的IBookManager.java文件,它是服务器端的代码。
当客户端向服务端发送连接请求时,如果客户端和服务端在同一进程中,那么服务端就向客户端返回Stub这个Binder对象,如果客户端和服务端在不同进程中,那么服务端就向客户端返回内部类Stub的内部代理类Proxy,然后客户端根据这个Proxy来调用Proxy内部的方法;
这个Proxy内部含有服务端真正的Binder对象也就是那个内部类Stub,在客户端调用Proxy内部的方法也就会导致调用Stub的transact方法,而Stub的transact方法又会回调它自己的onTransact方法,onTransact方法是在服务端运行的,而transact方法是在客户端调用的,这样就实现了客户端调用服务端的方法了。当然这所有的传递过程也少不了Parcel这个数据包的协助。
aidl文件
package com.ryg.chapter_2.aidl;
import android.os.Parcel;
import android.os.Parcelable;
/*
* (1)它是一个表示图示信息的类,
* 它实现了Parcelable接口,因为实现了Parcelable接口便可以进行序列化
* (2)Book.aidl是Book类在ADIL中的声明。
* (3)IBookManager.aidl是我们定义的一个接口,里面有两个方法:getBookList和addBook,
* 其中getBookList用于从远程服务端获取图书列表,而addBook用于往图书列表中添加一本书,
* 当然这两个方法主要是示例用,不一定要有实际意义。
* (4)尽管Book类和IBookManager位于相同的包中,但是在IBookManager中仍然要导入Book类,
* 这就是AIDL的特殊之处。
* */
public class Book implements Parcelable {
<span style="white-space:pre"> </span>public int bookId;
public String bookName;
/*
* 普通构造函数:
* */
public Book() {
<span style="white-space:pre"> </span>
}
/*
* 普通构造函数:
* */
public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
public int describeContents() {
return 0;
}
/*
* 序列化:
* */
public void writeToParcel(Parcel out, int flags) {
out.writeInt(bookId);
out.writeString(bookName);
}
/*
* 反序列化,
* 这个creator就是通过一个Parcle来创建一个book对象或者数组。
* */
public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {
public Book createFromParcel(Parcel in) {
return new Book(in);
}
public Book[] newArray(int size) {
return new Book[size];
}
};
/*
* 用于反序列化的构造函数:
* */
private Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}
@Override
public String toString() {
return String.format("[bookId:%s, bookName:%s]", bookId, bookName);
}
}
package com.ryg.chapter_2.aidl;
parcelable Book;
package com.ryg.chapter_2.aidl;
import com.ryg.chapter_2.aidl.Book;
import com.ryg.chapter_2.aidl.IOnNewBookArrivedListener;
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
void registerListener(IOnNewBookArrivedListener listener);
void unregisterListener(IOnNewBookArrivedListener listener);
}
Binder类
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package com.ryg.chapter_2.aidl;
/*
* IBookManager它继承了IInterface这个接口,同时它自己也还是个接口,
* 所有可以在Binder中传输的接口都要继承IInterface接口。
* 首先,它声明了两个方法getBookList和addBook,显然这就是我们在IBookManager.aidl中所声明的方法,
* 同时它还声明了两个整型的id分别用于标识这两个方法。
* 接着,它声明了一个内部类Stub,这个Stub就是一个Binder类,
* 当客户端和服务端都位于同一个进程时,方法调用不会走跨进程的transact过程,
* 而当两者位于不同进程时,方法调用需要走transact过程,
* 这个逻辑由Stub的内部代理类Proxy来完成。
* */
public interface IBookManager extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
/*
* 首先这个Stub,它是一个内部类,它继承了Binder,所以它是一个Binder,
* 同时Stub还实现了IBookManager中的方法。
* */
public static abstract class Stub extends android.os.Binder implements com.ryg.chapter_2.aidl.IBookManager
{
/*
* Binder的唯一标识符。
* */
private static final java.lang.String DESCRIPTOR = "com.ryg.chapter_2.aidl.IBookManager";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.ryg.chapter_2.aidl.IBookManager interface,
* generating a proxy if needed.
*/
/*
* 用于将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象,
* 这种转换过程是区分进程的,
* 如果客户端和服务端位于同一进程,那么此方法返回的就是服务端的Stub对象本身,
* 否则返回的是系统封装后的Stub.proxy代理对象。
* */
public static com.ryg.chapter_2.aidl.IBookManager asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
// 同一进程
if (((iin!=null)&&(iin instanceof com.ryg.chapter_2.aidl.IBookManager))) {
return ((com.ryg.chapter_2.aidl.IBookManager)iin);
}
// 不同进程
return new com.ryg.chapter_2.aidl.IBookManager.Stub.Proxy(obj);
}
/*
* 此方法用于返回当前Binder对象,也就是内部类Stub。
* */
@Override public android.os.IBinder asBinder()
{
return this;
}
/*
* 这个方法运行在服务端中的Binder线程池中,
* 当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理。
* 服务端通过code可以确定客户端所请求的目标方法是什么,
* 接着从data中取出目标方法所需的参数,
* 然后执行目标方法。
* 当目标方法执行完毕后,就向reply中写入返回值。
* 如果此方法返回false,那么客户端的请求会失败,因此我们可以利用这个特性来做权限验证。
* */
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getBookList:
{
data.enforceInterface(DESCRIPTOR);
/*
* 这句才是调用了真正的执行过程呢
* */
java.util.List<com.ryg.chapter_2.aidl.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook:
{
data.enforceInterface(DESCRIPTOR);
com.ryg.chapter_2.aidl.Book _arg0;
if ((0!=data.readInt())) {
_arg0 = com.ryg.chapter_2.aidl.Book.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
/*
* 这句才是调用了真正的执行过程呢
* */
this.addBook(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_registerListener:
{
data.enforceInterface(DESCRIPTOR);
com.ryg.chapter_2.aidl.IOnNewBookArrivedListener _arg0;
_arg0 = com.ryg.chapter_2.aidl.IOnNewBookArrivedListener.Stub.asInterface(data.readStrongBinder());
this.registerListener(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_unregisterListener:
{
data.enforceInterface(DESCRIPTOR);
com.ryg.chapter_2.aidl.IOnNewBookArrivedListener _arg0;
_arg0 = com.ryg.chapter_2.aidl.IOnNewBookArrivedListener.Stub.asInterface(data.readStrongBinder());
this.unregisterListener(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
/*
* 代理类Proxy。
* */
private static class Proxy implements com.ryg.chapter_2.aidl.IBookManager
{
/*
* 这个mRemote代表的就是目标对象角色,
* */
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
/*
* 这个方法运行在客户端,
* 因为当客户端和服务端不在同一进程时,服务端返回代理类Proxy,所以客户端会通过Proxy调用到代理类的getBookList方法,
* 当客户端远程调用此方法时,它的内部实现是这样的:
* 首先创建该方法所需要的输入型Parcel对象_data、输出型Parcel对象_reply和返回值对象List,
* 然后把该方法的参数信息写入_data中,
* 接着调用transact方法来发起RPC(远程过程调用)请求,同时当前线程挂起,
* 然后服务端的onTransact方法会被调用,直到RPC过程返回后,当前线程继续执行,
* 并从_reply中取出RPC过程的返回结果。
* 最后返回_reply中的数据。
* */
@Override public java.util.List<com.ryg.chapter_2.aidl.Book> getBookList() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.ryg.chapter_2.aidl.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.ryg.chapter_2.aidl.Book.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void addBook(com.ryg.chapter_2.aidl.Book book) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((book!=null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public void registerListener(com.ryg.chapter_2.aidl.IOnNewBookArrivedListener listener) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeStrongBinder((((listener!=null))?(listener.asBinder()):(null)));
mRemote.transact(Stub.TRANSACTION_registerListener, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public void unregisterListener(com.ryg.chapter_2.aidl.IOnNewBookArrivedListener listener) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeStrongBinder((((listener!=null))?(listener.asBinder()):(null)));
mRemote.transact(Stub.TRANSACTION_unregisterListener, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
}
/*
* 用于标识方法的整型id。
* 它们用于在transact过程总客户端所请求的到底是哪个方法。
* */
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_registerListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_unregisterListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
}
/*
* 声明了在IBookManager.aidl中所声明的方法。
* 这里才是真正的方法声明。具体实现我们仍然没有看到呢。
* */
public java.util.List<com.ryg.chapter_2.aidl.Book> getBookList() throws android.os.RemoteException;
public void addBook(com.ryg.chapter_2.aidl.Book book) throws android.os.RemoteException;
public void registerListener(com.ryg.chapter_2.aidl.IOnNewBookArrivedListener listener) throws android.os.RemoteException;
public void unregisterListener(com.ryg.chapter_2.aidl.IOnNewBookArrivedListener listener) throws android.os.RemoteException;
}
linkToDeath和unlinkToDeath
Binder运行在服务端进程,如果服务端进程由于某些原因异常终止,这个时候我们到服务端的Binder连接断裂,会导致我们的远程调用失败。Binder提供了两个配对的方法linkToDeath和unlinkToDeath,通过linkToDeath我们可以给Binder设置一个死亡代理,当Binder死亡时,我们会收到通知,这个时候我们就可以重新发起连接请求从而恢复连接。
/*
* 声明这个接口就好:
* */
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DearhRecipient(){
// 只需要重写这个方法就可以了。
@Override
public void binderDied(){
if(mBookManager == null)
return;
mBookManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
mBookManager = null;
// TODO: 这里重新绑定远程Service。
}
}
mService = IMessageBoxManager.Stub.asInterface(binder);
binder.linkToDeath(mDeathRecipient, 0);
Messager
Messenger对AIDL做了封装,使得我们可以更简便地进行进程间通信。由于它一次处理一个请求,所以在服务端我们不考虑线程同步的问题,因为服务端中不存在并发执行的情形。
通过它可以在不同进程中传递Message对象,在Message中仿佛我们需要传递的数据,就可以轻松地实现数据的进程间传递了。
有两个构造函数,分别接收Handler对象和IBinder对象。
实现步骤
(1)服务端进程:
首先需要在服务端创建一个Service来处理客户端的连接请求,同时创建一个Handler并以它作为参数来创建一个Messenger对象,然后在Service的onBind中返回这个Messenger对象底层的Binder即可。关键点就在于它的返回是返回给了要绑定这个服务端的客户端,然后客户端拿到这个Binder再去创建Messenger,再去发送Message等等。
(2)客户端进程:
客户端进程中,首先要绑定服务端的Service,绑定后服务端的onBind会返回一个Binder对象,然后客户端用服务端返回的这个Binder对象创建一个Messenger,通过这个Messenger就可以向服务器端发送消息了,发送消息类型为Message对象,如果需要服务端能够回应客户端,就像和服务端一个,我们还需要创建一个Handler并创建一个新的Messenger,并把这个Messenger对象在第一次客户端像服务端发送消息时通过Message的replyTo参数传递给服务端,服务端通过读取Message中的replyTo参数就是服务端给客户端的的Messenger,然后就可以回应客户端。
(3)注意点:
客户端给服务端发送消息的时候所用的Messenger是通过绑定服务端,然后依据onBind返回的Binder对象为参数来创建Messenger,而服务端在回应客户端的时候所用的Messenger是客户端在刚刚发送消息的时候将自身创建的Messenger作为刚刚发送消息的Message的replyTo参数传递给服务端的,所以在服务端直接读取出这个Messenger。
示例代码
服务端
package com.ryg.chapter_2.messenger;
import com.ryg.chapter_2.model.User;
import com.ryg.chapter_2.utils.MyConstants;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
/*
* 首先,这是一个服务。
* 其次,这个服务是需要注册的,并且要给它另起一个进程。
* <service
android:name=".messenger.MessengerService"
android:process=":remote" >
<intent-filter>
<action android:name="com.ryg.MessengerService.launch" />
</intent-filter>
</service>
* */
public class MessengerService extends Service {
private static final String TAG = "MessengerService";
/*
* 继承Handler,
* MessengerHandler用来处理客户端发送的消息,
* 并从消息中取出客户端发来的文本信息。
* */
private static class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
/*
* MyConstants是我们这个应用中的一个类,其中包含了几个变量的声明:
* public static final int MSG_FROM_CLIENT = 0;
public static final int MSG_FROM_SERVICE = 1;
* */
case MyConstants.MSG_FROM_CLIENT:
/*
* 这一条语句是在处理从客户端发来的消息,用Log日志打印出来:
* */
Log.i(TAG, "receive msg from Client:" + msg.getData().getString("msg"));
/*
* 这下面的语句是用来响应客户端,给客户端回馈消息的。
* (1)第一步是通过replyTo来获取客户端的Messenger对象。
* (2)第二步是创建一个Message消息,
* Message.obtain这个方法的第一个参数是Handler,第二个参数是消息的what字段。
* (3)第三步创建一个Bundle对象,然后向这个对象中添加String内容。
* (4)第四步是将Bundle对象设置给Message。
* (5)第五步是通过Messenger将Message发送出去,
* 因为我们的Messenger是通过客户端来获取的,而在客户端那边这个Messenger是以Handler为参数创建的,
* 所以在服务端通过客户端的Messenger发送消息后,在客户端的Handler就会处理这条消息,嘻嘻,就达到了消息传送的目的。
* */
Messenger client = msg.replyTo;
Message relpyMessage = Message.obtain(null, MyConstants.MSG_FROM_SERVICE);
Bundle bundle = new Bundle();
bundle.putString("reply", "嗯,你的消息我已经收到,稍后会回复你。");
relpyMessage.setData(bundle);
try {
client.send(relpyMessage);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
super.handleMessage(msg);
}
}
}
/*
* 这是我们服务端自己的Messenger,它是以上面的Handler对象为参数创建的,
* 这个Messenger是要通过绑定该服务器的时候onBind方法传递给客户端,
* 然后客户端获取了该Messenger,再以该Messenger来发送消息,
* 这样服务端就可以接收到该消息并处理。
* */
private final Messenger mMessenger = new Messenger(new MessengerHandler());
/*
* 这个方法是在绑定服务的过程中调用的并将结果返回给客户端的,
* 所以通过onBind方法客户端就可以获取我们Messenger的Binder对象了,
* 然后客户端可以根据该Binder对象来创建一个Messenger,
* 这样客户端中用的Messenger和这里的Messenger就是向对应的了。
* */
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
}
客户端
package com.ryg.chapter_2.messenger;
import com.ryg.chapter_2.R;
import com.ryg.chapter_2.R.layout;
import com.ryg.chapter_2.model.User;
import com.ryg.chapter_2.utils.MyConstants;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
/*
* 客户端,首先它是一个活动。
* 其次它也需要注册的。
* */
public class MessengerActivity extends Activity {
private static final String TAG = "MessengerActivity";
// 用来获取服务端的Messenger,用来给服务端传递消息用的。
private Messenger mService;
// 这是客户端自己的Messenger,传递给服务端,让服务端返回消息用的。
private Messenger mGetReplyMessenger = new Messenger(new MessengerHandler());
/*
* 这个Handler是用来处理服务端返回的消息的,
* 这个Handler将作为一个参数来创建自己的Messenger,
* 然后将这个Messenger传递给服务端,让服务端根据它返回消息。
* */
private static class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MyConstants.MSG_FROM_SERVICE:
// 处理消息,以Log日志显示出来。
Log.i(TAG, "receive msg from Service:" + msg.getData().getString("reply"));
break;
default:
super.handleMessage(msg);
}
}
}
/*
* 这个是客户端用来绑定服务端用的,
* 在绑定过程中会调用onServiceConnected,
* 它的第二个参数IBinder service,就是在服务端中onBind方法返回的结果,
* 这个结果是服务端的Messenger对象的Binder对象,
* 然后客户端通过这个Binder对象就可以创建一个Messenger,
* 所以就是在绑定服务的过程中将服务端的Messenger传递给了客户端,建立起了两者之间的桥梁
* */
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
/*
* (1)第一步是根据服务端的IBinder service对象为参数创建Messenger。
* (2)第二步是创建一个Message消息,其中第二个参数是msg的what字段。
* 这里有个重要的点就是设置msg的replyTo字段,这个字段保存了客户端自己的Messenger,
* 客户端将自己的Messenger传递给服务端,然后方便服务端根据这个Messenger将反馈消息用同样的方法传递回来。
* (3)第三步是创建一个Bundle对象,这个对象中添加了要返回的消息内容。
* (4)第四步将Bundle对象赋给Message。
* (5)第五步用Messenger的send方法将消息发送出去。
* */
mService = new Messenger(service);
Log.d(TAG, "bind service");
Message msg = Message.obtain(null, MyConstants.MSG_FROM_CLIENT);
Bundle data = new Bundle();
data.putString("msg", "hello, this is client.");
msg.setData(data);
msg.replyTo = mGetReplyMessenger;
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void onServiceDisconnected(ComponentName className) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_messenger);
/*
* 这个Intent的跳转是需要服务端设置的:
* <service
android:name=".messenger.MessengerService"
android:process=":remote" >
<intent-filter>
<action android:name="com.ryg.MessengerService.launch" />
</intent-filter>
</service>
* */
Intent intent = new Intent("com.ryg.MessengerService.launch");
/*
* 在bindService的时候,服务端会通过onBind方法返回一个包含了服务端业务调用的Binder对象,
* 通过这个对象,客户端就可以获取服务端提供的服务或者数据,
* 具体情况去下面的第二个参数mConnection中查看。
* */
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
unbindService(mConnection);
super.onDestroy();
}
}
Messager和AIDL对比
相同点:
都可以实现IPC,实时通信,都是Binder通信原理
Messager对AIDL进行一层封装
不同点:
- AIDL需要新建AIDL文件,Messager不用;
- AIDL可以实现RPC远程调用,Messager不能实现远程调用;
- service端,Messenger处理client端的请求是单线程的,而AIDL是多线程的。
使用AIDL的时候,service端每收到一个client端的请求时,就会启动一个线程去执行相应的操作。而Messenger,service收到的请求是放在Handler的MessageQueue里面。 - client端,使用AIDL获取返回值是同步的,而Messenger是异步的。
Messenger只提供了一个方法进行进程间通信,就是send(Message msg)方法,发送的是一个Message,没有返回值,要拿到返回值,需要把client的Messenger作为msg.replyTo参数传递过去,service端处理完之后,在调用客户端的Messenger的send(Message msg)方法把返回值传递回client,这个过程是异步的,而AIDL你可以自己指定方法,指定返回值,它获取返回值是同步的。
AIDL
是什么?
跨进程访问的服务。
优点
AIDL简化了Binder的代码逻辑,把跟Service交互的逻辑通过工具编译来生成。
AIDL通信流程
Client 端和Server端使用同一个AIDL,连包名都需要保持一致。
Server端继承自Service,重载一个onBind(),返回服务实体Stub(),Stub提供了一个asInterface(Binder)的方法。
如果是在同一个进程下那么asInterface()将返回Stub对象本身,否则返回Stub.Proxy对象。
IMyService.Stub mStub = new IMyService.Stub(){...};
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG,"onBind");
return mStub;//通过ServiceConnection在activity中拿到MyService
}
Client 绑定服务时通过拿到服务Stub.asInterface返回的服务的代理Stub.Proxy()
myService = IMyService.Stub.asInterface(service);
AIDL Client和Server交互
服务本地拿到了AIDL生成的服务实体Stub(), Client绑定服务后,拿到了服务的代理Stub.proxy()。
Binder通信
proxy-stub 设计模式
代理模式
为其他的对象提供一种代理以控制对这个对象的访问。
适用于当无法或不想直接访问某个对象时通过一个代理对象来间接访问。
为了保证客户端使用的透明性, 委托对象与代理对象需要实现相同的接口。
AIDL的具体流程如下
- Client和Server都使用同一个AIDL文件,包名相同,编译后,两边都会生成IMyService.java,其中有Stub实体和Proxy代理两个对象
- Server端通过AndroidManifest.xml 注册Service;Client通过bindService()获得服务的代理Stub.Proxy()。
- Client 调用AIDL的方法add()->Stub.Proxy.add()->BinderProxy.transact()向服务端发送数据。
- 进入到服务端的onTransact(),根据Client发送的TRANSACTION code,进入add()完成接口调用,调用完成后把数据写入Parcel,通过reply发送给Client。
AIDL异步实现
由于AIDL调用是同步的,可以通过客户端以异步方式(java的Runnable,Callable,Thread;Android的Rxjava,Handler,AsyncTask)向服务端发起请求,请求需要访问的AIDL接口;
服务端调用客户端定义AIDL请求回调接口返回客户端的请求结果。
Binder连接池
问题:随着AIDL数量的增加,我们不能无限制的增加Service。
我们需要减少Service的数量,将所有的AIDL放在同一个Service中去管理。
工作机制
每个业务模块创建自己的AIDL接口并实现此接口,然后向服务端提供自己的唯一标识和其对应的Binder对象。
对于服务端来说,只需要一个Service就可以了,服务端提供一个queryBind而接口,这个接口能够根据业务模块的特征来返回相应的Binder对象给它们,不同的业务模块拿到所需的Binder对象后就可以进行远程方法调用了。
由此可见,Binder连接池的主要作用是将每个业务模块的Binder请求统一转发到远程Service中去执行,从而避免了重复创建Service的过程。
示例代码
对于客户端而言,仅仅是有一个ISecurityCenter和ICompute的对象,它们是AIDL,这两个对象是没有方法的具体实现的,具体实现是在服务端的。
在服务端有SecurityCenterImpl和ComputeImpl来继承ISecurityCenter.Stub和ICompute.Stub,因为Stub是Binder对象,所以它们两个也是Binder,里面还给出了方法的具体实现。
但当我们使用了连接池BinderPool的时候,让连接池BinderPool与服务端BinderPoolService绑定。在服务端BinderPoolService中有这样一个对象:mBinderPool,它是BinderPool.BinderPoolImpl,BinderPool.BinderPoolImpl是BinderPool的一个内部类,里面有一个queryBinder方法,用来返回真正的对应客户端的Binder对象;
在连接池BinderPool与服务端绑定以后,服务端将这个mBinderPool对象返回给连接池,这样连接池就可以通过这个mBinderPool对象为客户端返回相应的Binder对象。这样当多个种类的客户端想要绑定服务端的时候,只需要直接调用连接池就可以了,因为连接池根据服务端给它的mBinderPool掌管了所有的Binder对象。
所有的Binder对象还是由服务端来掌管的。连接池会为对应的客户端返回对应的Binder对象,这些Binder对象就是SecurityCenterImpl具体实现方法的Binder。
AIDL接口
我们有两个AIDL接口(ISecurityCenter和ICompute)来模拟两个业务模块。
package com.ryg.chapter_2.binderpool;
interface ISecurityCenter {
String encrypt(String content);
String decrypt(String password);
}
package com.ryg.chapter_2.binderpool;
interface ICompute {
int add(int a, int b);
}
这是上面两个AIDL接口的实现:其中ISecurityCenter.Stub和ICompute.Stub是在系统在gen目录下自动生成的ISecurityCenter.java和ICompute.java文件中的内部类Stub。
package com.ryg.chapter_2.binderpool;
import android.os.RemoteException;
public class SecurityCenterImpl extends ISecurityCenter.Stub {
private static final char SECRET_CODE = '^';
@Override
public String encrypt(String content) throws RemoteException {
char[] chars = content.toCharArray();
for (int i = 0; i < chars.length; i++) {
chars[i] ^= SECRET_CODE;
}
return new String(chars);
}
@Override
public String decrypt(String password) throws RemoteException {
return encrypt(password);
}
}
package com.ryg.chapter_2.binderpool;
import android.os.RemoteException;
public class ComputeImpl extends ICompute.Stub {
@Override
public int add(int a, int b) throws RemoteException {
return a + b;
}
}
为Binder连接池创建AIDL接口IBinderPool.aidl
package com.ryg.chapter_2.binderpool;
interface IBinderPool {
/**
* @param binderCode, the unique token of specific Binder<br/>
* @return specific Binder who's token is binderCode.
*/
IBinder queryBinder(int binderCode);
}
queryBinder
为Binder连接池创建远程Service并实现IBinderPool。
下面是queryBinder的具体实现,当Binder连接池连接上远程服务时,会根据不同模块的标识即binderCode返回不同的Binder对象,通过这个Binder对象所执行的操作全部发生在远程服务端
@Override
public IBinder queryBinder(int binderCode) throws RemoteException {
IBinder binder = null;
switch (binderCode) {
case BINDER_SECURITY_CENTER: {
binder = new SecurityCenterImpl();
break;
}
case BINDER_COMPUTE: {
binder = new ComputeImpl();
break;
}
default:
break;
}
return binder;
}
远程Service
实现就比较简单了:以前直接返回的是服务端的Binder对象,如今在onBind中返回的是BinderPool连接池。
package com.ryg.chapter_2.binderpool;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
public class BinderPoolService extends Service {
private static final String TAG = "BinderPoolService";
/*
* 在服务端创建一个连接池,BinderPoolImpl是BinderPool的内部类,
* 它继承了IBinderPool.Stub,并实现了queryBinder方法。
* */
private Binder mBinderPool = new BinderPool.BinderPoolImpl();
@Override
public void onCreate() {
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind");
/*
* 返回连接池对象:
* */
return mBinderPool;
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
Binder连接池
在它的内部首先它要去绑定远程服务,绑定成功后,客户端就可以通过它的queryBinder方法去获取各自对应的Binder,拿到所需的Binder以后,不同业务模块就可以进行各自的操作了
package com.ryg.chapter_2.binderpool;
import java.util.concurrent.CountDownLatch;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
public class BinderPool {
private static final String TAG = "BinderPool";
public static final int BINDER_NONE = -1;
public static final int BINDER_COMPUTE = 0;
public static final int BINDER_SECURITY_CENTER = 1;
private Context mContext;
private IBinderPool mBinderPool;
private static volatile BinderPool sInstance;
private CountDownLatch mConnectBinderPoolCountDownLatch;
private BinderPool(Context context) {
mContext = context.getApplicationContext();
connectBinderPoolService();
}
/*
* 返回BinderPool的实例,如果没有的话就创建,有的话就直接返回。
* */
public static BinderPool getInsance(Context context) {
if (sInstance == null) {
synchronized (BinderPool.class) {
if (sInstance == null) {
sInstance = new BinderPool(context);
}
}
}
return sInstance;
}
/*
* 连接BinderPoolService服务器。
* */
private synchronized void connectBinderPoolService() {
/*
* mConnectBinderPoolCountDownLatch这个东西是干嘛的?
* */
mConnectBinderPoolCountDownLatch = new CountDownLatch(1);
Intent service = new Intent(mContext, BinderPoolService.class);
mContext.bindService(service, mBinderPoolConnection,
Context.BIND_AUTO_CREATE);
try {
mConnectBinderPoolCountDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* query binder by binderCode from binder pool
*
* @param binderCode
* the unique token of binder
* @return binder who's token is binderCode<br>
* return null when not found or BinderPoolService died.
*/
/*
* queryBinder,
* */
public IBinder queryBinder(int binderCode) {
IBinder binder = null;
try {
/*
* 这个mBinderPool是一个BinderPool.BinderPoolImpl对象。
* 对于客户端来说调用的是BinderPool的queryBinder方法,
* 而BinderPool的queryBinder方法又调用了BinderPool.BinderPoolImpl对象的queryBinder方法。
* mBinderPool这个对象是服务端返回给BinderPool的,对客户端是隐藏的,
* 客户端只知道BinderPool,
* mBinderPool是服务端和连接池的桥梁,
* BinderPool是客户端和连接池的桥梁
* */
if (mBinderPool != null) {
binder = mBinderPool.queryBinder(binderCode);
}
} catch (RemoteException e) {
e.printStackTrace();
}
return binder;
}
/*
* 连接服务器的时候用的,里面有连接成功和连接断开后的操作。
* */
private ServiceConnection mBinderPoolConnection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
// ignored.
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
/*
* 将服务器端的Binder转换成客户端所需的AIDL接口对象:
* 服务端返回的是BinderPool连接池,而不是单纯的一个Binder对象。
* */
mBinderPool = IBinderPool.Stub.asInterface(service);
try {
/*
* 设置死亡代理:
* */
mBinderPool.asBinder().linkToDeath(mBinderPoolDeathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
mConnectBinderPoolCountDownLatch.countDown();
}
};
/*
* 设置死亡代理:
* */
private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
Log.w(TAG, "binder died.");
mBinderPool.asBinder().unlinkToDeath(mBinderPoolDeathRecipient, 0);
mBinderPool = null;
connectBinderPoolService();
}
};
/*
* (1)这个是我们的Binder连接池,它源于IBinderPool.aidl这个AIDL,它里面包含一个queryBinder方法,
* 我们的Binder连接池是放在服务端用,
* 所以在服务端需要有这样一个BinderPoolImpl的实例,并且它是一个Binder:
* private Binder mBinderPool = new BinderPool.BinderPoolImpl();
* (2)那怎么用呢?
* 我们当前所在的类BinderPool.java就是用来绑定服务端的客户端,
* 在BinderPool绑定服务端的时候,服务端会将mBinderPool返回给客户端也就是我们这个类,
* 然后我们可以根据服务端返回的这个Binder来转换成客户端所需的AIDL接口对象,还是叫mBinderPool,
* 然后我们这个类中就可以调用mBinderPool中的方法:
* binder = mBinderPool.queryBinder(binderCode);
* (3)那另外的两个AIDL呢?ICompute.aidl和ISecurityCenter.aidl呢?
* 由于另外的两个AIDL的使用都是和服务端相关联的,是服务端的queryBinder方法将它们的Binder返回给客户端的,
* 客户端接到这两个AIDL的Binder以后,依旧是通过转换成AIDL接口对象来使用这两个AIDL中的方法的。
* */
public static class BinderPoolImpl extends IBinderPool.Stub {
public BinderPoolImpl() {
super();
}
@Override
public IBinder queryBinder(int binderCode) throws RemoteException {
IBinder binder = null;
switch (binderCode) {
case BINDER_SECURITY_CENTER: {
binder = new SecurityCenterImpl();
break;
}
case BINDER_COMPUTE: {
binder = new ComputeImpl();
break;
}
default:
break;
}
return binder;
}
}
}
客户端
package com.ryg.chapter_2.binderpool;
import com.ryg.chapter_2.R;
import android.app.Activity;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
/*
* 这里是客户端
* */
public class BinderPoolActivity extends Activity {
private static final String TAG = "BinderPoolActivity";
private ISecurityCenter mSecurityCenter;
private ICompute mCompute;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_binder_pool);
/*
* 在线程中去执行:
* */
new Thread(new Runnable() {
@Override
public void run() {
doWork();
}
}).start();
}
private void doWork() {
// 首先获取一个BinderPool的实例:这里是带了上下文的,避免创建多个。
BinderPool binderPool = BinderPool.getInsance(BinderPoolActivity.this);
/*
* 然后根据客户端编号bindercode查询Binder,返回的是对应的客户端的Binder。
* 在binderPool.queryBinder中,是根据在绑定服务端过程中返回的BinderPoolImpl的Binder,
* 这个BinderPoolImpl就是继承了IBinderPool的,所以也实现了其中的queryBinder的。
* 这样返回的才是真正对应的securityBinder。
* */
IBinder securityBinder = binderPool
.queryBinder(BinderPool.BINDER_SECURITY_CENTER);
;
/*
* 查到对应的Binder以后,就可以根据这个Binder来转换成客户端所需的AIDL接口对象:
* */
mSecurityCenter = (ISecurityCenter) SecurityCenterImpl
.asInterface(securityBinder);
Log.d(TAG, "visit ISecurityCenter");
String msg = "helloworld-安卓";
System.out.println("content:" + msg);
try {
/*
* 有了接口对象,自然就可以调用对象中的方法了:
* */
String password = mSecurityCenter.encrypt(msg);
System.out.println("encrypt:" + password);
System.out.println("decrypt:" + mSecurityCenter.decrypt(password));
} catch (RemoteException e) {
e.printStackTrace();
}
/*
* 下面这是另一个AIDL模块,使用方法和上面是一样的。
* */
Log.d(TAG, "visit ICompute");
IBinder computeBinder = binderPool
.queryBinder(BinderPool.BINDER_COMPUTE);
;
mCompute = ComputeImpl.asInterface(computeBinder);
try {
System.out.println("3+5=" + mCompute.add(3, 5));
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
故障问题
ANR
ANR是什么?
ANR(Application Not Responding)即应用程序无响应
ANR产生的原因?
应用进程自身引起的,比如:主线程阻塞、挂起、死循环,执行耗时操作等;
其他线程引起的,比如:其他线程CPU占用率过高,导致当前应用进程无法抢占到CPU时间片。常见问题如文件读写频繁,io处理CPU占用率过高,导致当前应用出现ANR;
ANR解决方案
尽量避免在主线程(UI线程)中作耗时操作。
各大组件生命周期中也应避免耗时操作,注意BroadcastReciever的onRecieve()、后台Service和ContentProvider也不要执行太长时间的任务。
OOM
OOM就是内存溢出,即Out Of Memory。也就是说内存占有量超过了VM所分配的最大。
OOM产生原因
1、加载大图片导致内存溢出
2、大量内存泄露
解决大图片导致内存溢出
使用软引用、弱引用,当堆内存不足的时候,就可以自动的释放这些缓存的Bitmap对象。
使用过的图并且不再使用,可以调用Bitmap.recycle()加速回收。
使用LRU算法设计cache对象缓存池,缓存生命周期长、复用率高的Bitmap对象。
利用BitmapFactory.Options的inSampleSize设置图片的压缩率
// 第一次解析将inJustDecodeBounds设置为true,来获取图片大小
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// 调用上面定义的方法计算inSampleSize值, calculateInSampleSize方法自己写,这里不再赘述
options.inSampleSize = calculateInSampleSize(options.outWidth, options.outHeight, reqWidth, reqHeight);
options.inJustDecodeBounds = false;
Bitmap bmp = BitmapFactory.decodeResource(res, resId, options);
解决内存泄露问题
receiver, cursor, inputStream, outputStream在使用完之后及时释放关闭资源。
static的泄露context
原因:
用static这个关键字修饰变量,因为他使得变量的生命周期大大延长,如果将Activity赋值到static mContext,那么即使该Activity已经onDestroy,但是由于仍有对象保存它的引用,因此该Activity依然不会被释放。
解决方法:
应该尽量避免static成员变量引用资源耗费过多的实例,比如Context。
Context尽量使用Application Context,因为Application的Context的生命周期比较长,引用它不会出现内存泄露的问题。
使用WeakReference代替强引用。比如可以使用WeakReference mContextRef;
内部类持有外部对象造成的内存泄露
原因:
线程是Activity的内部类,所以OneThread中保存了Activity的一个引用,当OneThread的run函数没有结束时,OneThread是不会被销毁的,因此它所引用的Activity也不会被销毁,因此就出现了内存泄露的问题。
解决方法:
将线程的内部类,改为静态内部类;
在线程内部采用弱引用保存Context引用。