无法在 API 级别 29 上的 wordpress 应用程序的 webview 中完全加载 wordpress post 内容。但它在 API 级别 26 上工作正常

Not able to load wordpress post content fully in webview in wordpress app on API Level 29. But it works fine on API Level 26

我正在开发 WordPress 应用程序,它从 Wordpress 站点获取 post 并将其加载到 webview 中。我正在使用 "WP REST API"(在 JSON 中获取 post 数据)和改造库。在 API 26 应用程序运行良好 WordPress post 在迁移到 androidx API 29 后完全加载 post 内容在一些 post & 在中仅加载 1-3 行另一个 post 10 行。这可能是什么原因?,内容没有完全加载。迁移到 androidX 后 post 未加载我收到错误 ERR_CLEARTEXT_NOT_PERMITTED 所以我在 post 加载但未完全加载后添加 android:usesCleartextTraffic="true"

这是我的 activity post 我选择的 Wordpress 博客 post 加载的地方

activity_post_details.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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"
    android:background="@color/white"
    android:fitsSystemWindows="true">

    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/app_bar_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:fitsSystemWindows="true"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <com.google.android.material.appbar.CollapsingToolbarLayout
            android:id="@+id/collapsing_toolbar"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fitsSystemWindows="true"
            app:expandedTitleMarginEnd="64dp"
            app:expandedTitleMarginStart="48dp"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <ImageView
                android:id="@+id/post_img"
                android:layout_width="match_parent"
                android:layout_height="@dimen/margin_250dp"
                android:fitsSystemWindows="true"
                android:scaleType="centerCrop"
                android:src="@color/white"
                app:layout_collapseMode="parallax" />

            <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="@drawable/bg_gradient_toolbar"
                app:layout_collapseMode="pin"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light">

                <RelativeLayout
                    android:layout_width="match_parent"
                    android:layout_height="match_parent">

                    <ImageButton
                        android:id="@+id/imgBtnShare"
                        android:layout_width="30dp"
                        android:layout_height="30dp"
                        android:layout_alignParentRight="true"
                        android:layout_centerVertical="true"
                        android:layout_marginRight="10dp"
                        android:background="?attr/selectableItemBackgroundBorderless"
                        android:padding="5dp"
                        android:scaleType="centerInside"
                        android:src="@drawable/ic_share_white" />

                </RelativeLayout>
            </androidx.appcompat.widget.Toolbar>
        </com.google.android.material.appbar.CollapsingToolbarLayout>

    </com.google.android.material.appbar.AppBarLayout>
    <androidx.core.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <include layout="@layout/content_post_details" />
            <include layout="@layout/content_comments" />
        </LinearLayout>
    </androidx.core.widget.NestedScrollView>

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/fab_new_comment"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/activity_vertical_margin"
        android:src="@drawable/ic_fab"
        android:visibility="gone" />
    <include layout="@layout/view_common_loader" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

这是我的内容布局post content_post_details.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/lyt_post_details"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white"
    android:visibility="gone">

    <LinearLayout
        android:id="@+id/li_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="@dimen/margin_8dp"
        android:layout_marginTop="@dimen/margin_8dp"
        android:orientation="horizontal"
        android:weightSum="1">
        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="@dimen/margin_20dp"
            android:layout_weight="0.5"
            android:gravity="center_vertical"
            android:padding="@dimen/margin_8dp">
            <ImageView
                android:id="@+id/author_image"
                android:layout_width="@dimen/margin_30dp"
                android:layout_height="@dimen/margin_30dp"
                android:background="@drawable/ic_author" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="@dimen/margin_20dp"
            android:layout_weight="0.5"
            android:gravity="center_vertical"
            android:padding="@dimen/margin_8dp">

            <ImageView
                android:id="@+id/post_date_image"
                android:layout_width="@dimen/margin_30dp"
                android:layout_height="@dimen/margin_30dp"
                android:layout_marginLeft="@dimen/margin_8dp"
                android:layout_toRightOf="@id/post_author"
                android:background="@drawable/ic_calendar" />

        </LinearLayout>

    </LinearLayout>

    <View
        android:id="@+id/viewDivider"
        android:layout_width="match_parent"
        android:layout_height="0.7dp"
        android:layout_below="@id/li_layout"
        android:layout_marginLeft="@dimen/margin_15dp"
        android:layout_marginRight="@dimen/margin_15dp"
        android:background="@color/toolbar_boarder" />

    <TextView
        android:id="@+id/title_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/viewDivider"
        android:layout_margin="@dimen/margin_8dp"
        android:textColor="@color/black"
        android:textSize="18sp"
        android:textStyle="bold"
        tools:text="This is sample text do yoy know that it supports multi-line" />

    <WebView
        android:id="@+id/web_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/title_text"
        android:layout_margin="@dimen/margin_8dp"
        android:paddingBottom="@dimen/margin_8dp" />

</RelativeLayout>

这是我的 java 文件,它处理 Post 详细信息。

PostdetailsActivity.java

package ak.wp.meto.activity;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import androidx.fragment.app.FragmentManager;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.text.Html;
import android.view.MenuItem;
import android.view.View;
import android.webkit.WebView;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import ak.wp.meto.R;
import ak.wp.meto.adapters.CommentsAdapter;
import ak.wp.meto.api.http.ApiUtils;
import ak.wp.meto.api.models.posts.post.CommentsAndReplies;
import ak.wp.meto.api.models.posts.post.PostDetails;
import ak.wp.meto.api.params.HttpParams;
import ak.wp.meto.data.constant.AppConstant;
import ak.wp.meto.data.sqlite.FavouriteDbController;
import ak.wp.meto.fragment.WriteACommentFragment;
import ak.wp.meto.listeners.ListItemClickListener;
import ak.wp.meto.models.FavouriteModel;
import ak.wp.meto.utility.ActivityUtils;
import ak.wp.meto.utility.AppUtils;
import ak.wp.meto.utility.TtsEngine;
import ak.wp.meto.webengine.WebEngine;
import ak.wp.meto.webengine.WebListener;
import java.util.ArrayList;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class PostDetailsActivity extends BaseActivity implements WriteACommentFragment.OnCompleteListener {
    // Variables
    private Activity mActivity;
    private Context mContext;
    // init views
    private RelativeLayout lytPostDetailsView, lytCommentDetails;
    private int clickedPostId;
    private ImageView imgPost;
    private TextView tvPostTitle, tvPostAuthor, tvPostDate, tvCommnentList;
    private WebView webView;
    private FloatingActionButton fabWriteAComment;
    private ImageButton imgBtnSpeaker, imgBtnFav, imgBtnShare;
    private PostDetails model = null;
    // Favourites view
    private List<FavouriteModel> favouriteList;
    private FavouriteDbController favouriteDbController;
    private boolean isFavourite = false;
    // Comments view
    private List<CommentsAndReplies> commentList;
    private List<CommentsAndReplies> zeroParentComments;
    List<CommentsAndReplies> onlyThreeComments;
    private int mPerPage = 5;
    private RecyclerView rvComments;
    private CommentsAdapter commentsAdapter = null;
    // Text to speech
    private TtsEngine ttsEngine;
    private boolean isTtsPlaying = false;
    private String ttsText;    
    private WebEngine webEngine;    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initVar();
        initView();
        initFunctionality();
        initListener();
    }

    private void initVar() {
        mActivity = PostDetailsActivity.this;
        mContext = mActivity.getApplicationContext();
            favouriteList = new ArrayList<>();
            commentList = new ArrayList<>();
        zeroParentComments = new ArrayList<>();
        onlyThreeComments = new ArrayList<>();

        Intent intent = getIntent();
        if (intent != null) {
            clickedPostId = getIntent().getIntExtra(AppConstant.BUNDLE_KEY_POST_ID, 0);
        }
    }

    private void initView() {
        setContentView(R.layout.activity_post_details);
        lytPostDetailsView = (RelativeLayout) findViewById(R.id.lyt_post_details);
        lytCommentDetails = (RelativeLayout) findViewById(R.id.lyt_comment_list);    
        imgPost = (ImageView) findViewById(R.id.post_img);
        tvPostTitle = (TextView) findViewById(R.id.title_text);
        tvPostAuthor = (TextView) findViewById(R.id.post_author);
        tvPostDate = (TextView) findViewById(R.id.date_text);
        imgBtnSpeaker = (ImageButton) findViewById(R.id.imgBtnSpeaker);
        imgBtnFav = (ImageButton) findViewById(R.id.imgBtnFavourite);
        imgBtnShare = (ImageButton) findViewById(R.id.imgBtnShare);

        initWebEngine();

        tvCommnentList = (TextView) findViewById(R.id.comment_count);
        fabWriteAComment = (FloatingActionButton) findViewById(R.id.fab_new_comment);

        rvComments = (RecyclerView) findViewById(R.id.rvComments);
        rvComments.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));    
        initLoader();    
        initToolbar();
        enableBackButton();    
    }

    public void initWebEngine() {    
        webView = (WebView) findViewById(R.id.web_view);    
        webEngine = new WebEngine(webView, mActivity);
        webEngine.initWebView();        
        webEngine.initListeners(new WebListener() {
            @Override
            public void onStart() {
                initLoader();
            }    
            @Override
            public void onLoaded() {
                hideLoader();
            }

            @Override
            public void onProgress(int progress) {
            }

            @Override
            public void onNetworkError() {
                showEmptyView();
            }

            @Override
            public void onPageTitle(String title) {
            }
        });
    }

    private void initFunctionality() {    
        favouriteDbController = new FavouriteDbController(mContext);
        favouriteList.addAll(favouriteDbController.getAllData());
        for (int i = 0; i < favouriteList.size(); i++) {
            if (favouriteList.get(i).getPostId() == clickedPostId) {
                isFavourite = true;
                break;
            }
        }    
        ttsEngine = new TtsEngine(mActivity);    
        commentsAdapter = new CommentsAdapter(mActivity, (ArrayList) commentList, (ArrayList) onlyThreeComments);
        rvComments.setAdapter(commentsAdapter);    
        showLoader();
        loadPostDetails();    
    }

    public void setFavImage() {
        if (isFavourite) {
            imgBtnFav.setImageDrawable(ContextCompat.getDrawable(mActivity, R.drawable.ic_book));
        } else {
            imgBtnFav.setImageDrawable(ContextCompat.getDrawable(mActivity, R.drawable.ic_un_book));
        }
    }

    public void initListener() {

        imgBtnSpeaker.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (model != null) {
                    toggleTtsPlay();
                }
            }
        });

        imgBtnFav.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (model != null) {
                    isFavourite = !isFavourite;
                    if (isFavourite) {
                        String imgUrl = null;
                        if (model.getEmbedded().getWpFeaturedMedias().size() >= 1) {
                            imgUrl = model.getEmbedded().getWpFeaturedMedias().get(0).getMediaDetails().getSizes().getFullSize().getSourceUrl();
                        }
                        favouriteDbController.insertData(
                                model.getID().intValue(),
                                imgUrl,
                                model.getTitle().getRendered(),
                                model.getOldDate(),
                                model.getEmbedded().getWpTerms().get(0).get(0).getName()
                        );
                    } else {
                        favouriteDbController.deleteFav(clickedPostId);
                    }
                    setFavImage();
                }
            }
        });

        imgBtnShare.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (model != null) {
                    Intent sendIntent = new Intent();
                    sendIntent.setAction(Intent.ACTION_SEND);
                    sendIntent.putExtra(Intent.EXTRA_TEXT, model.getPageUrl());
                    sendIntent.setType("text/plain");
                    startActivity(Intent.createChooser(sendIntent, getResources().getText(R.string.send_to)));
                }
            }
        });

        commentsAdapter.setItemClickListener(new ListItemClickListener() {
            @Override
            public void onItemClick(int position, View view) {
                int id = view.getId();
                CommentsAndReplies clickedComment = zeroParentComments.get(position);
                switch (id) {
                    case R.id.list_item:
                        ActivityUtils.getInstance().invokeCommentDetails(mActivity, CommentDetailsActivity.class, (ArrayList) commentList, clickedPostId, clickedComment, false, false);
                        break;
                    case R.id.reply_text:
                        ActivityUtils.getInstance().invokeCommentDetails(mActivity, CommentDetailsActivity.class, (ArrayList) commentList, clickedPostId, clickedComment, true, false);
                        break;
                    default:
                        break;
                }
            }
        });

        tvCommnentList.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                ActivityUtils.getInstance().invokeCommentList(mActivity,
                        CommentListActivity.class,
                        (ArrayList) commentList,
                        (ArrayList) zeroParentComments,
                        clickedPostId,
                        false);
            }
        });

        fabWriteAComment.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                FragmentManager manager = getSupportFragmentManager();
                WriteACommentFragment dialog = WriteACommentFragment.newInstance(clickedPostId, AppConstant.THIS_IS_COMMENT);
                dialog.show(manager, AppConstant.BUNDLE_KEY_DIALOG_FRAGMENT);
            }
        });
    }

    public void loadPostDetails() {
        ApiUtils.getApiInterface().getPostDetails(clickedPostId).enqueue(new Callback<PostDetails>() {
            @Override
            public void onResponse(Call<PostDetails> call, Response<PostDetails> response) {
                if (response.isSuccessful()) {
                    // bind data
                    model = response.body();
                    PostDetails m = model;
                    // visible parent view
                    lytPostDetailsView.setVisibility(View.VISIBLE);
                    // visible comments button
                    fabWriteAComment.setVisibility(View.VISIBLE);
                    loadCommentsAndReplies(model.getLinks().getRepliesList().get(0).getHref());    
                    setFavImage();        
                    tvPostTitle.setText(Html.fromHtml(model.getTitle().getRendered()));    
                    String imgUrl = null;
                    if (model.getEmbedded().getWpFeaturedMedias().size() > 0) {
                        if (model.getEmbedded().getWpFeaturedMedias().get(0).getMediaDetails() != null) {
                            if (model.getEmbedded().getWpFeaturedMedias().get(0).getMediaDetails().getSizes().getFullSize().getSourceUrl() != null) {
                                imgUrl = model.getEmbedded().getWpFeaturedMedias().get(0).getMediaDetails().getSizes().getFullSize().getSourceUrl();
                            }
                        }
                    }

                    if (imgUrl != null) {
                        Glide.with(getApplicationContext())
                                .load(imgUrl)
                                .into(imgPost);
                    }

                    String author = null;
                    if (model.getEmbedded().getAuthors().size() >= 1) {
                        author = model.getEmbedded().getAuthors().get(0).getName();
                    }

                    if (author == null) {
                        author = getString(R.string.admin);
                    }
                    tvPostAuthor.setText(Html.fromHtml(author));

                    String oldDate = model.getOldDate();
                    String newDate = AppUtils.getFormattedDate(oldDate);

                    if (newDate != null) {
                        tvPostDate.setText(Html.fromHtml(newDate));
                    }

                    String contentText = model.getContent().getRendered();

                    ttsText = new StringBuilder(Html.fromHtml(model.getTitle().getRendered())).append(AppConstant.DOT).append(Html.fromHtml(model.getContent().getRendered())).toString();    
                    //webView.loadData(contentText, "text/html", "UTF-8");    
                    contentText = new StringBuilder().append(AppConstant.CSS_PROPERTIES).append(contentText).toString();
                    webEngine.loadHtml(contentText);

                } else {
                    showEmptyView();
                }
            }

            @Override
            public void onFailure(Call<PostDetails> call, Throwable t) {
                t.printStackTrace();
                // hide common loader
                hideLoader();
                // show empty view
                showEmptyView();
            }
        });
    }

    public void loadCommentsAndReplies(final String commentsAndRepliesLink) {

        ApiUtils.getApiInterface().getCommentsAndReplies(commentsAndRepliesLink, mPerPage).enqueue(new Callback<List<CommentsAndReplies>>() {
            @Override
            public void onResponse(Call<List<CommentsAndReplies>> call, Response<List<CommentsAndReplies>> response) {
                if (response.isSuccessful()) {    
                    int totalItems = Integer.parseInt(response.headers().get(HttpParams.HEADER_TOTAL_ITEM));
                    int totalPages = Integer.parseInt(response.headers().get(HttpParams.HEADER_TOTAL_PAGE));    

                    if (totalPages > 1) {
                        mPerPage = mPerPage * totalPages;
                        loadCommentsAndReplies(commentsAndRepliesLink);

                    } else {
                        if (!commentList.isEmpty() || !zeroParentComments.isEmpty() || !onlyThreeComments.isEmpty()) {
                            commentList.clear();
                            zeroParentComments.clear();
                            onlyThreeComments.clear();
                        }

                        commentList.addAll(response.body());    
                        lytCommentDetails.setVisibility(View.VISIBLE);    
                        if (commentList.size() > 0) {
                            for (CommentsAndReplies commentsAndReplies : commentList) {
                                if (commentsAndReplies.getParent().intValue() == 0) {
                                    zeroParentComments.add(commentsAndReplies);
                                }
                            }

                            if (zeroParentComments.size() >= 3) {
                                for (int i = 0; i < 3; i++) {
                                    onlyThreeComments.add(zeroParentComments.get(i));
                                }
                            } else {
                                for (CommentsAndReplies commentsAndReplies : zeroParentComments) {
                                    onlyThreeComments.add(commentsAndReplies);
                                }
                            }
                            commentsAdapter.notifyDataSetChanged();
                            tvCommnentList.setText(String.format(getString(R.string.all_comment), commentList.size()));
                            tvCommnentList.setClickable(true);

                        } else {
                            tvCommnentList.setClickable(false);
                        }
                    }

                }
            }

            @Override
            public void onFailure(Call<List<CommentsAndReplies>> call, Throwable t) {
                showEmptyView();
                t.printStackTrace();
            }
        });
    }

    @Override
    public void onComplete(Boolean isCommentSuccessful, CommentsAndReplies commentsAndReplies) {
        if (isCommentSuccessful) {
            loadCommentsAndReplies(model.getLinks().getRepliesList().get(0).getHref());
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode != RESULT_OK) {
            return;
        }

        if (requestCode == AppConstant.REQUEST_CODE_COMMENT) {
            if (data == null) {
                return;
            }
            boolean isCommentSuccessful = CommentListActivity.wasCommentSuccessful(data);
            if (isCommentSuccessful) {
                loadCommentsAndReplies(model.getLinks().getRepliesList().get(0).getHref());
            }
        }
    }
}

还找到build.gradle build.gradle(应用级别)

apply plugin: 'com.android.application'
android {
    lintOptions{
        checkReleaseBuilds false
        abortOnError false
    }
    compileSdkVersion 29
    buildToolsVersion "28.0.3"
    defaultConfig {
        applicationId "ak.wp.meto"
        minSdkVersion 17
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}
dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
compile 'androidx.appcompat:appcompat:1.0.0'
compile 'com.google.android.material:material:1.0.0'
compile 'androidx.constraintlayout:constraintlayout:1.1.3'
compile 'androidx.cardview:cardview:1.0.0'
//retrofit, gson
compile 'com.squareup.retrofit2:converter-scalars:2.1.0'
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
//glide
compile 'com.github.bumptech.glide:glide:4.3.1'
//circular imageview
compile 'de.hdodenhof:circleimageview:2.1.0'
}
apply plugin: 'com.google.gms.google-services'

我对这个问题的临时解决方案是从我的站点中删除了 postrophe、分号、引号和连字符。 现在一切正常 post 正在完全加载。 在 JSON 中,数据连字符被编码为 – 在 webview 中,在剩余的行消失后仅显示“&”。所以我设法删除了特殊字符。

用那个特殊字符替换你的字符串String contentText = model.getContent().replace("&#8217;","'");