Android学习--还有一些小技巧

 

这些小技巧java


 

      经过上面的这些文章,就把简单的安卓项目总结了一遍,固然你说懂这些就能够作Android开发的话仍是不行的,欠缺的还有不少,但欠缺的这些咱们有只能在工做中去总结以及不断的提升,这篇文章咱们还有一些小技巧须要咱们总结一下,而后在后面准备作一个完整的实验项目,让咱们把学的这些串联起来,这篇咱们将说说下面这些技巧:android

 

      1、获取全局Context微信

      2、使用Intent传递对象网络

           一、Serializable方式app

           二、Parcelable方式异步

      3、日志控制ide

      4、建立定时任务学习

      5、聊聊Doze模式this

      6、多窗口debug

      七、禁止多窗口模式

      8、lambda表达式    这个表达式是JAVA 8 的新特性,咱们直接在后面完整的Demo中使用,用到的时候再具体的说明

 

 

获取全局Context


 

      这里咱们考虑这样一个问题,咱们再一个类中进行了一些异步操做,完了以后咱们须要一个Toast提示,这时候咱们须要Context,那咱们有那么获取Context。

      首先就有这样一种,咱们直接在初始化这个类的时候传递一个Context,的确这样是能解决问题的,但这不是最好的解决问题的办法,最好的办法是咱们获取一个全局的Context,下面咱们总结如何获取一个全局的Context。

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"

 <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(键)来获取咱们传递的值,经过这种方法咱们能传递的对象类型是有限的,也就常见的类型,那咱们有没有想过,要是须要专递的是一个自定义的对象的时候呢,咱们该怎样作?

      下面咱们就讨论一下这个问题:

      一、Serializable方式 (序列化)

      Serializable是序列化的意思,表示将一个对象转换成可存储或者可传输的状态,序列化后的对象能够在网络上进行传输,也能够存储在本地,至于序列化的方法也是很简单,只须要让一个类去实现Serializable接口就能够。

      好比咱们实现了一个person类,让它实现Serializable接口:

     

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;
    }
}

 

      接下来咱们看看这个自定义对象的传递以及获取:

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类,修改这个类的注意事项咱们在代码中都有加注释

/*
* 
* 实现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状态咱们是须要控制日志打印的,在安卓中咱们能够写这样的一个类来进行处理。

 

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的实际例子:

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模式下那些功能会受到影响:

      一、网络访问被限制

      二、系统忽略唤醒CPU或者屏幕操做

      三、系统再也不执行WIFI扫描

      四、系统再也不执行同步服务

      五、Alarm任务将会在下次退出Doze模式的时候执行 

 

多窗口


 

      Android在7.0以后导入了多窗口模式,在这里咱们能够大概的学习一下多窗口模式。

      在这里咱们说一下,在多窗口模式下并不会改变活动原有的生命周期,只是会将用户最近交互过的那个活动设置为运行状态,而将多窗口模式下另一个可见的活动设置为暂停状态,若是这时候用户又和暂停的活动进行交互,那么该活动就会进入运行状态,以前处于运行状态的活动变成暂停状态。

      前面咱们说到在多窗口模式下,活动的生命周期是不会发生改变的,那么有一些问题咱们就能够随之考虑一下:

      好比说,在多窗口模式下,用户任然处于能够看到暂停状态的应用,那么像视频播放之类的应用在此时就应该是继续播放视频才对,所以,咱们最好不要在活动的onPause方法中处理视频播放器的暂停逻辑,而是应该在onStop()方法中处理,而且在onStart方法中回复视频的播放。

      另外,针对进入多窗口模式时候,活动会被从新建立,若是你想改变这一默认行为,能够在 Androidmainfest.xml中进行以下配置:

 <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 表示横屏

android:screenOrientation="portrait"
相关文章
相关标签/搜索