Android Mvp案例解析

news2024/11/25 14:37:32

目录

    • 后端数据接口
      • 数据格式
    • App客户端
      • 布局逻辑
        • 主界面布局
      • M(Model)
      • V(View)
      • P(Presenter)
      • Okhttp+Retrofit+RxJava网络http请求
    • Mvp架构-初学者
      • MVP架构的契约者

后端数据接口

接口地址:https://apis.tianapi.com/caipu/index

请求示例:https://apis.tianapi.com/caipu/index?key=你的APIKEY&word=黄瓜

支持协议:http/https

请求方式:get/post

返回格式:utf-8 json

数据格式

{
    "code": 200,
    "msg": "success",
    "result": {
        "curpage": 1,
        "allnum": 5,
        "newslist": [
            {
                "id": "90e761e707dd996c431992c8e2a0a88b",
                "ctime": "2023-07-07 08:29",
                "title": "为避免轨道碰撞 SpaceX星链卫星6个月内“让路”",
                "description": "7月7日消息,在过去6个月里,为了避免轨道碰撞,埃隆·马斯克(ElonMusk)旗下SpaceX公司的星链卫星机动次数激增,这引发了人们对卫星长期可持续性运行的担忧,因为未来几年将有数万颗新卫星进入轨道。在最近向美国联邦通信委员会(FCC...[]",
                "source": "网易科技",
                "picUrl": "https://nimg.ws.126.net/?url=http%3A%2F%2Fcms-bucket.ws.126.net%2F2023%2F0707%2F0f703849p00rxeh90001cc0009c0070c.png&thumbnail=200y140&quality=100&type=jpg",
                "url": "https://www.163.com/tech/article/I91GDFVG00097U81.html"
            },
            {
                "id": "e8e49d6dd77be1a6ea82929a14abe6ef",
                "ctime": "2023-07-07 06:32",
                "title": "推特指责 Meta 挖角员工创建 Threads",
                "description": "",
                "source": "网易科技",
                "picUrl": "https://nimg.ws.126.net/?url=http%3A%2F%2Fbjnewsrec-cv.ws.126.net%2Flittle730693cc23fj00rxebr3004xc000fw00lcg.jpg&thumbnail=200y140&quality=100&type=jpg",
                "url": "https://www.163.com/dy/article/I919NRS10511B8LM.html"
            },
            {
                "id": "55cf44c545e02e8d9fc30e8b07980d3d",
                "ctime": "2023-07-06 21:08",
                "title": "分析人士:Meta的Threads对马斯克的Twitter构成",
                "description": "",
                "source": "网易科技",
                "picUrl": "https://nimg.ws.126.net/?url=http%3A%2F%2Fbjnewsrec-cv.ws.126.net%2Flittle32088c42c3cj00rxdlrp000xc000sg00hgg.jpg&thumbnail=200y140&quality=100&type=jpg",
                "url": "https://www.163.com/dy/article/I909EBMH0511B8LM.html"
            },
            {
                "id": "0bbc1f1f13121586804bb72e245448b2",
                "ctime": "2023-07-06 20:26",
                "title": "中国电信研究院副院长李安民:发展元宇宙,要提",
                "description": "",
                "source": "网易科技",
                "picUrl": "https://nimg.ws.126.net/?url=http%3A%2F%2Fbjnewsrec-cv.ws.126.net%2Flittle64910ee1b6bj00rxctez005jd000j600anp.jpg&thumbnail=200y140&quality=100&type=jpg",
                "url": "https://www.163.com/dy/article/I9071U0D0519DFFO.html"
            },
            {
                "id": "1962b01db3569398109fe336d5f8ff29",
                "ctime": "2023-07-06 20:42",
                "title": "XR“失宠”?头显出货量下降超三成,元宇宙再降",
                "description": "",
                "source": "网易科技",
                "picUrl": "https://nimg.ws.126.net/?url=http%3A%2F%2Fbjnewsrec-cv.ws.126.net%2Flittle252508775e3j00rxdev60080c000se00fsg.jpg&thumbnail=200y140&quality=100&type=jpg",
                "url": "https://www.163.com/dy/article/I907V12305199NPP.html"
            }
        ]
    }
}

App客户端

MVP规范接口:IView、IModel、IPresenter,用于规范接口

package com.xzln.eatwhatjava.view;

public interface IView {}  // 用于规范View层接口
package com.xzln.eatwhatjava.model;

public interface IModel {}  // 用于规范Model层接口
package com.xzln.eatwhatjava.presenter;

import com.xzln.eatwhatjava.view.IView;

/**
 * 抽离公共接口
 *      所有的P层对象都公有
 * 	    而V层和M层对象则都是根据具体的业务进行定制
 * @param <V>
 */
public interface IPresenter<V extends IView> {
    /**
     * 依附生命view
     * @param view v层对象
     */
    void attachView(V view);

    /**
     * 分离View
     */
    void detachView();

    /**
     * 判断View是否已经销毁
     * @return 是否销毁
     */
    boolean isViewAttached();
}

回调规范接口:ICallBack

package com.xzln.eatwhatjava.contact;

public interface ICallBack<T, K> {
    void onSuccess(T data);
    void onFail(K data);
}

布局逻辑

ViewPager2+Fragment

其中Fragment由refreshLayout+RecycleView组成,RecycleView放新闻条目。

ViewPager2用于对新闻进行分类

主界面布局
// activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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=".view.MainActivity"
    android:orientation="vertical">
    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tab_layout"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:background="@color/colorPrimary"
        app:tabGravity="fill"
        app:tabMode="fixed"
        app:tabTextColor="@android:color/white" />

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/view_pager2"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>
image-20230707135501986

M(Model)

// INewsModel.java
package com.xzln.eatwhatjava.model;

import com.xzln.eatwhatjava.contact.ICallBack;

/**
 * 定义规范接口:(新闻)
 *      单一接口的M层
 */
public interface INewsModel extends IModel {
    // post请求
    void getData(int page, int num, final ICallBack callback);
}
// NewsModel.java
package com.xzln.eatwhatjava.model;

import android.util.Log;

import com.xzln.eatwhatjava.api.NewsApi;
import com.xzln.eatwhatjava.bean.NewsListBean;
import com.xzln.eatwhatjava.bean.Result;
import com.xzln.eatwhatjava.config.NewsConfig;
import com.xzln.eatwhatjava.contact.ICallBack;
import com.xzln.eatwhatjava.utils.RetrofitUtils;

import io.reactivex.Observable;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
/**
 * 定义了News功能模块的M层类
 *       调用retrofit网络API,通过CallBack将数据传到P层
 * TODO: onNext中发生错误的情况 和 onError中发生错误的情况 都有哪些
 */
public class NewsModel implements INewsModel {
    public static final String TAG = "com.xzln.eatwhatjava.model.NewsModel";

    @Override
    public void getData(int page, int num, ICallBack callback) {
        fetchKejiNews(page, num)
                .observeOn(AndroidSchedulers.mainThread())  // Android 主线程观察
                .subscribeOn(Schedulers.io())   // 消费
                .subscribe(new Observer<Result<NewsListBean>>() { // URL访问成功
                    @Override
                    public void onSubscribe(Disposable d) {}
                    @Override
                    public void onNext(Result<NewsListBean> newsListBeanResult) {  // 数据迭代器?
                        if (null == newsListBeanResult) { callback.onFail("出现错误: null == newsListBeanResult"); } 
                        else if (newsListBeanResult.getCode() != 200) { callback.onFail(newsListBeanResult.getMsg());} 
                        else { callback.onSuccess(newsListBeanResult); }
                    }
                    @Override
                    public void onError(Throwable e) {  // 访问错误或者数据解析错误
                        e.printStackTrace();
                        callback.onFail("出现错误: onError");
                    }

                    @Override
                    public void onComplete() { Log.d(TAG, "onComplete"); }
                });
    }

    protected Observable<Result<NewsListBean>> fetchKejiNews(int page, int num) {
        return RetrofitUtils.getRetrofit().create(NewsApi.class).fetchKejiNews(NewsConfig.getKejiNewsFieldMap(page, num));
    }
}

V(View)

// INewsView.java
package com.xzln.eatwhatjava.view;

import com.xzln.eatwhatjava.presenter.IPresenter;

/**
 * 定义News功能模块的View类的规范接口
 *      (P层和V层的桥梁)
 * @param <P> P层对象类型
 * @param <T> P层访问成功时返回的类型
 * @param <V> P层访问失败时返回的类型
 */
public interface INewsView<P extends IPresenter, T, V> extends IView{
    void showNewsSuccess(T newsBeans);
    void showNewsFail(V data);
}
package com.xzln.eatwhatjava.base;

import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.xzln.eatwhatjava.presenter.IPresenter;
import com.xzln.eatwhatjava.view.IView;

/**
 * 抽象类,Fragment的公共抽象类
 * @param <P>
 */
public abstract class BaseFragment<P extends IPresenter> extends Fragment implements IView {
    protected P mPresenter;  // P层对象,用于和V层、M层进行联系
    protected View mView;  // V层对象,通过V层对象完成P和V之间的联系

    /**
     * 完成P层对象的创建和初始化
     * @param savedInstanceState If the fragment is being re-created from
     * a previous saved state, this is the state.
     */
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initPresenter();
        init();
    }

    /**
     *
     * @param inflater The LayoutInflater object that can be used to inflate
     * any views in the fragment,
     * @param container If non-null, this is the parent view that the fragment's
     * UI should be attached to.  The fragment should not add the view itself,
     * but this can be used to generate the LayoutParams of the view.
     * @param savedInstanceState If non-null, this fragment is being re-constructed
     * from a previous saved state as given here.
     *
     * @return 视图对象
     */
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        mView = inflater.inflate(getLayoutId(), container, false);
        return mView;
    }

    /**
     * Fragment销毁时,先取消P层对象和Fragment的绑定,避免空指针异常
     */
    @Override
    public void onDestroy() {
        if (mPresenter != null && mPresenter.isViewAttached()) {
            mPresenter.detachView();
        }
        super.onDestroy();
    }

    /**
     * 创建和初始化P层对象,将P层对象生命周期与当前Fragment绑定
     */
    protected void initPresenter() {
        mPresenter = createPresenter();
        //绑定生命周期
        if (mPresenter != null) {
            mPresenter.attachView(this);
        }
    }

    /**
     * 抽象方法,由子类根据对应的布局来实现,返回其布局ID,用于Fragment的OnCreateView
     * @return 布局ID
     */
    public abstract int getLayoutId();

    /**
     * 创建一个Presenter
     *
     * @return P层对象
     */
    protected abstract P createPresenter();

    /**
     * 用于子类的其他数据的初始化
     */
    protected abstract void init();


}
//NewsFragment.java
package com.xzln.eatwhatjava.view;

import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;

import com.xzln.eatwhatjava.R;
import com.xzln.eatwhatjava.adapter.NewsRecycleViewAdapter;
import com.xzln.eatwhatjava.base.BaseFragment;
import com.xzln.eatwhatjava.bean.NewsBean;
import com.xzln.eatwhatjava.presenter.NewsPresenter;

import java.util.LinkedList;
import java.util.List;

/**
 * News的布局碎片
 */
public class NewsFragment extends BaseFragment<NewsPresenter> implements INewsView<NewsPresenter, List<NewsBean>, String> {
    private static final String TAG = "com.xzln.eatwhatjava.view.NewsFragment";
    protected int mNewsType;  // News的类别,由创建者传入
    protected SwipeRefreshLayout mSwipeRefreshLayout;  // 顶部的刷新布局
    protected RecyclerView mRecycleView;
    protected List<NewsBean> mNewsBeans;  // 数据列表
    protected int mCurPage = 0;  // 当前页码
    protected int mNumOfPage = 25;  // 每页中数据数目
    protected NewsRecycleViewAdapter mNewsRecycleViewAdapter;  // RecycleView适配器
    protected LinearLayoutManager mLinearLayoutManager;
    private int mTotalItemCount;/*recycleView下的总item数目*/
    private int mFirstVisibleItem;/*当前可见区内最后一个item的position*/
    private int mLastVisibleItem;/*当前可见区内最后一个item的position*/
    /*Return the current number of child views attached to the parent RecyclerView.*/
    private int mVisibleItemCount;/*当前recycleView下可见的item数*/

    public NewsFragment(int newsType) {  mNewsType = newsType; }

    /**
     * 刷新时调用:页码清零、数据清空、重新获取数据
     */
    protected void refreshView() {
        mCurPage = 0;
        mNewsBeans.clear();
        mNewsRecycleViewAdapter.clearData();
        mPresenter.getNewsData(mCurPage++, mNumOfPage);
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return super.onCreateView(inflater, container, savedInstanceState);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        mSwipeRefreshLayout = mView.findViewById(R.id.layout_swipe_refresh);
        mRecycleView = mView.findViewById(R.id.view_recycle);
        // mPresenter.getNewsData(mCurPage++, mNumOfPage);
        refreshView();
        setupListener();
    }

    /**
     * 用于创建P层对象,由子类具体定制实现
     * @return P层对象
     */
    @Override
    protected NewsPresenter<NewsFragment> createPresenter() { return new NewsPresenter<>(this); }

    /**
     * 由子类定制,用于完成子类中的数据初始化
     *  视图id的绑定不可在这完成。因为视图View对象的生成在OnCreateView,而init函数在OnCreate中
     */
    @Override
    protected void init() {
        mNewsBeans = new LinkedList<>();
        mNewsRecycleViewAdapter = new NewsRecycleViewAdapter(getActivity(), mNewsBeans);
    }

    /**
     * @return 视图布局文件的ID
     */
    @Override
    public int getLayoutId() { return R.layout.activity_fragment_main; }

    /**
     * 启动监听器
     * 1. 刷新头
     * 2. RecycleView适配器、布局管理器
     */
    private void setupListener() {
        mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                Log.d(TAG, "mSwipeRefreshLayout.setOnRefreshListener.onRefresh");
//                mSwipeRefreshLayout.setRefreshing(false);
                refreshView();
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() { mSwipeRefreshLayout.setRefreshing(false); }
                },2000);
            }
        });
        mLinearLayoutManager = new LinearLayoutManager(getActivity());
        mRecycleView.setLayoutManager(mLinearLayoutManager);
        mRecycleView.setAdapter(mNewsRecycleViewAdapter);
        /*recycleView*/
        mRecycleView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                Log.d(TAG, "onScrollStateChanged  -->  newState" + newState);
                // 获取数据逻辑
                // 滚动状态改变 && 最后一个position + 1 >= total
                if (newState == RecyclerView.SCROLL_STATE_DRAGGING &&
                        mLastVisibleItem + 1 >= mTotalItemCount &&
                        mVisibleItemCount <= mTotalItemCount) {
                    mPresenter.getNewsData(mCurPage++, mNumOfPage);
                
            }

            @Override
            public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                mLinearLayoutManager.findFirstVisibleItemPosition();
                mTotalItemCount = mLinearLayoutManager.getItemCount();
                mFirstVisibleItem = mLinearLayoutManager.findFirstVisibleItemPosition();
                mLastVisibleItem = mLinearLayoutManager.findLastVisibleItemPosition();/*当前可见区内最后一个item的position*/
                /*Return the current number of child views attached to the parent RecyclerView.*/
                mVisibleItemCount = mLinearLayoutManager.getChildCount();/*当前recycleView下可见的item数*/
            }
        });
    }

    /**
     * 与P层联系的函数
     * @param newsBeans P层成功返回的数据
     */
    @Override
    public void showNewsSuccess(List<NewsBean> newsBeans) {
        Log.d(TAG, "showNewsSuccess  newsBeans.size()=" + newsBeans.size());
        mNewsRecycleViewAdapter.addData(newsBeans);
    }

    /**
     * 与P层联系的函数
     * @param data P层失败返回的数据
     */
    @Override
    public void showNewsFail(String data) {
        Log.d(TAG, "showNewsSuccess");
    }
}

P(Presenter)

// IPresenter.java
package com.xzln.eatwhatjava.presenter;

import com.xzln.eatwhatjava.view.IView;

/**
 * 抽离公共接口
 *      所有的P层对象都公有
 * @param <V>
 */
public interface IPresenter<V extends IView> {
    /**
     * 依附生命view
     * @param view v层对象
     */
    void attachView(V view);

    /**
     * 分离View
     */
    void detachView();

    /**
     * 判断View是否已经销毁
     * @return 是否销毁
     */
    boolean isViewAttached();
}

// BasePresenter.java
package com.xzln.eatwhatjava.base;

import com.xzln.eatwhatjava.presenter.IPresenter;
import com.xzln.eatwhatjava.view.IView;

/**
 * IPresenter接口需要所有的P层实现类继承
 * 对于生命周期这部分功能P层都是通用的,因此可以抽出来一个抽象基类BasePresenter,去实现IPresenter的接口.
 * @param <V>
 */
public abstract class BasePresenter<V extends IView> implements IPresenter<V> {
    protected V mView;

    @Override
    public void attachView(V view) { mView = view; }

    @Override
    public void detachView() { mView = null; }

    @Override
    public boolean isViewAttached() { return mView != null; }
}

P层对象中持有V层对象和M层对象
P层调用M层获取数据,并设置CallBack。通过CallBack完成M层和P层间的联系。
在CallBack中调用View对象,完成P层与V层的联系。

// NewsPresenter.java
package com.xzln.eatwhatjava.presenter;

import android.util.Log;

import com.xzln.eatwhatjava.base.BasePresenter;
import com.xzln.eatwhatjava.bean.NewsListBean;
import com.xzln.eatwhatjava.bean.Result;
import com.xzln.eatwhatjava.contact.ICallBack;
import com.xzln.eatwhatjava.model.INewsModel;
import com.xzln.eatwhatjava.model.NewsModel;
import com.xzln.eatwhatjava.view.INewsView;
import com.xzln.eatwhatjava.view.IView;

/**
 * News功能模块P层逻辑
 * @param <V>
 */
public class NewsPresenter<V extends IView> extends BasePresenter<V> {
    private static final String TAG = "com.xzln.eatwhatjava.presenter.NewsPresenter";
    protected INewsView mNewsView;
    protected INewsModel mNewsModel;
    public NewsPresenter(V view) {
        mNewsView = (INewsView) view;
        createModel();
    }

    private void createModel() {
        if (mNewsModel == null) { mNewsModel = new NewsModel(); }
    }

    public void getNewsData(int page, int num) {
        mNewsModel.getData(page, num,
                new ICallBack<Result<NewsListBean>, String>() {  // P层定义CallBack,从而将数据从
                    @Override
                    public void onSuccess(Result<NewsListBean> data) {
                        Log.d(TAG, ".getData.onSuccess");
                        mNewsView.showNewsSuccess(data.getResult().getNewslist());
                    }

                    @Override
                    public void onFail(String data) {
                        mNewsView.showNewsFail("数据获取失败");
                        Log.d(TAG, ".getData.onFail -->  " + data);
                    }
                });
    }
}

// ICallBack.java
package com.xzln.eatwhatjava.contact;

public interface ICallBack<T, K> {
    void onSuccess(T data);
    void onFail(K data);
}

Okhttp+Retrofit+RxJava网络http请求

RetrofitUtil

//RetrofitUtils.java
package com.xzln.eatwhatjava.utils;

import com.xzln.eatwhatjava.config.NewsConfig;

import java.util.concurrent.TimeUnit;

import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;

public class RetrofitUtils {
    private static Retrofit retrofit;
    private static OkHttpClient okhttp;

    /**
     * 自定义okhttp客户端
     *
     * @return okhttp客户端
     */
    public static OkHttpClient getOkhttp() {
        if (okhttp == null) {
            synchronized (RetrofitUtils.class) {
                if (okhttp == null) {
                    okhttp = new OkHttpClient.Builder()
                            .connectTimeout(15, TimeUnit.SECONDS)
                            .readTimeout(15, TimeUnit.SECONDS)
                            .writeTimeout(15, TimeUnit.SECONDS)
                            .build();
                }
            }
        }
        return okhttp;
    }

    /**
     * 单例retrofit客户端
     *
     * @return retrofit客户端
     */
    public static Retrofit getRetrofit() {
        if (retrofit == null) {
            synchronized (RetrofitUtils.class) {
                if (retrofit == null) {
                    retrofit = new Retrofit.Builder()
                            .baseUrl(NewsConfig.newsBaseUrl)
                            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                            .addConverterFactory(GsonConverterFactory.create())
                            .client(getOkhttp())
                            .build();
                }
            }
        }
        return retrofit;
    }
}

//NewsApi.java
package com.xzln.eatwhatjava.api;

import com.xzln.eatwhatjava.bean.NewsListBean;
import com.xzln.eatwhatjava.bean.Result;

import java.util.Map;
import io.reactivex.Observable;
import retrofit2.http.FieldMap;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.POST;

public interface NewsApi {
    // POST请求数据
    @FormUrlEncoded
    @POST("keji/index")
    Observable<Result<NewsListBean>> fetchKejiNews(@FieldMap Map<String, Object> map);
//    Observable<Result<List<NewsBean>>> fetchKejiNews(@FieldMap Map<String, Object> map);
}


Mvp架构-初学者

MVP架构是为了让各个模块之间降低耦合,方便维护,也可以让代码更简洁,让代码简洁的意思是让代码更清晰,并不是让代码更少;MVP契约者是为了进一步的低耦合、接口统一管理。

image.png

MVP对接口灵活的调用可以轻松的应对产品的变更。

presenter 类与View 和 Model 通信,做到视图和逻辑的解耦。

MVP架构的契约者

TODO

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1179484.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

APP埋点:页面统计与事件统计

我们平时所说的埋点&#xff0c;可以大致分为两部分&#xff0c;一部分是统计APP页面访问情况&#xff0c;即页面统计&#xff1b;另外一部分是统计APP内的操作行为&#xff0c;及自定义事件统计。 一、页面统计 页面统计&#xff0c;可以统计应用内各个页面的访问次数&#x…

fiddler抓包拦截请求转发到其他地址

使用Fiddler拦截请求转发到指定地址方便于本地调试&#xff0c;不需要进行打包切换地址&#xff0c;可以加快问题的确定修复效果 内容&#xff1a; 1&#xff1a;首先给app进行设置代理抓包内容&#xff0c;给进行 https://blog.csdn.net/qq_43717814/article/details/84317038…

精读《算法题 - 二叉树中的最大路径和》

今天我们看一道 leetcode hard 难度题目&#xff1a;二叉树中的最大路径和。 题目 二叉树中的 路径 被定义为一条节点序列&#xff0c;序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点&#xff0c;且不一定经过根节点…

Si4010 一款带有MCU SoC RF发射机芯片 无线遥控器

Si4010是一款完全集成的SoC RF发射机&#xff0c;带有嵌入式CIP-51 8051 MCU&#xff0c;专为1GHz以下ISM频带设计。该芯片针对电池供电的应用进行了优化&#xff0c;工作电压为1.8至3.6 V&#xff0c;待机电流小于10 nA的超低电流消耗。高功率放大器可提供高达10 dBm的输出功率…

服务器数据库中了elbie勒索病毒怎么办,elbie勒索病毒解密,数据恢复

网络技术的不断成熟&#xff0c;为企业的生产运营提供了强有力的支撑&#xff0c;但是&#xff0c;随之而来的网络安全威胁也不断增加。云天数据恢复中心陆陆续续接到很多企业的求助&#xff0c;企业的服务器数据库e遭到了elbie勒索病毒攻击&#xff0c;导致企业计算机系统瘫痪…

基于PHP语言的会员系统搭建(Docker版)

1、操作系统 ubuntu22 2、安装Docker # Add Dockers official GPG key: sudo apt-get update sudo apt-get install ca-certificates curl gnupg sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -…

【UART】UART QA

UART常见知识点整理 定义&#xff1a;Universal Asynchronous Receiver/Transmitter - 通用异步收发传输器。 特点&#xff1a;速率不快、可全双工、结构上一般由波特率产生器、UART发送器、UART接收器组成&#xff0c;硬件2-3线。 线&#xff1a;RXD&#xff0c;TXD&#xff0…

cordova Xcode打包ios以及发布流程(ionic3适用)

第一步 1、申请iOS证书 2、导入证书到钥匙串 第二步 1、xcode配置iOS证书 1.1用Xcode打开你的项目&#xff08;我的Xcode版本是新版&#xff09; 修改如下图 回到基本信息设置界面&#xff0c;Bundie 这项填写&#xff0c;最先创建的那个appid&#xff0c;跟创建iOS描述文件时选…

操作系统——文件空闲存储的空间管理(王道视频p64)

1.总体概述&#xff1a; 2.存储空间初始化&#xff1a; 3.空闲表法 4.空闲链&#xff1a; &#xff08;1&#xff09;空闲“盘块链”&#xff1a; &#xff08;2&#xff09;空闲“盘区链”&#xff1a; 5.位视图法&#xff1a; &#xff08;1&#xff09;基本结构&#xff1…

Leetcode刷题详解——二叉树剪枝

1. 题目链接&#xff1a;814. 二叉树剪枝 2. 题目描述&#xff1a; 给你二叉树的根结点 root &#xff0c;此外树的每个结点的值要么是 0 &#xff0c;要么是 1 。 返回移除了所有不包含 1 的子树的原二叉树。 节点 node 的子树为 node 本身加上所有 node 的后代。 示例 1&…

【Linux】进程管理命令,了解运维的五大性能监测

目录 一、程序与进程 1、程序是什么&#xff1f; 2、进程是什么&#xff1f;线程是什么&#xff1f;服务是什么&#xff1f; 3、进程的特点 4、进程使用内存的问题 二、运维相关命令学习 命令① ps 静态显示系统的进程 第一种静态查看进程信息&#xff1a;ps aux 第二…

解决若依Ruoyi 插入数据返回1,实现主键回填,返回主键ID

最开始的时候ruoyi 插入数据会返回1&#xff0c;开始以为是id&#xff0c;后来发现返回的逻辑是 0失败&#xff0c;1成功。 即便他利用mybatis设置了如下, useGeneratedKeys"true" keyProperty"id"​​​​​​​<selectKey></selectKey> 如…

【Redis】Redis整合SSMRedis中的缓存穿透、雪崩、击穿的原因以及解决方案(详解)

目录&#xff1a; 目录 一&#xff0c;SSM整合redis 二&#xff0c;redis注解式缓存 三&#xff0c;Redis中的缓存穿透、雪崩、击穿的原因以及解决方案&#xff08;附图&#xff09; 一&#xff0c;SSM整合redis 1.原因&#xff1a; 整合SSM和Redis可以提升系统的性能、可…

【迁移ORACLE数据到MogDB/openGauss时的字符集问题】

一、问题概述 ORACLE数据库在存储数据的时候&#xff0c;有时候会存在这样一种现象&#xff0c;一张表里的数据&#xff0c;既存在UTF8字符的&#xff0c;也存在GBK字符的&#xff0c;同时还有可能存在乱码数据。 NLS_CHARACTERSET是数据库字符集&#xff0c;NLS_NCHAR_CHARAC…

一键搞定!多个模型结果快速合并成一个三线表,model1、model2、model3。。。

欢迎参加郑老师2023年孟德尔随机化课程即将开始 发表文章后退款&#xff01;郑老师科研统计课程详情 在观察性研究中&#xff0c;我们经常同时构建多个统计模型&#xff0c;不同的模型放入不同的协变量&#xff0c;从零个的单因素回归分析&#xff0c;到多个协变量的回归模型。…

jenkins结合k8s部署动态slave

1、完成k8s连接 在完成jenkins的部署后现安装kubernets的插件 如果jenkins 是部署在k8s集群中只需要填写一下 如果是非本集群的部署则需要填写证书等 cat ./config echo ‘certificate-authority-data-value’ | base64 -d > ./ca.crt echo ‘client-certificate-data’ |…

MySQL 排序,分组,Limit的优化策略

目录 1. MySQL 中的两种排序方式 2. 排序优化策略 2.1 对排序字段添加索引 2.2 可以和WHERT字段创建联合索引 2.3 优化 FilerSort 排序方式 3. 分组优化策略 3.1 能WHERE不HAVING 3.2 减少ORDER BY&#xff0c;GROUP BY&#xff0c;DISTINCT 3.3 遵照最左前缀法则 4.…

python 之 正则表达式模块re

文章目录 findall例子&#xff1a;特点和注意事项&#xff1a; match示例&#xff1a;match 对象的方法和属性&#xff1a;注意事项&#xff1a; search示例&#xff1a;match 对象的方法和属性&#xff1a;注意事项&#xff1a; split示例&#xff1a;参数说明&#xff1a;注意…

民宿酒店服务预约小程序的作用

民宿往往是旅游者们前往某个城市感受风情常住的地方&#xff0c;也因此在景区或特定地方&#xff0c;总是不乏大小民宿品牌&#xff0c;但除了市场高需求外&#xff0c;商家们所遇的痛点也不少&#xff1a; 1、获客引流难 民宿生意虽然需求量高&#xff0c;但各家品牌众多&am…

Unity meta的一些常见属性

Unity会项目文件夹中的每个文件分配一个同名后缀为.meta的文件。 我们可以将meta文件理解不同文件之间的桥梁&#xff0c;通过它引擎可以管理不同文件之间的依赖关系。 使用TXT文本文件打开之后&#xff0c;大致属性如下&#xff1a; 其中常用的属性有guid、 assetBundleName以…