Android中一些高级技巧

1.全局获取Context的技巧
我们学过的内容中很多地方都要Context,弹出Toast 的时候需要,启动活动的时候需要,发送广播的时候需要,操作数据库的时候需要,使用通知的时候需要。等等
我们的很多操作都是在活动中进行的,而活动本身就是一个Context对象。但是,当应用程序的架构逐渐开始复杂起来的时候,很多的逻辑代码都将脱离Activity类,但此时恰恰需要Context,这时会很伤脑筋。
Android提供了一个Application类,每当应用程序启动的时候,系统就会自动将这个类进行初始化。而我们可以定制一个自己的Application类,以便管理程序内一些全局的状态信息,比如全局Context。
public class MyApplication extends Application{
private static Context context;
public void onCreate(){
context=getApplicationContext();
}
public static Context getContext(){
return context;
}
}
这里我们重写了父类的onCreate()方法,并通过调用getAppliactionContext()方法得到一个应用程序级别的Context,然后又提供了一个静态的getContext()方法,在这里将刚才获取到的context进行返回。接下来我们需要告诉系统,当程序启动的时候应该初始化MyAppliacation类,而不是默认的Application类。在AndroidManifest.xml文件的< application>标签下进行指定就可以了,
< application
android:name=“com.example.networktest.MyApplication”

< /application>
注意:这里指定的时候一定加上完整的包名,不然系统将无法找到这个类
这样便实现了一种全局获取Context的机制,之后不管想在项目的任何地方使用Context,只需调用一下MyApplication.getContext()就可以了。
回顾LitePal想要正常工作,要求必须在AndroidManifest.xml中配置如下内容
< applcation
android:name=“org.litepal.LitePalAppliction”>< /application>
这样配置LitePal就能在内部自动获取到Context了。
那么我们已经配置过自己的Application了岂不是要和LitePalApplication冲突了?任何项目只能配置一个Application,对于这种情况,litePal提供了很简单的解决方案,那就是在我们自己的Application中去调用litePal的初始化方法就可以了
public class MyApplication extends Application{
private static Context context;
public void onCreate(){
context=getApplicationContext();
LitePal.initialize(context);
//将全局的Context对象通过参数传递给了LitePal,效果和在AndroidManifest.xml中配置LitePalApplication是一模一样的。

}
public static Context getContext(){
return context;
}
}

2.使用Intent传递对象
(1).Serializable方式
Serializable是序列化的意思,表示将一个对象转换成可以传输的状态。序列化后的对象可以在网络上进行传输,也可以存储到本地。方法如下:
public class Persion implements Serializable{
private String name;
private int age;
public String getName(){
return name;
}
public void setName(String name){
this.name=name;
}
public int getAge(){
return age;
}
public void setAge(int age){
this.age=age;
}
}
其中set,get方法都是用于赋值和读取字段数据的,最重要的部分在第一行。这里的Person类取实现了Serializable接口,这样所有的Person对象就都是可序列化的了
接下来在FristActivity中的写法非常简单
Person person=new Person();
person.setName(“Tom”);
person.setAge(20);
Intent intent=new Intent(FristActivity.this,SecondActivity.class);
intent.putExtra(“person_data”,person);
startActivity(intent);
这里创建了一个Person的实例,然后就直接将他传入到putExtra()方法中。由于Person类实现了Serializable接口,所以才这样写。
接下来在SecondActivity中获取这个对象,写法如下:
Person person =(Person) getIntent().getSerializableExtra(“person_data”);
这里调用了getSerializableExtra()方法来获取通过参数传递过来的序列化对象,接着再将它向下转型成Person对象,这样就成功实现了使用Intent来传递对象的功能了。
(2)Parcelable方式
使用parcelable也可以实现相同的效果,不过不同于将对象进行序列化,Parcelable方式的实现原理是将一个完整的对象进行分解,而分解后的每一部分都是Intent所支持的数据类型,这样也就实现传递对象的功能了。
代码如下:
public class Person implement Parcelable{
private String name;
private int age;
public String getName(){
return name;
}
public void setName(String name){
this.name=name;
}
public int getAge(){
return age;
}
public void setAge(int age){
this.age=age;
}
public int describeContents(){
return 0;
}
public void writeToParcel(Parcel dest,int flags){
dest.writeString(name);//写出名字
dest.writeInt(age);//写出年龄
}
public static final Parcelable.Creator CREATOR=new Parcelable.Creator(){
public Person createFromParcel(Parcel source){
Person person=new person();
person,name=source.readString();//读取名字
person.age=source.reatInt();读取年龄
return person;
}
public Person[] newArray(int size){
return new Person[size];
}
};
}
Parcelable的实现方式稍微复杂一些;让Person类去实现了Parcelable接口,这样就必须重写describeContents()和WriteToParcel()这两个方法。其中describeContents()方法直接返回0就可以了,而writeToParcel()方法中我们需要调用Parcel的writeXxx()方法,将Person类中的字段一一写出。注意,字符串型数据就调用writeString()方法,整型数据就调用writeInt()方法,依次类推。
我们必须在Person类中提供一个名为CREATOR的常量,创建了一个Parcelable.Creator接口的一个实现,并将泛型指定为Person。接着需要重写createFromParcel()和newArray()这两个方法,在createFromParcel()方法中我们要去读取刚才写出的name和age字段,并创建一个Person对象进行返回,其中name,age都是调用Parcel的readXxx()方法读取到的。这里读取的顺序一定要和刚才写出的顺序完全相同。而newArray()方法中的实现就简单多了,只需要new出一个Person数组,并使用方法中传入的size作为数组大小就可以了。
接下来在FristActivity中我们仍然可以使用相同的代码来传递Person对象,只不过在SecondActivity中获取对象的时候需要稍加改动,如下:
Person person=(Person)getIntent().getParcelableExtra(“person_data”);
3.定制自己的日志工具
当我们编写一个庞大对项目的时候,我们期间进行测试的时候会打印一些日志。而当我们的项目完成后这些日志还是存在,这样不仅会降低程序的运行效率,还有可能将一些机密性的数据泄露出去。
一行一行的去删除所以日志是不现实的,不仅费时费力,而且以后继续维护的时候可能还会需要这些日志。因此最理想的情况是能够自由控制日志的打印,当程序处于开发阶段让日志打印出来,当程序上线之后就把日志屏蔽掉。
public class LogUtil {
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 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);
}
}
}
通过以上这种方法,日志工具创建好了,之后在项目中我们可以像使用普通的日志工具一样使用LogUtil,
比如打印一行DEBUG级别的日志可以写:
LogUtil.d(“TAG”,“debug log”)
然后修改level的值就可以自由的控制日志的打印行为了。
5.创建定时任务
Android中的定时任务有两种:1.使用Java API里提供的Timer类,2.使用Android的Alarm机制
实现类似效果,但Timer不太适合用于那些需要长期后台运行的定时任务。
(1)Alarm机制
用法:主要借助AlarmManager类实现的。这个类和NotificationManager有点类似,都是通过调用Context的getSystemService()方法来获取实例的,只是需要传入的参数是Context.ALARM_SERVICE.获取一个AlarmManager的实例可以写成:
AlarmManager manager=(AlarmManager) getSystemService(Context.AlARM_SERVICE);
接下来调用AlarmManager的set()方法就可以设置一个定时任务了,如想要设置一个10秒钟后执行
long triggerAtTime=SystemClock.elapsedRealtime()+101000;
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,triggerAtTime,pendingIntent);
set()方法中需要传递3个参数
第一个参数是一个整型参数,用于指定AlarmManager的工作类型有4种值可选
1.ELAPSED_REALTIME表示让定时任务的触发时间从系统开机开始算起,但不会唤醒CPU
2.ELAPSED_REALTIME_WAKEUP表示让定时任务的触发时间从系统开机开始算起,但会唤醒CPU
3.RTC让定时任务的触发时间从1970年1月1日0点开始算起,但不会唤醒CPU
4.RTC_WAKEUP让定时任务的触发时间从1970年1月1日0点开始算起,但会唤醒CPU
使用SystemClock.elapsedRealtime()方法可以获取到系统开机至今所经历时间的毫秒数
使用System.currentTimeMillis()方法可以获取发哦1970年1月1日0点至今所经历时间的毫秒数
第二个参数就是定时任务触发的时间,以毫秒为单位
第三个参数是一个PendIntent,
设置一个任务在10秒钟后执行也可以写出:
long triggerAtTime=System.currentTimeMillis()+10
1000;
manager.set(AlarmManager.RTC_WAKEUP,triggerAtTime,pendingIntent);
实现一个长时间在后台定时运行的服务
public 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 manager=(AlarmManager) getSystemService(ALARM_SERVICE);
    int anHour =60*60*1000;//一小时的毫秒数
    long triggerAtTime= SystemClock.elapsedRealtime()+anHour;
    Intent i=new Intent(this,LongRunningService.class);
    PendingIntent pi =PendingIntent.getService(this,0,i,0);
    manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,triggerAtTime,pi);
    return super.onStartCommand(intent,flags,startId);
}

(2)Doze模式
当用户的设备是Android6.0或者以上系统是,若该设备为插接电源,处于静止状态,且屏幕关闭了一段时间之后,就会进入Doze模式,在该模式下,系统会对CPU,网络,ALarm等活动进行限制,从而延长电池的使用使用寿命
当然系统并不会一直处于Doze模式,而是会间歇性地退出Doze模式一小段时间,这段时间里,应用就可以去完成他们的同步操作,Alarm任务。
Doze模式下哪些功能会受到限制
1.网络访问被禁止
2.系统忽略唤醒CPU或者屏幕操作
3.系统不再执行WIFI扫描
4.系统不再执行同步服务,
5.Alarm任务将会在下次退出Doze模式的时候执行。

6.进入多窗口模式
两种方式进入多窗口模式
1.在Overview列表界面长按任意一个活动的标题,将该活动拖动到屏幕突出的区域,则可以进入多窗口模式
2.打开任意一个程序,长按Overview按钮,也可以进入多窗口模式。
禁用多窗口模式
在AndroidManifest.xml的< application>或< activity>标签种加入如下属性即可
android:resizeableActivity=[“true”|“false”]
true表示支持,false表示不支持,若不配置默认true
< application
android:resizeableActivity=“false”>
< /application>
默认情况下我们的应用可以随着手机的旋转自由的横竖屏切换,不允许切换,修改AndroidManifest.xml的< activity>标签加入如下配置:
android:screenOrientation=[“portrait”|“landscape”]
前者表示支持竖屏,后者表示支持横屏。
7.Lambda表达式:
本质上是一种匿名方法,它既没有方法名,也即没有修饰符和返回值类型,使用他来编写diamagnetic将会更加简洁,易读
首先在app/build.gradle中添加如下配置:

android{

defaultConfig{

jackOptions.enabled=true
}
compileOptions{
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}

}

发布了25 篇原创文章 · 获赞 1 · 访问量 697

猜你喜欢

转载自blog.csdn.net/weixin_43551957/article/details/104185082