Android学习--还有一些小技巧
这些小技巧
通过上面的这些文章,就把简单的安卓项目总结了一遍,当然你说懂这些就可以做Android开发的话还是不行的,欠缺的还有很多,但欠缺的这些我们有只能在工作中去总结以及不断的提高,这篇文章我们还有一些小技巧需要我们总结一下,然后在后面准备做一个完整的实验项目,让我们把学的这些串联起来,这篇我们将说说下面这些技巧:
一、获取全局Context
二、使用Intent传递对象
1、Serializable方式
2、Parcelable方式
三、日志控制
四、创建定时任务
五、聊聊Doze模式
六、多窗口
七、禁止多窗口模式
八、lambda表达式 这个表达式是JAVA 8 的新特性,我们直接在后面完整的Demo中使用,用到的时候再具体的说明
获取全局Context
这里我们考虑这样一个问题,我们再一个类中进行了一些异步操作,完了之后我们需要一个Toast提示,这时候我们需要Context,那我们有那么获取Context。
首先就有这样一种,我们直接在初始化这个类的时候传递一个Context,的确这样是能解决问题的,但这不是最好的解决问题的办法,最好的办法是我们获取一个全局的Context,下面我们总结如何获取一个全局的Context。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | package com.example.skotc.servicedemo; import android.app.Application; import android.content.Context; /** * Created by skotc on 2018/8/20. */ public class MyApplication extends Application { private static Context context; @Override public void onCreate() { super .onCreate(); context = getApplicationContext(); } public static Context getContext() { return context; } } |
上面的代码我们就创建了一个MyApplication继承自Application,然后再以后的使用中我们就可以直接调用这个方法得到全局的Context, MyApplication.getContext()方法获取得到Context。
还有一点需要我们注意一下的,就是在创建了MyApplication之后我们还是需要在AndroidManifest.xml中声明一下。具体的代码如下,主要的就是这句: android:name=".MyApplication"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <application android:allowBackup= "true" android:icon= "@mipmap/ic_launcher" android:label= "@string/app_name" android:supportsRtl= "true" android:theme= "@style/AppTheme" android:name= ".MyApplication" > <activity android:name= ".ServiceMainActivity" android:label= "@string/app_name" android:theme= "@style/AppTheme.NoActionBar" > <intent-filter> <action android:name= "android.intent.action.MAIN" /> <category android:name= "android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> |
使用Intent传递对象
Intent相信我们都比较熟悉了,我们可以使它来启动活动,发送广播,启动广播等,在进行上述操作的时候,我们还可以在Intent中添加一些附加数据,已达到传值的效果,比如我们见过的调用 putExtra(键,值)方法来添加要传递的数据,之后通过调用 getIntent().getStringExtra(键)来获取我们传递的值,通过这种方法我们能传递的对象类型是有限的,也就常见的类型,那我们有没有想过,要是需要专递的是一个自定义的对象的时候呢,我们该怎样做?
下面我们就讨论一下这个问题:
1、Serializable方式 (序列化)
Serializable是序列化的意思,表示将一个对象转换成可存储或者可传输的状态,序列化后的对象可以在网络上进行传输,也可以存储在本地,至于序列化的方法也是很简单,只需要让一个类去实现Serializable接口就可以。
比如我们实现了一个person类,让它实现Serializable接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | class person implements Serializable{ private String name; private int age; public void setName(String name) { this .name = name; } public void setAge( int age) { this .age = age; } public String getName() { return name; } public int getAge() { return age; } } |
接下来我们看看这个自定义对象的传递以及获取:
1 2 3 4 5 6 7 8 9 10 | person zhangxu = new person(); zhangxu.setAge( 18 ); zhangxu.setName( "tiancia" ); //传递 Intent intent = new Intent(ServiceMainActivity. this ,SecondActivity. class ); intent.putExtra( "person" , zhangxu); startActivity(intent); // 获取 person zhangxu2 = (person) getIntent().getSerializableExtra( "person" ); |
一句话总结:我们之所以能将我们自定义的类在Intent中传递就是因为我们自定义为类实现了 Serializable 接口。
Parcelable
Parcelable方式的实现原理是将一个完整的对象进行分解,而分解后的每一部分都将是 Intent 所支持的数据类型,这样也就实现传递对象的功能。
接下来我们修改我们的额person类,修改这个类的注意事项我们在代码中都有加注释
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | /* * * 实现Parcelable接口 * 就要重写里面的两个方法 * describeContents * writeToParcel * * */ class person implements Parcelable{ private String name; private int age; public void setName(String name) { this .name = name; } public void setAge( int age) { this .age = age; } public String getName() { return name; } public int getAge() { return age; } @Override public int describeContents() { return 0 ; } // 把数据写入到parcel中 @Override public void writeToParcel(Parcel parcel, int i) { parcel.writeString(name); parcel.writeInt(age); } // 我们还必须在person类中创建一个CREATOR常量,这里创建了一个Parcelable.Creator接口的实现 // 并且将泛型类型指定为person,接着重写里面的两个方法 // createFromParcel 这个方法中读取刚才存入的字段 // newArray public static final Parcelable.Creator<person>CREATOR = new Parcelable.Creator<person>(){ @Override public person createFromParcel(Parcel parcel) { person person = new person(); // 读取数据 person.name = parcel.readString(); person.age = parcel.readInt(); return person; } @Override public person[] newArray( int i) { return new person[i]; } }; } |
说说它的传递方式,传入时候和我们之前写的Serializable是一样的,就不在重复,只是在读取的时候,有一点需要我们注意一下,就是方法名改变了:
person zhangxu3 = (person) getIntent().getParcelableExtra("person");
它们俩的区别:
serializable的方式比较简单,但由于会把整个对象进行序列化,因此效率会比Parcelable低一些,所以在通常情况下我们还是建议使用Parcelable方式!
日志控制
在iOS中我们经常有用到这个日志控制的问题,在安卓中也是,就是在debug阶段我们需要大量的日志,但是在release状态我们是不需要的,日志不仅仅会增加程序运行的成本,还会泄漏一些重要的信息,所以在编译release状态我们是需要控制日志打印的,在安卓中我们可以写这样的一个类来进行处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | class LogUntil{ 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 NOTHING = 6 ; public static final int leven = VERBOSE; public static void v(String tag,String msg){ if (leven<=VERBOSE){ Log.d(tag,msg); } } public static void d(String tag,String msg){ if (leven<=DEBUG){ Log.d(tag,msg); } } public static void i(String tag,String msg){ if (leven<=INFO){ Log.d(tag,msg); } } public static void w(String tag,String msg){ if (leven<=WARN){ Log.d(tag,msg); } } public static void e(String tag,String msg){ if (leven<=ERROR){ Log.d(tag,msg); } } } |
上面的这段代码就是我们常用的日志控制,在我们要发布的时候,我们设置leven的值为NOTHING的时候我们的日志也就不见了!和我们iOS的理解方式是一样的,我们iOS中会用到DEBUG这个变量,具体的我也就不再多说了,有兴趣的可以自己找找这方面的问题,我们直说安卓的。
创建定时任务
在Android中,实现定时器的任务是有两种方式的,一种是使用Java API 提供的Timer类,一种是使用Android的Alarm机制,这令中方式在大多数情况下都能实现类似的效果,但是Timer有一个致命的短板,它并不适用于那些长期在后台运行的定时器任务,我们都知道为了能让电池更加耐用,每一种手机都会有自己的休眠策略,Android手机在长时间不操作的情况下会让CPU处于睡眠状态,就会导致Timer中的定时器任务无法正常运行,而Alarm则具有唤醒CPU的功能,它保证在大多数情况下需要执行任务的时候CPU都能正常运行。这里需要注意唤醒CPU和唤醒屏幕完全不是同一个概念!不要混淆。
下面我们用代码写一个Alarm的实际例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | class LongRunningService extends Service{ @Nullable @Override public IBinder onBind(Intent intent) { return null ; } @Override public int onStartCommand(Intent intent, int flags, int startId) { new Thread( new Runnable() { @Override public void run() { } }).start(); // 获取AlarmManager对象 AlarmManager manager = (AlarmManager)getSystemService(ALARM_SERVICE); // 一个小时的毫秒数 int anHour = 60 * 60 * 1000 ; // SystemClock.elapsedRealtime方法表示系统开机至今经历的毫秒数 // SystemClock.currentTimeMillis方法表示获取1970年1月1日零时至今所经历的毫秒数 long triggerAtTime = SystemClock.elapsedRealtime()+anHour; // 指定处理定时任务的服务为LongRunningService 最后在调用set方法 Intent i = new Intent( this ,LongRunningService. class ); PendingIntent pendingIntent = PendingIntent.getService( this , 0 ,i, 0 ); //AlarmManager.ELAPSED_REALTIME_WAKEUP 表示让定时任务的触发时间从系统开机算起,但是会唤醒CPU //AlarmManager.ELAPSED_REALTIME 表示让定时任务的触发时间从系统开机算起,但是不会唤醒CPU //AlarmManager.RTC 表示让定时任务的触发时间从1970,1,1算起,但是不会唤醒CPU //AlarmManager.RTC_WAKEUP 表示让定时任务的触发时间从1970,1,1算起,但是会唤醒CPU //triggerAtTime 时间 manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,triggerAtTime,pendingIntent); return super .onStartCommand(intent, flags, startId); } } |
聊聊Doze模式
我们说说这个Doze模式,说说到底什么是Doze模式。当用户的设备是6.0或者以上系统的时候,如果该设备没有接电源,且并木关闭了一段时间之后,就会进入Doze模式。在Doze模式下,系统会对CPU,网络,Alarm等活动进行限制,从而延长电池的使用寿命。当然系统也不会一直处于Doze模式,而是间接性的退出Doze模式一小段时间,而在这一下欧丹时间中,应用就可以完成他们的同步操作,Alarm任务等等,
接下来看看在Doze模式下那些功能会受到影响:
1、网络访问被限制
2、系统忽略唤醒CPU或者屏幕操作
3、系统不再执行WIFI扫描
4、系统不再执行同步服务
5、Alarm任务将会在下次退出Doze模式的时候执行
多窗口
Android在7.0之后导入了多窗口模式,在这里我们可以大概的学习一下多窗口模式。
在这里我们说一下,在多窗口模式下并不会改变活动原有的生命周期,只是会将用户最近交互过的那个活动设置为运行状态,而将多窗口模式下另外一个可见的活动设置为暂停状态,如果这时候用户又和暂停的活动进行交互,那么该活动就会进入运行状态,之前处于运行状态的活动变成暂停状态。
前面我们说到在多窗口模式下,活动的生命周期是不会发生改变的,那么有一些问题我们就可以随之考虑一下:
比如说,在多窗口模式下,用户任然处于可以看到暂停状态的应用,那么像视频播放之类的应用在此时就应该是继续播放视频才对,因此,我们最好不要在活动的onPause方法中处理视频播放器的暂停逻辑,而是应该在onStop()方法中处理,并且在onStart方法中回复视频的播放。
另外,针对进入多窗口模式时候,活动会被重新创建,如果你想改变这一默认行为,可以在 Androidmainfest.xml中进行如下配置:
1 2 3 4 | <activity android:name= ".SecondActivity" android:configChanges= "orientation|keyboardHidden|screenSize|screenLayout" ></activity> |
加入这个配置之后,不管是进入多窗口模式,还是横竖屏切换,活动都不会被重新创建,而是会将屏幕发生变化的事件通知到Activity的onConfigurationChanged()方法中,所以你要是想在屏幕发生改变的时候进行相应的逻辑处理,那么在活动中重写onConfigurationChanged()方法即可。
禁止多窗口模式
上面我们说了一些关于多窗口模式的一些问题,现在我们再想一个场景,如果我们做的是游戏,要是进入了多窗口模式是不是很尴尬,总不是一边发微信一遍玩游戏的吧,看着自己GG,当然我们也有办法避免应用进入多窗口模式,禁止的方式也很简单:
Androidmainfest.xml 中这样配置:
android:resizeableActivity="false" true表示支持,false表示禁止
这样就OK了吗?其实还有一个问题需要我们考虑一下这个问题,这个属性是在我们指定 targetSdkVersion 大于等于24的时候才有效的,那小于24呢?没有这个属性我们怎么处理呢?我们再这里说一种解决方案:
Android规定,如果项目指定的targetSdkVersion低于24,并且活动是不允许横竖屏切换的,那么该应用也将不支持多窗口模式。
默认情况下,我们的应用是支持横竖屏切换的,如果想想要让应用不允许横竖屏切换,那么就需要在 Androidmainfest.xml的<activity>标签中加如下配置:
其中 portrait 表示竖屏 landscape 表示横屏
1 | android:screenOrientation= "portrait" |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话