Android学习笔记十二:Android基础知识

2018-04-25 22:17 评论 0 条

摘要:

这篇介绍Android基础知识的文章,重点介绍Activity、Service、BroadcastReceiver、ContentProvider四大组件在各种情况下的生命周期,ApplicationContext和ActivityContext的区别,接口和回调的理解,自定义估值器和自定义插值器等内容,进一步加深了对基础知识的理解,距离上篇文章距今将近一个月哈

一、四大组件是什么

Activity、Service、ContentProvider和BroadcastReceiver

四大组件的生命周期和简单用法

Activity

生命周期 简单用法
onCreate(Bundle) activity启动的时候回调的方法,进行大部分初始化的工作,比如:setContentView
onStart() 执行onCreate之后回调的方法或暂停的activity执行onRestart之后回调的方法
onRestart() 执行onStop之后的activity重新显示回调的方法
onResume() 执行onRestoreInstanceStateonRestartonPause之后,与用户交互回调的方法
onPause() activity切换到了后台(但还没有kill掉)回调的方法

可以这么理解:Activity B位于Activity A前面,Activity A会回调onPause方法,Activity B直到Activity A回调了onPause方法后才回调自身的onCreate方法

onStop() activity不再与用户交互时,回调的方法,紧接着可能会执行onRestartonDestroy方法,也可能什么也不做
onDestroy() 当activity结束或系统回收activity实例后回调的方法,执行最后的清理工作

Service

Context.startService()

生命周期 简单用法
onCreate() service创建的时候回调该方法
onStartCommand() 执行onCreate之后回调的方法,并获取Client传递过来的参数
onDestroy() service被有效地终止后回调的方法,执行最后的清理工作,比如停止线程、解除注册

Note that:除非调用stopService()stopSelf()停止service,否则service一种运行

Note that: 反复调用Context.startService()方法不会多次创建Service实例,即不会多次执行onCreate方法,但会多次执行onStartCommand方法

Context.bindService()

生命周期 简单用法
onCreate() 创建Service实例的时候,回调该方法
onBind() Client重写ServiceConnection.onServiceConnnected方法时,获取IBinder回调的方法
onUnbind() 当所有的客户端断开连接的时候回调该方法
onDestroy() service被有效地终止后回调的方法,执行最后的清理工作,比如停止线程、解除注册

ContentProvider

生命周期 简单用法
onCreate() 初始化provider回调的方法
query() 将数据返回给调用者
insert() 把新的数据插入到content provider
update() 更新content provider存在的数据
delete() 从content provider中删除数据
getType() 返回content provider数据的MIME类型

Note that: 数据访问的方法(如insert或update)可以同时被多个线程调用,它是线程安全的;其他的方法(如onCreate)只可以在应用程序的主线程中调用,同时避免耗时操作

BroadcastReceiver

BroadcastReceiver对象生命周期

生命周期 简单用法
onReceive() BroadcastReceiver对象的生命在onReceive方法没有回调的这段时间内,被认为是有效的;一旦执行了onReceive方法内的代码,系统认为该实例已经结束并不再存活

BroadcastReceiver进程生命周期

生命周期 简单用法
onReceive() 正在执行的BroadcastReceiver是一个运行在后台的进程,除非系统内存不足外,否则该进程一直存活着onReceive方法执行后,BroadcastReceiver的对象的生命周期结束

因此,要求在该方法执行的操作必须是同步的,否则在该方法执行完成后,系统先于非同步操作把对象所在的进程kill掉,将会存在很大的问题

二、Activity之间的通信方式

通信的方式 特点
I/O数据共享 将数据写入数据库或文件,在另一个activity中读取,数据的读写速度比较慢,简单的数据类型可以考虑SharedPreferences,在多线程环境下需要考虑线程安全
公共静态全局变量 对变量的修改,直接影响依赖该变量的所有activity,在多线程环境下需要考虑线程安全
Intent数据传递 Intent支持保存:基本数据类型、实现Parcelable接口类型、Bundle类型、基本数据类型数组
广播数据传递 在另一个activity中注册广播接收器(BroadcastReceiver)接收数据,在当前activity将数据保存Intent并发送,实质是利用Intent传递数据方式
注册监听 在另一个activity中监听接口,在当前activity中注册接口(难点在于怎么获取另一个activity的引用)
公共的成员变量 和注册监听一样,反射获取另一个activity的实例并调用其成员方法

利用java反射机制,使用android系统的内部类成员函数

三、Activity各种情况下的生命周期

正常情况
参考上文Activity生命周期介绍,绘制一个完成的生命周期图谱

Activity生命周期图谱

title description
情况1 一个标准的生命周期:onCreate——>onStart——>onResume——>onPause——>onStop——>onDestroy
情况2 onCreate——>onStart——>onResume——>onPause——>onResume..
情况3 onCreate——>onStart——>onResume——>onPause——>onStop——>onRestart——>onStart——>onReusme...

Note that:这种情况先执行onRestart,再重新执行onStart、onResume

情况3 onCreate——>onDestroy

这种情况直接从Activity A跳转到Activity B,之间不经过其他生命周期

情况4 比较熟悉的是上面的几种情况,因为Activity的生命周期是一个回调的方法,也就是说上面的方法可能会回调也可能不会回调,还可能只是回调其中的几种(按照组合的概念,还存在其他的情况)

异常情况

title description
情况1 onCreate——>onStart——>onReusme——>onPause——>onSaveInstanceState——>onStop...
情况2 onCreate——>onStart——>onResume——>onSaveInstanceState——>onPause——>onStop...结合情况1和情况2说明:

1、onSaveInstanceState可能出现在onPause之前,也可能出现在onPause之后

2、但onSaveInstanceState肯定出现在onStop之前

情况3 onCreate——>onStart——>onResume——>onSaveInstanceState——>onDestroy(还存在其他组合的情况)

切换横竖

title description
切换横屏或切换竖屏 onPause——>onSaveInstanceState——>onStop——>onDestroy(当前实例销毁)——>onCreate(重新初始化)——>onStart——>onRestoreInstanceState——>onResume

显示或隐藏键盘

title description
键盘显示或隐藏 onCreate——>onStart——>onResume

Note that:

onSaveInstanceState方法将数据写入Bundle对象中,可以在onCreate或onRestoreInstanceState方法中读取保存的数据

onRestoreInstanceStateonStart方法之后回调,即onStart——>onRestoreInstanceState

onSaveInstanceState方法和onRestoreInstanceState方法仅在Activity异常情况切换横竖屏情况下,才会被执行

onSaveInstanceState可能出现在onPause之前,也可能出现在onPause之后,但onSaveInstanceState肯定出现在onStop之前,即onSaveInstanceState——>onStop (情况2)

Activity正常和异常情况下的生命周期分析
Android:Activity——生命周期深入详解

Activity上有Dialog的时候按Home键时的生命周期

Activity启动到可见,经历生命周期:

onCreate——>onStart——>onResume

从Dialog弹出到按下Home键,经历生命周期:

onPause——>onSaveInstanceState——>onStop

重新打开上面的Activity,经历生命周期:

onRestart——>onStart——>onResume

测试Demo

横竖屏切换的时候,Activity 各种情况下的生命周期

先理解android:configChanges标签的作用,该可以添加的属性值包括:

orientation,指定Activity切换横竖屏影响其生命周期,参考上文生命周期介绍

keyboardHidden,指定Activity显示隐藏键盘影响其生命周期(演示生命周期没变化)

screenSize,指定Activity屏幕大小改变后影响其生命周期

title description
切换横屏或竖屏 1. onPause——>onSaveInstanceState——>onStop——>onDestroy(当前Activity销毁)——>onCreate(重新初始化)——>onStart——>onRestoreInstanceState——>onReusme...

包括下面情况:
android:configChanges="orientation"
android:configChanges="keyboardHidden"
android:configChanges="screenSize"
或不指定configChanges属性值

2. onCreate——>onStart——>onResume——>onConfigurationChanged

包括下面情况:
android:configChanges="orientation_screenSize"
android:configChanges="orientation_keyboardHidden"

弹出或隐藏键盘 除了onCreate——>onStart——>onResume生命周期外,不添加额外的(测试结果)

包括下面情况:
android:configChanges="keyboardHidden"
android:configChanges="keyboard"
android:configChanges="orientation_keyboardHidden"
android:configChanges="orientation_keyboard"
或不指定configChanges属性值

android-----横竖屏切换对Activity生命周期的影响
Android横竖屏切换及其相应布局载入问题

测试Demo

Activity与Fragment之间生命周期比较

四、Fragment生命周期

title description
onAttach(Context) onCreate方法前调用,将fragment挂载到其上下文环境中,随后调用onCreate方法
onCreate() 调用该方法初始化创建fragment,在onAttach之后onCreateView之前调用

注意,正当activity被创建的过程中调用该方法,因此不要在该方法中做一些与activity交互的操作,最好在activity创建好后,即在onActivityCreated处理与activity的交互

onCreateView() 回调该方法获取fragment实例与用户交互的视图,在onCreate方法和onActivityCreated方法之间被调用

Note that:重写该方法并返回View后,当View被释放的时候,需要回调其onDestroyView方法

onViewCreated() onCreateView返回后立即回调的方法,但是是在恢复保存的View状态之前
onActivityCreated() 当activity被创建且fragment的view视图实例化后回调该方法,通常用来做一些最后的初始化工作

onCreateView之后onViewStateRestored(Bundle)之前回调

onViewStateRestored() 当所有保存的状态被恢复到fragment的视图层次结构时回调该方法,可以用来做一些基于保存状态的初始化工作

onActivityCreated之后onStart之前调用

onStart() 当fragment对用户可见时回调该方法,同时该方法绑定于activity生命周期的onStart方法
onResume() 当fragment对用户可见时且正在运行时回调该方法,同时该方法绑定于activity生命周期的onResume方法
onPause() 当fragment不再可见的时候回调该方法,同时该方法绑定于activity生命周期的onPause方法
onStop() 当fragment不再启动的时候回调该方法,同时该方法绑定于activity生命周期的onStop方法
onDestroyView() 当由onCreateView方法创建的View从fragment卸载的时候回调该方法,该方法在onStop之后onDestroy之前回调
onDestroy() 当fragment不再被使用的时候回调该方法,同时该方法绑定于activity生命周期的onDestroy方法
onDetach() 当fragment从activity上卸载时回调该方法,该方法是在onDestroy之后调用

对比Activity生命周期图谱(参考上文)

发现,Fragment不仅具备Activity的全部生命周期方法,而且还添加了Activity生命周期没有的多个回调方法,这些回调方法包括:

onAttach(),当fragment挂载于activity时回调该方法

onCreateView(),当实例化fragment与用户交互的视图时回调该方法

onActivityCreated,当ativity被创建且fragment视图被实例化后回调该方法

onDestroyView,当fragment视图被卸载时回调该方法

onDetach,当fragment从activity卸载时回调该方法

各种情况下的生命周期

title description
Fragment在Activity中replace 新替换的Fragment:onAttach > onCreate > onCreateView > onViewCreated > onActivityCreated > onStart > onResume

被替换的Fragment:onPause > onStop > onDestroyView > onDestroy > onDetach

Fragment在Activity中replace,并addToBackStack 新替换的Fragment(没有在BackStack中):onAttach > onCreate > onCreateView > onViewCreated > onActivityCreated > onStart > onResume

新替换的Fragment(已经在BackStack中):onCreateView > onViewCreated > onActivityCreated > onStart > onResume

被替换的Fragment:onPause > onStop > onDestroyView

Fragment在ViewPager中切换 有多种情况,先将切换前的简称PF;切换后的简称NF;其他简称OF(在ViewPager中setUserVisibleHint能反映出Fragment是否被切换到后台或前台,所以在这里也当作生命周期)

情况1: Fragment未加载
NF:
setUserVisibleHint(false) > onAttach > onCreate > setUserVisibleHint(true) > onCreateView > onViewCreated > onActivityCreated > onStart > onResume
OF跟NF相邻:
setUserVisibleHint(false) > onAttach > onCreate > onCreateView > onViewCreated > onActivityCreated > onStart > onResume

情况2: Fragment已加载
NF跟PF相邻 :
setUserVisibleHint(true)<br>NF跟PF不相邻:<br>setUserVisibleHint(true) > onCreateView > onViewCreated > onActivityCreated > onStart > onResume
PF跟NF相邻:
setUserVisibleHint(false)
PF跟NF不相邻:
setUserVisibleHint(false) > onPause > onStop >onDestroyView
OF跟PF相邻:
onPause > onStop > onDestroyView
OF跟NF相邻:
onCreateView > onViewCreated > onActivityCreated > onStart > onResume
OF夹在PF和NF中间:不调用任何生命周期方法

情况3,重写FragmentPagerAdapter的destroyItem方法,且相关Fragment已加载
则相互切换时只会调用setUserVisibleHint

Fragment进入了运行状态 Fragment在上述的各种情况下进入了onResume后,则进入了运行状态,以下4个生命周期方法将跟随所属的Activity一起被调用:
onPause > onStop > onStart > onResume
关于Fragment的onActivityResult方法 使用Fragment的startActivity方法时,FragmentActivity的onActivityResult方法会回调相应的Fragment的onActivityResult方法,所以在重写FragmentActivity的onActivityResult方法时,注意调super.onActivityResult

Fragment在不同情况下的生命周期

五、两个Activity 之间跳转时必然会执行的是哪几个方法?

Activity A跳转到Activity B

首先,执行了Activity A的onCreate()——>onStart——>onResume——>onPause()方法

其次,执行Activity B的onCreate——>onStart——>onResume方法

最后,执行Activity A的onSaveInstanceState——>onStop

        //演示两个Activity 之间跳转时必然会执行的是哪几个方法?
        final Button button=new Button(this);
        button.setText("点我跳转喔");
        button.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                //第一种情况
                startActivity(new Intent(LifecycleHomePressedActivity.this,ChangeScreenStateActivity.class));
                //执行Activity A  生命周期:onCreate—>onStart—>onResume—>onPause,
                // 其次Activity B 生命周期:onCreate—>onStart—>onResume,
                // 再执行Activity A 生命周期:onSaveInstantState—>onStop
            }
        });
        ll.addView(button);
        //第二种情况
        startActivity(new Intent(LifecycleHomePressedActivity.this,ChangeScreenStateActivity.class));
        //执行Activity A  生命周期:onCreate—>onStart—>onResume—>onPause,
        // 其次Activity B 生命周期:onCreate—>onStart—>onResume,
        // 再执行Activity A 生命周期:onSaveInstantState—>onStop

测试Demo

前台切换到后台,然后再回到前台,Activity生命周期回调方法

title description
前台切换后台 onCreate——>onStart——>onResume——>onPause——>onStop
后台切换前台 onRestart——>onStart——>onResume

弹出Dialog,生命值周期回调方法

弹窗Dialog,Activity的生命周期不变,参考上文:Activity上有Dialog的时候按Home键时的生命周期

六、Activity的四种启动模式对比

title description
standard 标准启动模式,每次创建一个Activity实例并加入栈内
singleTop 启动的Activity位于栈顶,则重用该实例;否则创建新的实例并加入栈内
singleTask 启动的Activity位于栈中,则重用该实例并清除位于其栈顶上的所有实例

否则判断实例需要的任务栈是否存在,存在则创建Activity的实例并加入栈内

否则,先创建一个栈,再创建Activity的实例并加入栈内

singleInstance 加强版的singleTask,创建的Activity实例位于独立的任务栈

Activity状态保存与恢复

Activity在异常情况、横竖屏切换、按下Home键时回调其onSaveInstanceState方法,该方法用于保存需要恢复的状态信息

恢复状态信息可以在onCreate(Bundle)方法中执行,也可以在onRestoreInstanceState()方法中执行

Fragment状态保存startActivityForResult是哪个类的方法,在什么情况下使用?

startActivityForResult是FragmentActivity类中的方法

在当前Fragment需要跳转到某个Activity获取返回数据的时候使用,需要注意的是:

  1. 必须使用Fragment的startActivityForResult(Intent,int)方法(参考第一篇引用)
  2. Fragment嵌套的子fragment将无法接收到返回的数据()参考第三篇引用)
  3. activity重写了onActivityResult,一定要加上super.onActivityResult()

Android-Fragment 的 onActivityResult 收不到结果
Android踩坑随笔Fragment中onActivityResult方法不被调用
Android的Fragment中onActivityResult不被调用的解决方案(绝对管用)
谈谈Fragment中的onActivityResult

如何实现Fragment的滑动?

第一种方式:PagerSlidingTabStrip+ViewPager+Fragment(新闻栏目切换效果) demo源码

第二种方式:TabLayout+ViewPager+Fragment(类似微信底部导航切换效果) demo源码

第三种方式:ActionBar+ViewPager+Fragment demo源码

fragment之间传递数据的方式?

在当前的Fragment注册接口,在另一个Fragment中监听接口,实现数据的传递

通过Fragment通过的setArguments(Bundle)方法和getArguments(Bundle)实现数据的传递

可以多个Fragment共享挂载的Activity成员变量的方式实现数据的传递

也可以通过文件的方式或数据库的方式实现数据的传递

通过EventBus第三方框架实现数据的传递

Android中Fragment之间的跳转和数据的传递
Fragment之间数据传递的三种方式

七、Activity 怎么和Service 绑定?

通过startService(Intent),将一个Service绑定到当前Activity所在的上下文环境中,调用stopService(Intent)stopSelf()停止服务

通过bingService(Intent,ServiceConnection,int),将一个Service绑定到当前Activity的上下文环境中,调用unbindService(ServiceConnection)解除绑定

怎么在Activity 中启动自己对应的Service?

使用bindService(Intent,ServiceConnection,int),在Activity中启动自己对应的Service

Activity回调了onStop(),Service同时也停止运行,直到Activity回调onResume()方法才恢复运行

service和activity怎么进行数据交互?

通过广播的方式,在当前Activity中发送广播,在Service中注册广播并接收传递过来的数据

通过Handler+Message的方式,当前Activity持有Service的对象,可以使用Service声明的Handler实例发送消息,并在Service中处理

不同进程的Service和Activity,通过Messenger的方式实现跨进程通信

通过Intent的方式,将需要交互的数据存储到Intent中,并在Service的onStartCommand()方法中处理

Service的开启方式

一种是startService(Intent),使用该种方式开启的Service,经历onCreate()——>onStartCommand(),除非调用stopService()stopSelf()方法停止服务,否则服务在资源充足且无异常情况下将一种运行

另一种是bindService(Intent,ServiceConnection,int),使用该种方式开启的Service,经历onCreate()——>onBind(),服务的生命周期伴随着Activity的生命周期,当前Activity回调了onStop方法,服务停止;当Activity回调了onResume方法,服务继续运行,调用unbind(ServiceConnection)解除绑定

八、请描述一下Service 的生命周

使用startService(Intent)方法启动的服务,生命周期是onCreate()——>onStartCommand()——>onDestroy()。多次调用startService(Intent),执行一次onCreate()方法,执行多次onStartCommand()方法,除非调用stopService()stopSelf()方法,否则将在资源充足且无异常的情况下一种运行

使用bindService(Intent,ServiceConnection,int)方式启动的服务,生命周期是onCreate()——>onBind()——>onUnbind()——>onDestroy,服务的生命周期伴随着Activity的生命周期

Service生命周期

Android中startService的使用及Service生命周期
Android中bindService的使用及Service生命周期

九、谈谈你对ContentProvider的理解

ContentProvider提供在不同应用程序之间数据的共享,实现跨进程之间的通信,同时其insert()update()方法允许同步操作,它是线程安全的

在Context的上下文环境中,通过getContentResolver()返回一个ContentResolver对象,用它来操作ContentProvider提供的方法

说说ContentProvider、ContentResolver、ContentObserver 之间的关系

ContentProvider是当前应用程序提供数据共享的对象,ContentResolver可以看作是ContentProvider的代理,其他想要访问数据的应用程序只需要于ContentResolver交互,保证ContentProvider内部数据的安全

ContentObserver是观察Uri是否发生变化的观察者,通过ContentResolver.registerContentObserver()注册观察者

Android ContentProvider、ContentResolver和ContentObserver的使用

请描述一下广播BroadcastReceiver的理解

BroadcastReceiver是一个广播接收器,它是一个抽象类,子类通过重写onReceive(Context,Intent)处理接收到的信息

可以用于不同组件之间数据的传递

动态注册的广播仅当组件运行时,广播接收器才接收到广播;静态注册的广播随着程序一起启动,接收符合条件的广播

广播又区分为有序广播和无序广播:

有序广播的特点是高优先级的BroadcastReceiver先接收,相同优先级的先注册的接收,没有丢弃的广播继续传递给下一个接收器,支持使用getResultData()setResultData()abortBroadcast()常见的应用场景:广播拦截

android有序广播和无序广播的区别

广播的分类

title description
粘性广播 在Android 5.0被弃用
普通广播 注册多个接收器,不确定哪个先执行,常用的一种广播
有序广播 高优先级的接收器先接收,同时拥有终止广播的权利,相同优先级的广播,先注册的先接收,优先级低的最后接收
系统广播 比如:网络变化、电池电量、屏幕状态等
应用内广播 使用应用内广播实现跨进程通信

Android:BroadcastRecevicer广播类型汇总

广播使用的方式和场景

方式 场景
普通广播 实现不同组件之间的通信
有序广播 实现广播的拦截
系统广播 监听系统状态并提示
应用内广播 实现不同进程之间的通信

十、在manifest 和代码中如何注册和使用BroadcastReceiver?

在manifest注册

<receiver android:name=".CustomBroadcast">
    <intent-filter>
        <action android:name="cn.teachcourse.action"/>
        <category android:category="cn.teachcourse.category"/>
    </intent-filter>

</receiver>

在代码中注册

   IntentFilter filter=new IntentFilter("cn.teachcourse.action");

   registerBroadcast(mReceiver,filter);
   registerOrderedBroadcast();

本地广播和全局广播有什么差别?

本地广播仅限于当前应用程序内使用,更加高效和安全,不支持静态注册


   LocalBroadcastManager lbm=LocalBroadcastManager.getInstance(this);

   lbm.registerBroadcast(mReceiver,mIntentFilter);//注册本地广播

   sendBroadcast(new Intent(LOCAL_ACTION));//发送本地广播
   unregisterBroadcast(mReceiver);//解除本地广播

全局广播既可以在当前应用程序内使用,也可以在不同应用程序间使用

本地广播LocalBroadcastManager,和全局广播有什么区别
本地广播和全局广播的差别

BroadcastReceiver,LocalBroadcastReceiver 区别

参考上文:本地广播和全局广播有什么差别

AlertDialog,popupWindow,Activity区别

AlertDialog PopupWindow Activity
AlertDialog的所有构造方法都是protect,只可以通过内部类AlertDialog.Builder返回一个实例

AlertDialog封装了一系列的方法快速设置弹窗的样式,比如:单选样式、复选样式和对话框样式

AlertDialog默认居中显示,除显示内容外, 添加了一层遮罩,默认点击显示内容外区域弹窗消失

PopupWindow使用new关键字创建一个实例

PopupWindow需要指定显示的相对位置,提供showAtLocation()showAsDropDown()两组方法,方法传入相对位置的View实例,设置偏移量可以更精确调整弹窗位置

PopupWindow默认不支持点击空白区域弹窗消失

Activity设置Dialog主题样式,实现弹窗效果,效果和AlertDialog差不多

Activity实质是一个Activity组件,可以在生命周期内处理复杂的逻辑,如果你的弹窗需要显示较多的数据且需要组件之间的通信,可以考虑Activity弹窗

Activity继承了Dialog弹窗,考虑继承的单一性,制约了其他主题样式的使用

对话框:阻塞式PopupWindow 和非阻塞AlertDialog
Android AlertDialog和PopupWindow使用和区别
安卓开发中Dialog和PopupWindow和新建Activity的使用场景有什么区别?

Application 和 Activity 的 Context 对象的区别

Application的Context Activity的Context
Application的Context是整个应用程序的生命周期,应用程序退出,Context生命周期结束

在大部分情况下可以使用Application的Context代替Activity的Context,但与UI相关的,只能使用Activity的Context,比如:show a Dialog,start a Activity或Layout inflation

Application.this——>ContextWrapper——>Context

Activity的Context是Activity的生命周期,Activity销毁Context可能还被其他对象持有,造成内存泄露,除了在特定情况下使用Activity的Context,其他情况下建议使用Application的Context

Activity.this——>ContextThemeWrapper——>ContextWrapper——>Context

在Activity的Context中添加了ContextThemeWrapper封装类,允许修改和替换ContextWrapper

Android Application中的Context和Activity中的Context的异同
两种Context的区别,Activity 和Application

十一、Android属性动画特性

在一定时间内,通过不断对值执行改变并应用到属性上

Android 属性动画:这是一篇很详细的 属性动画 总结&攻略
Android图文详解属性动画

插值器

TimeInterpolator插值器实现类及其它们之间的区别:

常用插值器 区别
AccelerateDecelerateInterpolator 先加速再减速
AccelerateInterpolator 仅加速
AnticipateInterpolator 先向后再向前最后碰撞
AnticipateOvershootInterpolator 先向后再向前最后晃动
BounceInterpolator 小球弹跳效果
CycleInterpolator 循环播放效果
DecelerateInterpolator 减速效果
LinearInterpolator 线性效果
OvershootInterpolator 晃动效果

重写TimeInterpolator接口的getInterpolation()方法,实现各色各样的动画效果

例子源码
Android开发 - 图形化生成的贝塞尔插值器
Android动画TimeInterpolator(插值器)和TypeEvaluator(估值器)分析
渗透理解Animation时间插值Interpolator类
差值器动画演示(在线网站)

估值器

TypeEvaluator估值器传入一个具体的类型,实现evaluate(float fraction,T start,T end)抽象方法,估值的思想:start+(fraction*(end-start)),最后返回得到的值,这可以从源码看出来

ArgbEvaluator

    public Object evaluate(float fraction, Object startValue, Object endValue) {
        int startInt = (Integer) startValue;
        int startA = (startInt >> 24) & 0xff;
        int startR = (startInt >> 16) & 0xff;
        int startG = (startInt >> 8) & 0xff;
        int startB = startInt & 0xff;

        int endInt = (Integer) endValue;
        int endA = (endInt >> 24) & 0xff;
        int endR = (endInt >> 16) & 0xff;
        int endG = (endInt >> 8) & 0xff;
        int endB = endInt & 0xff;
        //估值的思想:`start+(fraction*(end-start))`
        return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
                (int)((startR + (int)(fraction * (endR - startR))) << 16) |
                (int)((startG + (int)(fraction * (endG - startG))) << 8) |
                (int)((startB + (int)(fraction * (endB - startB))));
    }

FloatArrayEvaluator

    public float[] evaluate(float fraction, float[] startValue, float[] endValue) {
        float[] array = mArray;
        if (array == null) {
            array = new float[startValue.length];
        }

        for (int i = 0; i < array.length; i++) {
            float start = startValue[i];
            float end = endValue[i];
            //估值的思想:`start+(fraction*(end-start))`
            array[i] = start + (fraction * (end - start));
        }
        return array;
    }

FloatEvaluator

    public Float evaluate(float fraction, Number startValue, Number endValue) {
        float startFloat = startValue.floatValue();
        //估值的思想:`start+(fraction*(end-start))`
        return startFloat + fraction * (endValue.floatValue() - startFloat);
    }

IntArrayEvaluator

    public int[] evaluate(float fraction, int[] startValue, int[] endValue) {
        int[] array = mArray;
        if (array == null) {
            array = new int[startValue.length];
        }
        for (int i = 0; i < array.length; i++) {
            int start = startValue[i];
            int end = endValue[i];
            //估值的思想:`start+(fraction*(end-start))`
            array[i] = (int) (start + (fraction * (end - start)));
        }
        return array;
    }

IntEvaluator

    public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
        int startInt = startValue;
        //估值的思想:`start+(fraction*(end-start))`
        return (int)(startInt + fraction * (endValue - startInt));
    }

PointFEvaluator

    public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
        //估值的思想:`start+(fraction*(end-start))`
        float x = startValue.x + (fraction * (endValue.x - startValue.x));
        float y = startValue.y + (fraction * (endValue.y - startValue.y));

        if (mPoint != null) {
            mPoint.set(x, y);
            return mPoint;
        } else {
            return new PointF(x, y);
        }
    }

RectEvaluator

    public Rect evaluate(float fraction, Rect startValue, Rect endValue) {
        int left = startValue.left + (int) ((endValue.left - startValue.left) * fraction);
        int top = startValue.top + (int) ((endValue.top - startValue.top) * fraction);
        int right = startValue.right + (int) ((endValue.right - startValue.right) * fraction);
        int bottom = startValue.bottom + (int) ((endValue.bottom - startValue.bottom) * fraction);
        if (mRect == null) {
            return new Rect(left, top, right, bottom);
        } else {
            mRect.set(left, top, right, bottom);
            return mRect;
        }
    }

它们之间的区别是,一种类型对应一种估值器,ValueAnimatorObjectAnimator针对TypeEvaluator编程,使用策略设计模式动态注入估值器实现类,实现估算当前类型的目的。

定义一个估值器,确定需要估算值类型,按照估值器思想:start+(fraction*(end-start)),返回得到的值

Android 属性动画探究(二)——TypeEvaluator解析与自定义
浅析Android动画(三),自定义Interpolator与TypeEvaluator
常用数学函数表达式演示(在线网站)

十二、如何导入外部数据库?

将外部数据库文件放置资源文件夹目录:res/raw,然后通过读取文件流的方式将文件写入到Android数据库目录: /data/data/package_name/db_name,最后使用数据库相关api操作数据库内容

如果数据库的大小发生了变化或上述方式读取和写入出错,可以批量导出SQL语句并批量将SQL语句导入数据库

[Android]如何导入已有的外部数据库

LinearLayout、RelativeLayout、FrameLayout的特性及对比,并介绍使用场景。

LinearLayout RelativeLayout FrameLayout
线性布局,可在水平方向或垂直方向按照权重大小排列多个控件 相对布局,选取父控件或相邻控件为参照,设定当前控件的位置,控件可以重叠 帧布局,栈型结构,最后添加的控件覆盖最先添加的控件

十三、谈谈对接口与回调的理解

回调的定义:

类A调用类B的方法methodB(),方法执行后,类B调用类A的方法methodA(),通知类A执行的结果,那么最后调用的methodA(),是一个回调的方法

public class SimpleExampleTest {

    public static void main(String[] args) {
        A a = new A();
        B b=new B(a);//同理,类B持有类A的引用

        a.setB(b);//类A持有类B的引用
        a.b.methodB();//类A调用类B的方法methodB(),然后类B回调了类A的方法methodA()
    }

    static class A {
        B b;

        public void setB(B b) {
            this.b = b;
        }

        /**
         * 在该方法中接收执行结果
         */
        void methodA() {
            System.out.println("类B回调了类A的方法:methodA()");
        }

    }

    static class B {
        A a;

        public B(A a) {
            this.a = a;
        }



        void methodB() {
            System.out.println("类B执行的方法:methodB()");
            a.methodA();// 通知类A,已经执行了类B的方法
        }
    }
}

接口符合回调的定义,接口充当类B的角色,接口定义的抽象方法称为回调方法,在另一个类A中回调接口的setXXX()方法,并在重写接口的回调方法中接收通知,因此接口可以用于不同类之间的通信

public interface IEmployee {
    void result();
}
public class Boss {
    IEmployee employee;

    public void setEmployee(IEmployee employee) {
        this.employee = employee;
    }

    /**
     * 老板给员工分配了一个任务
     */
    void allocateTask() {
        System.out.println("Boss:下班前,手写一个冒泡排序算法。。。");
        employee.result();
    }

}
public class IEmployeeImpl implements IEmployee{
    Boss boss;

    public IEmployeeImpl(Boss boss) {
        this.boss = boss;
    }

    @Override
    public void result() {
        int a[]={1,3,4,5,8,3,1};

        sortingOfBubble(a);

        System.out.println("Employee:已经完成冒泡排序算法的封装,经过测试没有问题。。。");
    }

    private void sortingOfBubble(int[] a) {
        for(int i=0;i<a.length-1;i++){
            for(int j=0;j<a.length-1;j++){
                int temp;
                if(a[j]>a[j+1]){
                    temp=a[j];
                    a[j]=a[j+1];
                    a[j+1]=temp;
                }
            }
        }
    }

}
public class TestDriver {

    public static void main(String[] args) {
        Boss boss = new Boss();
        IEmployee employee = new IEmployeeImpl(boss);

        boss.setEmployee(employee);
        boss.allocateTask();//Boss类调用allocateTask()方法,并调用了Employee的result()方法
    }

}

java 接口回调实例详解
一个经典例子让你彻彻底底理解java回调机制
接口回调(重点是理解)

回调的原理

根据上面回调的例子,大概明白一个原理,实现类重写了接口的方法,声明的接口变量最终执行的是被重写的方法,回调的原理使用的是多态的特性。

多态的特点:

将实例化对象的引用赋值给超类变量,超类变量引用的是被子类重写的方法,或者

将实例化对象的引用赋值给接口变量,接口变量引用的是被实现类重写的方法

例子源码

写一个回调demo

回调的定义:

类A调用类B的方法methodB(),方法执行后,类B调用类A的方法methodA(),通知类A执行的结果,那么最后调用的methodA(),是一个回调的方法

例子源码

十四、介绍下SurfaceView

SurfaceView继承自View,对View进行优化,适合在子线程频繁地刷新界面,并实现了双缓存机制

SurfaceHolder.Callback提供管理SurfaceView生命周期的三个方法:surfaceCreated()surfaceChanged()surfaceDestroyed(),在相应的回调方法中处理操作

Note that: 不用画布,直接在窗口上进行绘图叫做无缓冲绘图。用了一个画布,将所有内容都先画到画布上,在整体绘制到窗口上,就该叫做单缓冲绘图,那个画布就是一个缓冲区。用了一个画布和一个Bitmap,画布保存临时的绘图,Bitmap保存绘图的历史记录,这样就叫做双缓冲绘图。

public class DrawingRectangleBoard extends SurfaceView implements SurfaceHolder.Callback, Runnable {
    private SurfaceHolder mSurfaceHolder;
    private Canvas mCanvas;
    private boolean isDrawing;
    Paint mPaint;
    Path mPath;
    float firstX;
    float firstY;

    public DrawingRectangleBoard(Context context) {
        this(context, null);
    }

    public DrawingRectangleBoard(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    /**
     * 初始化
     */
    private void init() {
        mSurfaceHolder = getHolder();
        mSurfaceHolder.addCallback(this);

        setFocusable(true);
        setFocusableInTouchMode(true);
        this.setKeepScreenOn(true);

        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
        mPaint.setStrokeWidth(6f);
        mPaint.setColor(Color.parseColor("#FF4081"));
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        mPaint.setStrokeCap(Paint.Cap.ROUND);

        mPath = new Path();
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        isDrawing = true;
        Log.e("surfaceCreated", "--" + isDrawing);

        new Thread(this).start();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        isDrawing = false;
        Log.e("surfaceDestroyed", "--" + isDrawing);
    }

    @Override
    public void run() {
        while (isDrawing) {
            try {
                mCanvas = mSurfaceHolder.lockCanvas();
                if (mCanvas != null) {
                    mCanvas.drawColor(Color.WHITE);
                    mCanvas.drawPath(mPath, mPaint);
                }
            } finally {
                if (mCanvas != null) {
                    mSurfaceHolder.unlockCanvasAndPost(mCanvas);
                }
            }
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                firstX = x;
                firstY = y;
                break;
            case MotionEvent.ACTION_MOVE:
                //绘制矩形时,要先清除前一次的结果
                mPath.reset();
                if (firstX < x && firstY < y) {
                    //↘方向
                    mPath.addRect(firstX, firstY, x, y, Path.Direction.CCW);
                } else if (firstX > x && firstY > y) {
                    //↖方向
                    mPath.addRect(x, y, firstX, firstY, Path.Direction.CCW);
                } else if (firstX > x && firstY < y) {
                    //↙方向
                    mPath.addRect(x, firstY, firstX, y, Path.Direction.CCW);
                } else if (firstX < x && firstY > y) {
                    //↗方向
                    mPath.addRect(firstX, y, x, firstY, Path.Direction.CCW);
                }
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                break;
            default:
                break;
        }
        return true;
    }
}

例子源码
Android SurfaceView入门学习
Android视图SurfaceView的实现原理分析
Android中SurfaceView的使用详解

十五、RecycleView的使用

关于RecyclerView的简单使用:列表样式、网格样式、点击item监听和拖拽效果,参考例子源码

例子源码

十六、 两种序列化的区别

实现Parcelable接口和实现Serializable接口的作用:持久化数据对象,方便数据的读写

Parcelable Serializable
Parcelable是Android SDK提供的更加高效的序列化方式:读写速度更快内存占用更小

需要重写writeToParcel()方法、describeContents()方法以及静态内部接口Parcelable.Creator,操作比Serializable麻烦

Serializable是Java提供的传统序列化方式,不需要重写任何的方法,注意添加一个SerializableID即可,使用方便

当前文章价值3.69元,扫一扫支付后添加微信提供帮助!(如不能解决您的问题,可以申请退款)

你可能感兴趣的文章

来源:每日教程每日一例,深入学习实用技术教程,关注公众号TeachCourse
转载请注明出处: https://www.teachcourse.cn/2634.html ,谢谢支持!

资源分享

分类:面试题汇 标签:,
Eclipse手动安装SVN插件操作 Eclipse手动安装SVN插件操作
Handler方法解析 Handler方法解析
你会错过的接口理解三部曲:定义,设置和实现 你会错过的接口理解三部曲:定义
harmony学习页面(@Entry)生命周期 harmony学习页面(@Entry)生命周