首页 > 解决方案 > RecyclerView 适配器 OnClickListener 崩溃:从 Activity 上下文外部调用 startActivity() 需要 FLAG_ACTIVITY_NEW_TASK 标志

问题描述

我是 Android Studio 的新手,并通过我的一个项目开始学习。我意识到这个问题在过去几年中被问了很多次,但我已经阅读了其中的大部分内容并且无法修复我的代码。

这个想法是从在线 JSON 文件中解析一个列表,并制作可点击的卡片,将用户带到相关链接。

我设法将卡片显示在屏幕上,但在点击卡片的那一刻应用程序崩溃了。我也(认为我)明白,当我使用辅助布局 xml 并且对象位于错误的位置时,可能会出现此问题。

我的 MainActivity.java :(您可能会看到一些变量名离题,我正在使用模板来运行我的第一个项目)


package net.smallacademy.songslist;

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import android.os.Bundle;
import android.util.Log;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonArrayRequest;
import com.android.volley.toolbox.Volley;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    RecyclerView recyclerView;
    List<Song> songs;
    private static String JSON_URL = "JSON LINK";
    Adapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        recyclerView = findViewById(R.id.songsList);
        songs = new ArrayList<>();
        extractSongs();
    }

    private void extractSongs() {
        RequestQueue queue = Volley.newRequestQueue(this);
        JsonArrayRequest jsonArrayRequest = new JsonArrayRequest(Request.Method.GET, JSON_URL, null, new Response.Listener<JSONArray>() {
            @Override
            public void onResponse(JSONArray response) {
                for (int i = 0; i < response.length(); i++) {
                    try {
                        JSONObject songObject = response.getJSONObject(i);

                        Song song = new Song();
                        song.setTitle(songObject.getString("title").toString());
                        song.setLink(songObject.getString("link"));
                        song.setJournal(songObject.getString("journal".toString()));
                        song.setDate(songObject.getString("date"));
                        songs.add(song);

                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                }

                recyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
                adapter = new Adapter(getApplicationContext(),songs);
                recyclerView.setAdapter(adapter);
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Log.d("tag", "onErrorResponse: " + error.getMessage());
            }
        });

        queue.add(jsonArrayRequest);

    }
}

我的适配器.java:

package net.smallacademy.songslist;

import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import com.android.volley.AuthFailureError;

import org.w3c.dom.Text;

import java.util.List;

public class Adapter extends RecyclerView.Adapter<Adapter.ViewHolder> {
    LayoutInflater inflater;
    List<Song> songs;

    Context context;

    public Adapter(Context ctx, List<Song> songs){
        this.inflater = LayoutInflater.from(ctx);
        this.songs = songs;

        this.context = ctx;

    }


    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = inflater.inflate(R.layout.custom_list_layout,parent,false);
        return new ViewHolder(view);

    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, final int position) {
        // bind the data
        holder.artTitle.setText(songs.get(position).getTitle());
        holder.artJournal.setText(songs.get(position).getJournal());
        holder.artDate.setText(songs.get(position).getDate());

    }

    @Override
    public int getItemCount() {
        return songs.size();
    }

    public  class ViewHolder extends  RecyclerView.ViewHolder{
        TextView artTitle,artJournal,artDate,artLink;
        Button buttonVisit;
        public ViewHolder(@NonNull View itemView) {
            super(itemView);

            artTitle = itemView.findViewById(R.id.artTitle);
            artJournal = itemView.findViewById(R.id.artJournal);
            artDate = itemView.findViewById(R.id.artDate);

            // handle onClick

            artTitle.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.google.com"));
                    context.startActivity(intent);
                }
            });

        }
    }
}

我的activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/songsList"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

和我的自定义布局 xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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="wrap_content">

    <androidx.cardview.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginBottom="8dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <TextView
                android:id="@+id/artDate"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="DATE"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <TextView
                android:id="@+id/artTitle"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginStart="16dp"
                android:layout_marginTop="8dp"
                android:text="ARTICLE TITLE"
                android:textSize="18sp"
                android:textStyle="bold"
                app:layout_constraintTop_toBottomOf="@+id/artDate" />

            <TextView
                android:id="@+id/artJournal"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginStart="16dp"
                android:text="Journal Name"
                app:layout_constraintTop_toBottomOf="@+id/artTitle" />

        </androidx.constraintlayout.widget.ConstraintLayout>
    </androidx.cardview.widget.CardView>

</androidx.constraintlayout.widget.ConstraintLayout>

和堆栈跟踪:

    android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity  context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
        at android.app.ContextImpl.startActivity(ContextImpl.java:952)
        at android.app.ContextImpl.startActivity(ContextImpl.java:928)
        at android.content.ContextWrapper.startActivity(ContextWrapper.java:383)
        at net.smallacademy.songslist.Adapter$ViewHolder$1.onClick(Adapter.java:81)
        at android.view.View.performClick(View.java:7125)
        at android.view.View.performClickInternal(View.java:7102)
        at android.view.View.access$3500(View.java:801)
        at android.view.View$PerformClick.run(View.java:27336)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7356)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

我将 www.google.com 作为占位符链接。我需要从 JSON 文件中获取每个关联的链接。

如果你能指出我正确的方向,我会很高兴。

标签: javaandroidandroid-intentandroid-activityandroid-recyclerview

解决方案


你可以通过传递标志来解决这个问题,但是当涉及到你的后台堆栈和多任务处理时,这会产生不希望的副作用。问题的实际根本原因是您在getApplicationContext()应该传递时使用传递给适配器Activity

adapter = new Adapter(getApplicationContext(),songs);

您应该将上面的行替换为:

adapter = new Adapter(MainActivity.this,songs);

推荐阅读