欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android开发实战总结(开发中知识点要点)

程序员文章站 2022-04-01 10:52:59
全局获取Context不难看出Context一共有三种类型,分别是Application、Activity和Service。这三个类虽然分别各种承担着不同的作用,但它们都属于Context的一种,而它们具体Context的功能则是由ContextImpl类去实现的。由于Context的具体能力是由ContextImpl类去实现的,因此在绝大多数场景下,Activity、Service和Application这三种类型的Context都是可以通用的。不过有几种场景比较特殊,比如启动Activity,还有...

全局获取Context

Android开发实战总结(开发中知识点要点)

不难看出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中注册

 <application
        android:name="com.firstcode.advanceproject.MyApplication" 

之后在任何地方需要使用Context,只需要使用MyApplication.getContext();即可获得。

注意:因为一个程序只能有一个Application,所以这种全局获得Context的方式和之前使用的Litepal发生了冲突。

Android开发实战总结(开发中知识点要点)

如何处理呢?
只需要添加下面这句话即可:LitePal.initialize(mContext);

 @Override
    public void onCreate() {
        super.onCreate();
        mContext = getApplicationContext();
        LitePal.initialize(mContext);
    } 

使用Intent传递对象

方法一:Serializable

首先需要让对象实现Serializable接口。
然后使用intent.putExtra()进行发送。
最后使用 (强制转化)getIntent().getSerializableExtra(“xxx”);的方式进行接收。

方法二:Parcelable

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<Person> CREATOR = new Creator<Person>() {
        @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表示活动只支持横屏

Lambda表达式

有兴趣可以了解,能看明白就行,其实目前来看使用的还是比较少的。

本文地址:https://blog.csdn.net/SafeVidulInfo/article/details/107756768

相关标签: Android