Android 《第一行代码》简要笔记(四)
第十章、Service
1、线程的基本用法
继承
Thread
覆写run
方法123456class MyThread extends Thread{public void run(){//子线程逻辑}}然后
new MyThread().start()
即可调用子线程。实现Runnable接口
123456class MyRunnable implements Runnable {public void run(){//子线程逻辑}}然后
new Thread(new MyRunnable()).start()
推荐此种方式,便于解耦习惯写法
123456new Thread(new Runnable(){public void run(){//}}).start();
2、线程间通讯
Android中子线程是不允许更新UI的。常用的方式:Handler和AsyncTask
Handler
123456789101112131415161718192021222324252627public class MainActivity extends AppCompatActivity implements View.OnClickListener{public static final int UPDATE_TEXT = 1;private TextView text;private Handler handler = new Handler(){public void handleMessage(Message msg){switch(msg.what){case UPDATE_TEXT:text.setText("After Update Text");break;default:break;}}};...new Thread(new Runnabe(){public void run(){Message msg = new Message();msg.what = UPDATE_TEXT;handler.sendMessage(msg);}}).start();...}
简要分析Handler机制:
Android中异步消息处理主要有4部分组成:
message
、handler
、MeaageQueue
、Looper
;
Message
消息类,用于线程间少量信息的通讯。含有
what
、arg1
、arg2
和obj
Handler
消息处理者,
sendMessage()
和handleMessage()
方法MessageQueue
消息队列,一个线程一个队列FIFO的消息栈。存放handler发来的消息,由Looper循环,再有handler取出处理。
Looper
消息循环管理者,调用
loop()
方法,进入无限循环,用于取出消息给handler,每个线程一个looper。
Async Task
Android系统提供的异步操作类。使用时需要继承
AsyncTask
并实现对应的方法。需要制定三个参数Params
执行AsyncTask需要传入的参数,用于后台操作使用。
Progress
后台执行时候,可用于前台显示进度,泛型作为进度单位。
Result
后台执行完毕,需要返回的结果类型。
AsyncTask需要实现的子类方法:
- onPreExecute(),任务执行前准备工作
- doInBackground(Params…),后台执行的逻辑,如要反馈进度,调用
publishProgress(Progress...)
- onProgressUpdate(Progress…),更新进度
- onPostExecute(Result),后台执行完毕,返回给前台数据。
|
|
3、Service
Android四大组件之一,需要子类继承Service
来实现相应的逻辑。
|
|
Service的启动有两种:Context.startService()
和Context.bindService()
;前者启动服务后,不再过问,后者是与Service共命运。
停止可以用Service.stopSelf()即可停止。或者Context.stopServie()
,Context.unbindService
只是解绑,并不能停止。
Activity与Service通讯
通过Binder沟通Activity与Service
1234567891011...class MyBinder extends Binder {//自定义方法}...//Service中public IBinder onBind(Intent intent){return new MyBinder();}Activity中需要持有这个链接通讯:
123456789101112...private ServiceConnection conn = new ServiceConnection(){public void onServiceDisconnected(ComponentName name){//断开链接,,,}public void onServiceConnected(ComponentName name,IBinder service){MyBinder myBinder = (MyBinder)service;//获取Binder对象后,操作对应定义的方法,实现通讯。}}绑定服务
bingService(intent,conn,BIND_AUTO_CREATE);
Service 生命周期
onCreate()–>onStartCommand()–>onBind()–>onUnbind()–>onDestroy(),还有一个onRebind();
不论多次调用
onStartCommand()
都将是一个Service实例,而且只需要调用一次StopService或stopSelf即可停止。
- 只startService,stopService即可停止。
- 只bindService,unbindService即可销毁。
- startService后,又bindService,则需要unbindService,再stopService方可销毁。
前台服务,是服务处于前台,而不会轻易的被销毁,以前什么优先级、系统级,双服务之类的,效果都不那么好了。
|
|
IntentService
Service也是运行在主线程,不能运行耗时操作。若在Service中开启异步逻辑,需要的时候,可用service的stopSelf停止自己。
而Android官方提供了一个
IntentService
可以自动运行完逻辑,就可停止的Service。1234567891011121314public class MyIntentService extends IntentService {public MyIntentService(){super("MyIntentService");}protected void onHandleIntent(Intent intent){//处理逻辑,这里就是在异步子线程运行了,不用在new新的线程,而且运行完这里的逻辑,service会自动销毁。}public void onDestroy(){super.onDestroy();}}Android四大组建,都要在AndroidManifest中注册的哦
Git实践:
|
|
其他章节
使用第三方的服务SDK的讲解,最好的是看官方文档和示例代码。如Baidu定位、地图等。
12、material design
Android官方推荐ToolBar替换ActionBar。
|
|
|
|
滑动菜单可以用DrawerLayout
、Google还提供了更好的选择,就是NavigationView
在依赖兼容库中
|
|
NavigationView
中的item
可以分组,可以设置checkableBehavior="single"
单个选择。
悬浮按钮和可交互提示
FloatinActionButton
12345678<android.support.design.widget.FloatingActionButtonandroid:id="@+id/fab"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="bottom|end"android:layout_margin="16dp"android:src="@drawable/ic_done"android:elevation="8dp"/>其中
elevation
属性表示投影效果,数值大,就范围大而浅。其他属性类似Button没特别的。SnackBar
SnackBar类似与toast,但是多了一个可以撤销的按钮。它放在其他按钮中,调用到它的逻辑。
1234567891011121314...fab.setOnClickListener(new View.OnClickListener(){public void onClick(View view){Snackbar.make(view,"data deleted ?",Snackbar.LENGTH_SHORT).setAction("Undo",new View.OnClickListener(){public void onClick(View v){//撤销操作}}).show();}});...CoordinatorLayout
CoordinatorLayout
可谓是增强版的FrameLayout
,如上悬浮按钮fab点击后弹出一个snackbar,会遮挡到fab。这时需要使用CoordinatorLayout
来自动处理子空间的事件。它会自动偏移其他控件,来专注于当前。如上Bug,则需要把fab放入到CoordinatorLayout中即可,因为snackbar也是在fab中点击的,所以可以被控制。
CardView
1234567<android:support.v7.widget.CardViewandroid:layout_width="match_parent"android:layout_height="wrap_content"app:cardCornerRadius="4dp"app:elevation="5dp">...</android:support.v7.widget.CardView>cardview需要单独添加依赖包
Glide图片加载库
1compile 'com.github.bumptech.glide:glide:3.7.0'1Glide.with(mContext).load(imageId).into(ImageView)//glide的用法,更多可以参照api
全局布局时候,RecyclerView会吧ActionBar遮挡,原因是FrameLayout的内部布局从左上开始的缘故。
解决方案,使用AppBarLayout
包裹住Toolbar
然后下面的控件,设置layout_behavior
属性
|
|
上面app:layout_behavior="@string/appbar_scrolling_view_behavior"
都是固定的属性写法。
AppBarLayout
还有一些高级用法,它能控制内部一下空间的滚动时候的事件处理
比如Toolbar在其属性中加入
app:layout_scrollFlags="scroll"enterAlways|snap"
- scroll表示toolbar下面的recyclerView向上滚动时,toolbar会一起滚动,并隐藏。
- enterAlways,表示recyclerView向下滚动时,toolbar会向下滚动,并重新显示。
- snap表示,toolbar未完全隐藏或显示时候,根据滚动距离决定显示隐藏。
下拉刷新
SwipeRefreshLayout
包裹listView或RecyclerView即可是它们拥有下拉刷新功能效果。12345678<android.suport.v4.widget.SwipeRefreshLayout...app:layout_behavior="@string/appbar_scrolling_view_behavior"><ListView...></ListView></android.suport.v4.widget.SwipeRefreshLayout>12345678910SwipeRefreshLayout srl;srl.setonRefreshListener(new SwipeRefreshLayout.onRefreshListener(){public void onRefresh({...})});//刷新完毕,需要设置srl.setRefreshing(false);折叠式标题栏
CollapsingToolbarLayout用于使Toolbar更为炫酷,但是它必须为
AppBarLayout
的子控件才可,而AppBarLayout又必须是CoordinatorLayout的子布局,还是看代码吧~123456789101112131415161718192021222324252627282930313233<android.support.design.widget.CoordinatorLayout...><android.support.design.widget.AppBarLayout...><android.support.design.widget.CollapsingToolbarLayout...><!--这个imageView的效果就是那种下拉折叠式标题栏的背景--><ImageViewandroid:scaleType="centerCrop"app:layout_collapseMode="parallax"...></ImageView><android.support.v7.widget.Toolbarapp:layout_collapseMode="pin"...></android.support.v7.widget.Toolbar></android.support.design.widget.CollapsingToolbarLayout></android.support.design.widget.AppBarLayout><!--折叠式标题栏下面的正文空间--><android.support.v4.widget.NestedScrollView...app:layout_behavior="@string/appbar_scrolling_view_behavior"><!-- ScrollView 和 NestScrollView都只允许一个子布局,要想多布局,就用布局嵌套吧。--><LinearLayout...>...</LinearLayout></android.support.v4.widget.NestedScrollView> <android.support.design.widget.FloatingActionButton...app:layout_anchor="@id/appBar"app:layout_anchorGravity="bottom|end"/></android.support.design.widget.CoordinatorLayout>上面需要注意的就是
layout_collapseMode
属性,pin表示钉住不动,parallax表示随动偏移。layout_anchor
表示设置悬浮按钮的锚点,指定控件的id即可。使用
android:fitsSystemWindows
属性,设置布局,就可以是控件与系统状态栏融合。
13、进阶知识
全局Context,覆写Application, getApplication()。
对象序列化
Serializable
123public class Student implements Serializable {....}Parcelable
12345678910111213141516171819202122232425262728public class Student implements Parcelabe {private string name;private int age;...public int describeConents(){return 0}public void writeToParcel(Parcel dest,int flags){dest.writeString(name);//写出namedest.writeInt(age);//写出age}public static final Parcelable.Creator<Student> CREATOR = new Parcelable.Creator<Student>(){public Student createFromParcel(Parcel source){Student stu = new Student();stu.name = source.readString();stu.age = source.readInt();}public Student[] newArray(int size){return new Student[size];}}}
Intent中可以传递这些序列化的对象。
Log工具
为使的开发中的log不会带入到发行办,需要做个全局开关配置,或则log级别限制。
- 定义Level ,只有当全局Level =< log内的级别时候,打印出相应级别一下的log,如此,发布时候,定义level大于最高的log级别,就不会输出log。
- 定于debug开关。封装log类。
定时任务
Android中虽然也有timer
定时,但是由于cpu休眠等情况,导致它的计时并不会准确。不适合长时间的定时任务。
Alarm机制
1234567 > AlramManager manager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);> //十秒后执行一个任务> long triggerAtTime = SystemClock.elapsedRealtime() + 10*1000;> manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,triggerAtTime,pendingIntent);> //其中参数ELAPSED_REALTIME、ELAPSED_REALTIME_WAKEUP、表示系统开机以来的到现在的时间,wakeup表示唤醒cpu。> //RTC_REALTIME、RTC_REALTIME_WAKEUP、分别表示1970年以来的时间,wakeup表示唤醒cpu。>
>
逻辑操作也会耗时,所以定时任务放在子线程会让时间更精准点。
Android 4.4以后Alarm定时任务多有不准,因为休眠cpu时候,会逐级减少唤醒cpu的次数和增加时间间隔。若要保证任务准时,使用AlarmManager
的setExact()
代替set()方法。
- Doze模式
Android 6.0引入此模式,用于省电。网络、cpu、wifi、同步、Alarm等都会在这个模式下受到影响。若要alarm精准,使得在Doze模式下也运作,调用AlarmManager.setAndAllowWhileIdle()
或setExactAndAllowWhildeIdle()
多窗口模式,7.0引入,同时多个窗口,也只有一个真正活动,生命周期一样的。
12<!-- 禁用多窗口 -->android:resizeableActivity = "false"Lambda表达式
JAVA8的新特性,Gralde配置新增
123456789101112android{...defaultConfit{...jackOptions.enabled = true}compileOptions {sourceCompatibility JavaVersion.VERSION_1_8targetCompatibility JavaVersion.VERSION_1_8}...}详细的Lambda需要专门学习一下,这里就简单说一个,单个参数,或简单参数,接口只有一个方法的形式,可以简略。
1234567891011new Thread(new Runnable(){public void run(){//}}).start();// -- > lambdanew Thread(() -> {//}).start();