Android Review—5

零蚀

  • 全局context
public class MyApplication extends Application {

    private static Context context;

    @Override
    public void onCreate() {
        super.onCreate();
        context = getApplicationContext();
    }

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

主要用于Intent传输数据上面,表示将一个对象转换成可存储或可传输状态,序列化后可以在网络上进行传输,也可以储存在本地。

//数据类的书写
public class SerializableDemoBean implements Serializable {
    
    private int a;

    public int getA() {
        return a;
    }

    public void setA(int a) {
        this.a = a;
    }
}

//传输
SerializableDemoBean bean=new SerializableDemoBean();
bean.setA(2);
Intent intent=new Intent(this,Main2Activity.class);
intent.putExtra("data",bean);
startActivity(intent);
//接收
SerializableDemoBean data = (SerializableDemoBean) getIntent().getSerializableExtra("data");
  • Parcelable
    功能和Serializable一致,都是Intent 的多支持的类型。实现Parcelable接口,需要实现describeContents()和 writeToParcel(Parcel dest, int flags)方法,describeContents()可直接返回0,而另一个方法需要调用writeString/writeInt/…方法将字段写出。其他地方和Serializable一致。
public class ParcelableDemoBean implements Parcelable {


    private int a;

    public static final Creator<ParcelableDemoBean> CREATOR = new Creator<ParcelableDemoBean>() {
        @Override
        public ParcelableDemoBean createFromParcel(Parcel in) {
            ParcelableDemoBean bean=new ParcelableDemoBean();
            bean.a=in.readInt();
            return bean;
        }

        @Override
        public ParcelableDemoBean[] newArray(int size) {
            return new ParcelableDemoBean[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(a);
    }
}
  • Alarm
    功能是唤醒CPU解除CPU的睡眠状态(非屏显),例如设置一个一分钟后的唤醒任务
PendingIntent pendingIntent=PendingIntent.getService(this,0,new Intent(),0);
//定在1分钟后
long time= SystemClock.elapsedRealtime()+60*1000;
AlarmManager manager= (AlarmManager) getSystemSertvice(ALARM_SERVICE);   manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,time,pendingIntent);

一般定义在Sevice里面作为定时任务。

manager.set()的第一个参数是AlarmManager的工作类型,有4个参数可选:

  1. ELAPSED_REALTIME_WAKEUP 让定时任务从系统的开机至今经历的毫秒数开始,会唤醒CPU
  2. ELAPSED_REALTIME 让定时任务从系统的开机至今经历的毫秒数开始算,不会唤醒CPU
  3. RTC 触发事件从1970-1-1 0:0时刻开始算,不会唤醒CPU
  4. RTC_WAKEUP 触发事件从1970-1-1 0:0时刻开始算,会唤醒CPU
System.currentTimeMillis(); //会获取从1970-1-1 0:0到现在的毫秒数
  • Doze模式

android 6.0开始使用,用于延长电池寿命,如果该设备未接电源,处于静止(7.0删除了这一条)且屏幕关闭了一段时间后,就会进入Doze模式。在此模式下,会对CPU,Alram,网络etc.活动进行一些限制,从而延长电池的使用寿命。但是系统不会一直处于这种状态,会间歇性的退出这种状态,在这段时间内,可以完成需要的任务。

//以下两个方法便可以在Doze模式下正常执行,但是版本要求都是23.
AlarmManager.setAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME,time,pendingIntent);
AlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME,time,pendingIntent);
  • 窗口开发

多窗口开发:打开一个应用,长按🔲(overview)选择另一个应用,关于屏幕旋转方法如下:

//屏幕方向锁定
android:screenOrientation="portrait"
//1. portrait 只支持竖屏 <肖像>
//2.landscape 只支持横屏 <景观>
/**
*  获取屏幕的旋转信息
*/
android:configChanges="orientation|screenLayout|screenSize">
//activity 方法中获取对应信息
@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
}

禁止使用多窗口模式,当然这个方法不是都适用,只有在24及以上的版本才可以用,否则这个属性是无效的。

//在<application>或者是在<activity>中添加一下的属性
android:resizeableActivity="false"
//长按overview会提示不支持多窗口模式
  • Lambda

java8的新特性,Lambda表达式本质上是一种匿名的方法,Lambda最低兼容到2.3系统,基本上覆盖了所有的安卓手机了。首先需要在app/build.gradle中添加如下的代码:

android {
    compileSdkVersion XX
    defaultConfig {
        ........
    }
    // 添加以下的代码
    compileOptions{
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    
    .........
}

在Java 8里面,Lambda的类型是一个接口,而Lambda表达式本身,也就是实现方式的简化,比如匿名内部类。简而言之就是,Lambda表达式本身就是一个接口的实现。

案例一:

//Lambda简写模式(匿名内部类)
new Thread(()->{
    //TODO:new Runnable(){线程内容填写}
}).start();

//按钮的lambda的点击事件
((CoordinatorLayout)findViewById(R.id.layout)).setOnClickListener((v)->{
    //实现点击事件
});
//按钮的()也是可以去掉的,直接参数指向实现函数
((CoordinatorLayout)findViewById(R.id.layout)).setOnClickListener(v->{
    //实现点击事件
});

    
//但是
new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
};
//是无法进行Lambda的,会提示lambda转换必须是接口(Target type of lambda conversion must be an interface)

所以综上所诉,我们还可以这么写:

案例二:

// Lambda简化版Runnable接口实现
Runnable runnableLambda=()->{
    //run方法的编写
};

案例三:

//我们也可以自己实现一个接口
public interface LambdaDemoInterface{
    void display(Binder binder);
}

    //接下来就是关于Lambda的实现,以下方式都是可行的。
LambdaDemoInterface inter1=(binder -> {});
LambdaDemoInterface inter2=(binder) -> {};
LambdaDemoInterface inter3=binder -> {};

天气APP开发

  • 功能分析
  1. 可以罗列出所有的省,市,县
  2. 可以查看任意的城市天气信息
  3. 可以自由地切换城市,去查看其他的城市天气
  4. 提供后台自动更新功能

天气的免费api接口现在已然不多了,大部分都已经开始收费模式,很多书上资料上的链接都已经找不到了,中国天气网也没有api接口提供测试。所以在免费中找了一个与民同乐的平台,部分可以免费使用。

 

  • 案例
    案例功能也很简单,首先下载SDK,将jar包放入lib,然后 add as library。然后就可以按照开发文档进行开发了。
//初始化
HeConfig.init(“Username/ID“,"key")
//默认是收费版,需要切换成免费版
HeConfig.switchToFreeServerNode();
//数据请求
HeWeather.getWeatherNow(MainActivity.this, "CN101010100", Lang.CHINESE_SIMPLIFIED , Unit.METRIC , new HeWeather.OnResultWeatherNowBeanListener() {
        @Override
        public void onError(Throwable e) {
            Log.e(TAG, "Weather Now onError: ", e);
        }

        @Override
        public void onSuccess(Now dataObject) {
            Log.e(TAG, " Weather Now onSuccess: " + new Gson().toJson(dataObject));
            //先判断返回的status是否正确,当status正确时获取数据,若status不正确,可查看status对应的Code值找到原因
            if ( Code.OK.getCode().equalsIgnoreCase(dataObject.getStatus()) ){
                //此时返回数据
                NowBase now = dataObject.getNow();
                Log.e(TAG, "onSuccess: "+now.toString() );
            } else {
                //在此查看返回数据失败的原因
                String status = dataObject.getStatus();
                Code code = Code.toEnum(status);
                Log.e(TAG, "failed code: " + code);
            }
        }
});

抄完文档基本就能免费获取到天气信息

{"now":{"cloud":"91","cond_code":"101","cond_txt":"多云","fl":"-3","hum":"65","pcpn":"0.0","pres":"1025","tmp":"0","vis":"16","wind_deg":"82","wind_dir":"东风","wind_sc":"2","wind_spd":"6"},"basic":{"admin_area":"北京","cid":"CN101010100","cnty":"中国","lat":"39.90498734","location":"北京","lon":"116.4052887","parent_city":"北京","tz":"+8.00"},"status":"ok","update":{"loc":"2019-12-XX 14:18","utc":"2019-12-XX 06:18"}}

  1. 将路径切换到本地工程项目Application目录,然后git clone进行下载,这样下载的project就在我们本地的project中
  2. 将下载的project内容全部移动到上层/本地的project中,注意的是这里有四个文件:证书,ReadMe,.gitignore,.git。后两个文件都是隐藏的,不能漏了,显示隐藏文件快捷键(command+shift+.)
  3. .gitignore 下载的文件基本包含本地文件的内容。
  4. git status 状况查对好,commit后进行git push origin master

详细拓展见 Android Git 专题


应用打包

  • 生成正式的签名APK
    为什么要签名,首先我们运行在手机上的其实也是有默认签名的,就是debug签名,Android Studio使用了一个默认的keystore文件帮我们自动生成签名,点击Android Studio 右侧的工具栏,我们会看到Gradle–>项目–>:app–>Task–>android,双击signingReport。logcat显示已经生成了debug.keystore。也就是Android studio是通过这个文件进行签名,单这只是适用于开发阶段,并不适用发布的正式签名APK文件。
  • 使用Android Studio进行签名(极懒的我一般不轻易贴图)
  1. 点击导航栏的Build–>Generate Signed Bundle/APK(生成已签字的APK)
  2. 选择APK -->Create new
  3. key store path:是对应到你所定义的kestore名字xxx.jks,会自动生成,validity(有效–year),填好空就OK了。
  • 使用Gradle生成签名的APK文件(生成路径app/build/outputs/apk)
android {
    compileSdkVersion 28
        defaultConfig {
                .....
        }
    //创建的生成签名的必须信息
    signingConfigs{
        config{
            storeFile file('../weatherkey/weatherboykey.jks')
            storePassword '123456'
            keyAlias 'weatherboykey'
            keyPassword '123456'
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            //调用config信息
            signingConfig signingConfigs.config
        }
    }

}

⚠️Groovy语法的调用闭包内容,类似于python中的函数调用,不可以把调用的闭包写在调用的方法后面,不然显示找不到。如案例中signingConfigs ,不能再buildTypes之后,因为其中release{signingConfig signingConfigs.config}调用了其中内容。
⚠️在填写file的路径时,需要在copy下来的相对路径前添加…/来表示是当前的项目下(mac)。

接下来打包,点击右侧工具栏Gradle–>项目名–>:app–>other–>assembleRelease(组装正式包) 有的
说自己的Android studio不是在other文件夹下,而是在build文件下,自己找找assembleRelease,除此之外还可以打debug包,同理assmebleDebug。

上面代码还可以继续优化,因为所有需要的数据还都是明文显示,这样不太安全,Android推荐的做法是将这类敏感的数据配置在一个独立的文件里面。然后再有build.gradle去读取这些数据,这样我们可以在gradle.properties中添加以下内容:

# 在gradle.propertise中填写的内容
KEY_PATH=../weatherkey/weatherboykey.jks
KEY_PASS=123456
ALIAS_NAME=weatherboykey
ALIAS_PASS=123456

在buidl.gradle中调用

signingConfigs{
    config{
        storeFile file(KEY_PATH)
        storePassword KEY_PASS
        keyAlias ALIAS_NAME
        keyPassword ALIAS_PASS
    }
}

发布应用

  • 多渠道
    多渠道的由来是因为目前Android领域的应用商店很多,不像苹果一个App store。但是如果我们需要针对不同平台有不同的需求的话,就很麻烦,例如:崩坏三哔站和官服的应用图标就是不一样的,不会每次上传都要跟换图标,再重新打包,真的极麻烦了。gradle设置如下:
productFlavors{
    baidu{
        //多维度打包或者叫版本差异化打包
        flavorDimensions "baidu"
        applicationId 'com.example.weatherboy.baidu'
    }
    q360{
        flavorDimensions "q360"
        applicationId 'com.example.weatherboy.q360'
    }

}

flavorDimensions‘’用来创建纬度,没有回报错,这里的applicationId,相当于重新创建了它的包名ID,右侧的gradle工具栏就出现了assembleQ360和assembleBaiDu。现在可以打包了,但是打包内容都一致,要想每个渠道打出不同的包,需要子啊在app/src下创建我们的渠道文件夹baidu & q360,如果我们需要不同的内容,我们就创建对应的文件夹,和文件,这样会覆盖远来的文件内容(必须贴图了):

Android 怎么判断是否锁定竖屏_ide

以下是打包的操作

Android 怎么判断是否锁定竖屏_android_02

⚠️ 如果我们想覆盖远来的文件或样式,文件的名字和对象id一定要一致,比如之前应用icon是用的ic_launch,现在想换渠道图片,我们新的图片也得叫ic_launch。保持打包的一致性。

  • 发布应用到360应用平台