Android四大组件详解

Activity活动

activity生命周期执行顺序

Activity生命周期是学习安卓开发要掌握的最基本知识.那么在页面创建到显示出来,页面切换至后台,或者页面以窗体形式可见不可交换状态,都执行了生命周期的那些方法,顺序是怎样的你是否都了解清楚呢?设置了不同的启动模式之后又走了哪些方法?这也是面试中必问的问题.这篇文章通将过demo的方式将默认启动模式的每个生命周期方法都打印出来.

创建了三个activity.每个Activity页面只有一个按钮,点击顺序Main->Two->Three.

在这里插入图片描述
因为这里的启动模式是默认(standard),每启动一个Activity,就会创建一个实例放在栈顶,顺序启动三个activity : main -> Two -> Three, 而正常的返回也是根据打开顺序,从栈顶依次移除出栈.activity栈中实例图简单如下:
在这里插入图片描述
启动时:
在这里插入图片描述
点击跳转Two:
在这里插入图片描述
ThreeActivity 这里在manifest中添加了下面的一行代码,设置为窗体模式

android:theme="@android:style/Theme.Dialog"

点击跳转Three 效果图制作如下:
在这里插入图片描述
生命周期方法执行顺序如下:
在这里插入图片描述
可以看到TwoActivity此时处于可见但不可交互状态.启动ThreeActivity时正常的执行了onCreate() -> onStart() -> onResume(),但TwoActivity相比少了onStop() 方法的执行.

点击上图中遮罩部分,使第二个页面获得焦点
在这里插入图片描述
生命周期调用如下:
在这里插入图片描述
可以看到第二个页面只调用了onResume()方法.而且执行顺序是: 第三个页面onPause()->第二个页面onResume()->第三个页面onStop()->第三个页面onDestroy().接下来我们再点击返回按钮,应该是从第二个页面返回第一个页面(main),看一下生命周期会调用哪些方法
在这里插入图片描述
这里第一个页面(main)调用了onRestart()->onStart(),而没有onCreate(),是因为activity栈中有当前要显示(main)的activity实例.
在Main页面,直接点击HOME键,返回桌面,生命周期方法执行如下:
在这里插入图片描述
在桌面点击应用程序,如下:
在这里插入图片描述
点击返回键,正常退出应用程序:
在这里插入图片描述
在我们页面交互中,界面总是非A即B的状态.所以A跳转B,在A执行到onPause()之后,B页面没有实例,就onCreate()->onStart()->onResume();有则调用onRestart()->onResume(),一定是在B的onResume()执行后A才执行onStop() . 页面处于可见不可交互状态时,调用onPause().

activity几种跳转方式

方法一、

显式启动

Intent内部直接声明要启动的activity所对应的class。

Intent intent=new Intent(MainActivity.this, SecondActivity.class); startActivity(intent);

SecondActivity.start(MainActivity.this, url);

在SecondActivity.class里创建static方法

public static void start(Context context, String url) {
	start(context, url,“no”);
}
public static void start(Context context, String url, String isMy) {
	Intent starter = new Intent(context, WebActivity.class);
	starter.putExtra("URL", url);
	starter.putExtra(“ISMY”, isMy);
	context.startActivity(starter);
}

方法二、

隐式启动

进行三个匹配,一个是action, 一个是category,一个是data,全部或部分匹配,应用于广播原理BroadcastReceiver。

在AndroidManifest.xml文件里配置Activity的属性

action的名字要和Intent跳转内容一样

例如:

<activity

android:name="com.example.android.tst.SecondActivity" android:label="@string/title_activity_main" >

	<intent-filter>

		<action android:name="com.example.android.tst.SecondActivity"/>

		<category android:name="android.intent.category.DEFAULT" />

	</intent-filter>

</activity>

在需要用跳转的地方(使用与Adapter等不能直接显示跳转的地方)

Intent intent=new Intent("com.example.android.tst.SecondActivity"); startActivity(intent);

方法三、

跳转后,当返回时能返回数据

在MainActivity.java里面

Intent intent = new Intent(MainActivity.this, OtherActivity.class);
intent.putExtra("a", a);
intent.putExtra("b", b);
// startActivity(intent);

并不能返回结果,要用

startActivityForResult(intent, REQUESTCODE); //REQUESTCODE--->1

// 获取结果

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
	super.onActivityResult(requestCode, resultCode, data);
	// RESULT_OK,判断另外一个activity已经结束数据输入功能,Standard activity result:
	// operation succeeded. 默认值是-1
	if (resultCode == 2) {
		if (requestCode == REQUESTCODE) {
			int three = data.getIntExtra("c", 0);
			//设置结果显示框的显示数值
			result.setText(String.valueOf(three));
		}
	}
}

在OtherActivity.java里面

此处并不是创建而是直接获取一个intent对象Return the intent that started this activity.

Intent intent = getIntent();
int a = intent.getIntExtra("a", 0); // 没有输入值默认为0
int b = intent.getIntExtra("b", 0); // 没有输入值默认为0
int c=a+b;

处理完获取数据后,在结束本Activity的时候回传数据

Intent intent = new Intent();
intent.putExtra("c", c); //将计算的值回传回去

//通过intent对象返回结果,必须要调用一个setResult方法,
//setResult(resultCode, data);第一个参数表示结果返回码,一般只要大于1就可以

setResult(2, intent);

activity向fragment传值

activity与fragment之间进行数据传递是,在Activity中将要传递的数据封装在一Bundle中,使用setArgument(Bundel bundel)方法传递数据,在要传递到的Fragment中 使用this.getArgment(),得到传递到的Bundle。下面分别说明两种方法:

第一种方法:
Main_activity中的布局:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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="match_parent"
    tools:context=".MainActivity">
 
    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:layout_marginTop="8dp"
        android:text="传递数据"
        app:layout_constraintBottom_toTopOf="@+id/guideline"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
 
    <android.support.constraint.Guideline
        android:id="@+id/guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_begin="255dp" />
 
    <FrameLayout
        android:id="@+id/framelayout"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@+id/guideline"
        app:layout_constraintVertical_bias="0.0"></FrameLayout>
</android.support.constraint.ConstraintLayout>
FrameLayout用于动态的更新fragment。

Activity:

public class MainActivity extends AppCompatActivity  {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button =(Button)findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                FirstFragment fragment = new FirstFragment();
                Bundle bundle = new Bundle();
                bundle.putString("data","传递到的数据");
                fragment.setArguments(bundle);//数据传递到fragment中
                FragmentManager fragmentManager = getSupportFragmentManager();
                FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
                fragmentTransaction.replace(R.id.framelayout,fragment);
                fragmentTransaction.commit();
            }
        });
 
    }
 
}

replace方法的第二个参数不能使用new 类名();这样数据是不能传递的
Fragment:

public class FirstFragment extends Fragment {
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_first,container,false);
        TextView textView = (TextView)view.findViewById(R.id.textview);
        Bundle bundle =this.getArguments();//得到从Activity传来的数据
        String mess = null;
        if(bundle!=null){
            mess = bundle.getString("data");
        }
        textView.setText(mess);
        return view;
    }
}

这样一来当按下Button的时候,碎片会加载进Framelayout中,并且bundle中的数据也会传递到fragment的textview中。第一中方法很简单,但是实现起来可能有一些麻烦,下面我们新建一个Fragment:

public class BlankFragment extends Fragment {
    // TODO: Rename parameter arguments, choose names that match
    // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
    private static final String ARG_PARAM1 = "param1";
    private static final String ARG_PARAM2 = "param2";
 
    // TODO: Rename and change types of parameters
    private String mParam1;
    private String mParam2;
 
    private OnFragmentInteractionListener mListener; 
 
    public BlankFragment() {
        // Required empty public constructor
    }
 
    /**
     * Use this factory method to create a new instance of
     * this fragment using the provided parameters.
     *
     * @param param1 Parameter 1.
     * @param param2 Parameter 2.
     * @return A new instance of fragment BlankFragment.
     */
    // TODO: Rename and change types and number of parameters
    public static BlankFragment newInstance(String param1, String param2) {
        BlankFragment fragment = new BlankFragment();
        Bundle args = new Bundle();
        args.putString(ARG_PARAM1, param1);
        args.putString(ARG_PARAM2, param2);
        fragment.setArguments(args);
        return fragment;
    }
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mParam1 = getArguments().getString(ARG_PARAM1);
            mParam2 = getArguments().getString(ARG_PARAM2);
        }
    }
 
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_blank, container, false);
        TextView textView = view.findViewById(R.id.textview);
        Bundle bundle =getArguments();
        String message = null;
        if(bundle!=null){
            message = bundle.getString(ARG_PARAM1);
        }
        textView.setText(message);
        return view;
    }
 
    // TODO: Rename method, update argument and hook method into UI event
    public void onButtonPressed(Uri uri) {
        if (mListener != null) {
            mListener.onFragmentInteraction(uri);
        }
    }
 
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (context instanceof OnFragmentInteractionListener) {
            mListener = (OnFragmentInteractionListener) context;
        } else {
 
            throw new RuntimeException(context.toString()
                    + " must implement OnFragmentInteractionListener");
        }
    }
 
    @Override
    public void onDetach() {
        super.onDetach();
        mListener = null;
    }
 
    
    public interface OnFragmentInteractionListener {
        // TODO: Update argument type and name
        void onFragmentInteraction(Uri uri);
    }
}

在这个类中发现一个newInstance的静态方法,仔细看这个方法似乎可以用来将数据传递到fragment中
重新定义MainActivity:

public class MainActivity extends AppCompatActivity implements BlankFragment.OnFragmentInteractionListener {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button =(Button)findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                BlankFragment blankFragment = BlankFragment.newInstance("传递来的数据1","传递来的数据2");
                FragmentManager fragmentManager = getSupportFragmentManager();
                FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
                fragmentTransaction.replace(R.id.framelayout,blankFragment);
                fragmentTransaction.commit();
            }
        });
 
    }
 
    @Override
    public void onFragmentInteraction(Uri uri) {
 
    }
}

activity中必须实现fragment类中的内部接口,然后我们见直接定义一个BlankFragment就可以直接不是局传递过去啦·,在onCreateView中接收数据就行啦,我这里只接收到一个数据。结果一样可以运行。

配置activity的ConfigChanges

android中的组件Activity在manifest.xml文件中可以指定参数android:ConfigChanges,用于捕获手机状态的改变。 在Activity中添加了android:configChanges属性,在当所指定属性(Configuration Changes)发生改变时,通知程序调用onConfigurationChanged()函数。
设置方法:将下列字段用“|”符号分隔开,例如:“locale|navigation|orientation”

“mcc“ 移动国家号码,由三位数字组成,每个国家都有自己独立的MCC,可以识别手机用户所属国家。
“mnc“ 移动网号,在一个国家或者地区中,用于区分手机用户的服务商。
“locale“ 所在地区发生变化。
“touchscreen“ 触摸屏已经改变。(这不应该常发生。)
“keyboard“ 键盘模式发生变化,例如:用户接入外部键盘输入。
“keyboardHidden“ 用户打开手机硬件键盘
“navigation“ 导航型发生了变化。(这不应该常发生。)
“orientation“ 设备旋转,横向显示和竖向显示模式切换。
“fontScale“ 全局字体大小缩放发生改变

对android:configChanges属性,一般认为有以下几点:
1、不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次

2、设置Activity的android:configChanges="orientation"时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次

3、设置Activity的android:configChanges="orientation|keyboardHidden"时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法

但是,自从Android 3.2(API 13),在设置Activity的android:configChanges=“orientation|keyboardHidden"后,还是一样会重新调用各个生命周期的。因为screen size也开始跟着设备的横竖切换而改变。所以,在AndroidManifest.xml里设置的MiniSdkVersion和 TargetSdkVersion属性大于等于13的情况下,如果你想阻止程序在运行时重新加载Activity,除了设置"orientation”,你还必须设置"ScreenSize"。
解决方法:
AndroidManifest.xml中设置android:configChanges="orientation|screenSize“

配置activity的screenOrientation屏幕方向属性

android:screenOrientation用于控制activity启动时方向,取值可以为:
unspecified,默认值,由系统决定,不同手机可能不一致
landscape,强制横屏显示
portrait,强制竖屏显
behind,与前一个activity方向相同
sensor,根据物理传感器方向转动,用户90度、180度、270度旋转手机方向,activity都更着变化
sensorLandscape,横屏旋转,一般横屏游戏会这样设置
sensorPortrait,竖屏旋转
nosensor,旋转设备时候,界面不会跟着旋转。初始化界面方向由系统控制
user,用户当前设置的方向
reversePortrait,竖屏旋转180
reverseLandscape,横屏旋转180

后台服务service

1.Service简单概述

2.Service在清单文件中的声明

3.Service启动服务实现方式及其详解

4.Service绑定服务的三种实现方式

5.关于启动服务与绑定服务间的转换问题

6.前台服务以及通知发送

7.服务Service与线程Thread的区别

8.管理服务生命周期的要点

9.Android 5.0以上的隐式启动问题及其解决方案

10.保证服务不被杀死的实现思路

这里推荐一篇很详细的讲service的文章
关于Android Service真正的完全详解,你需要知道的一切

ContentProvider内容提供者

ContentProvider是Android中提供的专门用于不同应用间数据交互和共享的组件。
关于ContentProvider我们需要了解以下几个方面

1.ContentProvider的使用场景

一般不常用ContentProvider,虽然ContentProvider具有数据管理和数据共享的功能,但是多半的优势还是集中在数据共享上,在数据共享的前提下,这个数据管理的优势也才能更加明显。像一些系统内置应用,联系人,日历和短信,就比较适合使用ContentProvider,因为他们经常需要给其他App提供数据。而对于像普通App,一般出于安全原因,不会把数据提供给第三方App使用,而在App内部访问数据库的话,完全可以自己实现一套数据库访问框架,因为ContentProvider为了屏蔽数据库的访问细节,实质上是在其基础上再封装了一层接口而已,而对于App内部的数据库访问来说,没有这个必要

2.如何使用ContentProvider

这里给大家推荐一篇文章写的很详细,Android四大组件——ContentProvider(基础篇)

广播BroadcastReceiver

广播是什么

广播接收器

本地广播

系统广播

广播内部的实现机制

推荐一个很详细的博客超详细 Broadcast

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值