小丁的屋舍

Android Studio 近期学习复盘&心得

利用暑假在“BiliBili 开放大学”来学习一下 Java 以及 Android Studio ,跟着 longway777 老师从新手的角度下学习了 Android 开发的基本内容,目前已经学到 P13 了,今天晚上就不继续学了,来复盘一下最近学的东西,我将按照 内容优先>集数顺序 由表及里的进行回顾复盘。

安装并对 AS 的基础组件、布局进行认识

p1 Android Studio下载和安装

俗话说要致富,先下载编译工具!在 Android Studio 官网 下载并安装编译工具,Android Studio 也是用于开发 Android 应用的官方 IDE,提供 Compose 设计工具、灵活的构建系统和 Android 模拟器。

刚下载这个就感觉头有点大,他的环境真的难得搭建,光是 Gradle 的配置与拉取就要花费很长的时间,每次新建一个 demo 项目就会要重新下载一次 gradle 下载倒还是小事,问题是下载的像乌龟一样,明明挂了加速器,仍然下的好慢,在网上一查说是要用阿里云的加速库就会快一些,结果到现在我都不知道配置正确没有,配置了以后仍然在 gradle 的官网上面给我拉去,真的有一种吐血的想法,好在现在已经下载过许多次了,直接复制之前项目的 .gradle 与 gradle 目录就好了 因为目前我使用的 gradle 版本都是 8.0 的保证环境的一定性是学习的前提!

p2 Hello World! 创建第一个项目

Hello World! 是编程爱好者新接触一个语言比接触的一个 Demo AS的 Hello World!

注意 Hardcoded 警告的处理

需要以界面的形式存在,在命名的时候就需要注意[code] Hardcoded string [/code]问题,我们要用@string 的方式将静态资源存放到 resource 里面就不会报提示三角形了

我们可以将这些常量信息保存到 values 里面,values 文件夹也分为 string.xml color.xml theme.xml 默认是这三个,就可以将数据在里面定义 调用再使用 [code]@string/id[/code] 取出来!

学习使用 ConstraintLayout 组件

在之前使用 iApp 编程的时候,我无不赞叹线性布局是个好东西,相对布局有时候也有妙用,但是在 Android Studio 里面 ConstraintLayout 瞬间吸引到我了他可以创建百分比布局,可以解决不同尺寸的设备界面显示问题

说到 ConstraintLayout 布局就不能不提到他的 guideline 组件了 完美的辅助线来对控件进行排布,而不会是单纯限于线性布局的 dp 大法 十分的银杏😁

附录 各种语言的 Hello World!

1. 编程语言之首——Java

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}

2. 嵌入式领域的王者——C语言

#include<stdio.h>
    int main(void) {
        printf("Hello World!\n");
        return 0;
}

3. 通用型脚本语言——Python

print("Hello World")

4. Web前端开发主流语言——JavaScript

console.log("Hello, World")

5. 世界范围内网站运用率最高的编程语言——PHP

<?php
echo "Hello World";
?>

6. 多范式编程语言——C++

#include<iostream>
Using namespace std;
int main(){
    cout << "\nHello World!";
    return 0;
}

7. 高级程序编程语言——C#

using System;
namespace helloWorld{
    class HelloWorld
    {
        static void Main(String[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }
}

8. 基于对象的程序设计语言——Visual Basic

    subsub main
msgbox "Hello World!"
end sub
}

9. 统计分析的可编程语言——R

cat("helloworld")

10. 静态强类型、编译型、并发型、并具有垃圾回收功能的编程语言——GO

package main
import "fmt"
func main() {
    fmt.Println("Hello World!")
}

p3 使用 JetPack 对 App 进行开发架构

在Google I/O 2018发布了JetPack之后,Android应用架构和开发技术都发生了很大的变化。

Activity LifeCycle(理解Android应用的运行机制)

Lifecycles 其实从名字看肯定是与生命周期相关,那它与生命周期又有什么联系?先参考一下官方文档:

Lifecycles 是一个生命周期感知组件,当 Activity 或者 Fragment 的生命周期发生改变的时会,Lifecycles也会做出相应的生命周期状态的改变,它保存关于组件生命周期状态的信息(比如活动或片段),并允许其他对象观察这种状态。

可以看出 Lifecycles 是一个组件,具有感知生命周期的功能,既然是个组件,那就说明可以嵌入到其他地方,比如 VewModel 便是其中之一。
我们知道,当我们需要处理与生命周期相关的组件的时候,在没有Lifecycles提供的时候,需要设置各种回调,如下所示:

class MyLocationListener {
    public MyLocationListener(Context context, Callback callback) {
        // ...
    }

    void start() {
        // connect to system location service
    }

    void stop() {
        // disconnect from system location service
    }
}

class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

    @Override
    public void onCreate(...) {
        myLocationListener = new MyLocationListener(this, (location) -> {
            // update UI
        });
    }

    @Override
    public void onStart() {
        super.onStart();
        myLocationListener.start();
        // manage other components that need to respond
        // to the activity lifecycle
    }

    @Override
    public void onStop() {
        super.onStop();
        myLocationListener.stop();
        // manage other components that need to respond
        // to the activity lifecycle
    }
}

当组件数目过多的时候,便会需要在生命周期回调中管理多个组件从而徒增代码量,使得项目难以维护,而且在生命周期中执行过多的耗时操作极易引起内存泄漏,而这些都可以通过Lifecycles来解决。
看下Lifecycles的源码吧,反正也不长

public abstract class Lifecycle {

    /**
     * Lifecycle coroutines extensions stashes the CoroutineScope into this field.
     *
     * @hide used by lifecycle-common-ktx
     */
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    @NonNull
    AtomicReference<Object> mInternalScopeRef = new AtomicReference<>();

    //添加生命周期状态的观察者
    @MainThread
    public abstract void addObserver(@NonNull LifecycleObserver observer);

    //这个也就是移除观察者
    @MainThread
    public abstract void removeObserver(@NonNull LifecycleObserver observer);

    //获得当前生命周期状态
    @MainThread
    @NonNull
    public abstract State getCurrentState();

    //再熟悉不过的生命周期回调
    //是从框架和生命周期类分派的生命周期事件,这些事件映射到活动和片段中的回调事件。
    @SuppressWarnings("WeakerAccess")
    public enum Event {
        /**
         * Constant for onCreate event of the {@link LifecycleOwner}.
         */
        ON_CREATE,
        /**
         * Constant for onStart event of the {@link LifecycleOwner}.
         */
        ON_START,
        /**
         * Constant for onResume event of the {@link LifecycleOwner}.
         */
        ON_RESUME,
        /**
         * Constant for onPause event of the {@link LifecycleOwner}.
         */
        ON_PAUSE,
        /**
         * Constant for onStop event of the {@link LifecycleOwner}.
         */
        ON_STOP,
        /**
         * Constant for onDestroy event of the {@link LifecycleOwner}.
         */
        ON_DESTROY,
        /**
         * An {@link Event Event} constant that can be used to match all events.
         */
        ON_ANY
    }

    //这个也就此当前所处于的状态了
    @SuppressWarnings("WeakerAccess")
    public enum State {
        /**
         * Destroyed state for a LifecycleOwner. After this event, this Lifecycle will not dispatch
         * any more events. For instance, for an {@link android.app.Activity}, this state is reached
         * <b>right before</b> Activity's {@link android.app.Activity#onDestroy() onDestroy} call.
         */
        DESTROYED,

        /**
         * Initialized state for a LifecycleOwner. For an {@link android.app.Activity}, this is
         * the state when it is constructed but has not received
         * {@link android.app.Activity#onCreate(android.os.Bundle) onCreate} yet.
         */
        INITIALIZED,

        /**
         * Created state for a LifecycleOwner. For an {@link android.app.Activity}, this state
         * is reached in two cases:
         * <ul>
         *     <li>after {@link android.app.Activity#onCreate(android.os.Bundle) onCreate} call;
         *     <li><b>right before</b> {@link android.app.Activity#onStop() onStop} call.
         * </ul>
         */
        CREATED,

        /**
         * Started state for a LifecycleOwner. For an {@link android.app.Activity}, this state
         * is reached in two cases:
         * <ul>
         *     <li>after {@link android.app.Activity#onStart() onStart} call;
         *     <li><b>right before</b> {@link android.app.Activity#onPause() onPause} call.
         * </ul>
         */
        STARTED,

        /**
        * Resumed state for a LifecycleOwner. For an {@link android.app.Activity}, this state
        * is reached after {@link android.app.Activity#onResume() onResume} is called.
        */
        RESUMED;

        /**
        * Compares if this State is greater or equal to the given {@code state}.
        *
        * @param state State to compare with
        * @return true if this State is greater or equal to the given {@code state}
        */
        public boolean isAtLeast(@NonNull State state) {
        return compareTo(state) >= 0;
        }
    }
}

例子 以Chronometer为例子来示范生命周期监听

public class MyChronometer extends Chronometer implements LifecycleObserver {

    private long time;


    public MyChronometer(Context context) {
        super(context);
    }

    public MyChronometer(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MyChronometer(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    //以注解的形式完成监听
    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    private void stopMeter(){
        time = SystemClock.elapsedRealtime() - getBase();
        stop();
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    private void resumeMeter(){
        setBase(SystemClock.elapsedRealtime() - time);
        start();
    }
}

然后Activity如下:只需要添加观察者即可

public class LifecyclesActivity extends AppCompatActivity {

    MyChronometer chronometer;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_lifecycles);

        chronometer = findViewById(R.id.meter);
        getLifecycle().addObserver(chronometer);
    }

}

是不是感觉Activity与Chronometer之间解耦了,Chrononmeter自我实现了良好的封装,也方便了移植。
这也就是使用Lifecycles带来的好处。

LifeCycle 生命周期图解

ViewModel 数据管理小帮手

ViewModel的出现主要为了解决两个问题:
1.当Actvitiy销毁重建过程中的数据恢复问题,虽然原来可以使用onSaveInstanceState()来完成,但是只支持能被序列化的数据而且是小量数据,对于大量数据则显得有点无力。
2.UI控制器的工作繁忙,UI控制器主要用于处理显示,交互,其他的额外操作可以委托给其他类完成,将不应该分配给UI的任务分离出来是必要的,这也就是上面所说的分离关注点原则

ViewModel是一个负责为Fragment/Activity配置和管理数据的类,同时处理Fragment/Activity与Application其余部分的通信。
ViewModel始终与Scope(Fragment/Activity)相关联创建,同时一直保留当Scope存在时,例如Activity被finish。换句话说,ViewModel不会随着其拥有者因配置更改被销毁而销毁(旋转,横竖屏切换),新的拥有者会重新建立与ViewModel的关联。
ViewModel旨在获取和保存Activity或Fragment必要的信息,Activity或Fragment能够观察到ViewModel的数据改变,ViewModel通常通过LiveData或Android Data Binding暴露数据,也可以在你喜欢的框架中使用可观察构造。
ViewModel只负责管理UI数据,不应访问视图结构和持有Activity或Fragment的引用。

下面则是示意图

ViewModel实例

ViewModel在配置更改期间能自动保留其对象,以便它们所持有的数据可立即用于下一个 Activity 或片段Fragment
下面通过一个具体的实例:
MainActivity.java

public class MainActivity extends AppCompatActivity {

    private MyViewModel myViewModel;

    private TextView tv;
    private Button button1, button2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        myViewModel = ViewModelProviders.of(this).get(MyViewModel.class);

        tv = findViewById(R.id.textView);
        tv.setText(String.valueOf(myViewModel.num));
        button1 = findViewById(R.id.button1);
        button2 = findViewById(R.id.button2);

        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                myViewModel.num++;
                tv.setText(String.valueOf(myViewModel.num));
            }
        });

        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                myViewModel.num += 2;
                tv.setText(String.valueOf(myViewModel.num));
            }
        });
    }
}

MyViewModel.java

public class MyViewModel extends ViewModel {

    public int num = 0;

}

布局如下:Welcome对应TextView,+1对象button1,+2对应button2

加到7,当设备旋转

数据仍能得到保留,即使更改系统语言都可以,也就是说如果Activity被重建,将接收到第一个Activity创建的相同的MyViewModel实例。当所有者Activity完成后,会调用ViewModel对象的onCleared()方法,以便它能够清理资源。
同时这里也需要注意一个问题,当ViewModel持有外部引用的时候会阻止回收,所以ViewModel绝不能引用View、Lifecycle(也是Jetpack组件之一)或任何可能包含对Activity上下文的引用的类。
回到最上面的那个图,图说明了ViewModel的作用域涉及到整个生命周期,当获取ViewModel时,ViewModel的生命周期限定为传入ViewModelProvider的对象的生命周期。也就是对于以下场景(引用官方示例)

public class SharedViewModel extends ViewModel {
    private final MutableLiveData<Item> selected = new MutableLiveData<Item>();

    public void select(Item item) {
        selected.setValue(item);
    }

    public LiveData<Item> getSelected() {
        return selected;
    }
}


public class MasterFragment extends Fragment {
    private SharedViewModel model;
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        itemSelector.setOnClickListener(item -> {
            model.select(item);
        });
    }
}

public class DetailFragment extends Fragment {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        model.getSelected().observe(this, { item ->
                                    // Update the UI.
                                    });
    }
}

由于传入的是Activity,所以其作用域为整个Activity,不同的Fragment可以通过ViewModelProviders获取到同一个ViewModel,这样有以下的好处:

LiveData还支持map的映射转换

其实感觉这个liveData和rxJava大同小异
官方示例代码

LiveData<User> userLiveData = ...;
LiveData<String> userName = Transformations.map(userLiveData, user -> {
    user.name + " " + user.lastName
});

当前页面是本站的「Google AMP」版。查看和发表评论请点击:完整版 »