Android其余三大组件和Fragment | 青训营笔记

111 阅读9分钟

这是我参与「第四届青训营 」笔记创作活动的第2天

二、Fragment

1、什么是Fragment

可以把Fragment简单理解为,显示在Activity中的子Activity。因为Fragment是依附于Activity存在的,因此它的生命周期期受到Activity的生命周期影响

2、Fragment的生命周期

image-20220803102357362.png

上图出自字节跳动客户端基础知识必备ppt

Fragment比Activity多了几个生命周期的回调方法

  • onAttach(Activity) 当Fragment与Activity发生关联的时候调用
  • onCreateView(LayoutInflater, ViewGroup, Bundle) 创建该Fragment的视图
  • onActivityCreated(Bundle) 当Activity的onCreated方法返回时调用
  • onDestroyView() 与onCreateView方法相对应,当该Fragment的视图被移除时调用
  • onDetach() 与onAttach方法相对应,当Fragment与Activity取消关联时调用

3、Fragment的使用方法

3.1创建Fragment布局文件

布局文件item_fragment.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/re
s/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="@mipmap/ic_launcher" />
</RelativeLayout>
​
3.2创建Fragment子类,继承Fragment类,加载item_fragment.xml布局文件
public class MyFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup
container, Bundle savedInstanceState) {
/*
* 参数1:布局文件的id
* 参数2:容器
* 参数3:是否将这个生成的View添加到这个容器中去
* 作用是将布局文件封装在一个View对象中,并填充到此Fragment中
* */
View v = inflater.inflate(R.layout.item_fragment, contai
ner, false);
return v;
}
}
​
3.3在Activity中加载Fragment
3.3.1静态加载

直接在Aactivity布局中声明 Fragment,将其作为 元素插入的 Activity 布局

Activity对应的布局文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/
android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.usher.fragment.MainActivity">
​
<fragment
android:id="@+id/myfragment"
<!--fragment android:name = "fragment所在文件路径"-->         
android:name="com.usher.MyFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
​
3.3.2动态加载

可以在 Activity 布局中声明一个 FramLayout 作为 Fragment 容器,利用 java 代码将其插入容器:

Activity对应的布局文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/
android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.usher.fragment.MainActivity">
<FrameLayout
android:id="@+id/myframelayout"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

MainActivity类加载Fragment

public class MainActivity extends AppCompatActivity {
private FragmentManager manager;
private MyFragment fragment1;
@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  fragment1 = new MyFragment();
​
//初始化FragmentManager对象
manager = getSupportFragmentManager();
//使用FragmentManager对象用来开启一个Fragment事务
FragmentTransaction transaction = manager.beginTransaction();
//默认显示fragment1
transaction.add(R.id.myframelayout, fragment1).commit();
}
​

FragmentTransaction对象【以下直接用transaction代替】,transaction的方法 主要有以下几种:

  • transaction.add() 向Activity中添加一个Fragment
  • transaction.remove() 从Activity中移除一个Fragment,如果被移除的Fragment 没有添加到回退栈(回退栈后面会详细说),这个Fragment实例将会被销毁
  • transaction.replace() 使用另一个Fragment替换当前的,实际上就是remove() 然后add()的合体
  • transaction.hide() 隐藏当前的Fragment,仅仅是设为不可见,并不会销毁
  • transaction.show() 显示之前隐藏的Fragment
  • detach() 会将view从UI中移除,和remove()不同,此时fragment的状态依然由 FragmentManager维护
  • attach() 重建view视图,附加到UI上并显示
  • ransatcion.commit() 提交事务

注意:在add/replace/hide/show以后都要commit其效果才会在屏幕上显示出来

4、 Fragment与Activity之间的通信

4.1组件获取

1.Fragment获取Activity中的组件: getActivity().findViewByld(R.id.xxx)

2.Activity获取Fragment中的组件:getFragmentManager.findFragmentByld(R.id.fragment_xxx)

4.2数据传递

1 Activity传数据给Fragment: setArguments(Bundle bundle)

2 Fragment传数据给Activity:

a通过对象直接传递(方法调用/接口调用)

b通过viewmodel / handler / broadcast / eventbus等通信

4.3接口回调的具体实现
  1. 在 Fragment 中定义回调接口,在接口中定义的方法是通信的具体实现方法。
public class FragmentOne extends Fragment  {
​
public interface FOneBtnClickListener {
    void onFOneBtnClick();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_one, container, false);
    return view;
}
}

2.在宿主 Activity 中实现回调接口,并复写接口中的方法。

public class MainActivity extends Activity implements FOneBtnClickListener{
    private FragmentOne mFOne;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);
        mFOne = new FragmentOne();
        FragmentManager fm = getFragmentManager();
        FragmentTransaction tx = fm.beginTransaction();
        tx.add(R.id.id_content, mFOne, "ONE");
        tx.commit();
}
    
@Override
public void onFOneBtnClick() {
    //复写接口方法
}
}

3.利用 Fragment 类中的 onAttach() 回调函数,此函数在 Fragment 被添加到宿主 Activity 时会返回此 Activity 对象,利用此对象实例化一个回调接口,这样就可以通过这个实例实现 Fragment 和 Activity的通信。同时通过try catch 结构保证宿主已经实现了回调接口。

     @Override
        public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        Activity activity = (Activity) context;
​
​
        //使用onAttach方法传过来的context参数实例化接口,使用此接口内的函数在Fragment和Activity之间通信
        try {  //check实现接口是是否override了Read方法
            fOneBtnClickListener = (FragmentOne.FOneBtnClickListener) activity;
        }catch (ClassCastException e){
            throw new ClassCastException(activity.toString() + "must override onFOneBtnClick...");
        }
    }

Fragment与Activity之间的通信参考Android Fragment 基本使用方法

更加详细的Fragment与Activity的交互参考文章juejin.cn/post/711501…

三、Service

1.什么是Service

Service是Android程序中四大基础组件之一,它和Activity一样都是Context的子类, 只不过它没有UI界面,是在后台运行的组件。

Service是Android中实现程序后台运行的解决方案,它非常适用于去执行那些不需 要和用户交互而且还要求长期运行的任务。

2.Service的生命周期

下图出自字节跳动客户端基础知识必备ppt

image-20220803220449185.png

左图显示了使用 startService() 所创建的服务的生命周期,右图显示了使用 bindService() 所创建的服务的生命周期。

OnCreate()

系统在service第一次创建时执行此方法,来执行只运行一次的初始化工作。如果 service已经运行,这个方法不会被调用。

onStartCommand()

每次客户端调用startService()方法启动该Service都会回调该方法(多次调用)。一 旦这个方法执行,service就启动并且在后台长期运行。通过调用stopSelf()或 stopService()来停止服务。

OnBind()

当组件调用bindService()想要绑定到service时(比如想要执行进程间通讯)系统调用 此方法(一次调用,一旦绑定后,下次再调用bindService()不会回调该方法)。在 你的实现中,你必须提供一个返回一个IBinder来以使客户端能够使用它与service通 讯,你必须总是实现这个方法,但是如果你不允许绑定,那么你应返回null。

OnUnbind()

当前组件调用unbindService(),想要解除与service的绑定时系统调用此方法(一次 调用,一旦解除绑定后,下次再调用unbindService()会抛出异常)

OnDestory()

系统在service不再被使用并要销毁时调用此方法(一次调用)。service应在此方法 中释放资源,比如线程,已注册的侦听器,接收器等等.这是service收到的最后一 个调用。

3、Service的几种典型使用实例

1.不可交互的后台服务

不可交互的后台服务即是普通的Service,通过startService()方式开启。Service的 生命周期很简单,分别为onCreate、onStartCommand、onDestroy这三个。

创建服务类:

public class BackService extends Service {
    private Thread mThread;
    
    @Override
    public void onCreate() {
        super.onCreate();
    }
​
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        System.out.println("onBind");
        return null;
    }
​
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //执行耗时操作
        mThread = new Thread() {
            //....
            System.out.println("执行耗时操作");
​
        }.start;
        return super.onStartCommand(intent, flags, startId);
    }
    @Override
    public void onDestroy() {
        super.onDestroy();
        //停止线程
        mThread.interrupt();
    }
​
}

配置服务:

<service android:name=".BackService">
</service>

如果想配置成远程服务,加如下代码:

android:process="remote"

配置好Service类,只需要在前台,调用startService()方法,就会启动耗时操作。

注意:

  1. 不运行在一个独立的进程中,它同样执行在UI线程中,因此,在Service中创建了 子线程来完成耗时操作。
  2. 当Service关闭后,如果在onDestory()方法中不关闭线程,你会发现我们的子线 程进行的耗时操作是一直存在的,此时关闭该子线程的方法需要直接关闭该应用程 序。因此,在onDestory()方法中要进行必要的清理工作。
2.可交互的后台服务

可交互的后台服务是指前台页面可以调用后台服务的方法,通过bindService()方式 开启。Service的生命周期很简单,分别为onCreate、onBind、onUnBind、 onDestroy这四个。

可交互的后台服务实现步骤是和不可交互的后台服务实现步骤是一样的,区别在于 启动的方式和获得Service的代理对象。

创建服务类

和普通Service不同在于这里返回一个代理对象,返回给前台进行获取,即前台可以 获取该代理对象执行后台服务的方法

public class BackService extends Service {
    private Thread mThread;
    
    @Override
    public void onCreate() {
        super.onCreate();
    }
​
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        System.out.println("onBind");
        return new MyBinder();
    }
​
    //需要返回给前台的Binder类
    class MyBinder extends Binder {
        public void showTip(){
            System.out.println("我是来此服务的提示");
        }
    }
​
    @Override
    public void onDestroy() {
        super.onDestroy();
    }
​
}

前台调用 通过以下方式绑定服务:

private ServiceConnection con = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        BackService.MyBinder myBinder = (BackService.MyBinder) service;
        myBinder.showTip();
    }
    @Override
    public void onServiceDisconnected(ComponentName name) {
        
    }
};
​
bindService(mIntent,con,BIND_AUTO_CREATE);

当建立绑定后,onServiceConnected中的service便是Service类中onBind的返回值。如此便可以调用后台服务类的方法,实现交互。

当调用unbindService()停止服务,同时要在onDestory()方法中做好清理工作。

注意:通过bindService启动的Service的生命周期依附于启动它的Context。因此 当前台调用bindService的Context销毁后那么服务会自动停止

3.混合型后台服务

将上面两种启动方式结合起来就是混合性交互的后台服务了,即可以单独运行后台 服务,也可以运行后台服务中提供的方法,其完整的生命周期是:onCreate- >onStartCommand->onBind->onUnBind->onDestroy

4.前台服务

所谓前台服务只不是通过一定的方式将服务所在的进程级别提升了。前台服务会一直有一个正在运行的图标在系统的状态栏显示,非常类似于通知的效果

由于后台服务优先级相对比较低,当系统出现内存不足的情况下,它就有可能会被 回收掉,所以前台服务就是来弥补这个缺点的,它可以一直保持运行状态而不被系 统回收。

创建服务类

在Service的基础上创建一个Notification,然后使用 Service的startForeground()方法即可启动为前台服务。

public class BackService extends Service {
    private Thread mThread;
    
    @Override
    public void onCreate() {
        super.onCreate();
    }
​
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        System.out.println("onBind");
        return null;
    }
    
    private void beginForeService() {
        //创建通知
        Notification.Builder mBuilder = new Notification.Builder(this)
            .setSmallIcon(R.mipmap.ic_launcher)
            .setContentText("2017-2-27")
            .setContentText("您有一条未读短信...");
        //创建点跳转的Intent(这个跳转是跳转到通知详情页)
        Intent intent = new Intent(this,NotificationShow.class);
        //创建通知详情页的栈
        TaskStackBuilder stackBulider = TaskStackBuilder.create(this);
        //为其添加父栈 当从通知详情页回退时,将退到添加的父栈中
        stackBulider.addParentStack(NotificationShow.class);
        PendingIntent pendingIntent = stackBulider.getPendingIntent(0,PendingIntent.FLAG_UPDATE_CURRENT);
        //设置跳转Intent到通知中
        mBuilder.setContentIntent(pendingIntent);
        //获取通知服务
        NotificationManager nm = (NotificationManager) getSystem
            Service(Context.NOTIFICATION_SERVICE);
        //构建通知
        Notification notification = mBuilder.build();
        //显示通知
        nm.notify(0,notification);
        //启动前台服务
        startForeground(0,notification);
    }
}
​
​
    @Override
    public void onDestroy() {
        super.onDestroy();
        //停止线程
        mThread.interrupt();
    }
​
}

启动前台服务

startService(new Intent(this, ForeService.class));

四、BroadcastReceiver

自己总结的不是很好,参考文章:

1.juejin.cn/post/705216…

2.juejin.cn/post/699320…

五、ContentProvider

参考文章:juejin.cn/post/699330…