Activity
是参考了慕课网BAT某大神的视频。
本文分为四个部分:
1.Activity生命周期
2.Activity任务栈
3.Activity启动模式
4.scheme跳转协议
一、Activity的生命周期
什么是Activity?
android与用户进行交互的时候,提供了一个界面,供用户进行点击滑动等各种操作。这就是Activity!
Activity的四种状态:
running/paused/stopped/killed
running:处于栈顶
paused:失去焦点,或者被一个透明的Activity挡住,失去了与用户交互的能力
stopped:activity被另一个activity完全覆盖,不可见,内存状态信息都有可能还在(内存不紧张,同paused一样)
killed:信息没了
启动流程:
onCreate()–>onStart()[activity处于启动状态,已经可见不可触摸]–>onResume()[已经可以交互了]
按Home键返回主页面:
onPause()[用户可见,但已经不能交互了,对应onresume]–>onStoped()[activity已经停止,完全不可见,后台运行,若资源吃紧,可能会回收掉]
返回原Activity:
onRestart()–>onStart()–>onResume()
退出当前Activity:
onPaused()–>onStopped()–>onDestory()
小知识点:android进程优先级
前台/可见/服务/后台/空
二、android任务栈
任务栈并不是唯一的;
三、activity启动模式
1.standard
标准模式,也是默认的模式,每次启动activity,都会重新创建一个加到任务栈中,没次进入都会完全的走一遍生命周期方法,很浪费资源。
2.singletop
栈顶复用;如果创建的activity在栈顶,才会复用;如果不是栈顶,那就不复用了,感觉用的地方很少。
3.singletask
栈内复用;检查整个栈内有没有此acitivity,有的话就直接复用。对比第2个,你就发现这个是加强版(快速点击一个跳转acitivty的地方会打开多个页面,这个时候用singletask就能完美解决了)
4.singleinstance
这个模式用的就很少了,一个activity占用一个栈
四、shceme跳转协议!
android的scheme是一种页面内跳转协议,通过定义自己的scheme协议,可以非常方便跳转app中的各个页面;通过scheme协议,服务器可以定制化告诉app跳转哪个页面,可以通过通知栏消息定制化跳转页面,可以通过H5页面跳转到app的页面。
应用场景:
1.服务端可以下发一个url路径,客户端可以根据这个url跳转到相应页面
2.从H5页面跳转到本地的activity
3.app可以根据一个url跳转到另一个app的页面中(大公司会有多个app)
现在用处很多,需要进行详细了解!!!
Fragment
一、Fragment为什么被称为第五大组件
使用频率高,ui切换效果好,更节省内存,因为其有自己的生命周期,所以也算不到四大组件里取;fragment必须依附于activity存在。
二、Fragment加载到activity中的两个方式
静态加载,动态加载,(太基础了不说了)
三、FragmentPagerAdapter和FragmentStatePagerAdapter的区别
在实际的使用过程中Fragment常常和Adapter一起搭配使用;(前提)
两个adapter的区别在于,前者用于页面较少的情况下,后者用于页面较多的情况下;
原因呢,通过后者的源码可以看出:destroyItem方法中,最后一句是remove掉Fragment的,真正的释放了fragment内存;而前者的destroyItem方法中,最后一句是detach,这个并不是真正的释放内存的。
四、Fragment的生命周期
如图:
1.onAttach方法是用来创建fragment用的,不过此时activity还未创建成功
2.onCreateView是第一次绘制页面时调用,返回的必须是fragment的根视图
3.onActivityCreated是activity已经渲染成功后调用
生命周期:
五、Fragment之间的通信
1.在Fragment中调用Activity的getActivity
2.在Fragment增加接口在Activity中进行实现,接口回调
3.Fragment与Fragment之间的通信,用findFragmentById
六、Fragment几个操作的区别:
1.Add:往容器中添加一个Fragment
2.Replace:替换容器中的一个Fragment
3.Hide/Show:纯粹的隐藏与显示,不移除
4.Attach/Detach:从布局上移除,但存储到缓存队列中,不会被测量;但可重用。
5.Remove:直接移除掉,并不缓存。
Service
主要讲解两个部分:
一、service的应用场景,以及和Thread的区别
二、开启service的两种方式以及区别
第一部分又可以分为:
Service基础:
1.Service是什么?
Service是一种可以在后台执行长时间运行操作而没有用户界面的应用组件。
可以进行一些长时间的逻辑操作,用户并不会看到相关界面,必须的时候可以在app退出后,仍保持一段时间。(所以现在Service保活是必须要掌握的了)
需要注意的是:
Service和广播都有一个共同点,都是运行在主线程中的,都不能进行耗时操作。
2.Service和Thread的区别
1.Thread运行是相对独立的,而本地Service是依附于主线程的
2.其实Thread和Service是没有任何关系的!就像老婆饼跟老婆有关系吗?我昨天刚吃了一个老婆饼,那咋不给我个老婆呢?
之所以把两者联系在一起,理解问题,总觉得Service是服务是用来进行耗时操作的!不要把后台和子线程联系在一起!android的后台是指即使页面退出了,app退出了,也照样进行服务的。
若是Service要进行耗时操作,那么也要开启子线程。
第二部分:
开启service有两种方式:
一.startService:
通过在activity调用此方法创建的service,一旦服务开始,就会在后期无限运行,即使activity销毁了,照样运行,除非手动销毁。
首先说一下service的生命周期:
① onbind():
这个是第二种方式bindService中才用到的,startService中可以直接返回null
②oncreate():
在onbind和onstartcommand之前调用,首次创建服务时调用,如果服务已在运行,就不会走这个方法,该方法只调用一次
③onstartcommand():
每次通过startServic()方法启动Service是都会被回调
④ondestroy():
服务销毁时调用
使用步骤:
1.定义一个类继承Service
2.在Manifest.xml文件中配置该Service
3.使用Context的startService(intent)
4. 不再使用时,调用stopService(intent)
二、bindService
服务和Activity处于绑定状态,允许service和activity进行数据交互,service处于不同进程中时,可以通信。这种service只有绑定activity之后才可以运行,并且多个activity可以绑定同一个service,如果绑定全部取消,就会自动销毁
使用步骤:
1.创建BindService服务端,继承自Service并在类中,创建一个实现IBinder接口的实例对象并提供公共方法给客户端调用
2.从onBind()回调方法返回此Binder实例
3.在客户端中,从onServiceConnected()回调方法接收Binder,并使用提供的方法调用绑定服务
Tips:Activity和Service是通过ServiceConnection进行连接的!ServicConnction是与服务端交互的接口方法,绑定服务的时候调用其内部有两个方法:onServiceConnected、onServiceDisconnected,前者是绑定成功时调用,后者是当Service意外销毁时调用!正常解绑是不会调用的!该类使用方法如下:
private ServiceTest.MyBinder myBinder;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
//实例化service
myBinder = (ServiceTest.MyBinder) iBinder;
//开始调用Service中的方法
myBinder.startDownload();
Log.i(TAG, “onServiceConnected: -----------bindService---------”);
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
Log.i(TAG, "onServiceDisconnected: --------unbindService--------");
}
};
Intent intent3 = new Intent(this, ServiceTest.class);
bindService(intent3, connection, BIND_AUTO_CREATE);
*************
一篇比较详细的Service使用博客:
## Service的简单介绍
1.四大组件之一
2.用于在后台处理耗时的操作(开启子线程)
3.不受activity生命周期影响
可以在**设置里面看正在运行的程序**,有很多程序是开启了服务,而没有显示在后台程序队列中。
Service是Android系统中的四大组件之一,主要有两个应用场景:后台运行和跨进程访问。Service可以在后台执行长时间运行操作而不提供用户界面,除非系统必须回收内存资源,否则系统不会停止或销毁服务。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行;但是不要被“后台”这两个字迷惑,**service仍然是运行在UI线程中的,如果进行耗时任务,一定要开启子线程!**
## Service的生命周期
1.onCreate()--->创建
2.onStartCommand()--->开启
3.onBind()--->绑定
4.onUnbind()--->解绑
5.onDestroy()--->销毁
可以把Service当做没有界面的Activity进行处理!
## Service的两种启动模式
service有两种启动方式,一种是通过 startService() 来启动的,另一种是通过 bindService() 来启动的。
**一、通过startService()**

**如果服务已经开启,就不会重复的执行 onCreate() ,而是调用 onStart() 或 onStartCommand()。而服务停止的时候调用 onDestory() 。**
**特点**:
1.一旦服务开启跟开启者就没有任何关系;
2.开启者退出之后,服务还是可以在后台长期运行的。前提是没有调用 3.stopService(Intent);
4.开启者不能调用服务里面的方法
**二、通过bindService()开启**

**绑定服务不会调用 onStart() 或 onStartCommand() 方法。**
**特点:**
bind的方式开启服务,绑定服务,调用者挂了,服务也会跟着挂掉。
绑定者可以调用服务里面的方法。
两种启动方式的区别:
startService不会调用onBind()方法,所以只需要返回null即可。onBind()是在绑定服务的时候调用,也就是通过bindService启动的Service。
实际代码说:
首先说明,每个service都要在清单文件中进行声明!毕竟人家也是四大长老之一嘛!
```
第一步:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
>
<Button
android:id="@+id/startService"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/btn_bk1"
android:text="startService"
android:textAllCaps="false"
android:layout_marginRight="10dp"/>
<Button
android:id="@+id/stopService"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="stopService"
android:textAllCaps="false"
android:background="@drawable/btn_bk1"
android:layout_marginRight="10dp"
/>
<Button
android:id="@+id/service"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Service"
android:textAllCaps="false"
android:background="@drawable/btn_bk1"
android:visibility="invisible"
/>
<!-- android:background="@drawable/input_box"-->
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
>
<Button
android:id="@+id/sService"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/btn_bk1"
android:text="startService"
android:textAllCaps="false"
android:layout_marginRight="10dp"
android:visibility="invisible"
/>
<Button
android:id="@+id/bindService"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="bindService"
android:textAllCaps="false"
android:background="@drawable/btn_bk1"
android:layout_marginRight="10dp"
/>
<Button
android:id="@+id/unbindService"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="unbindService"
android:textAllCaps="false"
android:background="@drawable/btn_bk1"
/>
<!-- android:background="@drawable/input_box"-->
</LinearLayout>
</LinearLayout>
这里放四个按钮,用于startService、stopService,bindService,unbindService。
第二部,创建我们的Service:
package com.rye.catcher.project.services;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import com.rye.catcher.BaseActivity;
import com.rye.catcher.R;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
/**
* Created by Zzg on 2018/2/25.
*/
public class ServiceMainActivity extends BaseActivity {
public static final String TAG = "ServiceMainActivity";
@BindView(R.id.startService)
Button startService;
@BindView(R.id.stopService)
Button stopService;
@BindView(R.id.service)
Button service;
@BindView(R.id.sService)
Button sService;
@BindView(R.id.bindService)
Button bindService;
@BindView(R.id.unbindService)
Button unbindService;
//与Service进行绑定
private LocalService.MyBinder myBinder;
//IBinder
//ServiceConnection用于绑定客户端和service
//进度监控
private ServiceConnection connection = new ServiceConnection() {
//服务绑定成功后调用,毕竟onBind方法只执行一次,绑定成功/失败调用下面的两个方法
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
//实例化service
myBinder = (LocalService.MyBinder) iBinder;
//开始调用Service中的方法
myBinder.startDownload();
Log.i(TAG, "onServiceConnected: -----------bindService---------");
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
Log.i(TAG, "onServiceDisconnected: --------unbindService--------");
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.service_main);
ButterKnife.bind(this);
}
@OnClick({R.id.startService, R.id.stopService, R.id.service,
R.id.sService, R.id.bindService, R.id.unbindService})
public void onViewClicked(View view) {
switch (view.getId()) {
case R.id.startService:
//开启Service
Intent intent1 = new Intent(this, LocalService.class);
startService(intent1);
break;
case R.id.stopService:
Intent intent2 = new Intent(this, LocalService.class);
stopService(intent2);
break;
case R.id.service:
break;
case R.id.sService:
break;
case R.id.bindService:
Intent intent3 = new Intent(this, LocalService.class);
bindService(intent3, connection, BIND_AUTO_CREATE);
break;
case R.id.unbindService:
unbindService(connection);
break;
}
}
}
从上面的代码中我们可以看出,启动service所用的是:
//开启Service
Intent intent1 = new Intent(this, ServiceTest.class);
startService(intent1);
和activity一样是通过intent启动的,不同的是activity所用的方法是startActivity、而service所用的是startService。
一个service创建成功后,如果不销毁,再次startService后,就不会再创建,而是再次启动,也没有销毁再创建,就不走onCreate,而是直接走onStartCommand方法:
通过startService启动的Service的生命周期和Activity的生命周期没关联。
接着我们stopService后,调用bindService,log为:
11-27 11:00:31.488 1205-1205/com.rye.catcher.zzg.anyOATest I/ServiceTest: onCreate:--------ServiceStart--------
11-27 11:00:31.492 1205-1205/com.rye.catcher.zzg.anyOATest I/ServiceTest: onBind: 绑定
11-27 11:00:31.493 1205-1205/com.rye.catcher.zzg.anyOATest I/ServiceTest: startDownload: ------Service与Activity绑定成功,开始执行方法-------
调用unbind后:
11-27 11:03:31.595 1205-1205/com.rye.catcher.zzg.anyOATest I/ServiceTest: onUnbind: ...
11-27 11:03:31.596 1205-1205/com.rye.catcher.zzg.anyOATest I/ServiceTest: onDestroy: --------stopService-------
而如果service已经启动,比如说被startService启动了,那么这时候调用bindService,只有onBind方法被调用,unBindService方法只会调用onUnBind。
调用bindService后,如果销毁activity,那么就会:
11-27 11:11:14.274 1205-1205/com.rye.catcher.zzg.anyOATest I/ServiceTest: onUnbind: ...
可以看出通过bindService绑定activity的service会在activity销毁的时候进行销毁。
绑定服务:最大的作用是用来实现对Service执行的任务进行进度监控【比如下载任务,AsyncTask】
ServiceConnection:
当服务绑定成功/结束调用的时候分别调用该类的两个方法:
bindService是异步调用和Service进行绑定, 如果绑定成功, 则会调用ServiceConnection的onServiceConnected 当调用bindService方法后就会回调Activity的onServiceConnected,在这个方法中会向Activity中传递一个IBinder的实例,Acitity需要保存这个实例。
一些需要注意的点:
1.当bind和start同时存在时,如果已经绑定了,那么直接stop会失败,需要先unbind。
2.如果解绑之后再绑定,再点击绑定就失效了。哪怕已经解绑了,也只会执行一次onBind方法,而后续客户端和服务端的连接就完全由ServiceConnction来掌控了
3.不要在activity的onCreate里bindService
Handler
一、什么是handler
android消息机制的上层接口。
handler通过发送和处理Message和Runnable对象来关联对应线程的MessageQuene。
1.可以让对应的Message和Runnable在未来的某个时间点进行相应处理。
2.把耗时操作放在子线程中,让更新ui的操作放在主线程中。
二、handler的用法
handler有两种使用方法:
1.post(Runnable)
2.sendMessage(message)
当在主线程声明handler时(成员变量),handler是绑定在主线程中的,这样是线程安全的,如果在内部类中声明,会导致线程不安全,抛出异常。
** 子线程中不能直接声明handler!!!!!**
原因如下:
handler需要一个MessageQuene消息队列来保存所发送的消息,子线程默认是不开启looper轮询器的,而MessageQuene又需要looper来管理,故会抛出异常。
如果想在子线程中使用handler, 那就必须自己初始化一个looper,然后再通过looper.loops方法开启一个循环。也就是在子线程初始化handler之前调用Looper.prepare()方法。
(代码规范:
执行一个耗时操作:
在onclick方法中声明一个Thread,调用其start()方法。
在Thread中,进行耗时操作后,声明一个runnable,,然后调用handler.post方法。
这样看起来贼舒服。
)
post其底层仍旧是sendMessage方法(看源码!)。
三、handler机制的原理
Looper:通过prepare()创造looper,一个线程中只能有一个looper;通过Looper.loop()开启循环,实际上loop方法就是创建个死循环,不断从消息队列中取出消息进行处理。
四、handler引起的内存泄漏以及解决方法
原因:静态内部类持有外部类的匿名引用,导致外部activity无法释放。
解决方法:
1.设置handler为静态内存,也就是声明的时候加上static修饰。
2.在activity的ondestroy方法中调用 mHandler.removeCallback();
3.handler内部持有外部activity的弱引用。
五、总结:
1.每个线程只能有且只有一个Looper和MessageQueue对象
2.handler除了在主线程,都需要在new Handler之前调用Looper.prepare方法和在3.new Handler之后调用Looper.loop方法,主线程则不用且不能
4.handler在哪个线程新建的,那么不管是在哪个线程调用sendMessage方法,最终的dispatchMessage都会在新建handler的线程处理消息队列
5.MessageQueue是一个有序的以时间排列的单链表
六、handler的两个方法:hasMessage和removeMessages
//测试Handler
private Handler zHander=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 1:
runOnUiThread(()->{
ToastUtils.shortMsg("弹出框。。。");
});
break;
}
}
};
//按钮点击事件
private void testHandler(){
if (zHander.hasMessages(1)){
zHander.removeMessages(1);
Log.i(TAG, "testHandler: ");
}
zHander.sendEmptyMessageDelayed(1,3000);
}
AsyncTask
一、什么是AsyncTask
本质上就是一个封装了线程池和handler的异步框架。
AsyncTask适用于短时间的耗时操作,耗时很长的还是要用线程池来做。
二、AsyncTask的使用方法
1.三个参数
Integer、String等,(传参)
Integer,(进度)
String,Integer等,(返回)
2.五个方法
①onPreExecute:
执行异步任务之前的相关处理
②doInBackground:
执行耗时操作,返回值传递到onPostExecute
③onProgressUpdate:
在doInBackground里调用publishProgress(int);方法之后就会被调用,给progressbar设置进度等
④publishProgress:
在doInBackground里调用,更新进度。
⑤onPostExecute:
返回值
三、AsyncTask的机制原理
1、AsyncTask的本质是一个静态的线程池,AsyncTask派生出的子类可以实现不同的异步任务,这些异步任务都是提交到静态的线程池中执行。
2、线程池中的工作线程执行doInBackground(mParams)方法执行异步任务。
3、当任务状态改变之后,工作线程会向UI线程发送消息,AsyncTask内部的InternalHandler相应这些消息,并调用相关的回调函数。
四、AsyncTask注意事项
1.AsyncTask和handler都会有内存泄漏,原理都是非静态内部类持有外部类的匿名引用。当Activity销毁时,如果AsyncTask还在doinbackground里执行后台任务,因为持有activity,导致activity不能被回收,就会导致内存泄漏。所以,需要在activity的ondestroy方法里调用asyncTask的cancel方法。不调用cancel方法就会导致asyncTask不会被销毁。
2.asyncTask结果丢失,由于activity旋转或者内存不足导致被杀死的时候就会导致结果丢失。
Application
一、概念:
Application是维护应用全局状态的基类。Android系统会在启动应用进程时创建一个Application对象
二、使用:
写一个子类继承于Application,并在AndroidManifest.xml中application下用name属性使用Application的子类。
三、Application对象的生命周期:
Application对象诞生于其他任何组件对象之前,并且一直存活,直到应用结束。
四、Application对象的回调函数:
Application对象由Android系统管理,它的回调函数都运行与线程。函数如下:
1.onCreate:
Application创建的时候调用,如果不强制杀掉,那么手机将会为我们尽可能长的保活进程
2.onConfigurationChanged:
当系统配置信息发生改变时调用,比如横竖屏、手机语言修改
3.onLowMemory
当系统内存吃紧的时候调用
四、Application对象的作用:
Application对象全局可访问,且全程陪同应用进程。适合完成以下任务:
1.共享全局状态
2.初始化全应用所需的服务
ex:
如果在application的子类中声明一个变量,实现get、set方法,赋值完之后就可以在其他地方全局调用了。
五、application的启动流程
现场保护
一、Activity的状态
在java中,对象是状态和行为的结合体,对象的所有成员变量运行时构成了对象的状态。
二、运行时系统配置变更
当屏幕横竖屏切换时,系统语言变更等,会导致activity销毁重建;其中横竖屏切换是最常见的场景。可以在onCreate和onDestroy里打log看一下销毁与创建。
为了避免activity对象的丢失,有有以下几个应对策略:
1.限定屏幕方向
在清单文件里,给activity加上标签:
android:screenOrientation="portrait"
2.自己处理变更
需要加上标签:
android:configChanges="keyboardHidden|orientation|screenSize"
当加上这句话的时候,就是表明配置发生变更时,用同一个activity和布局文件,横竖屏切换的时候,就不会销毁activity再重新创建了。
3.让系统处理配置变更
需要了解几个方法:
1>onSaveInstanceState
当需要保存activity状态的时候被调用,需要在这个方法里保存要被销毁的activity的状态
2>onCreate
3>onRestoreInstanceState
底下这两个都有一个bundle,被销毁的activity的状态就保存在bundle中。
0.0
onSaveInstanceState的调用时机【系统自动调用】:
1>按HOME键时
2>被来电覆盖时
是否要保存activity的状态,取决于是否符合用户的预期。
在oncreate和onRestoreInstanceState里都会收到保存的bundle对象,一般是在oncreate方法里进行恢复。
如果想测试的话,可以用系统时间来测试,看看切屏的时候时间是否变化,是否保存即可。
保持Fragment对象
1.扩展Fragment
2.在onCreate函数里调用setRetainInstance(true);
3.把Fragment对象添加到Activity中
4.当Activity重启时,通过FragmentManager获取此Fragment
分两种情况:
一、配置发生改变时,不需要加载不同的资源文件:
1.首先在view创建的时候调用setRetainInstance(true);
【最关键的一句话,去掉了,就不保存状态了】
2.在activity加载Fragment的时候不要每次都new新的Fragment,而是通过FragmentManager的findFragmentByTag来加载fragment:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
this.view=inflater.inflate(R.layout.fragment_site_protect, container, false);
unbinder=ButterKnife.bind(this,view);
//现场保护,re-creation
setRetainInstance(true);
return view;
}
//加载Fragment
FragmentManager fm=getFragmentManager();
currentFragment= (SiteProtectFragment) fm.findFragmentByTag("areYouOk");
if (currentFragment==null){//为空就加进来
fm.beginTransaction()
.replace(R.id.container,new SiteProtectFragment(),"areYouOk")
.commit();
}
二、配置发生改变时,需要加载不同的资源文件:
有些时候需要对运行的fragment进行重启,这时候就需要用到两个方法了:
1.onSaveInstanceState
重启前保存fragment的状态
2.onActivtiyCreated
恢复fragment的状态
和activity不同的是,fragment没有onRestoreInstanceState方法。
操作和activity相同,这里就不示例了。
Broadcast Receiver
实际上现在用eventBus的比较多,但是广播好歹作为四大组件之一,还是很有必要掌握的。
广播类似于Java的观察者模式。
一、广播的使用场景:
1.同一个app具有多个进程的不同组件进行交互
2.不同的app之间组件进行通信(两个app之间通过发送广播交互)
二、广播的种类
按照广播类型分类,有两种:
全局广播:可以被其他程序接收,也可以接受来自其他任意程序的广播(需要在清单文件中注册)
本地广播:只能在应用程序内进行传递的广播
按照广播机制来分,可分为两种:
无序广播:所有的接受者都会接收事件,不可以被拦截,不可以被修改
有序广播:按照优先级,一级一级向下传递,接受者可以修改广播数据,也可以终止 (想要截断广播,只需要在onreceive()方法中调用aboryBroadcast()即可是广播不再传递下去)
三,实现广播
1.静态注册:注册完成就一直运行
2.动态注册:跟随activity的生命周期,一定不要忘了在ondestroy中销毁!
四、广播内部的实现机制
1.自定义广播接受者BroadcastReceiver,并复写onRecvice()方法;
2.通过Binder机制(进程间通信的核心)向AMS(Activity Manager Service)进行注册。
3.广播发送者通过Binder机制向AMS发送广播
4.AMS查找符合相应条件的BroadcastReceiver,将广播发送到BroadcastReceiver(一般情况下是Activity)相应的消息循环队列中;
5.消息循环执行拿到此广播,回调BroadcastReceiver中的onReceive()方法
五**、LocalBroadcastManager详解**
1.使用它发送的广播将只在自身App内传播,因为不必担心泄露隐私数据
2.其他app无法对你的app发送该广播,因为根本就不可能接收到非自身应用发送的该广播,因此不必担心有安全漏洞可以利用
3.比系统的全局广播更加高效
(有关安全漏洞这方面,用全局的话,如果你混淆没做,或者做的不好,被别人反编译了之后获得了action,那么第三方应用就可以发送广播给你,搞各种事情给你!让你跳链接什么的;同样,第三方应用也可以获取到你发送的广播,拿到应用内的个人的各种信息)
LocalBroadcastManager部分源码解读:
可以看到其内部使用handler实现的,其中context.get MainLooper表明是运行在主线程中。
LocalBroadcastManager有三个重要的集合类:
mPendingBroadcasts:是存储了广播接收器的存储器
Tips:
1.LocalBroadcastManager高效的主要原因主要是因为它内部是通过Handler实现的,它的sendBroadcast()方法含义并非和我们平时所用的一样,它的sendBroadcast()方法实际上是通过handler发送一个Message实现的
2.因为内部是通过Handler来实现广播的发送的,相比系统广播通过Binder实现肯定更加高效,同时使用Handler来实现,别的应用无法向我们的应用发送该广播,而我们应用内发送的广播也不会离开我们的应用
3.LocalBroadcastManager内部协作主要靠这两个Map集合:mReceivers和mActions,当然还有一个List集合mPendingBroadcasts,这个主要即使存储待接收的广播对象。
代码混淆
一、proguard到底是什么:
proguard就是我们打包过程中压缩、混淆代码的工具,主作用是可以移除代码中的无用类,字段,方法和属性同时可以混淆。这样就可以让我们的apk体积更小,更难反编译。【所以,有时候apk包变大了,可能是混淆忘开了】
二、Proguard技术的功能
1.压缩
打包过程中检查和移除没有用到的类、字段、属性、方法等,相当于打包过程中的文件不包括无用的类等,这样体积自然就笑了,但本地又有这些类,以防以后用到了找不到,可以说双方受益。
2.优化
通过javac编译好的class文件,会移除掉那些无用的指令
3.混淆
通过无意义的名词混淆掉我们有意义的名称,别人看不到
4.预检测
三、Proguard工作原理
EntryPoint
可以理解一种标志,它是在proguard过程中不会处理的类和方法。被标记entrypoint类和方法,不希望被我们混淆掉。
最重要的是,我们为什么要进行混淆呢?
java是一种跨平台的解释性语言,它的源代码会被编译成字节码存储在我们的.class文件中,由于跨平台的需要,java的字节码中包含了很多源码的信息,包括变量名,方法名等,并且通过这些名称来访问变量和方法,这些符号包含很多无用信息,又特别容易被反编译成源代码,为了防止这种现象,我们就需要通过proguard来对java的字节码进行混淆,混淆就是对发布出去的程序release版,进行重新处理,使得功能一样代码却不一样,不容易反编译,即使范编译成功也很难读懂代码
还是贴一下我的app中的混淆代码吧,其实实现混淆也挺简单,根据造轮子定理,先拿到一个模板,然后对第三方库和jar混淆,然后根据build中的报错进行修改即可:
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in C:\Users\changshuchao\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
#############################################
#
# 对于一些基本指令的添加
#
#############################################
# 代码混淆压缩比,在0~7之间,默认为5,一般不做修改
-optimizationpasses 5
# 混合时不使用大小写混合,混合后的类名为小写
-dontusemixedcaseclassnames
# 指定不去忽略非公共库的类
-dontskipnonpubliclibraryclasses
# 这句话能够使我们的项目混淆后产生映射文件
# 包含有类名->混淆后类名的映射关系
-verbose
# 指定不去忽略非公共库的类成员
-dontskipnonpubliclibraryclassmembers
# 不做预校验,preverify是proguard的四个步骤之一,Android不需要preverify,去掉这一步能够加快混淆速度。
-dontpreverify
# 保留Annotation不混淆
-keepattributes *Annotation*,InnerClasses
# 避免混淆泛型
-keepattributes Signature
# 抛出异常时保留代码行号
-keepattributes SourceFile,LineNumberTable
# 指定混淆是采用的算法,后面的参数是一个过滤器
# 这个过滤器是谷歌推荐的算法,一般不做更改
-optimizations !code/simplification/cast,!field/*,!class/merging/*
#咱不知道
-ignorewarnings
#############################################
#
# Android开发中一些需要保留的公共部分
#
#############################################
# 保留我们使用的四大组件,自定义的Application等等这些类不被混淆
# 因为这些子类都有可能被外部调用
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Appliction
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class * extends android.view.View
-keep public class com.android.vending.licensing.ILicensingService
# 保留support下的所有类及其内部类
-keep class android.support.** {*;}
# 保留继承的
-keep public class * extends android.support.v4.**
-keep public class * extends android.support.v7.**
-keep public class * extends android.support.annotation.**
# 保留R下面的资源
-keep class **.R$* {*;}
# 保留本地native方法不被混淆
-keepclasseswithmembernames class * {
native <methods>;
}
# 保留在Activity中的方法参数是view的方法,
# 这样以来我们在layout中写的onClick就不会被影响
-keepclassmembers class * extends android.app.Activity{
public void *(android.view.View);
}
# 保留枚举类不被混淆
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
# 保留我们自定义控件(继承自View)不被混淆
-keep public class * extends android.view.View{
*** get*();
void set*(***);
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
}
# 保留Parcelable序列化类不被混淆
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
# 保留Serializable序列化的类不被混淆
-keepnames class * implements java.io.Serializable
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
!static !transient <fields>;
!private <fields>;
!private <methods>;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
# 对于带有回调函数的onXXEvent、**On*Listener的,不能被混淆
-keepclassmembers class * {
void *(**On*Event);
void *(**On*Listener);
}
# webView处理,项目中没有使用到webView忽略即可
-keepclassmembers class fqcn.of.javascript.interface.for.webview {
public *;
}
-keepclassmembers class * extends android.webkit.webViewClient {
public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
public boolean *(android.webkit.WebView, java.lang.String);
}
-keepclassmembers class * extends android.webkit.webViewClient {
public void *(android.webkit.webView, jav.lang.String);
}
#-----------------------webview--------------------
-keepclassmembers class fqcn.of.javascript.interface.for.webview {
public *;
}
-keepclassmembers class * extends android.webkit.webViewClient {
public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
public boolean *(android.webkit.WebView, java.lang.String);
}
-keepclassmembers class * extends android.webkit.webViewClient {
public void *(android.webkit.webView, jav.lang.String);
}
# 移除Log类打印各个等级日志的代码,打正式包的时候可以做为禁log使用,这里可以作为禁止log打印的功能使用
# 记得proguard-android.txt中一定不要加-dontoptimize才起作用
# 另外的一种实现方案是通过BuildConfig.DEBUG的变量来控制
#-assumenosideeffects class android.util.Log {
# public static int v(...);
# public static int i(...);
# public static int w(...);
# public static int d(...);
# public static int e(...);
#}
#############################################
#
# 项目中特殊处理部分
#
#############################################
#-----------处理反射类---------------
#-----------处理js交互---------------
#-----------处理实体类---------------
# 在开发的时候我们可以将所有的实体类放在一个包内,这样我们写一次混淆就行了。
#-keep class com.blankj.data.bean.**{ *; }
#-----------处理第三方依赖库---------
#-------------------------------------bugly混淆-------------------------------
-dontwarn com.tencent.bugly.**
-keep public class com.tencent.bugly.**{*;}
#-------------------------------------butterknife-----------------------------
-keep class butterknife.** { *; }
-dontwarn butterknife.internal.**
-keep class **$$ViewBinder { *; }
-keepclasseswithmembernames class * {
@butterknife.* <fields>;
}
-keepclasseswithmembernames class * {
@butterknife.* <methods>;
}
#--------------------------------greenDAO3--------------------------
-keepclassmembers class * extends org.greenrobot.greendao.AbstractDao {
public static java.lang.String TABLENAME;
}
-keep class **$Properties
# If you do not use SQLCipher:
-dontwarn org.greenrobot.greendao.database.**
# If you do not use RxJava:
-dontwarn rx.**
#-----------------------------Retrofit2--------------------------
# Retrofit does reflection on generic parameters and InnerClass is required to use Signature.
-keepattributes Signature, InnerClasses
# Retain service method parameters when optimizing.
-keepclassmembers,allowshrinking,allowobfuscation interface * {
@retrofit2.http.* <methods>;
}
# Ignore annotation used for build tooling.
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
# Ignore JSR 305 annotations for embedding nullability information.
-dontwarn javax.annotation.**
# Guarded by a NoClassDefFoundError try/catch and only used when on the classpath.
-dontwarn kotlin.Unit
# Top-level functions that can only be used by Kotlin.
-dontwarn retrofit2.-KotlinExtensions
#-------------------------------Rxjava---------------------------------
#------------------------------Okhttp3---------------------------------
-dontwarn com.squareup.okhttp.**
-keep class com.squareup.okhttp.{*;}
-dontwarn com.squareup.okhttp3.**
-keep class com.squareup.okhttp3.** { *;}
-dontwarn okio.**
#------------------------------gilde-----------------------------------
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public class * extends com.bumptech.glide.module.AppGlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
**[] $VALUES;
public *;
}
#------------------------------EventBus---------------------------------
-keepattributes *Annotation*
-keepclassmembers class ** {
@org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
#-----------------------------Permission--------------------------------
-dontwarn com.yanzhenjie.permission.**
#-----------------------------PullToRefresh-----------------------------
-dontwarn com.handmark.pulltorefresh.library.**
-keep class com.handmark.pulltorefresh.library.** { *;}
-dontwarn com.handmark.pulltorefresh.library.extras.**
-keep class com.handmark.pulltorefresh.library.extras.** { *;}
-dontwarn com.handmark.pulltorefresh.library.internal.**
-keep class com.handmark.pulltorefresh.library.internal.** { *;}
#----------------------------FastJson--------------------------
-dontwarn com.alibaba.fastjson.**
-keep class com.alibaba.fastjson.** { *; }
-keepattributes Signature
-keepattributes *Annotation*
#--------------------------高德定位-----------------------------
-keep class com.amap.api.location.**{*;}
-keep class com.loc.**{*;}
-keep class com.amap.api.fence.**{*;}
-keep class com.autonavi.aps.amapapi.model.**{*;}