不难看出Context一共有三种类型,分别是Application、Activity和Service。这三个类虽然分别各种承担着不同的作用,但它们都属于Context的一种,而它们具体Context的功能则是由ContextImpl类去实现的。由于Context的具体能力是由ContextImpl类去实现的,因此在绝大多数场景下,Activity、Service和Application这三种类型的Context都是可以通用的。不过有几种场景比较特殊,比如启动Activity,还有弹出Dialog。出于安全原因的考虑,Android是不允许Activity或Dialog凭空出现的,一个Activity的启动必须要建立在另一个Activity的基础之上,也就是以此形成的返回栈。而Dialog则必须在一个Activity上面弹出(除非是System Alert类型的Dialog),因此在这种场景下,我们只能使用Activity类型的Context,否则将会出错。
第一步:自定义Application
public class MyApplication extends Application {
private static Context mContext;
@Override
public void onCreate() {
super.onCreate();
mContext = getApplicationContext();
}
public static Context getContext() {
return mContext;
}
}
首先就是重写父类的onCreate(),然后获得一个应用程序级别的context,并赋值给成员变量。再编写一个getContext()函数,返回成员变量的值,就是刚刚获取的应用程序级别的context。
第二步:在Manifest.xml中注册
之后在任何地方需要使用Context,只需要使用MyApplication.getContext();
即可获得。
注意:因为一个程序只能有一个Application,所以这种全局获得Context的方式和之前使用的Litepal发生了冲突。
如何处理呢?
只需要添加下面这句话即可:LitePal.initialize(mContext);
@Override
public void onCreate() {
super.onCreate();
mContext = getApplicationContext();
LitePal.initialize(mContext);
}
首先需要让对象实现Serializable接口。
然后使用intent.putExtra()进行发送。
最后使用 (强制转化)getIntent().getSerializableExtra(“xxx”);的方式进行接收。
public class Person implements Parcelable {
private String name;
private int age;
/*
直接返回0就可以了
*/
@Override
public int describeContents() {
return 0;
}
/*
需要调用Parcel中的readXXX方法将成员变量一一写出
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);//写出name
dest.writeInt(age);//写出age
}
public static final Creator CREATOR = new Creator() {
@Override
public Person createFromParcel(Parcel in) {
//创建一个新的Person类,将成员变量一一读取,并返回
Person person = new Person();
person.name = in.readString();//读取name
person.age = in.readInt();//读取age
return person;
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
}
同样首先需要让对象实现Parcelable接口,只不过相较于Serializable接口更加复杂,需要实现几个方法。这是因为Parcelable实现传递对象的方法是将对象拆分,一一传递。
发送方法相同。
最后进行接收的方式也由getSerializableExtra变成getParcelableExtra。
其中Serializable比较简单,但是需要将整个实体类序列化,而Parcelable不需要,虽然比较复杂,但是效率比实现Serializable接口高。
public class LogUtils {
public static final int VERBOSE = 1;
public static final int DEBUG = 2;
public static final int INFO = 3;
public static final int WARN = 4;
public static final int ERROR = 5;
public static final int ASSERT = 6;
public static final int NOTHING = 7;
public static int level = VERBOSE;
public static void v(String tag, String msg) {
if (level <= VERBOSE) {
Log.v(tag, msg);
}
}
public static void d(String tag, String msg) {
if (level <= DEBUG) {
Log.v(tag, msg);
}
}
public static void i(String tag, String msg) {
if (level <= INFO) {
Log.v(tag, msg);
}
}
public static void w(String tag, String msg) {
if (level <= WARN) {
Log.v(tag, msg);
}
}
public static void e(String tag, String msg) {
if (level <= ERROR) {
Log.v(tag, msg);
}
}
}
非常简单的一个封装,在需要打印时可以调用自定义日志工具的相应函数。在不需要打印的时候,可以调高level等级,这样就不会打印了。
其实自定义日志工具,还可以进行很多个性化的操作。这里就赘述了,推荐大家一个好用的日志工具——Timber。
没什么特殊的,看书跟着操作就OK了
主要使用Alarm机制,通过AlarmManager类来实现。
先获得AlarmManager类实例:
AlarmManager manager = (AlarmManager)getSystemService(Context.ALARM_SERVISE);
接下来调用AlarmManager的set()方法就可以设置一个定时任务 了,比如说想要设定一个任务在10秒钟后执行,就可以写成:
long triggerAtTime = SystemClock.elapsedRealtime() + 10 * 1000;
manager.set (AlarmManager.ELAPSED_ REALTIME_WAKEUP, triggerAtTime, pendingIntent);
set()方法中需要传入的3个参数。
第一个参数是一个整型参数, 用于指定AlarmManager的工作类型,有4种值可选,分别是ELAPSED_REALTIME、ELAPSED_REALTIME_WAKEUP、RTC和RTC_WAKEUP。
其中ELAPSED_REALTIME表示让定时任务的触发时间从系统开机开始算起,但不会唤醒CPU。ELAPSED_REALTIME_WAKEUP 同样表示让定时任务的触发时间从系统开机开始算起,但会唤醒CPU。RTC表示让定时任务的触发时间从1970年1月1日0点开始算起,但不会唤醒CPU。RTC_ WAKEUP同样表示让定时任务的触发时间从1970年1月1日0点开始算起,但会唤醒CPU。
使用SystemClock.elapsedRealtime ( )方法可以获取到系统开机至今所经历时间的毫秒数,使用System.currentTimeMillis()方法可以获取到1970年1月1日0点至今所经历时间的毫秒数。
然后看一下第二个参数,这个参数就好理解多了,就是定时任务触发的时间,以毫秒为单位。如果第一个参数使用的是ELAPSED_REALTIME 或ELAPSED_ REALTIME_WAKEUP, 则这里传入开机至今的时间再加上延迟执行的时间。如果第一个参数使用的是RTC或RTC__WAKEUP,则这里传入1970年1月1日0点至今的时间再加上延迟执行的时间。
第三个参数是一个PendingIntent,这里我们一般会调用getService()方法或者getBroadcast()方法来获取一个能够执行服务或广播的PendingIntent。这样当定时任务被触发的时候,服务的onStartCommand()方法或广播接收器的onReceive( )方法就可以得到执行。
如果我们要实现一个长时间后台定时运行的服务,我们需要新建一个普通的服务
public class LongTimeService extends Service {
public LongTimeService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
//在这里执行具体的逻辑
}
}).start();
AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);
int anHour = 60 * 60 * 1000;
long triggerAtTime = SystemClock.elapsedRealtime() + anHour;
Intent i = new Intent(this, LongTimeService.class);
PendingIntent pi = PendingIntent.getService(this, 0, i, 0);
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,triggerAtTime,pi);
return super.onStartCommand(intent, flags, startId);
}
}
最后,只需要在你想要启动定时服务的时候调用如下代码即可:
Intent intent = new Intent (context, LongRunningService. class);
context.startService (intent);
书中还介绍了很多方法,例如Android4.4之后如果想要准确无误的获取时间,而不被保护电池而睡眠CPU,就需要使用setExact()代替set()。在Android7.0之后Doze()模式下,需要调用AlarmManager的setAndAllowWhileIdle()或setExactAndAllowWhileIdle()方法让定时任务即使在Doze模式下也能正常执行。这两个方法之间的区别和set()、setExact()方法之间的区别是一样的。
多窗口模式并不会改变活动原有的生命周期,只是会将最近交互过的那个活动(即刚开启窗口的那个活动)设置为运行状态,而将多窗口模式下另一个可见的活动设置为暂停状态,这时用户又去和暂停的活动进行交互,那么该活动就编程运行状态,之前处于运行状态的活动编程暂停状态。
————————————————————————————————————————————————
作者:小徐andorid
链接:https://www.jianshu.com/p/c47ea055f604
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
打开一个MaterialTest项目首先onCreate``onStart``onResum
方法启动,然后进入多窗口模式后onPause``onStop``onDestory``onCreate``onStart``onResum``onPause
方法启动。
进入多窗口模式后活动的大小发生了比较大的变化,此时默认是会重新创建活动的.除此之外,像横竖屏切换也是会重新创建活动的.进入多窗口模式后,MaterialTest变成暂停状态.在Overview界面选中LBSTest程序,LBSTest的onCreate``onStart``onResum
方法依次得到执行说明LBSTest变成了运行状态.然后我们再操作一下MaterialTest程序,发现LBSTest的onPause
方法执行,MaterialTest的onResum
方法得到了执行,说明LBSTest变成了暂停状态,MaterialTest变成了运行状态.
了解了多窗口模式的生命周期的作用:在多窗口模式下,用户仍然可以看到处于暂停状态下的应用,那么像视频播放之类的应用此时就应该能播放视频才对,我们最好不要在活动的onPause方法中处理视频播放器的暂停逻辑,而是应该在onStop方法中去处理,并且在onStart方法中恢复视频的播放.
针对进入多窗口模式时程序会被重新创建,如果我们想改变这一行为,我们可以在AndroidManifest.xml文件中对活动(在活动标签中配置)进行配置android:configChanges="orientation|keyboardHidden|screenSize|screenLayout"
然后不管进入多窗口,还是横竖屏切换,活动都不会被重新创建,而是将屏幕发生变化的事件通知到Activity的onConfigurationChanged()方法当中.如果想在屏幕发生变化的时候进行相应的逻辑处理,那么在活动中重写onConfigurationChanged()方法即可.
在androidManifest.xml的或者标签中加入
android:resizeableActivity=[“true”|“false”]
其中true表示支持多窗口模式false表示不支持.(默认值是true即支持多窗口模式)
但存在一个问题也就是低版本这个属性对低版本不支持,这个属性只有当项目的targetSdkVersion指定成24或者更高的时候才会有用,否则这个属性是无效的.针对这种情况Android提供了一种解决方案:如果项目指定的targetSDKVersion低于24,并且活动是不允许横竖屏切换的,那么应用也就不支持多窗口模式.
想让应用不允许横竖屏切换,需要在AndroidManifest.xml文件中的标签中加入如下配置:
android:screenOrientation=[“portrait”|“landscape”]
portrait表示活动只支持竖屏,landscape表示活动只支持横屏
有兴趣可以了解,能看明白就行,其实目前来看使用的还是比较少的。