IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android学习总结 -> 正文阅读

[移动开发]Android学习总结

任务:

  1. 强化布局学习,用不同的布局类型实现计算器页面,并且保证每个xml都可以直接使用,同时学习了解每种布局的特性和优劣势
  2. 强化编码规范,方法的抽取、变量定义、命名规则,尽快根据文档进行学习
  3. Activity改成Fragment,搞清楚两者之间的关系和使用方式
  4. 重新梳理各自代码后,学习使用UML工具,把计算器demo的实现思路和典型场景的时序图画清楚
  5. 强调产品和需求意识,对照手机计算器功能和逻辑完善demo

不需要这么复杂,glide就是先下载到本地,然后下一次显示,已经下载的就拿出来,如果内存缓存有了,就直接用。接口的话也简单,直接给okhttp做个缓存就行了。不需要自己一个一个保存。框架本来就提供这样功能,只是看你用不用

约束布局

优点:极大程度减少布局层级,可以实现一些其他布局管理器不能实现的样式,适合复杂的大型布局

规则:

  • 每个视图都必须至少有两个约束条件:一个水平约束条件,一个垂直约束条件
  • 只能在共用同一平面的约束手柄与定位点之间创建约束条件。因此,视图的垂直平面(左侧和右侧)只能约束在另一个垂直平面上;而基准线则只能约束到其他基准线上。
  • 每个约束句柄只能用于一个约束条件,但您可以在同一定位点上创建多个约束条件(从不同的视图)

约束布局使用margin必须注意的点:

  1. 控件必须在布局里约束一个相对位置;
  2. margin只能大于等于0;
约束布局的常用属性
    // 常用属性
	 layout_constraintLeft_toLeftOf//目标view左边与另一个view左边对齐
     layout_constraintLeft_toRightOf//目标view左边与另一个view右边对齐
     layout_constraintRight_toLeftOf//目标view右边与另一个view左边对齐
     layout_constraintRight_toRightOf//目标view右边与另一个view右边对齐
     layout_constraintTop_toTopOf//目标view顶部与另一个view顶部对齐
     layout_constraintTop_toBottomOf//目标view顶部与另一个view底部对齐
     layout_constraintBottom_toTopOf//目标view底部与另一个view顶部对齐
     layout_constraintBottom_toBottomOf//目标view底部与另一个view底部对齐
     layout_constraintBaseline_toBaselineOf//基于baseline对齐
     layout_constraintStart_toEndOf//目标view起始边缘与另一个view结束边缘对齐
     layout_constraintStart_toStartOf//目标view起始边缘与另一个view起始边缘对齐
     layout_constraintEnd_toStartOf//目标view结束边缘与另一个view起始边缘对齐
     layout_constraintEnd_toEndOf//目标view结束边缘与另一个view结束边缘对齐
约束布局中权重的使用

重点:使用约束布局的权重,控件之间需要两两关联,不然就算设置了0dp还是没有效果。

TextView2里用到了app:layout_constraintLeft_toRightOf="@+id/TextView1"这个属性,他的意思是把TextView2的左边约束到TextView1的右边;

app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"

RelativeLayout中的水平居中layout_centerHorizontal相当于在ConstraintLayout约束控件的左右为parent的左右;RelativeLayout中的垂直居中layout_centerVertical相当于在ConstraintLayout约束控件的上下为parent的上下;

log的等级

  • Verbose 详细的,推荐使用颜色白色
  • Debug 调试信息,推荐使用绿色
  • Info 通用信息,推荐使用蓝色
  • Warning 警告信息,推荐使用黄色
  • Error 错误信息,推荐使用红色

空指针异常

空指针的解决思路

首先我们要知道的是为什么会引发空指针一场,因为对象为空。那么问题就来了:为什么对象为空呢?

所以,同学们要明白的是对象的创建与使用时机,也就是说,发生空指针的时候

  1. 去找对象什么时候创建或者赋值;
  2. 去找空指针发生的地方;
  3. 如果对象为空时,不影响程序执行,可以加判空处理;
  4. 如果对象为空时,影响程序执行,则需要解决对象创建的时序问题。

完成:

豆瓣APP实现思路:

  1. 创建MovieBean类,里面的成员变量是api接口对应的字段;(用了一个AS的插件 GsonFormatPlus,直接将JSON字段转成对应的变量)

    package com.example.douban.bean;
    
    import java.util.List;
    
    
    public class MovieBean {
    
    
        private List<SubjectsDTO> subjects;
    
        public List<SubjectsDTO> getSubjects() {
            return subjects;
        }
    
        public void setSubjects(List<SubjectsDTO> subjects) {
            this.subjects = subjects;
        }
    
        @Override
        public String toString() {
            return "MovieBean{" +
                    "subjects=" + subjects +
                    '}';
        }
    
        public static class SubjectsDTO {
    
            private String episodesInfo;
            private String rate;
            private Integer coverX;
            private String title;
            private String url;
            private Boolean playable;
            private String cover;
            private String id;
            private Integer coverY;
            private Boolean isNew;
    
            public String getEpisodesInfo() {
                return episodesInfo;
            }
    
            public void setEpisodesInfo(String episodesInfo) {
                this.episodesInfo = episodesInfo;
            }
    
            public String getRate() {
                return rate;
            }
    
            public void setRate(String rate) {
                this.rate = rate;
            }
    
            public Integer getCoverX() {
                return coverX;
            }
    
            public void setCoverX(Integer coverX) {
                this.coverX = coverX;
            }
    
            public String getTitle() {
                return title;
            }
    
            public void setTitle(String title) {
                this.title = title;
            }
    
            public String getUrl() {
                return url;
            }
    
            public void setUrl(String url) {
                this.url = url;
            }
    
            public Boolean getPlayable() {
                return playable;
            }
    
            public void setPlayable(Boolean playable) {
                this.playable = playable;
            }
    
            public String getCover() {
                return cover;
            }
    
            public void setCover(String cover) {
                this.cover = cover;
            }
    
            public String getId() {
                return id;
            }
    
            public void setId(String id) {
                this.id = id;
            }
    
            public Integer getCoverY() {
                return coverY;
            }
    
            public void setCoverY(Integer coverY) {
                this.coverY = coverY;
            }
    
            public Boolean getNew() {
                return isNew;
            }
    
            public void setNew(Boolean aNew) {
                isNew = aNew;
            }
    
            @Override
            public String toString() {
                return "SubjectsDTO{" +
                        "episodesInfo='" + episodesInfo + '\'' +
                        ", rate='" + rate + '\'' +
                        ", coverX=" + coverX +
                        ", title='" + title + '\'' +
                        ", url='" + url + '\'' +
                        ", playable=" + playable +
                        ", cover='" + cover + '\'' +
                        ", id='" + id + '\'' +
                        ", coverY=" + coverY +
                        ", isNew=" + isNew +
                        '}';
            }
        }
    }
    
    
  2. 创建Fragment布局和对应的Fragment类加载布局,布局中添加recyclerview控件,recyclerview的条目布局;

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <androidx.recyclerview.widget.RecyclerView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/movie_recyclerview"/>
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    
    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="5dp">
    
        <ImageView
            android:id="@+id/cover"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/ic_launcher_round"
    
            />
    
        <TextView
            android:id="@+id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="10dp"
            android:layout_toRightOf="@id/cover"
            android:layout_marginLeft="10dp"
            android:text="title"
            android:textSize="20dp" />
    
        <TextView
            android:id="@+id/score"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="10dp"
            android:layout_below="@+id/title"
            android:text="0.0"
            android:layout_alignParentEnd="true"
            android:layout_marginTop="20dp"
            android:textSize="20dp" />
    
    
    </RelativeLayout>
    
    
    package com.example.douban.Fragment;
    
    import android.os.Bundle;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    
    import androidx.annotation.NonNull;
    import androidx.annotation.Nullable;
    import androidx.Fragment.app.Fragment;
    import androidx.recyclerview.widget.LinearLayoutManager;
    
    import com.example.douban.adapter.MovieAdapter;
    import com.example.douban.databinding.MovieFragmentBinding;
    
    public class MovieFragment extends Fragment {
        public MovieAdapter movieAdapter;
        MovieFragmentBinding mBinding;
    //    List<String> list;
    
    
    
        @Nullable
        @Override
        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            mBinding = MovieFragmentBinding.inflate(inflater,container,false);
            initMovieRecyclerView();
    //        list = SPsave.getHistory(getContext());
            return mBinding.getRoot();
    
    
    
        }
    
        private void initMovieRecyclerView() {
            mBinding.movieRecyclerview.setLayoutManager(new LinearLayoutManager(getContext()));
            movieAdapter = new MovieAdapter();
            mBinding.movieRecyclerview.setAdapter(movieAdapter);
        }
    
    }
    
  3. 为recyclerview 创建自定义适配器;

    package com.example.douban.adapter;
    
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.ImageView;
    import android.widget.TextView;
    
    import androidx.annotation.NonNull;
    import androidx.recyclerview.widget.RecyclerView;
    
    import com.example.douban.R;
    import com.example.douban.bean.MovieBean;
    import com.squareup.picasso.Picasso;
    
    import java.util.ArrayList;
    import java.util.List;
    
    
    
    public class MovieAdapter extends RecyclerView.Adapter<MovieAdapter.ViewHolder> {
    
        private static final String TAG ="MovieAdapter";
        List<MovieBean.SubjectsDTO> data = new ArrayList<>();
        @NonNull
        @Override
        public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_movie, parent, false);
            ViewHolder holder = new ViewHolder(view);
            return holder;
        }
    
        @Override
        public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
            ImageView imgCover = holder.itemView.findViewById(R.id.cover);
            TextView titile = holder.itemView.findViewById(R.id.title);
            TextView score = holder.itemView.findViewById(R.id.score);
    
            MovieBean.SubjectsDTO movieBean = data.get(position);
            titile.setText(movieBean.getTitle());
            score.setText(movieBean.getRate());
    //        Log.d(TAG, "onBindViewHolder: "+movieBean.getCover());
            Picasso.with(imgCover.getContext())
                    .load(movieBean.getCover())
                    .into(imgCover);
            /*Glide.with(imgCover.getContext())
                    .load(movieBean.getCover())
                    .dontAnimate()
    
                    .into(imgCover);*/
    
        }
    
        @Override
        public int getItemCount() {
            return data.size();
        }
    
        public void setData(MovieBean movieBean) {
            data.clear();
            data.addAll(movieBean.getSubjects());
            notifyDataSetChanged();
        }
    
        public class ViewHolder extends RecyclerView.ViewHolder {
            public ViewHolder(@NonNull View itemView) {
                super(itemView);
    
            }
        }
    }
    
  4. 在MainActivity中进行视图绑定 使用ViewBinding;

    package com.example.douban;
    
    import android.os.Bundle;
    import android.util.Log;
    import android.view.View;
    
    import androidx.appcompat.app.ActionBar;
    import androidx.appcompat.app.AppCompatActivity;
    import androidx.Fragment.app.Fragment;
    import androidx.Fragment.app.FragmentManager;
    import androidx.Fragment.app.FragmentTransaction;
    
    import com.example.douban.bean.MovieBean;
    import com.example.douban.bean.MusicBean;
    import com.example.douban.databinding.ActivityMainBinding;
    import com.example.douban.Fragment.BookFragment;
    import com.example.douban.Fragment.MovieFragment;
    import com.example.douban.Fragment.MusicFragment;
    import com.example.douban.util.RetrofitManager;
    
    import java.net.HttpURLConnection;
    
    import retrofit2.Call;
    import retrofit2.Callback;
    import retrofit2.Response;
    import retrofit2.Retrofit;
    
    public class MainActivity extends AppCompatActivity implements View.OnClickListener{
        ActivityMainBinding mBinding;
        private static final String TAG = "MainActivity";
        MovieFragment movieFragment;
        MusicFragment musicFragment;
        BookFragment bookFragment;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            mBinding = ActivityMainBinding.inflate(getLayoutInflater());
            setContentView(mBinding.getRoot());
    
            //隐藏标题栏
            ActionBar actionBar = getSupportActionBar();
            if (actionBar != null) {
                actionBar.hide();
            }
            init();
            initEvent();
    
    
            repalceFragment(movieFragment);
            getMovie();
    
    
        }
    
        private void init() {
            movieFragment = new MovieFragment();
            musicFragment = new MusicFragment();
            bookFragment = new BookFragment();
        }
    
    
        private void initEvent() {
    
            mBinding.btnBook.setOnClickListener(this);
            mBinding.btnMovie.setOnClickListener(this);
            mBinding.btnMusic.setOnClickListener(this);
        }
    
    
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.btn_movie:
                    repalceFragment(movieFragment);
                    getMovie();
    
                    break;
                case R.id.btn_music:
                    repalceFragment(musicFragment);
                    getMusic();
                    break;
                case R.id.btn_book:
    
                    break;
                default:
                    break;
            }
        }
    
        private void getMusic() {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Retrofit retrofit = RetrofitManager.getRetrofit("https://api.uomg.com");
                    DouBanAPI api = retrofit.create(DouBanAPI.class);
                    Call<MusicBean> task = api.getMusic();
                    task.enqueue(new Callback<MusicBean>() {
                        @Override
                        public void onResponse(Call<MusicBean> call, Response<MusicBean> response) {
                            Log.d(TAG, "onResponse: "+response.code());
                            if (response.code()== HttpURLConnection.HTTP_OK) {
                                MusicBean result = response.body();
                                Log.d(TAG, "onResponse: "+result.toString());
    //                            updateMusicList(result);
                            }
                        }
    
                        @Override
                        public void onFailure(Call<MusicBean> call, Throwable t) {
                            Log.d(TAG, "onFailure: "+t.toString());
                        }
                    });
                }
    
    
            }).start();
        }
    
        /**
         * 动态加载Fragment
         * @param Fragment
         */
        private void repalceFragment(Fragment Fragment) {
            FragmentManager FragmentManager = getSupportFragmentManager();
            FragmentTransaction transaction = FragmentManager.beginTransaction();
            transaction.replace(R.id.content_Fragment,Fragment);
            transaction.addToBackStack(null);
            transaction.commit();
        }
    
        private void getMovie() {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Retrofit retrofit = RetrofitManager.getRetrofit("https://movie.douban.com");
                    DouBanAPI api = retrofit.create(DouBanAPI.class);
                    Call<MovieBean> task = api.getMovie();
                    task.enqueue(new Callback<MovieBean>() {
                        @Override
                        public void onResponse(Call<MovieBean> call, Response<MovieBean> response) {
                            Log.d(TAG, "onResponse: "+response.code());
                            if (response.code()== HttpURLConnection.HTTP_OK) {
                                MovieBean result = response.body();
                                Log.d(TAG, "onResponse: "+result.toString());
                                updateMovieList(result);
                            }
                        }
    
                        @Override
                        public void onFailure(Call<MovieBean> call, Throwable t) {
                            Log.d(TAG, "onFailure: "+t.toString());
                        }
                    });
                }
            }).start();
    
        }
    
        private void updateMovieList(MovieBean movieBean) {
    
            movieFragment.movieAdapter.setData(movieBean);
        }
        private void updateMusicList(MusicBean musicBean) {
            musicFragment.musicAdapter.setData(musicBean);
        }
    }
    

    创建Retrofit实例的工具类

    package com.example.douban.util;
    
    import retrofit2.Retrofit;
    import retrofit2.converter.gson.GsonConverterFactory;
    
    public class RetrofitManager {
    
        
        public static Retrofit getRetrofit(String url){
    
            return new Retrofit.Builder()
                    .baseUrl(url)
                    //对服务器返回的数据进行解析
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
        }
    
    
    
    }
    
  5. 创建Retrofit对象调用create()方法获得接口实例;

  6. 接口实例调用抽象方法形成一个任务对象;

  7. 任务对象异步执行;

  8. 判断response.code是200获得响应体;

  9. 依赖的.addConverterFactory(GsonConverterFactory.create())方法将json转成javaBean对象;

  10. 将对象数据传进recyclerview的适配器中;

  11. 适配器在onBindViewHolder中将数据绑定显示出来;

基本实现了放豆瓣APP,但是在APP断网情况下,不能对里面的内容进行缓存。

Android是使用栈来管理活动的,这个栈也被称为返回栈,先进后出。

活动状态

  1. 运行状态

    活动位于返回栈的栈顶,这时就是运行状态,系统最不愿意回收这种状态的活动。

  2. 暂停状态

    活动不在栈顶,但仍然可见。例如:对话框

    系统也不愿意回收这种状态的活动,但是在内存极低的情况下,才会去考虑回收这种状态下的活动。

  3. 停止状态

    活动不在栈顶,完全不可见,系统仍然会为这种活动保存响应的状态和成员变量,但是在其他地方需要内存的时候,停止状态的活动可能会被系统吸收。

  4. 销毁状态

    活动从返回栈中移除,那么就是销毁状态了。系统最倾向于回收这种状态的活动,从而保证手机的内存充足。

活动的生命周期

定义了7个回调方法

  1. onCreate():活动第一次创建的时候被调用,在这个方法中完成活动初始化操作。

  2. onStart():活动由不可见变为可见的时候调用,可见但是不可以和用户进行交互。

  3. oResume():活动准备好和用户进行交互的时候调用,活动位于返回栈的栈顶,此时活动处于运行状态。

  4. onPause():系统准备去启动或者恢复另一个活动的时候调用,在这个方法中将一些消耗的CPU资源释放掉,保存一些关键的数据,方法执行一定要快。在这里可以做一些存储的操作,因为onPause是进程被杀死唯一一个一定会被执行的操作,但是手机死机或者关机除外。存储的操作方法一定要靠谱,执行要快,否则会影响下一个活动的恢复或者启动,进而影响用户的体验。

    这个时候活动还是可见的,只是处于停止状态。可见但是不可操作

  5. onStop():活动完全不可见的时候调用,它和onPause()方法的主要区别在于启动的新活动是一个对话框的时候onPause()会执行 onStop()不会执行。

    可见转为不可见的时候调用。

  6. onDestroy():在活动被销毁之前调用,之后活动就是销毁状态了。

  7. onRestart():活动由停止状态变为运行状态之前调用,也就是活动被重新启动了。

完整生存期

onCreate() 初始化操作onDestroy()释放内存

可见生存期

onStart() 加载可见资源 onStop()对资源进行释放

前台生存期

onResume() onPause()活动一直处在运行状态 可以和用户进行交互

生命周期解析:

  1. 当Activity首次被创建时,会调用onCreate()方法,接着当显示给用户时,调用onStart(),如果要让Activity位于前台的话就需要调用onResume()方法,此时activity位于栈顶。
  2. 当有另一个activity覆盖当前的activity时,这个时候调用onPause()方法,将前一个activity的数据保存起来。
  3. 此时,如果你想让前一个activity不会再显示的话,调用onStop()方法停止该activity,但是如果你想让它回到前台的话,重新获得焦点的话,可以调用onResume()方法。
  4. onStop()后,你可以调用onDestroy()方法来销毁该activity,也是该activity最后一次被调用了,可以通过finish()关闭activity。
  5. 当内存资源不足的时候,就可能杀死处于onPause()的activity所在的进程,但是这种极端的情况很少会发生。
Fragment和Activity之间的通信
//从布局中获取Fragment的实例
Fragment Fragment = getSupportFragmentManager().findFragmentById(R.id.history_Fragment);
//从Fragment中获取activity实例
MainActivity activity = (MainActivity) requireActivity();

Fragment生命周期

我们可以将Fragment看做是一个小的activity,又称activity片段。使用Fragment将屏幕划分成几块,进行分组,进行模块化管理,从而可以更加方便的在运行过程中动态的更新activity的用户界面。

Fragment不能单独使用,它需要嵌套在activity中使用,即使拥有自己的生命周期,但是还是会受到宿主activity的生命周期的影响。

Fragment的四种状态

  1. 运行状态

    Fragment可见,且它所关联的活动也处于运行状态,碎片也处于运行状态。

  2. 暂停状态

    活动进入暂停状态(由于一个未占满屏幕的活动被添加到了栈顶),和它相关联的可见碎片就会进入到暂停状态。

  3. 停止状态

    活动进入停止状态,与他相关联的碎片进入停止状态

  4. 销毁状态

    碎片总是依附于活动存在的,活动销毁,碎片也销毁

生命周期

  1. onAttach():Fragment添加到activity中,只调用一次

  2. onCreate():创建Fragment时调用,只调用一次

  3. onCreateView():每次创建Fragment的view组件时调用,会返回显示的view

  4. onActivityCreate():Fragment所在的activity启动完成后回调

  5. onStart():启动Fragment时回调

  6. onResume():恢复Fragment时回调,onStart方法后一定回调onResume,onStart可见,onResume才能交互

  7. onPause():暂停Fragment时被回调

  8. onStop():停止Fragment时被回调

  9. onDestroyView():销毁Fragment所包含view组件的时候

  10. onDestroy():销毁Fragment时

  11. onDetach():将Fragment从activity中删除/替换完成后

Fragment生命周期解析
  1. activity加载Fragment的时候,依次调用下面的方法:onAttach onCreate onCreateVIew onActivityCreated onStart onResume
  2. 当我们弄出一个悬浮的对话框风格的activity,或者其他,就是让Fragment所在的activity可见,但是不获得焦点onPause
  3. 当对话框关闭,activity又获得焦点:onResume
  4. 替换Fragment,并调用addToBackStack()将他添加到back栈中 onPause onStop onDestroyView 注意此时的Fragment还没有被销毁
  5. 当我们按下键盘上的回退键。Fragment会再次显示出来:onCreateView onActivityCreated onStart onResume
  6. 如果我们替换后,在事务commit之前没有调用addToBackStack()方法Fragment添加到back栈中的话,又或者退出了activity的话,那么用户执行回退操作进行Fragment的恢复,该Fragment将重新启动,如果不向返回栈添加事务,则系统会移除或者替换Fragment时将其销毁。

addToBackStack()方法的作用:当移除或替换一个Fragment并向返回栈添加事务时,系统会停止(而非销毁)移除的Fragment。如果用户执行回退操作进行Fragment的恢复,该Fragment将重新启动,如果不像返回栈添加事务,则系统会在移除或替换Fragment时将其销毁。

静态加载Fragment必须添加ID属性

动态添加Fragment
  1. 通过getFragmentManager() 获得FragmentManager对象
  2. 获得FragmentTransaction对象fm.beginTransaction();
  3. 调用add()方法或者replace()方法加载Fragment;add(要传入的容器,Fragment对象)
  4. 在前面的基础上还需调用commit()方法提交事务,当然还有其他的方法,如remove

Fragment与Activity的交互

  1. 组件获取:
    1. Fragment获取activity中的组件:getActivity().findViewById()
    2. activity中获取Fragment中的组件:getFragmentManager.findFragmentById()
  2. 数据传递:
    1. activity传递数据给Fragment:在activity中创建Bundle数据包,调用Fragment实例的setArguments(bundle)从而将Bundle数据包传给Fragment
    2. Fragment传递数据给activity:在Fragment中定义一个内部回调接口,在让包含该Fragment的activity实现该回调接口,Fragment就可以通过回调方法传数据了

缓存的设置

AsyncTask

// jiangfudao
    public void readMovieListFromFile(){
//        AsyncTask asyncTask  = new MyReadMovieListFromFileTask();
//        asyncTask.execute();
        new MyReadMovieListFromFileTask().execute();
    }

    public class MyReadMovieListFromFileTask extends AsyncTask{

        @Override
        protected void onPreExecute() {

            super.onPreExecute();
        }

        @Override
        protected Object doInBackground(Object[] objects) {
//在任务被线程池执行时调用,通常用来做一些准备操作
            return null;
        }

        @Override
        protected void onPostExecute(Object o) {
            super.onPostExecute(o);
        }
    
缓存思路

MovieBeanModify

package com.example.douban.bean;

import android.content.Context;
import android.content.SharedPreferences;

import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

// jiangfudao
public class MovieBeanCopy {


    private List<SubjectsDTO> subjects = new ArrayList<>();


    public List<SubjectsDTO> getSubjects() {
        return subjects;
    }

    public void setSubjects(List<SubjectsDTO> subjects) {
        this.subjects = subjects;
    }

    @Override
    public String toString() {
        return "MovieBean{" +
                "subjects=" + subjects +
                '}';
    }

    /**
     * 保存到文件
     *
     * @param context 上下文
     * @param saveType 0 表示使用SP存储,1表示文件存储
     */
    public void saveToFiles(Context context, int saveType){
        JsonArray ja = toJson();
        if(0 == saveType){
            SharedPreferences sharedPreferences = context.getSharedPreferences("movie", 0);
            sharedPreferences.edit().putString("movie", ja.toString()).commit();
        }else{
            try {
                File file = context.getExternalFilesDir("movie.txt").getAbsoluteFile();
                file.createNewFile();
                FileOutputStream fileOutputStream = new FileOutputStream(file);
                fileOutputStream.write(ja.toString().getBytes("utf-8"));
                fileOutputStream.flush();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 从文件读取
     * @param context
     * @param saveType
     * @return
     */
    public static MovieBeanCopy readFromFile(Context context, int saveType){
        MovieBeanCopy movieBeanCopy = new MovieBeanCopy();
        String json  = "";
        if(0 == saveType){
            SharedPreferences sharedPreferences = context.getSharedPreferences("movie", 0);
            json  =  sharedPreferences.getString("movie", "");
        }else{
            File file = context.getExternalFilesDir("movie.txt").getAbsoluteFile();
            if(file.exists()){
                try {
                    FileInputStream input = new FileInputStream(file);
                    byte[] b = new byte[input.available()];
                    input.read(b);
                    json = new String(b, "utf-8");
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        JsonArray jsonArray = (JsonArray) JsonParser.parseString(json);
        int n = jsonArray.size();
        for(int i=0; i<n; i++){
            JsonObject jsonObject = (JsonObject) jsonArray.get(i);
            movieBeanCopy.subjects.add(SubjectsDTO.fromJson(jsonObject));
        }
        return movieBeanCopy;
    }

    public JsonArray toJson(){
        JsonArray jsonArray  = new JsonArray();
        for(SubjectsDTO dto : subjects){
            jsonArray.add(dto.toJson());
        }
        return jsonArray;
    }

    public static class SubjectsDTO{
        private String episodesInfo;
        private String rate;// 评分
        private Integer coverX;
        private String title;// 标题
        private String url;
        private Boolean playable;
        private String cover;// 封面
        private String id;
        private Integer coverY;
        private Boolean isNew;

        public SubjectsDTO(String title, String cover, String rate){
            this.title = title;
            this.cover = cover;
            this.rate = rate;
        }

        public JsonObject toJson(){
            JsonObject jsonObject = new JsonObject();
            jsonObject.addProperty("title", title);
            jsonObject.addProperty("cover", cover);
            jsonObject.addProperty("rate", rate);
            return jsonObject;
        }

        public static SubjectsDTO fromJson(JsonObject jsonObject){
            SubjectsDTO s = new SubjectsDTO(
                    jsonObject.get("title").toString(),
                    jsonObject.get("cover").toString(),
                    jsonObject.get("rate").toString()
            );
            return s;
        }

        public String getEpisodesInfo() {
            return episodesInfo;
        }

        public void setEpisodesInfo(String episodesInfo) {
            this.episodesInfo = episodesInfo;
        }

        public String getRate() {
            return rate;
        }

        public void setRate(String rate) {
            this.rate = rate;
        }

        public Integer getCoverX() {
            return coverX;
        }

        public void setCoverX(Integer coverX) {
            this.coverX = coverX;
        }

        public String getTitle() {
            return title;
        }

        public void setTitle(String title) {
            this.title = title;
        }

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }

        public Boolean getPlayable() {
            return playable;
        }

        public void setPlayable(Boolean playable) {
            this.playable = playable;
        }

        public String getCover() {
            return cover;
        }

        public void setCover(String cover) {
            this.cover = cover;
        }

        public String getId() {
            return id;
        }

        public void setId(String id) {
            this.id = id;
        }

        public Integer getCoverY() {
            return coverY;
        }

        public void setCoverY(Integer coverY) {
            this.coverY = coverY;
        }

        public Boolean getNew() {
            return isNew;
        }

        public void setNew(Boolean aNew) {
            isNew = aNew;
        }

        @Override
        public String toString() {
            return "SubjectsDTO{" +
                    "episodesInfo='" + episodesInfo + '\'' +
                    ", rate='" + rate + '\'' +
                    ", coverX=" + coverX +
                    ", title='" + title + '\'' +
                    ", url='" + url + '\'' +
                    ", playable=" + playable +
                    ", cover='" + cover + '\'' +
                    ", id='" + id + '\'' +
                    ", coverY=" + coverY +
                    ", isNew=" + isNew +
                    '}';
        }
    }
}

开启小窗口

  1. adb install -t ----apk
  2. adb push -----json/sdcard
  3. adb root
  4. adb remount
  5. adb shell am startservice -n com.gwm.app.tool/com.gwm.app.tool.ToolService

Activity的启动模式

  1. standard 标准模式
  2. singleTop 栈顶复用模式
  3. singleTask 栈内复用模式
  4. singleInstance 单例模式
standard :标准模式

默认启动模式 就是新建

singleTop:栈顶复用模式

若需新建的activity位于任务栈栈顶 那么此activity实例就不会重建 而是重用栈顶的实例

singleTask: 栈内复用模式

若需要创建的activity位于栈内 如果不是栈顶 就需要将该活动的上面的活动都进行出栈

singleintance: 单例模式

创建新的任务栈

AlertDialog

  • setTitle:设置对话框的标题,比如“提示”、“警告”等;
  • setMessage:设置对话框要传达的具体信息;
  • setIcon: 设置对话框的图标;
  • setCancelable: 点击对话框以外的区域是否让对话框消失,默认为true;
  • setPositiveButton:设置正面按钮,表示“积极”、“确认”的意思,第一个参数为按钮上显示的文字,下同;
  • setNegativeButton:设置反面按钮,表示“消极”、“否认”、“取消”的意思;
  • setNeutralButton:设置中立按钮;
  • setOnShowListener:对话框显示时触发的事件;
  • setOnCancelListener:对话框消失时触发的事件。
创建方式:
AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("提示:")
                .setIcon(R.drawable.ic_launcher_foreground)
                .setMessage("这是一个错误")
                .setIcon(R.mipmap.ic_launcher)
                .setCancelable(true);

最后调用show()方法显示出来。

活动生命周期

  1. onCreate():活动一开始创建的时候,在这里面进行一些初始化操作
  2. onStart():活动由不可见变为可见的时候调用
  3. onResume():活动准备好和用户进行交互的时候调用,这个时候活动位于返回栈的栈顶,处于运行状态。
  4. onPause:这个时候系统准备去启动或者恢复另一个活动的时候调用,这个时候活动还是可见的
  5. onStop:活动由可见转为不可见的时候调用,它和onPause方法的区别就在于。如果启动的活动是一个对话框的时候,那么onPause会执行,而onStop不会执行
  6. onDestroy:活动由停止状态转为销毁状态调用
  7. onRestart:活动由停止状态转为运行状态的时候调用,也就是活动被重新启动了,这个时候不会再去调用onCreate() 只会调用onStart() onResume()
完整的生命周期

onCreate——onDestroy

可见生存期

onStart(加载可见资源)——onStop(释放内存)

前台生存期

onResume——onPause活动一直处在运行状态,可以和用户进行交互

注意:

这些方法都是回调方法,我们不能够去调用,只能重写方法里面的内容,什么时候调用是Activity来决定的,我们能够手动调用的就只有finish()方法,该方法用于关掉某个Activity。

活动的启动模式

  1. standard:标准模式,无脑模式,就是新建
  2. singleTop:栈顶复用模式,如果新建的activity位于返回栈的栈顶,那么就直接用这个实例,不用新建,如果没有位于栈顶,那么还是得重建。
  3. singleTask:栈内复用模式,如果新建的activity位于返回栈中,那么会将这个activity之上的活动都进行出栈。
  4. singleInstance:单例模式,直接创建一个新的返回栈,将新建的活动放在这个新的返回栈中。

Handler

如果要让新启动的线程周期性的修改UI组件的属性值,怎么办?

Handler类

  1. UI线程:就是我们的主线程,系统在创建UI线程的时候会初始化一个Looper对象,同时也会创建一个与其关联的MessageQueue;
  2. Handler:作用就是发送与处理信息,如果希望Handler正常工作,在当前线程中要有一个Looper对象
  3. Message:Handler接收与处理的消息对象
  4. MessageQueue:消息队列,先进先出管理Message,在初始化Looper对象时会创建一个与之关联的MessageQueue;
  5. Looper:每个线程只能够有一个Looper,管理MessageQueue,不断地从中取出Message分发给对应的Handler处理!

当我们的子线程想修改Activity中的UI组件时,我们可以新建一个Handler对象,通过这个对象向主线程发送信息;而我们发送的信息会先到主线程的MessageQueue进行等待,由Looper按先入先出顺序取出,再根据message对象的what属性分发给对应的Handler进行处理!

void handleMessage(Message msg):处理消息的方法,通常是用于被重写!
sendEmptyMessage(int what):发送空消息
sendEmptyMessageDelayed(int what,long delayMillis):指定延时多少毫秒后发送空信
息 se
ndMessage(Message msg):立即发送信息
sendMessageDelayed(Message msg):指定延时多少毫秒后发送信息
final boolean hasMessage(int what):检查消息队列中是否包含what属性为指定值的消息
如果是参数为(int what,Object object):除了判断what属性,还需要判断Object属性是否为
指定对象的消息

UML时序图学习

作用:

  1. 展示对象之间的交互顺序

  2. 相比其他UML图,时序图更强调交互的时间顺序

  3. 可以直观的描述并发进程

  4. 角色:系统角色,可以是人或者其他系统

  5. 对象:对象的命名有三种:

    1. 对象名和类名;
    2. 只显示类名,不显示对象,即为一个匿名类;
    3. 只显示对象名,不显示类名。
  6. 生命线:时序图每个对象和底部中心都有一条垂直的虚线,这就是对象的生命线(对象的时间线)。以一条垂直的虚线表示。

  7. 控制焦点:控制焦点代表时序图中在对象时间线上某段时期执行的操作。以一个很窄的矩形表示。

  8. 消息:表示代表对象之间发送的消息。消息分为三种类型;

    1. 同步消息:消息发送者把控制传递给消息的接受者,然后停止活动,等待消息的接受者放弃或者返回控制。用来表示同步的意义。以一条实线+实心箭头表示。
    2. 异步消息:消息发送者把控制传递给消息的接受者,然后继续自己的活动,不等待接受者返回消息或者控制。异步消息的接受者和发送者是并发工作的。以一条实线+大于号表示。
    3. 返回消息:返回消息表示从过程调用返回,以小于号+虚线表示。
  9. 自关联消息:表示方法的自身调用或者一个对象内的一个方法调用另外一个方法。以一个半闭合的长方形+下方实心箭头表示。

变更仿豆瓣部分代码
  1. 有网络:
    1. 响应:缓存,再将数据添加到Adapter中;
    2. 未响应:主线程吐司提示;
  2. 无网络:读取缓存
    1. 缓存为空:吐司提示;
    2. 缓存非空:以set的形式将缓存数据传递给Adapter;

时序图绘制

常用控件属性学习

  • Src指的内容,填入图片后并不会拉伸
  • Background指的是背景,填入图片后会根据给定的宽高拉伸

ViewModel:以注重生命周期的方式管理界面相关的数据

使用ViewModel来保存数据

LiveData:在底层数据库更改时通知视图

DataBinding

使用好处:借助布局文件中的绑定组件,可以移除 Activity 中的许多界面框架调用,使其维护起来更简单、方便。还可以提高应用性能,并且有助于防止内存泄漏以及避免发生 Null 指针异常。

<layout xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:app="http://schemas.android.com/apk/res-auto">
        <data>
            <variable
                name="viewmodel"
                type="com.myapp.data.ViewModel" />
        </data>
        <ConstraintLayout... /> <!-- UI layout's root element -->
    </layout>
    

添加

buildFeatures {
        dataBinding true
    }

ViewModel存储方式

注意:

上下文:Context context ,比较可靠的方式是使用ApplicationContext:可以理解为指向app的顶级引用。

上下文传进去的是MainActivity可能会导致内存泄漏,因为MIanActivity频繁的创建和删除会被保存占用内存资源。

Java的四种修饰符

  1. public:全局可见;(在整个工程中可见)
  2. private:只在自己的类中可见;
  3. protected:在自己及子类中可见;
  4. 默认:在其所在包中可见;

存储持久化数据的应该写在onPause()中

因为onPause()是唯一一个保证进程被杀之前会调用的。

在onPause()中可以做一些资源回收和数据存储的工作,但是不能太耗时,否则会影响到新的Activity的显示。

意外情况:手机直接关机、死机,这种情况数据是不会保存的。

  1. onAttach 将fragment添加到activity中
  2. onCreate 创建fragment
  3. onCreateView 加载fragment中的view组件 返回view
  4. onActivityCreate fragment所在的activity启动完成时回调
  5. onStart fragment由不可见转为可见的时候
  6. onResume fragment可以跟用户进行交互的时候
  7. onPause fragment进入暂停状态,可见不可交互
  8. onStop fragment停止状态,由可见转为不可见
  9. onDestroyView 销毁fragment中的view组件
  10. onDestroy 销毁fragment
  11. onDetach 将fragment从activity中移除、替换(解除关联的时候调用)

Handler类

如果想让新创建的线程周期性的更新UI组件的属性值,怎么办?

Handler类

  1. UI线程:就是我们的主线程,系统在创建UI线程的时候会初始化一个Looper对象,同时也会创建一个与其关联的MessageQueue;
  2. Handler:作用就是发送和处理信息,如果希望Handler类正常工作,在当前线程中要有一个Looper对象
  3. Message:接收和处理的消息对象
  4. MessageQueue:消息队列,先进先出管理Message,在初始化Looper对象时会创建一个与之关联的MessageQueue
  5. Looper:每个线程只能够有一个Looper,管理MessageQueue,不断地从中取出Message分发给对应的Handler处理

apk发版

  1. 找到自己对应的模块
  2. 选择Build with Paramerters

Service学习

定义:存在后台为我们执行一些耗时或者需要长时间执行的一些操作。

Service两种启动模式,同样都有生命周期,启动模式不同对应的生命周期也不同。

生命周期函数解析:
  1. onCreate():当Service第一次被创建后立即回调该方法,该方法在整个生命周期中只会调用一次
  2. onDestroy():当Service被关闭时回调该方法,该方法只会调用一次
  3. onStartCommand(intent,flag,startId):当客户端调用startService(Intent)方法时会回调,多次调用StartService方法,但不会再创建新的Service对象,而是继续复用前面产生的Service对象,但会继续回调onStartCommand()方法。
  4. IBinder onOnbind(intent):该方法是Service都必须实现的方法,该方法会返回一个IBinder对象,APP通过该对象与Service组件进行通信
  5. onUnbind(intent):当该Service上绑定的所有客户端都断开时会回调该方法
启动方式:
  1. StartService()启动Service
  2. BindService()启动Service
  3. PS:还有一种,就是启动Service后,绑定Service

BroadcastReceiver:

  1. 标准广播

    完全异步执行的广播,在广播发出之后,所有的广播接收器会在同一时间接收到这条广播,无法被截断

  2. 有序广播

    同步执行的广播,在广播发出之后,优先级高的广播接收器可以优先接收到广播

注册广播

不要再广播里添加过多的逻辑或者进行任何耗时操作,因为在广播中是不允许开辟线程的,当onReceiver()方法运行较长时间(超过10秒)还没有结束的话,那么程序会报错(ANR),广播更多的时候扮演的是一个打开其他组件的角色,比如启动Service,Notification提示,Activity等。

注册时间和接触注册时间:

一般我们在onResume的时候进行注册,在onDestory的时候解除注册。

仿豆瓣APP时序图绘制

请添加图片描述

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-10-01 17:00:30  更:2021-10-01 17:00:58 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 20:46:47-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码